mirror of https://github.com/AMT-Cheif/drift.git
Move base dao/db classes, query engine into separate files
This commit is contained in:
parent
1974307961
commit
22c692c69e
|
@ -14,7 +14,7 @@ export 'package:moor/src/runtime/executor/executor.dart';
|
|||
export 'package:moor/src/runtime/executor/transactions.dart';
|
||||
export 'package:moor/src/runtime/data_verification.dart';
|
||||
export 'package:moor/src/runtime/data_class.dart';
|
||||
export 'package:moor/src/runtime/database.dart';
|
||||
export 'package:moor/src/runtime/api/runtime_api.dart';
|
||||
export 'package:moor/src/runtime/types/sql_types.dart'
|
||||
hide ComparableType, Monoid, FullArithmetic;
|
||||
export 'package:moor/src/runtime/exceptions.dart';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
part of 'database.dart';
|
||||
part of 'runtime_api.dart';
|
||||
|
||||
/// Contains operations to run queries in a batched mode. This can be much more
|
||||
/// efficient when running a lot of similar queries at the same time, making
|
|
@ -0,0 +1,102 @@
|
|||
part of 'runtime_api.dart';
|
||||
|
||||
/// A database connection managed by moor. Contains three components:
|
||||
/// - a [SqlTypeSystem], which is responsible to map between Dart types and
|
||||
/// values understood by the database engine.
|
||||
/// - a [QueryExecutor], which runs sql commands
|
||||
/// - a [StreamQueryStore], which dispatches table changes to listening queries,
|
||||
/// on which the auto-updating queries are based.
|
||||
class DatabaseConnection {
|
||||
/// The type system to use with this database. The type system is responsible
|
||||
/// for mapping Dart objects into sql expressions and vice-versa.
|
||||
final SqlTypeSystem typeSystem;
|
||||
|
||||
/// The executor to use when queries are executed.
|
||||
final QueryExecutor executor;
|
||||
|
||||
/// Manages active streams from select statements.
|
||||
final StreamQueryStore streamQueries;
|
||||
|
||||
/// Constructs a raw database connection from the three components.
|
||||
DatabaseConnection(this.typeSystem, this.executor, this.streamQueries);
|
||||
|
||||
/// Constructs a [DatabaseConnection] from the [QueryExecutor] by using the
|
||||
/// default type system and a new [StreamQueryStore].
|
||||
DatabaseConnection.fromExecutor(this.executor)
|
||||
: typeSystem = SqlTypeSystem.defaultInstance,
|
||||
streamQueries = StreamQueryStore();
|
||||
}
|
||||
|
||||
/// Manages a [DatabaseConnection] to send queries to the database.
|
||||
abstract class DatabaseConnectionUser {
|
||||
/// The database connection used by this [DatabaseConnectionUser].
|
||||
@protected
|
||||
final DatabaseConnection connection;
|
||||
|
||||
/// The type system to use with this database. The type system is responsible
|
||||
/// for mapping Dart objects into sql expressions and vice-versa.
|
||||
SqlTypeSystem get typeSystem => connection.typeSystem;
|
||||
|
||||
/// The executor to use when queries are executed.
|
||||
QueryExecutor get executor => connection.executor;
|
||||
|
||||
/// Manages active streams from select statements.
|
||||
@visibleForTesting
|
||||
@protected
|
||||
StreamQueryStore get streamQueries => connection.streamQueries;
|
||||
|
||||
/// Constructs a database connection user, which is responsible to store query
|
||||
/// streams, wrap the underlying executor and perform type mapping.
|
||||
DatabaseConnectionUser(SqlTypeSystem typeSystem, QueryExecutor executor,
|
||||
{StreamQueryStore streamQueries})
|
||||
: connection = DatabaseConnection(
|
||||
typeSystem, executor, streamQueries ?? StreamQueryStore());
|
||||
|
||||
/// Creates another [DatabaseConnectionUser] by referencing the implementation
|
||||
/// from the [other] user.
|
||||
DatabaseConnectionUser.delegate(DatabaseConnectionUser other,
|
||||
{SqlTypeSystem typeSystem,
|
||||
QueryExecutor executor,
|
||||
StreamQueryStore streamQueries})
|
||||
: connection = DatabaseConnection(
|
||||
typeSystem ?? other.connection.typeSystem,
|
||||
executor ?? other.connection.executor,
|
||||
streamQueries ?? other.connection.streamQueries,
|
||||
);
|
||||
|
||||
/// Constructs a [DatabaseConnectionUser] that will use the provided
|
||||
/// [DatabaseConnection].
|
||||
DatabaseConnectionUser.fromConnection(this.connection);
|
||||
|
||||
/// Marks the tables as updated. This method will be called internally
|
||||
/// whenever a update, delete or insert statement is issued on the database.
|
||||
/// We can then inform all active select-streams on those tables that their
|
||||
/// snapshot might be out-of-date and needs to be fetched again.
|
||||
void markTablesUpdated(Set<TableInfo> tables) {
|
||||
streamQueries.handleTableUpdates(tables);
|
||||
}
|
||||
|
||||
/// Creates and auto-updating stream from the given select statement. This
|
||||
/// method should not be used directly.
|
||||
Stream<T> createStream<T>(QueryStreamFetcher<T> 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 extends Table, D extends DataClass>(
|
||||
TableInfo<T, D> table, String alias) {
|
||||
return table.createAlias(alias).asDslTable;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
part of 'runtime_api.dart';
|
||||
|
||||
/// Class that runs queries to a subset of all available queries in a database.
|
||||
///
|
||||
/// This comes in handy to structure large amounts of database code better: The
|
||||
/// migration logic can live in the main [GeneratedDatabase] class, but code
|
||||
/// can be extracted into [DatabaseAccessor]s outside of that database.
|
||||
/// For details on how to write a dao, see [UseDao].
|
||||
abstract class DatabaseAccessor<T extends GeneratedDatabase>
|
||||
extends DatabaseConnectionUser with QueryEngine {
|
||||
@override
|
||||
final bool topLevel = true;
|
||||
|
||||
/// The main database instance for this dao
|
||||
@protected
|
||||
final T db;
|
||||
|
||||
/// Used internally by moor
|
||||
DatabaseAccessor(this.db) : super.delegate(db);
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
part of 'runtime_api.dart';
|
||||
|
||||
/// A base class for all generated databases.
|
||||
abstract class GeneratedDatabase extends DatabaseConnectionUser
|
||||
with QueryEngine {
|
||||
@override
|
||||
final bool topLevel = true;
|
||||
|
||||
/// Specify the schema version of your database. Whenever you change or add
|
||||
/// tables, you should bump this field and provide a [migration] strategy.
|
||||
int get schemaVersion;
|
||||
|
||||
/// Defines the migration strategy that will determine how to deal with an
|
||||
/// increasing [schemaVersion]. The default value only supports creating the
|
||||
/// database by creating all tables known in this database. When you have
|
||||
/// changes in your schema, you'll need a custom migration strategy to create
|
||||
/// the new tables or change the columns.
|
||||
MigrationStrategy get migration => MigrationStrategy();
|
||||
MigrationStrategy _cachedMigration;
|
||||
MigrationStrategy get _resolvedMigration => _cachedMigration ??= migration;
|
||||
|
||||
/// A list of tables specified in this database.
|
||||
List<TableInfo> get allTables;
|
||||
|
||||
/// A [Type] can't be sent across isolates. Instances of this class shouldn't
|
||||
/// be sent over isolates either, so let's keep a reference to a [Type] that
|
||||
/// definitely prohibits this.
|
||||
// ignore: unused_field
|
||||
final Type _$dontSendThisOverIsolates = Null;
|
||||
|
||||
/// Used by generated code
|
||||
GeneratedDatabase(SqlTypeSystem types, QueryExecutor executor,
|
||||
{StreamQueryStore streamStore})
|
||||
: super(types, executor, streamQueries: streamStore) {
|
||||
executor?.databaseInfo = this;
|
||||
}
|
||||
|
||||
/// Used by generated code to connect to a database that is already open.
|
||||
GeneratedDatabase.connect(DatabaseConnection connection)
|
||||
: super.fromConnection(connection) {
|
||||
connection?.executor?.databaseInfo = this;
|
||||
}
|
||||
|
||||
/// Creates a [Migrator] with the provided query executor. Migrators generate
|
||||
/// sql statements to create or drop tables.
|
||||
///
|
||||
/// This api is mainly used internally in moor, for instance in
|
||||
/// [handleDatabaseCreation] and [handleDatabaseVersionChange]. However, it
|
||||
/// can also be used if you need to create tables manually and outside of a
|
||||
/// [MigrationStrategy]. For almost all use cases, overriding [migration]
|
||||
/// should suffice.
|
||||
@protected
|
||||
Migrator createMigrator([SqlExecutor executor]) {
|
||||
final actualExecutor = executor ?? customStatement;
|
||||
return Migrator(this, actualExecutor);
|
||||
}
|
||||
|
||||
/// Handles database creation by delegating the work to the [migration]
|
||||
/// strategy. This method should not be called by users.
|
||||
Future<void> handleDatabaseCreation({@required SqlExecutor executor}) {
|
||||
final migrator = createMigrator(executor);
|
||||
return _resolvedMigration.onCreate(migrator);
|
||||
}
|
||||
|
||||
/// Handles database updates by delegating the work to the [migration]
|
||||
/// strategy. This method should not be called by users.
|
||||
Future<void> handleDatabaseVersionChange(
|
||||
{@required SqlExecutor executor, int from, int to}) {
|
||||
final migrator = createMigrator(executor);
|
||||
return _resolvedMigration.onUpgrade(migrator, from, to);
|
||||
}
|
||||
|
||||
/// Handles the before opening callback as set in the [migration]. This method
|
||||
/// is used internally by database implementations and should not be called by
|
||||
/// users.
|
||||
Future<void> beforeOpenCallback(
|
||||
QueryExecutor executor, OpeningDetails details) {
|
||||
final migration = _resolvedMigration;
|
||||
|
||||
if (migration.beforeOpen != null) {
|
||||
return _runEngineZoned(
|
||||
BeforeOpenRunner(this, executor),
|
||||
() => migration.beforeOpen(details),
|
||||
);
|
||||
}
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
/// Closes this database and releases associated resources.
|
||||
Future<void> close() async {
|
||||
await executor.close();
|
||||
}
|
||||
}
|
|
@ -1,136 +1,10 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/src/runtime/executor/stream_queries.dart';
|
||||
|
||||
part 'batch.dart';
|
||||
part of 'runtime_api.dart';
|
||||
|
||||
const _zoneRootUserKey = #DatabaseConnectionUser;
|
||||
|
||||
typedef _CustomWriter<T> = Future<T> Function(
|
||||
QueryExecutor e, String sql, List<dynamic> vars);
|
||||
|
||||
/// Class that runs queries to a subset of all available queries in a database.
|
||||
///
|
||||
/// This comes in handy to structure large amounts of database code better: The
|
||||
/// migration logic can live in the main [GeneratedDatabase] class, but code
|
||||
/// can be extracted into [DatabaseAccessor]s outside of that database.
|
||||
/// For details on how to write a dao, see [UseDao].
|
||||
abstract class DatabaseAccessor<T extends GeneratedDatabase>
|
||||
extends DatabaseConnectionUser with QueryEngine {
|
||||
@override
|
||||
final bool topLevel = true;
|
||||
|
||||
/// The main database instance for this dao
|
||||
@protected
|
||||
final T db;
|
||||
|
||||
/// Used internally by moor
|
||||
DatabaseAccessor(this.db) : super.delegate(db);
|
||||
}
|
||||
|
||||
/// A database connection managed by moor. Contains three components:
|
||||
/// - a [SqlTypeSystem], which is responsible to map between Dart types and
|
||||
/// values understood by the database engine.
|
||||
/// - a [QueryExecutor], which runs sql commands
|
||||
/// - a [StreamQueryStore], which dispatches table changes to listening queries,
|
||||
/// on which the auto-updating queries are based.
|
||||
class DatabaseConnection {
|
||||
/// The type system to use with this database. The type system is responsible
|
||||
/// for mapping Dart objects into sql expressions and vice-versa.
|
||||
final SqlTypeSystem typeSystem;
|
||||
|
||||
/// The executor to use when queries are executed.
|
||||
final QueryExecutor executor;
|
||||
|
||||
/// Manages active streams from select statements.
|
||||
final StreamQueryStore streamQueries;
|
||||
|
||||
/// Constructs a raw database connection from the three components.
|
||||
DatabaseConnection(this.typeSystem, this.executor, this.streamQueries);
|
||||
|
||||
/// Constructs a [DatabaseConnection] from the [QueryExecutor] by using the
|
||||
/// default type system and a new [StreamQueryStore].
|
||||
DatabaseConnection.fromExecutor(this.executor)
|
||||
: typeSystem = SqlTypeSystem.defaultInstance,
|
||||
streamQueries = StreamQueryStore();
|
||||
}
|
||||
|
||||
/// Manages a [DatabaseConnection] to send queries to the database.
|
||||
abstract class DatabaseConnectionUser {
|
||||
/// The database connection used by this [DatabaseConnectionUser].
|
||||
@protected
|
||||
final DatabaseConnection connection;
|
||||
|
||||
/// The type system to use with this database. The type system is responsible
|
||||
/// for mapping Dart objects into sql expressions and vice-versa.
|
||||
SqlTypeSystem get typeSystem => connection.typeSystem;
|
||||
|
||||
/// The executor to use when queries are executed.
|
||||
QueryExecutor get executor => connection.executor;
|
||||
|
||||
/// Manages active streams from select statements.
|
||||
@visibleForTesting
|
||||
@protected
|
||||
StreamQueryStore get streamQueries => connection.streamQueries;
|
||||
|
||||
/// Constructs a database connection user, which is responsible to store query
|
||||
/// streams, wrap the underlying executor and perform type mapping.
|
||||
DatabaseConnectionUser(SqlTypeSystem typeSystem, QueryExecutor executor,
|
||||
{StreamQueryStore streamQueries})
|
||||
: connection = DatabaseConnection(
|
||||
typeSystem, executor, streamQueries ?? StreamQueryStore());
|
||||
|
||||
/// Creates another [DatabaseConnectionUser] by referencing the implementation
|
||||
/// from the [other] user.
|
||||
DatabaseConnectionUser.delegate(DatabaseConnectionUser other,
|
||||
{SqlTypeSystem typeSystem,
|
||||
QueryExecutor executor,
|
||||
StreamQueryStore streamQueries})
|
||||
: connection = DatabaseConnection(
|
||||
typeSystem ?? other.connection.typeSystem,
|
||||
executor ?? other.connection.executor,
|
||||
streamQueries ?? other.connection.streamQueries,
|
||||
);
|
||||
|
||||
/// Constructs a [DatabaseConnectionUser] that will use the provided
|
||||
/// [DatabaseConnection].
|
||||
DatabaseConnectionUser.fromConnection(this.connection);
|
||||
|
||||
/// Marks the tables as updated. This method will be called internally
|
||||
/// whenever a update, delete or insert statement is issued on the database.
|
||||
/// We can then inform all active select-streams on those tables that their
|
||||
/// snapshot might be out-of-date and needs to be fetched again.
|
||||
void markTablesUpdated(Set<TableInfo> tables) {
|
||||
streamQueries.handleTableUpdates(tables);
|
||||
}
|
||||
|
||||
/// Creates and auto-updating stream from the given select statement. This
|
||||
/// method should not be used directly.
|
||||
Stream<T> createStream<T>(QueryStreamFetcher<T> 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 extends Table, D extends DataClass>(
|
||||
TableInfo<T, D> table, String alias) {
|
||||
return table.createAlias(alias).asDslTable;
|
||||
}
|
||||
}
|
||||
|
||||
/// Mixin for a [DatabaseConnectionUser]. Provides an API to execute both
|
||||
/// high-level and custom queries and fetch their results.
|
||||
mixin QueryEngine on DatabaseConnectionUser {
|
||||
|
@ -442,95 +316,3 @@ mixin QueryEngine on DatabaseConnectionUser {
|
|||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
/// A base class for all generated databases.
|
||||
abstract class GeneratedDatabase extends DatabaseConnectionUser
|
||||
with QueryEngine {
|
||||
@override
|
||||
final bool topLevel = true;
|
||||
|
||||
/// Specify the schema version of your database. Whenever you change or add
|
||||
/// tables, you should bump this field and provide a [migration] strategy.
|
||||
int get schemaVersion;
|
||||
|
||||
/// Defines the migration strategy that will determine how to deal with an
|
||||
/// increasing [schemaVersion]. The default value only supports creating the
|
||||
/// database by creating all tables known in this database. When you have
|
||||
/// changes in your schema, you'll need a custom migration strategy to create
|
||||
/// the new tables or change the columns.
|
||||
MigrationStrategy get migration => MigrationStrategy();
|
||||
MigrationStrategy _cachedMigration;
|
||||
MigrationStrategy get _resolvedMigration => _cachedMigration ??= migration;
|
||||
|
||||
/// A list of tables specified in this database.
|
||||
List<TableInfo> get allTables;
|
||||
|
||||
/// A [Type] can't be sent across isolates. Instances of this class shouldn't
|
||||
/// be sent over isolates either, so let's keep a reference to a [Type] that
|
||||
/// definitely prohibits this.
|
||||
// ignore: unused_field
|
||||
final Type _$dontSendThisOverIsolates = Null;
|
||||
|
||||
/// Used by generated code
|
||||
GeneratedDatabase(SqlTypeSystem types, QueryExecutor executor,
|
||||
{StreamQueryStore streamStore})
|
||||
: super(types, executor, streamQueries: streamStore) {
|
||||
executor?.databaseInfo = this;
|
||||
}
|
||||
|
||||
/// Used by generated code to connect to a database that is already open.
|
||||
GeneratedDatabase.connect(DatabaseConnection connection)
|
||||
: super.fromConnection(connection) {
|
||||
connection?.executor?.databaseInfo = this;
|
||||
}
|
||||
|
||||
/// Creates a [Migrator] with the provided query executor. Migrators generate
|
||||
/// sql statements to create or drop tables.
|
||||
///
|
||||
/// This api is mainly used internally in moor, for instance in
|
||||
/// [handleDatabaseCreation] and [handleDatabaseVersionChange]. However, it
|
||||
/// can also be used if you need to create tables manually and outside of a
|
||||
/// [MigrationStrategy]. For almost all use cases, overriding [migration]
|
||||
/// should suffice.
|
||||
@protected
|
||||
Migrator createMigrator([SqlExecutor executor]) {
|
||||
final actualExecutor = executor ?? customStatement;
|
||||
return Migrator(this, actualExecutor);
|
||||
}
|
||||
|
||||
/// Handles database creation by delegating the work to the [migration]
|
||||
/// strategy. This method should not be called by users.
|
||||
Future<void> handleDatabaseCreation({@required SqlExecutor executor}) {
|
||||
final migrator = createMigrator(executor);
|
||||
return _resolvedMigration.onCreate(migrator);
|
||||
}
|
||||
|
||||
/// Handles database updates by delegating the work to the [migration]
|
||||
/// strategy. This method should not be called by users.
|
||||
Future<void> handleDatabaseVersionChange(
|
||||
{@required SqlExecutor executor, int from, int to}) {
|
||||
final migrator = createMigrator(executor);
|
||||
return _resolvedMigration.onUpgrade(migrator, from, to);
|
||||
}
|
||||
|
||||
/// Handles the before opening callback as set in the [migration]. This method
|
||||
/// is used internally by database implementations and should not be called by
|
||||
/// users.
|
||||
Future<void> beforeOpenCallback(
|
||||
QueryExecutor executor, OpeningDetails details) {
|
||||
final migration = _resolvedMigration;
|
||||
|
||||
if (migration.beforeOpen != null) {
|
||||
return _runEngineZoned(
|
||||
BeforeOpenRunner(this, executor),
|
||||
() => migration.beforeOpen(details),
|
||||
);
|
||||
}
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
/// Closes this database and releases associated resources.
|
||||
Future<void> close() async {
|
||||
await executor.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/src/runtime/executor/stream_queries.dart';
|
||||
|
||||
part 'batch.dart';
|
||||
part 'connection.dart';
|
||||
part 'db_base.dart';
|
||||
part 'dao_base.dart';
|
||||
part 'query_engine.dart';
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:moor/backends.dart';
|
||||
import 'package:moor/src/runtime/database.dart';
|
||||
import 'package:moor/moor.dart' show GeneratedDatabase;
|
||||
import 'package:moor/src/utils/hash.dart';
|
||||
|
||||
/// A query executor is responsible for executing statements on a database and
|
||||
|
|
Loading…
Reference in New Issue