refactor: Add delete database method

This adds a delete database
method used for migration to
correctly delete the whole
legacy database instead just
empty it.
This commit is contained in:
Krille 2023-12-12 08:30:00 +01:00
parent b3ec966238
commit eb869462aa
No known key found for this signature in database
10 changed files with 165 additions and 112 deletions

View File

@ -3006,7 +3006,11 @@ class Client extends MatrixApi {
final migrateClient = await legacyDatabase?.getClient(clientName); final migrateClient = await legacyDatabase?.getClient(clientName);
final database = this.database; final database = this.database;
if (migrateClient != null && legacyDatabase != null && database != null) { if (migrateClient == null || legacyDatabase == null || database == null) {
await legacyDatabase?.close();
_initLock = false;
return;
}
Logs().i('Found data in the legacy database!'); Logs().i('Found data in the legacy database!');
onMigration?.call(); onMigration?.call();
_id = migrateClient['client_id']; _id = migrateClient['client_id'];
@ -3088,8 +3092,7 @@ class Client extends MatrixApi {
} }
} }
Logs().d('Migrate user device keys info...'); Logs().d('Migrate user device keys info...');
await database.storeUserDeviceKeysInfo( await database.storeUserDeviceKeysInfo(userId, deviceKeysList.outdated);
userId, deviceKeysList.outdated);
} }
} }
Logs().d('Migrate inbound group sessions...'); Logs().d('Migrate inbound group sessions...');
@ -3113,17 +3116,14 @@ class Client extends MatrixApi {
Logs().e('Unable to migrate inbound group sessions!', e, s); Logs().e('Unable to migrate inbound group sessions!', e, s);
} }
await legacyDatabase.clear(); await legacyDatabase.delete();
}
await legacyDatabase?.close();
_initLock = false; _initLock = false;
if (migrateClient != null) {
return init( return init(
waitForFirstSync: false, waitForFirstSync: false,
waitUntilLoadCompletedLoaded: false, waitUntilLoadCompletedLoaded: false,
); );
} }
}
} }
class SdkError { class SdkError {

View File

@ -316,4 +316,8 @@ abstract class DatabaseApi {
Future<void> storePresence(String userId, CachedPresence presence); Future<void> storePresence(String userId, CachedPresence presence);
Future<CachedPresence?> getPresence(String userId); Future<CachedPresence?> getPresence(String userId);
/// Deletes the whole database. The database needs to be created again after
/// this. Used for migration only.
Future<void> delete();
} }

View File

@ -1594,6 +1594,9 @@ class HiveCollectionsDatabase extends DatabaseApi {
return false; return false;
} }
} }
@override
Future<void> delete() => _collection.deleteFromDisk();
} }
class TupleKey { class TupleKey {

View File

@ -1474,6 +1474,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
// see no need to implement this in a deprecated part // see no need to implement this in a deprecated part
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future<void> delete() => Hive.deleteFromDisk();
} }
dynamic _castValue(dynamic value) { dynamic _castValue(dynamic value) {

View File

@ -7,13 +7,16 @@ import 'dart:indexed_db';
class BoxCollection { class BoxCollection {
final Database _db; final Database _db;
final Set<String> boxNames; final Set<String> boxNames;
final String _name;
final IdbFactory _idbFactory;
BoxCollection(this._db, this.boxNames); BoxCollection(this._db, this.boxNames, this._name, this._idbFactory);
static Future<BoxCollection> open( static Future<BoxCollection> open(
String name, String name,
Set<String> boxNames, { Set<String> boxNames, {
Object? sqfliteDatabase, Object? sqfliteDatabase,
Object? sqfliteFactory,
IdbFactory? idbFactory, IdbFactory? idbFactory,
}) async { }) async {
idbFactory ??= window.indexedDB!; idbFactory ??= window.indexedDB!;
@ -24,7 +27,7 @@ class BoxCollection {
db.createObjectStore(name, autoIncrement: true); db.createObjectStore(name, autoIncrement: true);
} }
}); });
return BoxCollection(db, boxNames); return BoxCollection(db, boxNames, name, idbFactory);
} }
Box<V> openBox<V>(String name) { Box<V> openBox<V>(String name) {
@ -61,15 +64,19 @@ class BoxCollection {
} }
Future<void> clear() async { Future<void> clear() async {
final txn = _db.transaction(boxNames, 'readwrite');
for (final name in boxNames) { for (final name in boxNames) {
_db.deleteObjectStore(name); unawaited(txn.objectStore(name).clear());
} }
await txn.completed;
} }
Future<void> close() async { Future<void> close() async {
assert(_txnCache == null, 'Database closed while in transaction!'); assert(_txnCache == null, 'Database closed while in transaction!');
return _db.close(); return _db.close();
} }
Future<void> delete() => _idbFactory.deleteDatabase(_name);
} }
class Box<V> { class Box<V> {

View File

@ -144,10 +144,15 @@ class MatrixSdkDatabase extends DatabaseApi {
/// typed. /// typed.
final dynamic idbFactory; final dynamic idbFactory;
/// Custom SQFlite Database Factory used for high level operations on IO
/// like delete. Set it if you want to use sqlite FFI.
final DatabaseFactory? sqfliteFactory;
MatrixSdkDatabase( MatrixSdkDatabase(
this.name, { this.name, {
this.database, this.database,
this.idbFactory, this.idbFactory,
this.sqfliteFactory,
this.maxFileSize = 0, this.maxFileSize = 0,
this.fileStoragePath, this.fileStoragePath,
this.deleteFilesAfterDuration, this.deleteFilesAfterDuration,
@ -179,6 +184,7 @@ class MatrixSdkDatabase extends DatabaseApi {
_seenDeviceKeysBoxName, _seenDeviceKeysBoxName,
}, },
sqfliteDatabase: database, sqfliteDatabase: database,
sqfliteFactory: sqfliteFactory,
idbFactory: idbFactory, idbFactory: idbFactory,
); );
_clientBox = _collection.openBox<String>( _clientBox = _collection.openBox<String>(
@ -1596,4 +1602,7 @@ class MatrixSdkDatabase extends DatabaseApi {
return CachedPresence.fromJson(copyMap(rawPresence)); return CachedPresence.fromJson(copyMap(rawPresence));
} }
@override
Future<void> delete() => _collection.delete();
} }

View File

@ -1,20 +1,22 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:sqflite_common/sqlite_api.dart'; import 'package:sqflite_common/sqflite.dart';
/// Key-Value store abstraction over Sqflite so that the sdk database can use /// Key-Value store abstraction over Sqflite so that the sdk database can use
/// a single interface for all platforms. API is inspired by Hive. /// a single interface for all platforms. API is inspired by Hive.
class BoxCollection { class BoxCollection {
final Database _db; final Database _db;
final Set<String> boxNames; final Set<String> boxNames;
final DatabaseFactory? _factory;
BoxCollection(this._db, this.boxNames); BoxCollection(this._db, this.boxNames, this._factory);
static Future<BoxCollection> open( static Future<BoxCollection> open(
String name, String name,
Set<String> boxNames, { Set<String> boxNames, {
Object? sqfliteDatabase, Object? sqfliteDatabase,
DatabaseFactory? sqfliteFactory,
dynamic idbFactory, dynamic idbFactory,
}) async { }) async {
if (sqfliteDatabase is! Database) { if (sqfliteDatabase is! Database) {
@ -28,7 +30,7 @@ class BoxCollection {
batch.execute('CREATE INDEX IF NOT EXISTS k_index ON $name (k)'); batch.execute('CREATE INDEX IF NOT EXISTS k_index ON $name (k)');
} }
await batch.commit(noResult: true); await batch.commit(noResult: true);
return BoxCollection(sqfliteDatabase, boxNames); return BoxCollection(sqfliteDatabase, boxNames, sqfliteFactory);
} }
Box<V> openBox<V>(String name) { Box<V> openBox<V>(String name) {
@ -110,6 +112,9 @@ class BoxCollection {
); );
Future<void> close() => _db.close(); Future<void> close() => _db.close();
Future<void> delete() =>
(_factory ?? databaseFactory).deleteDatabase(_db.path);
} }
class Box<V> { class Box<V> {

View File

@ -15,6 +15,7 @@ void main() {
'testbox', 'testbox',
boxNames, boxNames,
sqfliteDatabase: db, sqfliteDatabase: db,
sqfliteFactory: databaseFactoryFfi,
); );
}); });
@ -76,5 +77,9 @@ void main() {
expect(await box.get('fluffy'), null); expect(await box.get('fluffy'), null);
expect(await box.get('loki'), null); expect(await box.get('loki'), null);
}); });
test('Box.delete', () async {
await collection.delete();
});
}); });
} }

View File

@ -472,6 +472,19 @@ void main() {
test('Close', () async { test('Close', () async {
await database.close(); await database.close();
}); });
test('Delete', () async {
final database = await getMatrixSdkDatabase(null);
await database.storeAccountData(
'm.test.data',
jsonEncode({'foo': 'bar'}),
);
await database.delete();
// Check if previously stored data is gone:
final reopenedDatabase = await getMatrixSdkDatabase(null);
final dump = await reopenedDatabase.getAccountData();
expect(dump.isEmpty, true);
});
}); });
} }

View File

@ -44,7 +44,11 @@ Future<HiveCollectionsDatabase> getHiveCollectionsDatabase(Client? c) async {
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
Future<MatrixSdkDatabase> getMatrixSdkDatabase(Client? c) async { Future<MatrixSdkDatabase> getMatrixSdkDatabase(Client? c) async {
final database = await databaseFactoryFfi.openDatabase(':memory:'); final database = await databaseFactoryFfi.openDatabase(':memory:');
final db = MatrixSdkDatabase('unit_test.${c?.hashCode}', database: database); final db = MatrixSdkDatabase(
'unit_test.${c?.hashCode}',
database: database,
sqfliteFactory: databaseFactoryFfi,
);
await db.open(); await db.open();
return db; return db;
} }