Block database after error in migration

This commit is contained in:
Simon Binder 2024-01-31 23:41:25 +01:00
parent af55bd0f92
commit 0ec19f4705
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
3 changed files with 35 additions and 2 deletions

View File

@ -1,3 +1,9 @@
## 2.16.0-dev
- When a migration throws, the database will now block subsequent operations
instead of potentially allowing them to operate on a database in an
inconsistent state.
## 2.15.0
- Methods in the query builder API now respect custom types.

View File

@ -386,6 +386,7 @@ class _WrappingTransactionExecutor extends _TransactionExecutor {
class DelegatedDatabase extends _BaseExecutor {
/// The [DatabaseDelegate] to send queries to.
final DatabaseDelegate delegate;
(Object, StackTrace)? _migrationError;
@override
bool logStatements;
@ -415,6 +416,12 @@ class DelegatedDatabase extends _BaseExecutor {
'database connection and open that instead.'));
}
// If we have been unable to run migrations, the database is likely in an
// inconsistent state and we should prevent subsequent operations on it.
if (_migrationError case (var err, var trace)?) {
Error.throwWithStackTrace(err, trace);
}
final alreadyOpen = await delegate.isOpen;
if (alreadyOpen) {
_ensureOpenCalled = true;
@ -423,8 +430,14 @@ class DelegatedDatabase extends _BaseExecutor {
await delegate.open(user);
_ensureOpenCalled = true;
await _runMigrations(user);
return true;
try {
await _runMigrations(user);
return true;
} catch (e, s) {
_migrationError = (e, s);
rethrow;
}
});
}

View File

@ -231,6 +231,20 @@ void main() {
)),
);
});
test('prevents database access after failed migrations', () async {
final db = TodoDb(NativeDatabase.memory());
final exception = 'exception';
db.migration = MigrationStrategy(onCreate: (_) async => throw exception);
await expectLater(db.customSelect('SELECT 1').get(), throwsA(exception));
// The database should now be unusable.
db.migration = MigrationStrategy();
await expectLater(db.customSelect('SELECT 1').get(), throwsA(exception));
await db.close();
});
}
class _FakeExecutorUser extends QueryExecutorUser {