Fix moved files are removed from DB in some case

This commit is contained in:
Ming Ming 2022-12-14 00:41:46 +08:00
parent 8c85f98293
commit 5e06136b8a
2 changed files with 131 additions and 11 deletions

View file

@ -129,14 +129,12 @@ class FileSqliteCacheUpdater {
throw StateError("Row ID for dir is null");
}
final dirChildRowIdQuery = db.selectOnly(db.dirFiles)
..addColumns([db.dirFiles.child])
..where(db.dirFiles.dir.equals(_dirRowId))
..orderBy([sql.OrderingTerm.asc(db.dirFiles.rowId)]);
final dirChildRowIds =
await dirChildRowIdQuery.map((r) => r.read(db.dirFiles.child)!).get();
final diff = list_util.diff(
dirChildRowIds, _childRowIds.sorted(Comparable.compare));
final dirFileQuery = db.select(db.dirFiles)
..where((t) => t.dir.equals(_dirRowId))
..orderBy([(t) => sql.OrderingTerm.asc(t.rowId)]);
final dirFiles = await dirFileQuery.get();
final diff = list_util.diff(dirFiles.map((e) => e.child),
_childRowIds.sorted(Comparable.compare));
if (diff.item1.isNotEmpty) {
await db.batch((batch) {
// insert new children
@ -145,9 +143,34 @@ class FileSqliteCacheUpdater {
});
}
if (diff.item2.isNotEmpty) {
// delete obsolete children
await _removeSqliteFiles(db, dbAccount, diff.item2);
await db.cleanUpDanglingFiles();
// remove entries from the DirFiles table first
await diff.item2.withPartitionNoReturn((sublist) async {
final deleteQuery = db.delete(db.dirFiles)
..where((t) => t.child.isIn(sublist))
..where((t) =>
t.dir.equals(_dirRowId) | t.dir.equalsExp(db.dirFiles.child));
await deleteQuery.go();
}, sql.maxByFileIdsSize);
// select files having another dir parent under this account (i.e.,
// moved files)
final moved = await diff.item2.withPartition((sublist) async {
final query = db.selectOnly(db.dirFiles).join([
sql.innerJoin(db.accountFiles,
db.accountFiles.file.equalsExp(db.dirFiles.dir)),
]);
query
..addColumns([db.dirFiles.child])
..where(db.accountFiles.account.equals(dbAccount.rowId))
..where(db.dirFiles.child.isIn(sublist));
return query.map((r) => r.read(db.dirFiles.child)!).get();
}, sql.maxByFileIdsSize);
final removed = diff.item2.where((e) => !moved.contains(e)).toList();
if (removed.isNotEmpty) {
// delete obsolete children
await _removeSqliteFiles(db, dbAccount, removed);
await db.cleanUpDanglingFiles();
}
}
});
}

View file

@ -33,6 +33,9 @@ void main() {
test("too many files", _updaterTooManyFiles,
timeout: const Timeout(Duration(minutes: 2)),
skip: "too slow on gitlab");
test("moved file (to dir in front of the from dir)",
_updaterMovedFileToFront);
test("moved file (to dir behind the from dir)", _updaterMovedFileToBehind);
});
test("FileSqliteCacheEmptier", _emptier);
}
@ -541,6 +544,100 @@ Future<void> _updaterTooManyFiles() async {
// expect here
}
/// Moved a file from test2 to test1, where test2 is sorted behind test1
///
/// Expect: file moved
Future<void> _updaterMovedFileToFront() async {
final account = util.buildAccount();
final files = (util.FilesBuilder()
..addDir("admin")
..addDir("admin/test1")
..addDir("admin/test2")
..addJpeg("admin/test2/test1.jpg"))
.build();
final c = DiContainer(
sqliteDb: util.buildTestDb(),
);
addTearDown(() => c.sqliteDb.close());
await c.sqliteDb.transaction(() async {
await c.sqliteDb.insertAccountOf(account);
await util.insertFiles(c.sqliteDb, account, files);
await util.insertDirRelation(
c.sqliteDb, account, files[0], files.slice(1, 3));
await util.insertDirRelation(c.sqliteDb, account, files[1], []);
await util.insertDirRelation(c.sqliteDb, account, files[2], [files[3]]);
});
final movedFile = files[3].copyWith(
path: "remote.php/dav/files/admin/test1/test1.jpg",
);
await FileSqliteCacheUpdater(c)(
account,
files[1],
remote: [files[1], movedFile],
);
await FileSqliteCacheUpdater(c)(
account,
files[2],
remote: [files[2]],
);
expect(
await util.listSqliteDbFiles(c.sqliteDb),
{...files.slice(0, 3), movedFile},
);
final dirResult = await util.listSqliteDbDirs(c.sqliteDb);
expect(dirResult[files[0]], {...files.slice(0, 3)});
expect(dirResult[files[1]], {files[1], movedFile});
expect(dirResult[files[2]], {files[2]});
}
/// Moved a file from test1 to test2, where test1 is sorted in front of test2
///
/// Expect: file moved
Future<void> _updaterMovedFileToBehind() async {
final account = util.buildAccount();
final files = (util.FilesBuilder()
..addDir("admin")
..addDir("admin/test1")
..addDir("admin/test2")
..addJpeg("admin/test1/test1.jpg"))
.build();
final c = DiContainer(
sqliteDb: util.buildTestDb(),
);
addTearDown(() => c.sqliteDb.close());
await c.sqliteDb.transaction(() async {
await c.sqliteDb.insertAccountOf(account);
await util.insertFiles(c.sqliteDb, account, files);
await util.insertDirRelation(
c.sqliteDb, account, files[0], files.slice(1, 3));
await util.insertDirRelation(c.sqliteDb, account, files[1], [files[3]]);
await util.insertDirRelation(c.sqliteDb, account, files[2], []);
});
final movedFile = files[3].copyWith(
path: "remote.php/dav/files/admin/test2/test1.jpg",
);
await FileSqliteCacheUpdater(c)(
account,
files[1],
remote: [files[1]],
);
await FileSqliteCacheUpdater(c)(
account,
files[2],
remote: [files[2], movedFile],
);
expect(
await util.listSqliteDbFiles(c.sqliteDb),
{...files.slice(0, 3), movedFile},
);
final dirResult = await util.listSqliteDbDirs(c.sqliteDb);
expect(dirResult[files[0]], {...files.slice(0, 3)});
expect(dirResult[files[1]], {files[1]});
expect(dirResult[files[2]], {files[2], movedFile});
}
/// Empty dir in cache
///
/// Expect: dir removed from DirFiles table;