drift/moor_flutter/lib/moor_flutter.dart

181 lines
4.8 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 'package:meta/meta.dart';
import 'package:path/path.dart';
2019-03-09 07:37:22 -08:00
import 'package:moor/moor.dart';
2019-03-10 04:00:25 -07:00
import 'package:sqflite/sqflite.dart' as s;
2019-03-09 07:37:22 -08:00
export 'package:moor_flutter/src/animated_list.dart';
2019-03-27 10:15:29 -07:00
export 'package:moor/moor.dart';
2019-03-10 04:00:25 -07:00
abstract class _DatabaseOwner extends QueryExecutor {
_DatabaseOwner(this.logStatements);
@visibleForOverriding
s.DatabaseExecutor get db;
2019-02-17 02:32:54 -08:00
final bool logStatements;
void _log(String sql, [List args]) {
2019-03-10 04:00:25 -07:00
if (logStatements == true) {
final formattedArgs = (args?.isEmpty ?? true) ? ' no variables' : args;
print('moor: $sql with $formattedArgs');
}
}
@override
Future<int> runDelete(String statement, List args) {
_log(statement, args);
return db.rawDelete(statement, args);
}
@override
Future<int> runInsert(String statement, List args) {
_log(statement, args);
return db.rawInsert(statement, args);
}
@override
Future<List<Map<String, dynamic>>> runSelect(String statement, List args) {
_log(statement, args);
return db.rawQuery(statement, args);
}
@override
Future<int> runUpdate(String statement, List args) {
_log(statement, args);
return db.rawUpdate(statement, args);
}
@override
Future<void> runCustom(String statement) {
_log(statement, null);
return db.execute(statement);
}
}
/// A query executor that uses sqflite internally.
class FlutterQueryExecutor extends _DatabaseOwner {
final bool _inDbPath;
final String path;
@override
s.Database db;
2019-03-06 12:43:16 -08:00
bool _hadMigration = false;
2019-03-10 04:00:25 -07:00
FlutterQueryExecutor({@required this.path, bool logStatements})
: _inDbPath = false,
super(logStatements);
2019-02-17 02:32:54 -08:00
FlutterQueryExecutor.inDatabaseFolder(
2019-03-10 04:00:25 -07:00
{@required this.path, bool logStatements})
: _inDbPath = true,
super(logStatements);
@override
Future<bool> ensureOpen() async {
2019-03-10 04:00:25 -07:00
if (db != null && db.isOpen) {
return true;
}
String resolvedPath;
if (_inDbPath) {
2019-03-10 04:00:25 -07:00
resolvedPath = join(await s.getDatabasesPath(), path);
} else {
resolvedPath = path;
}
2019-03-10 04:00:25 -07:00
db = await s.openDatabase(resolvedPath, version: databaseInfo.schemaVersion,
onCreate: (db, version) {
_hadMigration = true;
return databaseInfo.handleDatabaseCreation(
executor: _migrationExecutor(db),
);
}, onUpgrade: (db, from, to) {
_hadMigration = true;
return databaseInfo.handleDatabaseVersionChange(
executor: _migrationExecutor(db), from: from, to: to);
}, onOpen: (db) async {
2019-03-10 04:00:25 -07:00
db = db;
// the openDatabase future will resolve later, so we can get an instance
// where we can send the queries from the onFinished operation;
final fn = databaseInfo.migration.onFinished;
if (fn != null && _hadMigration) {
await fn();
2019-03-06 12:43:16 -08:00
}
});
return true;
}
SqlExecutor _migrationExecutor(s.Database db) {
return (sql) {
if (logStatements) {
_log(sql);
}
db.execute(sql);
};
}
2019-03-10 04:00:25 -07:00
@override
TransactionExecutor beginTransaction() {
return _SqfliteTransactionExecutor.startFromDb(this);
2019-02-17 02:32:54 -08:00
}
2019-03-10 04:00:25 -07:00
}
2019-02-17 02:32:54 -08:00
2019-03-10 04:00:25 -07:00
class _SqfliteTransactionExecutor extends _DatabaseOwner
implements TransactionExecutor {
@override
2019-03-10 04:00:25 -07:00
s.Transaction db;
2019-03-10 04:00:25 -07:00
/// This future should complete with the transaction once the transaction has
/// been created.
final Future<s.Transaction> _open;
// This completer will complete when send() is called. We use it because
// sqflite expects a future in the db.transaction() method. The transaction
// will be executed when that future completes.
final Completer _actionCompleter;
2019-03-10 04:00:25 -07:00
/// This future should complete when the call to db.transaction completes.
final Future _sendFuture;
_SqfliteTransactionExecutor(
this._open, this._actionCompleter, this._sendFuture, bool logStatements)
: super(logStatements) {
_open.then((transaction) => db = transaction);
}
2019-03-10 04:00:25 -07:00
factory _SqfliteTransactionExecutor.startFromDb(FlutterQueryExecutor db) {
final actionCompleter = Completer();
final openingCompleter = Completer<s.Transaction>();
final sendFuture = db.db.transaction((t) {
openingCompleter.complete(t);
return actionCompleter.future;
});
return _SqfliteTransactionExecutor(
openingCompleter.future, actionCompleter, sendFuture, db.logStatements);
}
@override
2019-03-10 04:00:25 -07:00
TransactionExecutor beginTransaction() {
throw StateError('Transactions cannot create another transaction!');
}
@override
2019-03-10 04:00:25 -07:00
Future<bool> ensureOpen() => _open.then((_) => true);
@override
2019-03-10 04:00:25 -07:00
Future<void> send() {
_actionCompleter.complete(null);
return _sendFuture;
}
}