Migrate wrong bestDateTime in DB on upgrade

This commit is contained in:
Ming Ming 2022-11-07 00:34:54 +08:00
parent 72ebbfdd08
commit 62b28c67a0
2 changed files with 179 additions and 23 deletions

View file

@ -0,0 +1,81 @@
import 'package:drift/drift.dart' as sql;
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/entity/sqlite_table.dart' as sql;
import 'package:nc_photos/entity/sqlite_table_extension.dart' as sql;
import 'package:nc_photos/iterable_extension.dart';
import 'package:tuple/tuple.dart';
class CompatV55 {
static Future<void> migrateDb(
sql.SqliteDb db, {
void Function(int current, int count)? onProgress,
}) {
return db.use((db) async {
final countExp = db.accountFiles.rowId.count();
final countQ = db.selectOnly(db.accountFiles)..addColumns([countExp]);
final count = await countQ.map((r) => r.read<int>(countExp)).getSingle();
onProgress?.call(0, count);
final needUpdates = <Tuple2<int, DateTime>>[];
for (var i = 0; i < count; i += 1000) {
final q = db.select(db.files).join([
sql.innerJoin(
db.accountFiles, db.accountFiles.file.equalsExp(db.files.rowId)),
sql.innerJoin(db.images,
db.images.accountFile.equalsExp(db.accountFiles.rowId)),
]);
q
..orderBy([
sql.OrderingTerm(
expression: db.accountFiles.rowId,
mode: sql.OrderingMode.asc,
),
])
..limit(1000, offset: i);
final dbFiles = await q
.map((r) => sql.CompleteFile(
r.readTable(db.files),
r.readTable(db.accountFiles),
r.readTable(db.images),
null,
null,
))
.get();
for (final f in dbFiles) {
final bestDateTime = file_util.getBestDateTime(
overrideDateTime: f.accountFile.overrideDateTime,
dateTimeOriginal: f.image?.dateTimeOriginal,
lastModified: f.file.lastModified,
);
if (f.accountFile.bestDateTime != bestDateTime) {
// need update
needUpdates.add(Tuple2(f.accountFile.rowId, bestDateTime));
}
}
onProgress?.call(i, count);
}
_log.info("[migrateDb] ${needUpdates.length} rows require updating");
if (kDebugMode) {
_log.fine(
"[migrateDb] ${needUpdates.map((e) => e.item1).toReadableString()}");
}
await db.batch((batch) {
for (final pair in needUpdates) {
batch.update(
db.accountFiles,
sql.AccountFilesCompanion(
bestDateTime: sql.Value(pair.item2),
),
where: (sql.$AccountFilesTable table) =>
table.rowId.equals(pair.item1),
);
}
});
});
}
static final _log = Logger("use_case.compat.v55.CompatV55");
}

View file

@ -1,10 +1,12 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kiwi/kiwi.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/sqlite_table_extension.dart' as sql;
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/mobile/android/activity.dart';
import 'package:nc_photos/platform/k.dart' as platform_k;
@ -12,6 +14,7 @@ import 'package:nc_photos/pref.dart';
import 'package:nc_photos/theme.dart';
import 'package:nc_photos/use_case/compat/v29.dart';
import 'package:nc_photos/use_case/compat/v46.dart';
import 'package:nc_photos/use_case/compat/v55.dart';
import 'package:nc_photos/widget/changelog.dart';
import 'package:nc_photos/widget/home.dart';
import 'package:nc_photos/widget/setup.dart';
@ -68,10 +71,11 @@ class _SplashState extends State<Splash> {
Widget _buildContent(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Center(
child: Stack(
children: [
Column(
child: Stack(
fit: StackFit.expand,
children: [
Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -88,25 +92,36 @@ class _SplashState extends State<Splash> {
),
],
),
if (_isUpgrading)
Positioned(
left: 0,
right: 0,
bottom: 64,
child: Column(
children: const [
SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(),
),
SizedBox(height: 8),
Text("Updating"),
],
),
),
],
),
),
if (_isUpgrading)
BlocBuilder<_UpgradeCubit, _UpgradeState>(
bloc: _upgradeCubit,
builder: (context, state) {
return Positioned(
left: 0,
right: 0,
bottom: 64,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(state.text),
const SizedBox(height: 8),
if (state.count == null)
const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(),
)
else
LinearProgressIndicator(
value: state.current / state.count!,
),
],
),
);
},
),
],
),
);
}
@ -164,6 +179,9 @@ class _SplashState extends State<Splash> {
if (lastVersion < 460) {
await _upgrade46(lastVersion);
}
if (lastVersion < 550) {
await _upgrade55(lastVersion);
}
}
Future<void> _upgrade29(int lastVersion) async {
@ -204,6 +222,33 @@ class _SplashState extends State<Splash> {
}
}
Future<void> _upgrade55(int lastVersion) async {
final c = KiwiContainer().resolve<DiContainer>();
try {
_log.info("[_upgrade55] migrate DB");
await CompatV55.migrateDb(
c.sqliteDb,
onProgress: (current, count) {
_upgradeCubit.setState(
L10n.global().migrateDatabaseProcessingNotification,
current,
count,
);
},
);
} catch (e, stackTrace) {
_log.shout("[_upgrade55] Failed while migrateDb", e, stackTrace);
await c.sqliteDb.use((db) async {
await db.truncate();
final accounts = Pref().getAccounts3Or([]);
for (final a in accounts) {
await db.insertAccountOf(a);
}
});
}
_upgradeCubit.setIntermediate();
}
Future<void> _showChangelogIfAvailable(int lastVersion) async {
if (Changelog.hasContent(lastVersion)) {
try {
@ -222,6 +267,36 @@ class _SplashState extends State<Splash> {
final _changelogCompleter = Completer();
var _isUpgrading = false;
late final _upgradeCubit = _UpgradeCubit();
static final _log = Logger("widget.splash._SplashState");
}
class _UpgradeState {
const _UpgradeState(String text, int current, int count)
: this._(text, current, count);
const _UpgradeState.intermediate([String? text])
: this._(text ?? "Updating", 0, null);
const _UpgradeState._(this.text, this.current, this.count);
@override
String toString() => "_UpgradeState {"
"current: $current, "
"count: $count, "
"}";
final String text;
final int current;
final int? count;
}
class _UpgradeCubit extends Cubit<_UpgradeState> {
_UpgradeCubit() : super(const _UpgradeState.intermediate());
void setIntermediate() => emit(const _UpgradeState.intermediate());
void setState(String text, int current, int count) =>
emit(_UpgradeState(text, current, count));
}