diff --git a/docs/pages/docs/Advanced Features/migrations.md b/docs/pages/docs/Advanced Features/migrations.md index 5f658156..541d454c 100644 --- a/docs/pages/docs/Advanced Features/migrations.md +++ b/docs/pages/docs/Advanced Features/migrations.md @@ -271,21 +271,29 @@ To begin, let's create the first schema representation: ``` $ mkdir drift_schemas -$ dart run drift_dev schema dump lib/database/database.dart drift_schemas/drift_schema_v1.json +$ dart run drift_dev schema dump lib/database/database.dart drift_schemas/ ``` This instructs the generator to look at the database defined in `lib/database/database.dart` and extract its schema into the new folder. After making a change to your database schema, you can run the command again. For instance, let's say we -made a change to our tables and increased the `schemaVersion` to `2`. We would then run: +made a change to our tables and increased the `schemaVersion` to `2`. To dump the new schema, just run the +command again: ``` -$ dart run drift_dev schema dump lib/database/database.dart drift_schemas/drift_schema_v2.json +$ dart run drift_dev schema dump lib/database/database.dart drift_schemas/ ``` You'll need to run this command every time you change the schema of your database and increment the `schemaVersion`. -Remember to name the files `drift_schema_vX.json`, where `X` is the current `schemaVersion` of your database. + +Drift will name the files in the folder `drift_schema_vX.json`, where `X` is the current `schemaVersion` of your +database. +If drift is unable to extract the version from your `schemaVersion` getter, provide the full path explicitly: + +``` +$ dart run drift_dev schema dump lib/database/database.dart drift_schemas/drift_schema_v3.json +``` #### Generating test code diff --git a/drift_dev/lib/src/analyzer/dart/parser.dart b/drift_dev/lib/src/analyzer/dart/parser.dart index 17d0f16c..773b505c 100644 --- a/drift_dev/lib/src/analyzer/dart/parser.dart +++ b/drift_dev/lib/src/analyzer/dart/parser.dart @@ -55,16 +55,20 @@ class MoorDartParser { } @visibleForTesting - Expression? returnExpressionOfMethod(MethodDeclaration method) { + Expression? returnExpressionOfMethod(MethodDeclaration method, + {bool reportErrorOnFailure = true}) { final body = method.body; if (body is! ExpressionFunctionBody) { - step.reportError(ErrorInDartCode( - affectedElement: method.declaredElement, - severity: Severity.criticalError, - message: 'This method must have an expression body ' - '(use => instead of {return ...})', - )); + if (reportErrorOnFailure) { + step.reportError(ErrorInDartCode( + affectedElement: method.declaredElement, + severity: Severity.criticalError, + message: 'This method must have an expression body ' + '(use => instead of {return ...})', + )); + } + return null; } diff --git a/drift_dev/lib/src/analyzer/dart/use_moor_parser.dart b/drift_dev/lib/src/analyzer/dart/use_moor_parser.dart index 8a3679e7..bec9bcaa 100644 --- a/drift_dev/lib/src/analyzer/dart/use_moor_parser.dart +++ b/drift_dev/lib/src/analyzer/dart/use_moor_parser.dart @@ -52,6 +52,7 @@ class UseMoorParser { daos: daoTypes, declaredIncludes: includes, declaredQueries: parsedQueries, + schemaVersion: await _readSchemaVersion(element), ); } @@ -63,4 +64,30 @@ class UseMoorParser { .toList() ?? []; } + + Future _readSchemaVersion(ClassElement dbClass) async { + final element = dbClass.thisType.getGetter('schemaVersion')?.variable; + if (element == null) return null; + + final helper = MoorDartParser(step); + + if (element.isSynthetic) { + // Getter, read from `=>` body if possible. + final expr = helper.returnExpressionOfMethod( + await helper.loadElementDeclaration(element.getter!) + as MethodDeclaration, + reportErrorOnFailure: false); + if (expr is IntegerLiteral) { + return expr.value; + } + } else { + final astField = + await helper.loadElementDeclaration(element) as VariableDeclaration; + if (astField.initializer is IntegerLiteral) { + return (astField.initializer as IntegerLiteral).value; + } + } + + return null; + } } diff --git a/drift_dev/lib/src/cli/commands/schema/dump.dart b/drift_dev/lib/src/cli/commands/schema/dump.dart index b32b9f62..6e1f49de 100644 --- a/drift_dev/lib/src/cli/commands/schema/dump.dart +++ b/drift_dev/lib/src/cli/commands/schema/dump.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:drift_dev/src/analyzer/runner/results.dart'; import 'package:drift_dev/src/services/schema/schema_files.dart'; +import 'package:path/path.dart'; import '../../cli.dart'; @@ -51,6 +52,33 @@ class DumpSchemaCommand extends Command { final db = result.declaredDatabases.single; final writer = SchemaWriter(db); - await File(rest[1]).writeAsString(json.encode(writer.createSchemaJson())); + var target = rest[1]; + // This command is most commonly used to write into + // `/drift_schema_vx.json`. When we get a directory as a second arg, + // try to infer the file name. + if (await FileSystemEntity.isDirectory(target) || + !target.endsWith('.json')) { + final version = db.schemaVersion; + + if (version == null) { + // Couldn't read schema from database, so fail. + usageException( + 'Target is a directory and the schema version could not be read from ' + 'the database class. Please use a full filename (e.g. ' + '`$target/drift_schema_v3.json`)', + ); + } + + target = join(target, 'drift_schema_v$version.json'); + } + + final file = File(target); + final parent = file.parent; + if (!await parent.exists()) { + await parent.create(recursive: true); + } + + await File(target).writeAsString(json.encode(writer.createSchemaJson())); + print('Wrote to $target'); } } diff --git a/drift_dev/lib/src/model/database.dart b/drift_dev/lib/src/model/database.dart index 3e3600e2..289baad2 100644 --- a/drift_dev/lib/src/model/database.dart +++ b/drift_dev/lib/src/model/database.dart @@ -59,8 +59,16 @@ abstract class BaseMoorAccessor implements HasDeclaration { class Database extends BaseMoorAccessor { final List daos; + /// If the source database class overrides `schemaVersion` and returns a + /// simple integer literal, stores that version. + /// + /// This is optionally used by the migration tooling to store the schema in a + /// versioned file. + int? schemaVersion; + Database({ this.daos = const [], + this.schemaVersion, DatabaseOrDaoDeclaration? declaration, List declaredTables = const [], List declaredViews = const [], diff --git a/drift_dev/test/analyzer/dart/database_parser_test.dart b/drift_dev/test/analyzer/dart/database_parser_test.dart new file mode 100644 index 00000000..01b4708b --- /dev/null +++ b/drift_dev/test/analyzer/dart/database_parser_test.dart @@ -0,0 +1,46 @@ +import 'package:drift_dev/src/analyzer/runner/results.dart'; +import 'package:test/test.dart'; + +import '../utils.dart'; + +void main() { + test('parses schema version getter', () async { + final state = TestState.withContent({ + 'a|lib/main.dart': r''' +import 'package:drift/drift.dart'; + +@DriftDatabase() +class MyDatabase extends _$MyDatabase { + @override + int get schemaVersion => 13; +} +''', + }); + addTearDown(state.close); + + final file = (await state.analyze('package:a/main.dart')).currentResult!; + final db = (file as ParsedDartFile).declaredDatabases.single; + + expect(db.schemaVersion, 13); + }); + + test('parses schema version field', () async { + final state = TestState.withContent({ + 'a|lib/main.dart': r''' +import 'package:drift/drift.dart'; + +@DriftDatabase() +class MyDatabase extends _$MyDatabase { + @override + final int schemaVersion = 23; +} +''', + }); + addTearDown(state.close); + + final file = (await state.analyze('package:a/main.dart')).currentResult!; + final db = (file as ParsedDartFile).declaredDatabases.single; + + expect(db.schemaVersion, 23); + }); +} diff --git a/extras/migrations_example/README.md b/extras/migrations_example/README.md index 57f3047a..e98231d5 100644 --- a/extras/migrations_example/README.md +++ b/extras/migrations_example/README.md @@ -9,7 +9,7 @@ See `test/migration_test.dart` on how to use the generated verification code. After adapting a schema and incrementing the `schemaVersion` in the database, run ``` -dart run drift_dev schema dump lib/database.dart moor_migrations/moor_schema_v2.json +dart run drift_dev schema dump lib/database.dart drift_migrations/ ``` Replace `_v2` with the current `schemaVersion`.