mirror of https://github.com/AMT-Cheif/drift.git
Allow retrying if database setup fails
This commit is contained in:
parent
d7f1bfb61c
commit
39645f669c
|
@ -1,3 +1,8 @@
|
||||||
|
## 2.8.0-dev
|
||||||
|
|
||||||
|
- Don't keep databases in an unusable state if the `setup` callback throws an
|
||||||
|
exception. Instead, drift will retry the next time the database is used.
|
||||||
|
|
||||||
## 2.7.0
|
## 2.7.0
|
||||||
|
|
||||||
- Add support for `CASE` expressions without a base in the Dart API with the
|
- Add support for `CASE` expressions without a base in the Dart API with the
|
||||||
|
|
|
@ -14,10 +14,12 @@ import 'native_functions.dart';
|
||||||
/// through `package:js`.
|
/// through `package:js`.
|
||||||
abstract class Sqlite3Delegate<DB extends CommonDatabase>
|
abstract class Sqlite3Delegate<DB extends CommonDatabase>
|
||||||
extends DatabaseDelegate {
|
extends DatabaseDelegate {
|
||||||
/// The underlying database instance from the `sqlite3` package.
|
DB? _database;
|
||||||
late DB database;
|
|
||||||
|
|
||||||
bool _hasCreatedDatabase = false;
|
/// The underlying database instance from the `sqlite3` package.
|
||||||
|
DB get database => _database!;
|
||||||
|
|
||||||
|
bool _hasInitializedDatabase = false;
|
||||||
bool _isOpen = false;
|
bool _isOpen = false;
|
||||||
|
|
||||||
final void Function(DB)? _setup;
|
final void Function(DB)? _setup;
|
||||||
|
@ -35,8 +37,7 @@ abstract class Sqlite3Delegate<DB extends CommonDatabase>
|
||||||
/// A delegate using an underlying sqlite3 database object that has already
|
/// A delegate using an underlying sqlite3 database object that has already
|
||||||
/// been opened.
|
/// been opened.
|
||||||
Sqlite3Delegate.opened(
|
Sqlite3Delegate.opened(
|
||||||
this.database, this._setup, this.closeUnderlyingWhenClosed)
|
this._database, this._setup, this.closeUnderlyingWhenClosed) {
|
||||||
: _hasCreatedDatabase = true {
|
|
||||||
_initializeDatabase();
|
_initializeDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,26 +56,33 @@ abstract class Sqlite3Delegate<DB extends CommonDatabase>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> open(QueryExecutorUser db) async {
|
Future<void> open(QueryExecutorUser db) async {
|
||||||
if (!_hasCreatedDatabase) {
|
if (!_hasInitializedDatabase) {
|
||||||
_createDatabase();
|
assert(_database == null);
|
||||||
|
_database = openDatabase();
|
||||||
|
|
||||||
|
try {
|
||||||
_initializeDatabase();
|
_initializeDatabase();
|
||||||
|
} catch (e) {
|
||||||
|
// If the initialization fails, we effectively don't have a usable
|
||||||
|
// database, so reset
|
||||||
|
_database?.dispose();
|
||||||
|
_database = null;
|
||||||
|
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isOpen = true;
|
_isOpen = true;
|
||||||
return Future.value();
|
return Future.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _createDatabase() {
|
|
||||||
assert(!_hasCreatedDatabase);
|
|
||||||
_hasCreatedDatabase = true;
|
|
||||||
|
|
||||||
database = openDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _initializeDatabase() {
|
void _initializeDatabase() {
|
||||||
|
assert(!_hasInitializedDatabase);
|
||||||
|
|
||||||
database.useNativeFunctions();
|
database.useNativeFunctions();
|
||||||
_setup?.call(database);
|
_setup?.call(database);
|
||||||
versionDelegate = _VmVersionDelegate(database);
|
versionDelegate = _SqliteVersionDelegate(database);
|
||||||
|
_hasInitializedDatabase = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronously prepares and runs [statements] collected from a batch.
|
/// Synchronously prepares and runs [statements] collected from a batch.
|
||||||
|
@ -127,10 +135,10 @@ abstract class Sqlite3Delegate<DB extends CommonDatabase>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _VmVersionDelegate extends DynamicVersionDelegate {
|
class _SqliteVersionDelegate extends DynamicVersionDelegate {
|
||||||
final CommonDatabase database;
|
final CommonDatabase database;
|
||||||
|
|
||||||
_VmVersionDelegate(this.database);
|
_SqliteVersionDelegate(this.database);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> get schemaVersion => Future.value(database.userVersion);
|
Future<int> get schemaVersion => Future.value(database.userVersion);
|
||||||
|
|
|
@ -90,6 +90,36 @@ void main() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('calls setup twice if first invocation fails', () async {
|
||||||
|
const exception = 'exception';
|
||||||
|
var count = 0;
|
||||||
|
final db = NativeDatabase.memory(
|
||||||
|
setup: expectAsync1(
|
||||||
|
(_) {
|
||||||
|
if (count++ == 0) {
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
count: 2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expectLater(db.ensureOpen(_FakeExecutorUser()), throwsA(exception));
|
||||||
|
|
||||||
|
// Should also prevent subsequent open attempts
|
||||||
|
await expectLater(db.ensureOpen(_FakeExecutorUser()), completes);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throwing in setup prevents the database from being opened', () async {
|
||||||
|
const exception = 'exception';
|
||||||
|
final db = NativeDatabase.memory(setup: (_) => throw exception);
|
||||||
|
|
||||||
|
await expectLater(db.ensureOpen(_FakeExecutorUser()), throwsA(exception));
|
||||||
|
|
||||||
|
// Should also prevent subsequent open attempts
|
||||||
|
await expectLater(db.ensureOpen(_FakeExecutorUser()), throwsA(exception));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FakeExecutorUser extends QueryExecutorUser {
|
class _FakeExecutorUser extends QueryExecutorUser {
|
||||||
|
|
Loading…
Reference in New Issue