Warn when a database class isn't used as a singleton

This commit is contained in:
Simon Binder 2019-11-15 09:52:38 +01:00
parent 22c692c69e
commit c0aa88f4d0
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
6 changed files with 62 additions and 3 deletions

View File

@ -14,6 +14,8 @@ abstract class TestExecutor {
}
void runAllTests(TestExecutor executor) {
moorRuntimeOptions.dontWarnAboutMultipleDatabases = true;
tearDown(() async {
await executor.deleteData();
});

View File

@ -1,5 +1,14 @@
part of 'runtime_api.dart';
/// Keep track of how many databases have been opened for a given database
/// type.
/// We get a number of error reports of "moor not generating tables" that have
/// their origin in users opening multiple instances of their database. This
/// can cause a race conditions when the second [GeneratedDatabase] is opening a
/// underlying [DatabaseConnection] that is already opened but doesn't have the
/// tables created.
Map<Type, int> _openedDbCount = {};
/// A base class for all generated databases.
abstract class GeneratedDatabase extends DatabaseConnectionUser
with QueryEngine {
@ -33,12 +42,38 @@ abstract class GeneratedDatabase extends DatabaseConnectionUser
{StreamQueryStore streamStore})
: super(types, executor, streamQueries: streamStore) {
executor?.databaseInfo = this;
assert(_handleInstantiated());
}
/// Used by generated code to connect to a database that is already open.
GeneratedDatabase.connect(DatabaseConnection connection)
: super.fromConnection(connection) {
connection?.executor?.databaseInfo = this;
assert(_handleInstantiated());
}
bool _handleInstantiated() {
if (!_openedDbCount.containsKey(runtimeType) ||
moorRuntimeOptions.dontWarnAboutMultipleDatabases) {
_openedDbCount[runtimeType] = 1;
return true;
}
final count = ++_openedDbCount[runtimeType];
if (count > 1) {
print(
'WARNING (moor): It looks like you\'ve created the database '
'$runtimeType multiple times. When these two databases use the same '
'QueryExecutor, race conditions will ocur and might corrupt the '
'database. \n'
'Try to follow the advice at https://moor.simonbinder.eu/faq/#using-the-database '
'or, if you know what you\'re doing, set moorRuntimeOptions.dontWarnAboutMultipleDatabases = true\n'
'Here is the stacktrace from when the database was opened a second '
'time:\n${StackTrace.current}\n'
'This warning will only appear on debug builds.',
);
}
return true;
}
/// Creates a [Migrator] with the provided query executor. Migrators generate
@ -89,5 +124,6 @@ abstract class GeneratedDatabase extends DatabaseConnectionUser
/// Closes this database and releases associated resources.
Future<void> close() async {
await executor.close();
_openedDbCount[runtimeType]--;
}
}

View File

@ -9,3 +9,16 @@ part 'connection.dart';
part 'db_base.dart';
part 'dao_base.dart';
part 'query_engine.dart';
/// Defines additional runtime behavior for moor. Changing the fields of this
/// class is rarely necessary.
class MoorRuntimeOptions {
/// Don't warn when a database class isn't used as singleton.
bool dontWarnAboutMultipleDatabases = false;
}
/// Stores the [MoorRuntimeOptions] describing global moor behavior across
/// databases.
///
/// Note that is is adapting this behavior is rarely needed.
MoorRuntimeOptions moorRuntimeOptions = MoorRuntimeOptions();

View File

@ -7,7 +7,9 @@ part 'custom_tables.g.dart';
queries: {'writeConfig': 'REPLACE INTO config VALUES (:key, :value)'},
)
class CustomTablesDb extends _$CustomTablesDb {
CustomTablesDb(QueryExecutor e) : super(e);
CustomTablesDb(QueryExecutor e) : super(e) {
moorRuntimeOptions.dontWarnAboutMultipleDatabases = true;
}
@override
int get schemaVersion => 1;

View File

@ -99,8 +99,12 @@ class CustomConverter extends TypeConverter<MyCustomObject, String> {
},
)
class TodoDb extends _$TodoDb {
TodoDb(QueryExecutor e) : super(e);
TodoDb.connect(DatabaseConnection connection) : super.connect(connection);
TodoDb(QueryExecutor e) : super(e) {
moorRuntimeOptions.dontWarnAboutMultipleDatabases = true;
}
TodoDb.connect(DatabaseConnection connection) : super.connect(connection) {
moorRuntimeOptions.dontWarnAboutMultipleDatabases = true;
}
@override
MigrationStrategy get migration => MigrationStrategy();

View File

@ -32,6 +32,8 @@ class _FakeDb extends GeneratedDatabase {
}
void main() {
moorRuntimeOptions.dontWarnAboutMultipleDatabases = true;
test('status of OpeningDetails', () {
expect(const OpeningDetails(null, 1).wasCreated, true);
expect(const OpeningDetails(2, 4).wasCreated, false);