Skip to content

Commit 790d96b

Browse files
committed
db: Start generating schema versions for migrations
Generating schema_versions.g.dart is crucial because we otherwise only have access to the latest schema when running migrations. That would mean that if we later drop or alter a table or column mentioned in an existing migration, the meaning of the existing migration would change. The affected existing migration would then migrate to an unintended state that doesn't match what later migrations expect, and it or a later migration might fail. See also discussion: zulip#1248 (comment) --- An alternative to using all these commands for generating files is `dart run drift_dev make-migrations`, which is essentially a wrapper for the `schema {dump,generate,steps}` subcommands. `make-migrations` let us manage multiple database schemas by configuring them with `build.yaml`, and it dictates the which subdirectories the generated files will be created at. Because `make-migrations` does not offer the same level of customizations to designate exactly where the output files will be, opting out from it for now. We can revisit this if it starts to offer features that are not available with the subcommands, or that we find the need for managing multiple databases. See also: https://drift.simonbinder.eu/migrations/step_by_step/#manual-generation Signed-off-by: Zixuan James Li <[email protected]>
1 parent 43eb74f commit 790d96b

File tree

3 files changed

+124
-4
lines changed

3 files changed

+124
-4
lines changed

lib/model/database.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import 'package:drift/drift.dart';
22
import 'package:drift/remote.dart';
33
import 'package:sqlite3/common.dart';
44

5+
import 'schema_versions.g.dart';
6+
57
part 'database.g.dart';
68

79
/// The table of [Account] records in the app's database.
@@ -85,7 +87,8 @@ class AppDatabase extends _$AppDatabase {
8587
assert(1 <= from && from <= to && to <= schemaVersion);
8688

8789
if (from < 2 && 2 <= to) {
88-
await m.addColumn(accounts, accounts.ackedPushToken);
90+
final schema = Schema2(database: m.database);
91+
await m.addColumn(schema.accounts, schema.accounts.ackedPushToken);
8992
}
9093
// New migrations go here.
9194
}

lib/model/schema_versions.g.dart

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// dart format width=80
2+
import 'package:drift/internal/versioned_schema.dart' as i0;
3+
import 'package:drift/drift.dart' as i1;
4+
import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import
5+
6+
// GENERATED BY drift_dev, DO NOT MODIFY.
7+
final class Schema2 extends i0.VersionedSchema {
8+
Schema2({required super.database}) : super(version: 2);
9+
@override
10+
late final List<i1.DatabaseSchemaEntity> entities = [
11+
accounts,
12+
];
13+
late final Shape0 accounts = Shape0(
14+
source: i0.VersionedTable(
15+
entityName: 'accounts',
16+
withoutRowId: false,
17+
isStrict: false,
18+
tableConstraints: [
19+
'UNIQUE(realm_url, user_id)',
20+
'UNIQUE(realm_url, email)',
21+
],
22+
columns: [
23+
_column_0,
24+
_column_1,
25+
_column_2,
26+
_column_3,
27+
_column_4,
28+
_column_5,
29+
_column_6,
30+
_column_7,
31+
_column_8,
32+
],
33+
attachedDatabase: database,
34+
),
35+
alias: null);
36+
}
37+
38+
class Shape0 extends i0.VersionedTable {
39+
Shape0({required super.source, required super.alias}) : super.aliased();
40+
i1.GeneratedColumn<int> get id =>
41+
columnsByName['id']! as i1.GeneratedColumn<int>;
42+
i1.GeneratedColumn<String> get realmUrl =>
43+
columnsByName['realm_url']! as i1.GeneratedColumn<String>;
44+
i1.GeneratedColumn<int> get userId =>
45+
columnsByName['user_id']! as i1.GeneratedColumn<int>;
46+
i1.GeneratedColumn<String> get email =>
47+
columnsByName['email']! as i1.GeneratedColumn<String>;
48+
i1.GeneratedColumn<String> get apiKey =>
49+
columnsByName['api_key']! as i1.GeneratedColumn<String>;
50+
i1.GeneratedColumn<String> get zulipVersion =>
51+
columnsByName['zulip_version']! as i1.GeneratedColumn<String>;
52+
i1.GeneratedColumn<String> get zulipMergeBase =>
53+
columnsByName['zulip_merge_base']! as i1.GeneratedColumn<String>;
54+
i1.GeneratedColumn<int> get zulipFeatureLevel =>
55+
columnsByName['zulip_feature_level']! as i1.GeneratedColumn<int>;
56+
i1.GeneratedColumn<String> get ackedPushToken =>
57+
columnsByName['acked_push_token']! as i1.GeneratedColumn<String>;
58+
}
59+
60+
i1.GeneratedColumn<int> _column_0(String aliasedName) =>
61+
i1.GeneratedColumn<int>('id', aliasedName, false,
62+
hasAutoIncrement: true,
63+
type: i1.DriftSqlType.int,
64+
defaultConstraints:
65+
i1.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
66+
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
67+
i1.GeneratedColumn<String>('realm_url', aliasedName, false,
68+
type: i1.DriftSqlType.string);
69+
i1.GeneratedColumn<int> _column_2(String aliasedName) =>
70+
i1.GeneratedColumn<int>('user_id', aliasedName, false,
71+
type: i1.DriftSqlType.int);
72+
i1.GeneratedColumn<String> _column_3(String aliasedName) =>
73+
i1.GeneratedColumn<String>('email', aliasedName, false,
74+
type: i1.DriftSqlType.string);
75+
i1.GeneratedColumn<String> _column_4(String aliasedName) =>
76+
i1.GeneratedColumn<String>('api_key', aliasedName, false,
77+
type: i1.DriftSqlType.string);
78+
i1.GeneratedColumn<String> _column_5(String aliasedName) =>
79+
i1.GeneratedColumn<String>('zulip_version', aliasedName, false,
80+
type: i1.DriftSqlType.string);
81+
i1.GeneratedColumn<String> _column_6(String aliasedName) =>
82+
i1.GeneratedColumn<String>('zulip_merge_base', aliasedName, true,
83+
type: i1.DriftSqlType.string);
84+
i1.GeneratedColumn<int> _column_7(String aliasedName) =>
85+
i1.GeneratedColumn<int>('zulip_feature_level', aliasedName, false,
86+
type: i1.DriftSqlType.int);
87+
i1.GeneratedColumn<String> _column_8(String aliasedName) =>
88+
i1.GeneratedColumn<String>('acked_push_token', aliasedName, true,
89+
type: i1.DriftSqlType.string);
90+
i0.MigrationStepWithVersion migrationSteps({
91+
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
92+
}) {
93+
return (currentVersion, database) async {
94+
switch (currentVersion) {
95+
case 1:
96+
final schema = Schema2(database: database);
97+
final migrator = i1.Migrator(database, schema);
98+
await from1To2(migrator, schema);
99+
return 2;
100+
default:
101+
throw ArgumentError.value('Unknown migration from $currentVersion');
102+
}
103+
};
104+
}
105+
106+
i1.OnUpgrade stepByStep({
107+
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
108+
}) =>
109+
i0.VersionedSchema.stepByStepHelper(
110+
step: migrationSteps(
111+
from1To2: from1To2,
112+
));

tools/check

+8-3
Original file line numberDiff line numberDiff line change
@@ -378,13 +378,15 @@ run_l10n() {
378378

379379
run_drift() {
380380
local schema_dir=test/model/schemas/
381+
local migration_helper_path=lib/model/schema_versions.g.dart
382+
local outputs=( "${schema_dir}" "${migration_helper_path}" )
381383

382384
# Omitted from this check:
383385
# pubspec.{yaml,lock} tools/check
384-
files_check lib/model/database{,.g}.dart "${schema_dir}" \
386+
files_check lib/model/database{,.g}.dart "${outputs[@]}" \
385387
|| return 0
386388

387-
check_no_uncommitted_or_untracked "${schema_dir}" \
389+
check_no_uncommitted_or_untracked "${outputs[@]}" \
388390
|| return
389391

390392
dart run drift_dev schema dump \
@@ -393,8 +395,11 @@ run_drift() {
393395
dart run drift_dev schema generate --data-classes --companions \
394396
"${schema_dir}" "${schema_dir}" \
395397
|| return
398+
dart run drift_dev schema steps \
399+
"${schema_dir}" "${migration_helper_path}" \
400+
|| return
396401

397-
check_no_changes "schema updates" "${schema_dir}"
402+
check_no_changes "schema or migration-helper updates" "${outputs[@]}"
398403
}
399404

400405
filter_flutter_pub_run_output() {

0 commit comments

Comments
 (0)