mirror of https://github.com/AMT-Cheif/drift.git
More documentation for transactions
This commit is contained in:
parent
e36470211c
commit
6d45805035
|
@ -4,6 +4,7 @@ import 'package:moor/moor.dart';
|
||||||
|
|
||||||
/// Subclasses represent a table in a database generated by moor.
|
/// Subclasses represent a table in a database generated by moor.
|
||||||
abstract class Table {
|
abstract class Table {
|
||||||
|
/// Defines a table to be used with moor.
|
||||||
const Table();
|
const Table();
|
||||||
|
|
||||||
/// The sql table name to be used. By default, moor will use the snake_case
|
/// The sql table name to be used. By default, moor will use the snake_case
|
||||||
|
|
|
@ -61,6 +61,20 @@ abstract class DatabaseConnectionUser {
|
||||||
Stream<T> createStream<T>(QueryStreamFetcher<T> stmt) =>
|
Stream<T> createStream<T>(QueryStreamFetcher<T> stmt) =>
|
||||||
streamQueries.registerStream(stmt);
|
streamQueries.registerStream(stmt);
|
||||||
|
|
||||||
|
/// Creates a copy of the table with an alias so that it can be used in the
|
||||||
|
/// same query more than once.
|
||||||
|
///
|
||||||
|
/// Example which uses the same table (here: points) more than once to
|
||||||
|
/// differentiate between the start and end point of a route:
|
||||||
|
/// ```
|
||||||
|
/// var source = alias(points, 'source');
|
||||||
|
/// var destination = alias(points, 'dest');
|
||||||
|
///
|
||||||
|
/// select(routes).join([
|
||||||
|
/// innerJoin(source, routes.startPoint.equalsExp(source.id)),
|
||||||
|
/// innerJoin(destination, routes.startPoint.equalsExp(destination.id)),
|
||||||
|
/// ]);
|
||||||
|
/// ```
|
||||||
T alias<T, D>(TableInfo<T, D> table, String alias) {
|
T alias<T, D>(TableInfo<T, D> table, String alias) {
|
||||||
return table.createAlias(alias).asDslTable;
|
return table.createAlias(alias).asDslTable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ import 'package:moor/src/runtime/components/component.dart';
|
||||||
import 'package:moor/src/runtime/statements/query.dart';
|
import 'package:moor/src/runtime/statements/query.dart';
|
||||||
import 'package:moor/src/runtime/structure/table_info.dart';
|
import 'package:moor/src/runtime/structure/table_info.dart';
|
||||||
|
|
||||||
class DeleteStatement<T, D> extends Query<T, D> {
|
class DeleteStatement<T, D> extends Query<T, D>
|
||||||
|
with SingleTableQueryMixin<T, D> {
|
||||||
/// This constructor should be called by [GeneratedDatabase.delete] for you.
|
/// This constructor should be called by [GeneratedDatabase.delete] for you.
|
||||||
DeleteStatement(QueryEngine database, TableInfo<T, D> table)
|
DeleteStatement(QueryEngine database, TableInfo<T, D> table)
|
||||||
: super(database, table);
|
: super(database, table);
|
||||||
|
|
|
@ -31,16 +31,6 @@ abstract class Query<Table, DataClass> {
|
||||||
@visibleForOverriding
|
@visibleForOverriding
|
||||||
void writeStartPart(GenerationContext ctx);
|
void writeStartPart(GenerationContext ctx);
|
||||||
|
|
||||||
void where(Expression<bool, BoolType> filter(Table tbl)) {
|
|
||||||
final predicate = filter(table.asDslTable);
|
|
||||||
|
|
||||||
if (whereExpr == null) {
|
|
||||||
whereExpr = Where(predicate);
|
|
||||||
} else {
|
|
||||||
whereExpr = Where(and(whereExpr.predicate, predicate));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs the query that can then be sent to the database executor.
|
/// Constructs the query that can then be sent to the database executor.
|
||||||
@protected
|
@protected
|
||||||
GenerationContext constructQuery() {
|
GenerationContext constructQuery() {
|
||||||
|
@ -75,6 +65,18 @@ abstract class Query<Table, DataClass> {
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin SingleTableQueryMixin<Table, DataClass> on Query<Table, DataClass> {
|
||||||
|
void where(Expression<bool, BoolType> filter(Table tbl)) {
|
||||||
|
final predicate = filter(table.asDslTable);
|
||||||
|
|
||||||
|
if (whereExpr == null) {
|
||||||
|
whereExpr = Where(predicate);
|
||||||
|
} else {
|
||||||
|
whereExpr = Where(and(whereExpr.predicate, predicate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Applies a [where] statement so that the row with the same primary key as
|
/// Applies a [where] statement so that the row with the same primary key as
|
||||||
/// [d] will be matched.
|
/// [d] will be matched.
|
||||||
|
@ -116,3 +118,12 @@ abstract class Query<Table, DataClass> {
|
||||||
whereExpr = Where(predicate);
|
whereExpr = Where(predicate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mixin LimitContainerMixin<T, D> on Query<T, D> {
|
||||||
|
/// Limits the amount of rows returned by capping them at [limit]. If [offset]
|
||||||
|
/// is provided as well, the first [offset] rows will be skipped and not
|
||||||
|
/// included in the result.
|
||||||
|
void limit(int limit, {int offset}) {
|
||||||
|
limitExpr = Limit(limit, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:meta/meta.dart';
|
||||||
import 'package:moor/moor.dart';
|
import 'package:moor/moor.dart';
|
||||||
import 'package:moor/src/runtime/components/component.dart';
|
import 'package:moor/src/runtime/components/component.dart';
|
||||||
import 'package:moor/src/runtime/components/join.dart';
|
import 'package:moor/src/runtime/components/join.dart';
|
||||||
import 'package:moor/src/runtime/components/limit.dart';
|
|
||||||
import 'package:moor/src/runtime/database.dart';
|
import 'package:moor/src/runtime/database.dart';
|
||||||
import 'package:moor/src/runtime/executor/stream_queries.dart';
|
import 'package:moor/src/runtime/executor/stream_queries.dart';
|
||||||
import 'package:moor/src/runtime/statements/query.dart';
|
import 'package:moor/src/runtime/statements/query.dart';
|
||||||
|
@ -12,7 +11,8 @@ import 'package:moor/src/runtime/structure/table_info.dart';
|
||||||
|
|
||||||
typedef OrderingTerm OrderClauseGenerator<T>(T tbl);
|
typedef OrderingTerm OrderClauseGenerator<T>(T tbl);
|
||||||
|
|
||||||
class JoinedSelectStatement<FirstT, FirstD> extends Query<FirstT, FirstD> {
|
class JoinedSelectStatement<FirstT, FirstD> extends Query<FirstT, FirstD>
|
||||||
|
with LimitContainerMixin {
|
||||||
JoinedSelectStatement(
|
JoinedSelectStatement(
|
||||||
QueryEngine database, TableInfo<FirstT, FirstD> table, this._joins)
|
QueryEngine database, TableInfo<FirstT, FirstD> table, this._joins)
|
||||||
: super(database, table);
|
: super(database, table);
|
||||||
|
@ -57,6 +57,7 @@ class JoinedSelectStatement<FirstT, FirstD> extends Query<FirstT, FirstD> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes this statement and returns the result.
|
||||||
Future<List<TypedResult>> get() async {
|
Future<List<TypedResult>> get() async {
|
||||||
final ctx = constructQuery();
|
final ctx = constructQuery();
|
||||||
final results = await ctx.database.executor.doWhenOpened((e) async {
|
final results = await ctx.database.executor.doWhenOpened((e) async {
|
||||||
|
@ -81,17 +82,11 @@ class JoinedSelectStatement<FirstT, FirstD> extends Query<FirstT, FirstD> {
|
||||||
return TypedResult(map);
|
return TypedResult(map);
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Limits the amount of rows returned by capping them at [limit]. If [offset]
|
|
||||||
/// is provided as well, the first [offset] rows will be skipped and not
|
|
||||||
/// included in the result.
|
|
||||||
void limit(int limit, {int offset}) {
|
|
||||||
limitExpr = Limit(limit, offset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A select statement that doesn't use joins
|
/// A select statement that doesn't use joins
|
||||||
class SimpleSelectStatement<T, D> extends Query<T, D> {
|
class SimpleSelectStatement<T, D> extends Query<T, D>
|
||||||
|
with SingleTableQueryMixin<T, D>, LimitContainerMixin<T, D> {
|
||||||
SimpleSelectStatement(QueryEngine database, TableInfo<T, D> table)
|
SimpleSelectStatement(QueryEngine database, TableInfo<T, D> table)
|
||||||
: super(database, table);
|
: super(database, table);
|
||||||
|
|
||||||
|
@ -116,20 +111,46 @@ class SimpleSelectStatement<T, D> extends Query<T, D> {
|
||||||
return results.map(table.map).toList();
|
return results.map(table.map).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a select statement that operates on more than one table by
|
||||||
|
/// applying the given joins.
|
||||||
|
///
|
||||||
|
/// Example from the todolist example which will load the category for each
|
||||||
|
/// item:
|
||||||
|
/// ```
|
||||||
|
/// final results = await select(todos).join([
|
||||||
|
/// leftOuterJoin(categories, categories.id.equalsExp(todos.category))
|
||||||
|
/// ]).get();
|
||||||
|
///
|
||||||
|
/// return results.map((row) {
|
||||||
|
/// final entry = row.readTable(todos);
|
||||||
|
/// final category = row.readTable(categories);
|
||||||
|
/// return EntryWithCategory(entry, category);
|
||||||
|
/// }).toList();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// - [innerJoin], [leftOuterJoin] and [crossJoin], which can be used to
|
||||||
|
/// construct a [Join].
|
||||||
|
/// - [GeneratedDatabase.alias], which can be used to build statements that
|
||||||
|
/// refer to the same table multiple times.
|
||||||
JoinedSelectStatement join(List<Join> joins) {
|
JoinedSelectStatement join(List<Join> joins) {
|
||||||
return JoinedSelectStatement(database, table, joins);
|
return JoinedSelectStatement(database, table, joins);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Limits the amount of rows returned by capping them at [limit]. If [offset]
|
|
||||||
/// is provided as well, the first [offset] rows will be skipped and not
|
|
||||||
/// included in the result.
|
|
||||||
void limit(int limit, {int offset}) {
|
|
||||||
limitExpr = Limit(limit, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Orders the result by the given clauses. The clauses coming first in the
|
/// Orders the result by the given clauses. The clauses coming first in the
|
||||||
/// list have a higher priority, the later clauses are only considered if the
|
/// list have a higher priority, the later clauses are only considered if the
|
||||||
/// first clause considers two rows to be equal.
|
/// first clause considers two rows to be equal.
|
||||||
|
///
|
||||||
|
/// Example that first displays the users who are awesome and sorts users by
|
||||||
|
/// their id as a secondary criterion:
|
||||||
|
/// ```
|
||||||
|
/// (db.select(db.users)
|
||||||
|
/// ..orderBy([
|
||||||
|
/// (u) => OrderingTerm(expression: u.isAwesome, mode: OrderingMode.desc),
|
||||||
|
/// (u) => OrderingTerm(expression: u.id)
|
||||||
|
/// ]))
|
||||||
|
/// .get()
|
||||||
|
/// ```
|
||||||
void orderBy(List<OrderClauseGenerator<T>> clauses) {
|
void orderBy(List<OrderClauseGenerator<T>> clauses) {
|
||||||
orderByExpr = OrderBy(clauses.map((t) => t(table.asDslTable)).toList());
|
orderByExpr = OrderBy(clauses.map((t) => t(table.asDslTable)).toList());
|
||||||
}
|
}
|
||||||
|
@ -148,15 +169,27 @@ class SimpleSelectStatement<T, D> extends Query<T, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A select statement that is constructed with a raw sql prepared statement
|
||||||
|
/// instead of the high-level moor api.
|
||||||
class CustomSelectStatement {
|
class CustomSelectStatement {
|
||||||
/// Tables this select statement reads from
|
/// Tables this select statement reads from. When turning this select query
|
||||||
|
/// into an auto-updating stream, that stream will emit new items whenever
|
||||||
|
/// any of these tables changes.
|
||||||
final Set<TableInfo> tables;
|
final Set<TableInfo> tables;
|
||||||
|
|
||||||
|
/// The sql query string for this statement.
|
||||||
final String query;
|
final String query;
|
||||||
|
|
||||||
|
/// The variables for the prepared statement, in the order they appear in
|
||||||
|
/// [query]. Variables are denoted using a question mark in the query.
|
||||||
final List<Variable> variables;
|
final List<Variable> variables;
|
||||||
final QueryEngine db;
|
final QueryEngine _db;
|
||||||
|
|
||||||
CustomSelectStatement(this.query, this.variables, this.tables, this.db);
|
/// Constructs a new
|
||||||
|
CustomSelectStatement(this.query, this.variables, this.tables, this._db);
|
||||||
|
|
||||||
|
/// Constructs a fetcher for this query. The fetcher is responsible for
|
||||||
|
/// updating a stream at the right moment.
|
||||||
QueryStreamFetcher<List<QueryRow>> constructFetcher() {
|
QueryStreamFetcher<List<QueryRow>> constructFetcher() {
|
||||||
final args = _mapArgs();
|
final args = _mapArgs();
|
||||||
|
|
||||||
|
@ -167,35 +200,34 @@ class CustomSelectStatement {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes this query and returns the result.
|
||||||
Future<List<QueryRow>> execute() async {
|
Future<List<QueryRow>> execute() async {
|
||||||
return _executeWithMappedArgs(_mapArgs());
|
return _executeWithMappedArgs(_mapArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<dynamic> _mapArgs() {
|
List<dynamic> _mapArgs() {
|
||||||
final ctx = GenerationContext(db);
|
final ctx = GenerationContext(_db);
|
||||||
return variables.map((v) => v.mapToSimpleValue(ctx)).toList();
|
return variables.map((v) => v.mapToSimpleValue(ctx)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<QueryRow>> _executeWithMappedArgs(
|
Future<List<QueryRow>> _executeWithMappedArgs(
|
||||||
List<dynamic> mappedArgs) async {
|
List<dynamic> mappedArgs) async {
|
||||||
final result =
|
final result =
|
||||||
await db.executor.doWhenOpened((e) => e.runSelect(query, mappedArgs));
|
await _db.executor.doWhenOpened((e) => e.runSelect(query, mappedArgs));
|
||||||
|
|
||||||
return result.map((row) => QueryRow(row, db)).toList();
|
return result.map((row) => QueryRow(row, _db)).toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A result row in a [JoinedSelectStatement] that can consist of multiple
|
/// A result row in a [JoinedSelectStatement] that can consist of multiple
|
||||||
/// entities.
|
/// entities.
|
||||||
class TypedResult {
|
class TypedResult {
|
||||||
|
/// Creates the result from the parsed table data.
|
||||||
TypedResult(this._data);
|
TypedResult(this._data);
|
||||||
|
|
||||||
final Map<TableInfo, dynamic> _data;
|
final Map<TableInfo, dynamic> _data;
|
||||||
|
|
||||||
D operator []<T, D>(TableInfo<T, D> table) {
|
/// Reads all data that belongs to the given [table] from this row.
|
||||||
return _data[table] as D;
|
|
||||||
}
|
|
||||||
|
|
||||||
D readTable<T, D>(TableInfo<T, D> table) {
|
D readTable<T, D>(TableInfo<T, D> table) {
|
||||||
return _data[table] as D;
|
return _data[table] as D;
|
||||||
}
|
}
|
||||||
|
@ -203,9 +235,12 @@ class TypedResult {
|
||||||
|
|
||||||
/// For custom select statements, represents a row in the result set.
|
/// For custom select statements, represents a row in the result set.
|
||||||
class QueryRow {
|
class QueryRow {
|
||||||
|
/// The raw data in this row.
|
||||||
final Map<String, dynamic> data;
|
final Map<String, dynamic> data;
|
||||||
final QueryEngine _db;
|
final QueryEngine _db;
|
||||||
|
|
||||||
|
/// Construct a row from the raw data and the query engine that maps the raw
|
||||||
|
/// response to appropriate dart types.
|
||||||
QueryRow(this.data, this._db);
|
QueryRow(this.data, this._db);
|
||||||
|
|
||||||
/// Reads an arbitrary value from the row and maps it to a fitting dart type.
|
/// Reads an arbitrary value from the row and maps it to a fitting dart type.
|
||||||
|
|
|
@ -3,7 +3,8 @@ import 'dart:async';
|
||||||
import 'package:moor/moor.dart';
|
import 'package:moor/moor.dart';
|
||||||
import 'package:moor/src/runtime/components/component.dart';
|
import 'package:moor/src/runtime/components/component.dart';
|
||||||
|
|
||||||
class UpdateStatement<T, D> extends Query<T, D> {
|
class UpdateStatement<T, D> extends Query<T, D>
|
||||||
|
with SingleTableQueryMixin<T, D> {
|
||||||
UpdateStatement(QueryEngine database, TableInfo<T, D> table)
|
UpdateStatement(QueryEngine database, TableInfo<T, D> table)
|
||||||
: super(database, table);
|
: super(database, table);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue