drift/moor_flutter/lib/moor_flutter.dart

210 lines
6.7 KiB
Dart
Raw Normal View History

2019-03-09 07:37:22 -08:00
/// Flutter implementation for the moor database. This library merely provides
/// a thin level of abstraction between the
/// [sqflite](https://pub.dartlang.org/packages/sqflite) library and
2019-03-09 07:37:22 -08:00
/// [moor](https://github.com/simolus3/moor)
library moor_flutter;
import 'dart:async';
import 'dart:io';
import 'package:meta/meta.dart';
import 'package:path/path.dart';
2019-03-09 07:37:22 -08:00
import 'package:moor/moor.dart';
import 'package:moor/backends.dart';
2019-03-10 04:00:25 -07:00
import 'package:sqflite/sqflite.dart' as s;
2019-03-27 10:15:29 -07:00
export 'package:moor/moor.dart';
/// Signature of a function that runs when a database doesn't exist on file.
/// This can be useful to, for instance, load the database from an asset if it
/// doesn't exist.
typedef DatabaseCreator = FutureOr<void> Function(File file);
class _SqfliteDelegate extends DatabaseDelegate with _SqfliteExecutor {
int _loadedSchemaVersion;
@override
s.Database db;
2019-03-10 04:00:25 -07:00
final bool inDbFolder;
final String path;
2019-02-17 02:32:54 -08:00
bool singleInstance;
final DatabaseCreator creator;
_SqfliteDelegate(this.inDbFolder, this.path,
{this.singleInstance, this.creator}) {
singleInstance ??= true;
}
2019-02-17 02:32:54 -08:00
@override
DbVersionDelegate get versionDelegate {
return OnOpenVersionDelegate(() => Future.value(_loadedSchemaVersion));
2019-03-10 04:00:25 -07:00
}
@override
TransactionDelegate get transactionDelegate =>
_SqfliteTransactionDelegate(this);
2019-03-10 04:00:25 -07:00
@override
bool get isOpen => db != null;
2019-03-10 04:00:25 -07:00
@override
Future<void> open(QueryExecutorUser user) async {
String resolvedPath;
if (inDbFolder) {
resolvedPath = join(await s.getDatabasesPath(), path);
} else {
resolvedPath = path;
}
final file = File(resolvedPath);
if (creator != null && !await file.exists()) {
await creator(file);
}
// default value when no migration happened
_loadedSchemaVersion = user.schemaVersion;
db = await s.openDatabase(
resolvedPath,
version: user.schemaVersion,
onCreate: (db, version) {
_loadedSchemaVersion = 0;
},
onUpgrade: (db, from, to) {
_loadedSchemaVersion = from;
},
onDowngrade: (db, from, to) {
_loadedSchemaVersion = from;
},
singleInstance: singleInstance,
);
2019-03-10 04:00:25 -07:00
}
@override
Future<void> close() {
return db.close();
}
}
class _SqfliteTransactionDelegate extends SupportedTransactionDelegate {
final _SqfliteDelegate delegate;
_SqfliteTransactionDelegate(this.delegate);
2019-03-10 04:00:25 -07:00
@override
void startTransaction(Future<void> Function(QueryDelegate) run) {
delegate.db.transaction((transaction) async {
final executor = _SqfliteTransactionExecutor(transaction);
await run(executor);
}).catchError((_) {
// Ignore the error! We send a fake exception to indicate a rollback.
// sqflite will rollback, but the exception will bubble up. Here we stop
// the exception.
});
2019-03-10 04:00:25 -07:00
}
}
2019-03-10 04:00:25 -07:00
class _SqfliteTransactionExecutor extends QueryDelegate with _SqfliteExecutor {
2019-03-10 04:00:25 -07:00
@override
final s.DatabaseExecutor db;
_SqfliteTransactionExecutor(this.db);
}
mixin _SqfliteExecutor on QueryDelegate {
s.DatabaseExecutor get db;
2019-04-19 13:45:38 -07:00
@override
Future<void> runBatched(BatchedStatements statements) async {
2019-04-19 13:45:38 -07:00
final batch = db.batch();
for (final arg in statements.arguments) {
batch.execute(
statements.statements[arg.statementIndex], statements.arguments);
2019-04-19 13:45:38 -07:00
}
await batch.commit(noResult: true);
}
@override
Future<void> runCustom(String statement, List args) {
return db.execute(statement, args);
2019-02-17 02:32:54 -08:00
}
@override
Future<int> runInsert(String statement, List args) {
return db.rawInsert(statement, args);
}
@override
Future<QueryResult> runSelect(String statement, List args) async {
final result = await db.rawQuery(statement, args);
return QueryResult.fromRows(result);
}
@override
Future<int> runUpdate(String statement, List args) {
return db.rawUpdate(statement, args);
}
}
/// A query executor that uses sqflite internally.
class FlutterQueryExecutor extends DelegatedDatabase {
/// A query executor that will store the database in the file declared by
/// [path]. If [logStatements] is true, statements sent to the database will
/// be [print]ed, which can be handy for debugging. The [singleInstance]
/// parameter sets the corresponding parameter on [s.openDatabase].
/// The [creator] will be called when the database file doesn't exist. It can
/// be used to, for instance, populate default data from an asset. Note that
/// migrations might behave differently when populating the database this way.
/// For instance, a database created by an [creator] will not receive the
/// [MigrationStrategy.onCreate] callback because it hasn't been created by
/// moor.
FlutterQueryExecutor(
{@required String path,
bool logStatements,
bool singleInstance,
DatabaseCreator creator})
: super(
_SqfliteDelegate(false, path,
singleInstance: singleInstance, creator: creator),
logStatements: logStatements);
/// A query executor that will store the database in the file declared by
/// [path], which will be resolved relative to [s.getDatabasesPath()].
/// If [logStatements] is true, statements sent to the database will
/// be [print]ed, which can be handy for debugging. The [singleInstance]
/// parameter sets the corresponding parameter on [s.openDatabase].
/// The [creator] will be called when the database file doesn't exist. It can
/// be used to, for instance, populate default data from an asset. Note that
/// migrations might behave differently when populating the database this way.
/// For instance, a database created by an [creator] will not receive the
/// [MigrationStrategy.onCreate] callback because it hasn't been created by
/// moor.
FlutterQueryExecutor.inDatabaseFolder(
{@required String path,
bool logStatements,
bool singleInstance,
DatabaseCreator creator})
: super(
_SqfliteDelegate(true, path,
singleInstance: singleInstance, creator: creator),
logStatements: logStatements);
2020-02-01 07:15:14 -08:00
/// The underlying sqflite [s.Database] object used by moor to send queries.
2020-02-01 07:08:14 -08:00
///
/// Using the sqflite database can cause unexpected behavior in moor. For
/// instance, stream queries won't update for updates sent to the [s.Database]
/// directly.
/// For this reason, projects shouldn't use this getter unless they absolutely
/// need to. The database is exposed to make migrating from sqflite to moor
/// easier.
///
/// Note that this returns null until the moor database has been opened.
/// A moor database is opened lazily when the first query runs.
s.Database get sqfliteDb {
final sqfliteDelegate = delegate as _SqfliteDelegate;
return sqfliteDelegate.db;
}
}