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:
parent
b3ec966238
commit
eb869462aa
|
|
@ -3006,123 +3006,123 @@ class Client extends MatrixApi {
|
|||
final migrateClient = await legacyDatabase?.getClient(clientName);
|
||||
final database = this.database;
|
||||
|
||||
if (migrateClient != null && legacyDatabase != null && database != null) {
|
||||
Logs().i('Found data in the legacy database!');
|
||||
onMigration?.call();
|
||||
_id = migrateClient['client_id'];
|
||||
await database.insertClient(
|
||||
clientName,
|
||||
migrateClient['homeserver_url'],
|
||||
migrateClient['token'],
|
||||
migrateClient['user_id'],
|
||||
migrateClient['device_id'],
|
||||
migrateClient['device_name'],
|
||||
null,
|
||||
migrateClient['olm_account'],
|
||||
);
|
||||
Logs().d('Migrate SSSSCache...');
|
||||
for (final type in cacheTypes) {
|
||||
final ssssCache = await legacyDatabase.getSSSSCache(type);
|
||||
if (ssssCache != null) {
|
||||
Logs().d('Migrate $type...');
|
||||
await database.storeSSSSCache(
|
||||
type,
|
||||
ssssCache.keyId ?? '',
|
||||
ssssCache.ciphertext ?? '',
|
||||
ssssCache.content ?? '',
|
||||
if (migrateClient == null || legacyDatabase == null || database == null) {
|
||||
await legacyDatabase?.close();
|
||||
_initLock = false;
|
||||
return;
|
||||
}
|
||||
Logs().i('Found data in the legacy database!');
|
||||
onMigration?.call();
|
||||
_id = migrateClient['client_id'];
|
||||
await database.insertClient(
|
||||
clientName,
|
||||
migrateClient['homeserver_url'],
|
||||
migrateClient['token'],
|
||||
migrateClient['user_id'],
|
||||
migrateClient['device_id'],
|
||||
migrateClient['device_name'],
|
||||
null,
|
||||
migrateClient['olm_account'],
|
||||
);
|
||||
Logs().d('Migrate SSSSCache...');
|
||||
for (final type in cacheTypes) {
|
||||
final ssssCache = await legacyDatabase.getSSSSCache(type);
|
||||
if (ssssCache != null) {
|
||||
Logs().d('Migrate $type...');
|
||||
await database.storeSSSSCache(
|
||||
type,
|
||||
ssssCache.keyId ?? '',
|
||||
ssssCache.ciphertext ?? '',
|
||||
ssssCache.content ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
Logs().d('Migrate OLM sessions...');
|
||||
try {
|
||||
final olmSessions = await legacyDatabase.getAllOlmSessions();
|
||||
for (final identityKey in olmSessions.keys) {
|
||||
final sessions = olmSessions[identityKey]!;
|
||||
for (final sessionId in sessions.keys) {
|
||||
final session = sessions[sessionId]!;
|
||||
await database.storeOlmSession(
|
||||
identityKey,
|
||||
session['session_id'] as String,
|
||||
session['pickle'] as String,
|
||||
session['last_received'] as int,
|
||||
);
|
||||
}
|
||||
}
|
||||
Logs().d('Migrate OLM sessions...');
|
||||
try {
|
||||
final olmSessions = await legacyDatabase.getAllOlmSessions();
|
||||
for (final identityKey in olmSessions.keys) {
|
||||
final sessions = olmSessions[identityKey]!;
|
||||
for (final sessionId in sessions.keys) {
|
||||
final session = sessions[sessionId]!;
|
||||
await database.storeOlmSession(
|
||||
identityKey,
|
||||
session['session_id'] as String,
|
||||
session['pickle'] as String,
|
||||
session['last_received'] as int,
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logs().e('Unable to migrate OLM sessions!', e, s);
|
||||
}
|
||||
Logs().d('Migrate Device Keys...');
|
||||
final userDeviceKeys = await legacyDatabase.getUserDeviceKeys(this);
|
||||
for (final userId in userDeviceKeys.keys) {
|
||||
Logs().d('Migrate Device Keys of user $userId...');
|
||||
final deviceKeysList = userDeviceKeys[userId];
|
||||
for (final crossSigningKey
|
||||
in deviceKeysList?.crossSigningKeys.values ?? <CrossSigningKey>[]) {
|
||||
final pubKey = crossSigningKey.publicKey;
|
||||
if (pubKey != null) {
|
||||
Logs().d(
|
||||
'Migrate cross signing key with usage ${crossSigningKey.usage} and verified ${crossSigningKey.directVerified}...');
|
||||
await database.storeUserCrossSigningKey(
|
||||
userId,
|
||||
pubKey,
|
||||
jsonEncode(crossSigningKey.toJson()),
|
||||
crossSigningKey.directVerified,
|
||||
crossSigningKey.blocked,
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logs().e('Unable to migrate OLM sessions!', e, s);
|
||||
}
|
||||
Logs().d('Migrate Device Keys...');
|
||||
final userDeviceKeys = await legacyDatabase.getUserDeviceKeys(this);
|
||||
for (final userId in userDeviceKeys.keys) {
|
||||
Logs().d('Migrate Device Keys of user $userId...');
|
||||
final deviceKeysList = userDeviceKeys[userId];
|
||||
for (final crossSigningKey
|
||||
in deviceKeysList?.crossSigningKeys.values ?? <CrossSigningKey>[]) {
|
||||
final pubKey = crossSigningKey.publicKey;
|
||||
if (pubKey != null) {
|
||||
Logs().d(
|
||||
'Migrate cross signing key with usage ${crossSigningKey.usage} and verified ${crossSigningKey.directVerified}...');
|
||||
await database.storeUserCrossSigningKey(
|
||||
|
||||
if (deviceKeysList != null) {
|
||||
for (final deviceKeys in deviceKeysList.deviceKeys.values) {
|
||||
final deviceId = deviceKeys.deviceId;
|
||||
if (deviceId != null) {
|
||||
Logs().d('Migrate device keys for ${deviceKeys.deviceId}...');
|
||||
await database.storeUserDeviceKey(
|
||||
userId,
|
||||
pubKey,
|
||||
jsonEncode(crossSigningKey.toJson()),
|
||||
crossSigningKey.directVerified,
|
||||
crossSigningKey.blocked,
|
||||
deviceId,
|
||||
jsonEncode(deviceKeys.toJson()),
|
||||
deviceKeys.directVerified,
|
||||
deviceKeys.blocked,
|
||||
deviceKeys.lastActive.millisecondsSinceEpoch,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceKeysList != null) {
|
||||
for (final deviceKeys in deviceKeysList.deviceKeys.values) {
|
||||
final deviceId = deviceKeys.deviceId;
|
||||
if (deviceId != null) {
|
||||
Logs().d('Migrate device keys for ${deviceKeys.deviceId}...');
|
||||
await database.storeUserDeviceKey(
|
||||
userId,
|
||||
deviceId,
|
||||
jsonEncode(deviceKeys.toJson()),
|
||||
deviceKeys.directVerified,
|
||||
deviceKeys.blocked,
|
||||
deviceKeys.lastActive.millisecondsSinceEpoch,
|
||||
);
|
||||
}
|
||||
}
|
||||
Logs().d('Migrate user device keys info...');
|
||||
await database.storeUserDeviceKeysInfo(
|
||||
userId, deviceKeysList.outdated);
|
||||
}
|
||||
Logs().d('Migrate user device keys info...');
|
||||
await database.storeUserDeviceKeysInfo(userId, deviceKeysList.outdated);
|
||||
}
|
||||
Logs().d('Migrate inbound group sessions...');
|
||||
try {
|
||||
final sessions = await legacyDatabase.getAllInboundGroupSessions();
|
||||
for (var i = 0; i < sessions.length; i++) {
|
||||
Logs().d('$i / ${sessions.length}');
|
||||
final session = sessions[i];
|
||||
await database.storeInboundGroupSession(
|
||||
session.roomId,
|
||||
session.sessionId,
|
||||
session.pickle,
|
||||
session.content,
|
||||
session.indexes,
|
||||
session.allowedAtIndex,
|
||||
session.senderKey,
|
||||
session.senderClaimedKeys,
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logs().e('Unable to migrate inbound group sessions!', e, s);
|
||||
}
|
||||
|
||||
await legacyDatabase.clear();
|
||||
}
|
||||
await legacyDatabase?.close();
|
||||
Logs().d('Migrate inbound group sessions...');
|
||||
try {
|
||||
final sessions = await legacyDatabase.getAllInboundGroupSessions();
|
||||
for (var i = 0; i < sessions.length; i++) {
|
||||
Logs().d('$i / ${sessions.length}');
|
||||
final session = sessions[i];
|
||||
await database.storeInboundGroupSession(
|
||||
session.roomId,
|
||||
session.sessionId,
|
||||
session.pickle,
|
||||
session.content,
|
||||
session.indexes,
|
||||
session.allowedAtIndex,
|
||||
session.senderKey,
|
||||
session.senderClaimedKeys,
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logs().e('Unable to migrate inbound group sessions!', e, s);
|
||||
}
|
||||
|
||||
await legacyDatabase.delete();
|
||||
|
||||
_initLock = false;
|
||||
if (migrateClient != null) {
|
||||
return init(
|
||||
waitForFirstSync: false,
|
||||
waitUntilLoadCompletedLoaded: false,
|
||||
);
|
||||
}
|
||||
return init(
|
||||
waitForFirstSync: false,
|
||||
waitUntilLoadCompletedLoaded: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -316,4 +316,8 @@ abstract class DatabaseApi {
|
|||
Future<void> storePresence(String userId, CachedPresence presence);
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1594,6 +1594,9 @@ class HiveCollectionsDatabase extends DatabaseApi {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete() => _collection.deleteFromDisk();
|
||||
}
|
||||
|
||||
class TupleKey {
|
||||
|
|
|
|||
|
|
@ -1474,6 +1474,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
|
|||
// see no need to implement this in a deprecated part
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete() => Hive.deleteFromDisk();
|
||||
}
|
||||
|
||||
dynamic _castValue(dynamic value) {
|
||||
|
|
|
|||
|
|
@ -7,13 +7,16 @@ import 'dart:indexed_db';
|
|||
class BoxCollection {
|
||||
final Database _db;
|
||||
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(
|
||||
String name,
|
||||
Set<String> boxNames, {
|
||||
Object? sqfliteDatabase,
|
||||
Object? sqfliteFactory,
|
||||
IdbFactory? idbFactory,
|
||||
}) async {
|
||||
idbFactory ??= window.indexedDB!;
|
||||
|
|
@ -24,7 +27,7 @@ class BoxCollection {
|
|||
db.createObjectStore(name, autoIncrement: true);
|
||||
}
|
||||
});
|
||||
return BoxCollection(db, boxNames);
|
||||
return BoxCollection(db, boxNames, name, idbFactory);
|
||||
}
|
||||
|
||||
Box<V> openBox<V>(String name) {
|
||||
|
|
@ -61,15 +64,19 @@ class BoxCollection {
|
|||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
final txn = _db.transaction(boxNames, 'readwrite');
|
||||
for (final name in boxNames) {
|
||||
_db.deleteObjectStore(name);
|
||||
unawaited(txn.objectStore(name).clear());
|
||||
}
|
||||
await txn.completed;
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
assert(_txnCache == null, 'Database closed while in transaction!');
|
||||
return _db.close();
|
||||
}
|
||||
|
||||
Future<void> delete() => _idbFactory.deleteDatabase(_name);
|
||||
}
|
||||
|
||||
class Box<V> {
|
||||
|
|
|
|||
|
|
@ -144,10 +144,15 @@ class MatrixSdkDatabase extends DatabaseApi {
|
|||
/// typed.
|
||||
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(
|
||||
this.name, {
|
||||
this.database,
|
||||
this.idbFactory,
|
||||
this.sqfliteFactory,
|
||||
this.maxFileSize = 0,
|
||||
this.fileStoragePath,
|
||||
this.deleteFilesAfterDuration,
|
||||
|
|
@ -179,6 +184,7 @@ class MatrixSdkDatabase extends DatabaseApi {
|
|||
_seenDeviceKeysBoxName,
|
||||
},
|
||||
sqfliteDatabase: database,
|
||||
sqfliteFactory: sqfliteFactory,
|
||||
idbFactory: idbFactory,
|
||||
);
|
||||
_clientBox = _collection.openBox<String>(
|
||||
|
|
@ -1596,4 +1602,7 @@ class MatrixSdkDatabase extends DatabaseApi {
|
|||
|
||||
return CachedPresence.fromJson(copyMap(rawPresence));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete() => _collection.delete();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
import 'dart:async';
|
||||
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
|
||||
/// a single interface for all platforms. API is inspired by Hive.
|
||||
class BoxCollection {
|
||||
final Database _db;
|
||||
final Set<String> boxNames;
|
||||
final DatabaseFactory? _factory;
|
||||
|
||||
BoxCollection(this._db, this.boxNames);
|
||||
BoxCollection(this._db, this.boxNames, this._factory);
|
||||
|
||||
static Future<BoxCollection> open(
|
||||
String name,
|
||||
Set<String> boxNames, {
|
||||
Object? sqfliteDatabase,
|
||||
DatabaseFactory? sqfliteFactory,
|
||||
dynamic idbFactory,
|
||||
}) async {
|
||||
if (sqfliteDatabase is! Database) {
|
||||
|
|
@ -28,7 +30,7 @@ class BoxCollection {
|
|||
batch.execute('CREATE INDEX IF NOT EXISTS k_index ON $name (k)');
|
||||
}
|
||||
await batch.commit(noResult: true);
|
||||
return BoxCollection(sqfliteDatabase, boxNames);
|
||||
return BoxCollection(sqfliteDatabase, boxNames, sqfliteFactory);
|
||||
}
|
||||
|
||||
Box<V> openBox<V>(String name) {
|
||||
|
|
@ -110,6 +112,9 @@ class BoxCollection {
|
|||
);
|
||||
|
||||
Future<void> close() => _db.close();
|
||||
|
||||
Future<void> delete() =>
|
||||
(_factory ?? databaseFactory).deleteDatabase(_db.path);
|
||||
}
|
||||
|
||||
class Box<V> {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ void main() {
|
|||
'testbox',
|
||||
boxNames,
|
||||
sqfliteDatabase: db,
|
||||
sqfliteFactory: databaseFactoryFfi,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -76,5 +77,9 @@ void main() {
|
|||
expect(await box.get('fluffy'), null);
|
||||
expect(await box.get('loki'), null);
|
||||
});
|
||||
|
||||
test('Box.delete', () async {
|
||||
await collection.delete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -472,6 +472,19 @@ void main() {
|
|||
test('Close', () async {
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,11 @@ Future<HiveCollectionsDatabase> getHiveCollectionsDatabase(Client? c) async {
|
|||
// ignore: deprecated_member_use_from_same_package
|
||||
Future<MatrixSdkDatabase> getMatrixSdkDatabase(Client? c) async {
|
||||
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();
|
||||
return db;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue