From 61e33b777b476a1d4743029e93961852d1168d0e Mon Sep 17 00:00:00 2001 From: Ivan Chabanov Date: Fri, 20 Feb 2026 11:35:04 +0300 Subject: [PATCH 1/4] Add `column_check` method inside db --- lib/core/db/db.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/core/db/db.dart b/lib/core/db/db.dart index 527e2275..51a089ce 100644 --- a/lib/core/db/db.dart +++ b/lib/core/db/db.dart @@ -46,7 +46,13 @@ class Db extends _$Db with InfraLogger { await m.createTable(schema.geoAssetEntries); }, from3To4: (m, schema) async { - await m.addColumn(schema.profileEntries, schema.profileEntries.testUrl); + final columnExists = await _columnExists( + schema.profileEntries.actualTableName, + schema.profileEntries.testUrl.name, + ); + if (!columnExists) { + await m.addColumn(schema.profileEntries, schema.profileEntries.testUrl); + } }, from4To5: (m, schema) async { await m.deleteTable('geo_asset_entries'); @@ -58,6 +64,11 @@ class Db extends _$Db with InfraLogger { ), ); } + + Future _columnExists(String table, String column) async { + final result = await customSelect('PRAGMA table_info($table);').get(); + return result.any((row) => row.data['name'] == column); + } } @DataClassName('ProfileEntry') From e6e1f5586a165b1151aa5e1342607eb921f7875e Mon Sep 17 00:00:00 2001 From: Ivan Chabanov Date: Sat, 21 Feb 2026 14:52:44 +0300 Subject: [PATCH 2/4] Rename column check for more explicit one --- lib/core/db/db.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/db/db.dart b/lib/core/db/db.dart index 51a089ce..3787af88 100644 --- a/lib/core/db/db.dart +++ b/lib/core/db/db.dart @@ -46,11 +46,11 @@ class Db extends _$Db with InfraLogger { await m.createTable(schema.geoAssetEntries); }, from3To4: (m, schema) async { - final columnExists = await _columnExists( + final testUrlExists = await _columnExists( schema.profileEntries.actualTableName, schema.profileEntries.testUrl.name, ); - if (!columnExists) { + if (!testUrlExists) { await m.addColumn(schema.profileEntries, schema.profileEntries.testUrl); } }, From 2831dca94853938b7b8452c2df4b18b1d40681e7 Mon Sep 17 00:00:00 2001 From: Ivan Chabanov Date: Sat, 21 Feb 2026 14:53:10 +0300 Subject: [PATCH 3/4] Add column checks on 4to5 migration --- lib/core/db/db.dart | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/core/db/db.dart b/lib/core/db/db.dart index 3787af88..46a570ea 100644 --- a/lib/core/db/db.dart +++ b/lib/core/db/db.dart @@ -57,8 +57,23 @@ class Db extends _$Db with InfraLogger { from4To5: (m, schema) async { await m.deleteTable('geo_asset_entries'); await m.renameColumn(schema.profileEntries, 'test_url', schema.profileEntries.profileOverride); - await m.addColumn(schema.profileEntries, schema.profileEntries.userOverride); - await m.addColumn(schema.profileEntries, schema.profileEntries.populatedHeaders); + + final userOverrideExists = await _columnExists( + schema.profileEntries.actualTableName, + schema.profileEntries.userOverride.name, + ); + if (!userOverrideExists) { + await m.addColumn(schema.profileEntries, schema.profileEntries.userOverride); + } + + final populatedHeadersExists = await _columnExists( + schema.profileEntries.actualTableName, + schema.profileEntries.populatedHeaders.name, + ); + if (!populatedHeadersExists) { + await m.addColumn(schema.profileEntries, schema.profileEntries.populatedHeaders); + } + await m.createTable(schema.appProxyEntries); }, ), From 50411653faa5d28ea553a472eaffd74b6a2c69cd Mon Sep 17 00:00:00 2001 From: Ivan Chabanov Date: Sat, 21 Feb 2026 15:42:42 +0300 Subject: [PATCH 4/4] Add test in migration that column check works properly --- test/drift/db/migration_test.dart | 60 +++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/drift/db/migration_test.dart b/test/drift/db/migration_test.dart index fed3b51c..2b5440ae 100644 --- a/test/drift/db/migration_test.dart +++ b/test/drift/db/migration_test.dart @@ -8,6 +8,8 @@ import 'generated/schema.dart'; import 'generated/schema_v1.dart' as v1; import 'generated/schema_v2.dart' as v2; +import 'generated/schema_v3.dart' as v3; +import 'generated/schema_v4.dart' as v4; void main() { driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; @@ -68,4 +70,62 @@ void main() { }, ); }); + + group('_columnExists-backed migrations', () { + test('migration from v3 to v4 adds test_url when missing', () async { + final schema = await verifier.schemaAt(3); + addTearDown(() => schema.rawDatabase.dispose()); + + final oldDb = v3.DatabaseAtV3(schema.newConnection()); + final oldColumns = await oldDb + .customSelect('PRAGMA table_info(profile_entries);') + .get(); + + expect( + oldColumns.where((row) => row.data['name'] == 'test_url'), + isEmpty, + ); + await oldDb.close(); + + final migratedDb = Db(schema.newConnection()); + await verifier.migrateAndValidate(migratedDb, 4); + await migratedDb.close(); + + final newDb = v4.DatabaseAtV4(schema.newConnection()); + final newColumns = await newDb + .customSelect('PRAGMA table_info(profile_entries);') + .get(); + expect( + newColumns.where((row) => row.data['name'] == 'test_url'), + hasLength(1), + ); + await newDb.close(); + }); + + test( + 'migration from v3 to v4 skips adding test_url when it already exists', + () async { + final schema = await verifier.schemaAt(3); + addTearDown(() => schema.rawDatabase.dispose()); + + schema.rawDatabase.execute( + 'ALTER TABLE profile_entries ADD COLUMN test_url TEXT NULL;', + ); + + final migratedDb = Db(schema.newConnection()); + await verifier.migrateAndValidate(migratedDb, 4); + await migratedDb.close(); + + final newDb = v4.DatabaseAtV4(schema.newConnection()); + final newColumns = await newDb + .customSelect('PRAGMA table_info(profile_entries);') + .get(); + expect( + newColumns.where((row) => row.data['name'] == 'test_url'), + hasLength(1), + ); + await newDb.close(); + }, + ); + }); }