mirror of https://github.com/AMT-Cheif/drift.git
Merge branch 'simple-schema-versions' into develop
This commit is contained in:
commit
2420c8d7c6
|
@ -0,0 +1 @@
|
||||||
|
{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.0.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"todos","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"title","getter_name":"title","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[{"allowed-lengths":{"min":6,"max":10}}]},{"name":"body","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"category","getter_name":"category","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]}
|
|
@ -0,0 +1 @@
|
||||||
|
{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.0.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"todos","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"title","getter_name":"title","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[{"allowed-lengths":{"min":6,"max":10}}]},{"name":"body","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"category","getter_name":"category","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"due_date","getter_name":"dueDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]}
|
|
@ -0,0 +1 @@
|
||||||
|
{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.0.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"todos","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"title","getter_name":"title","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[{"allowed-lengths":{"min":6,"max":10}}]},{"name":"body","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"category","getter_name":"category","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"due_date","getter_name":"dueDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"priority","getter_name":"priority","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]}
|
|
@ -1,5 +1,11 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
// #docregion stepbystep
|
||||||
|
// This file was generated by `drift_dev schema steps drift_schemas lib/database/schema_versions.dart
|
||||||
|
import 'schema_versions.dart';
|
||||||
|
|
||||||
|
// #enddocregion stepbystep
|
||||||
|
|
||||||
part 'migrations.g.dart';
|
part 'migrations.g.dart';
|
||||||
|
|
||||||
const kDebugMode = false;
|
const kDebugMode = false;
|
||||||
|
@ -91,3 +97,27 @@ class Example extends _$Example {
|
||||||
// #enddocregion change_type
|
// #enddocregion change_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class StepByStep {
|
||||||
|
// #docregion stepbystep
|
||||||
|
MigrationStrategy get migration {
|
||||||
|
return MigrationStrategy(
|
||||||
|
onCreate: (Migrator m) async {
|
||||||
|
await m.createAll();
|
||||||
|
},
|
||||||
|
onUpgrade: stepByStep(
|
||||||
|
from1To2: (m, schema) async {
|
||||||
|
// we added the dueDate property in the change from version 1 to
|
||||||
|
// version 2
|
||||||
|
await m.addColumn(schema.todos, schema.todos.dueDate);
|
||||||
|
},
|
||||||
|
from2To3: (m, schema) async {
|
||||||
|
// we added the priority property in the change from version 1 or 2
|
||||||
|
// to version 3
|
||||||
|
await m.addColumn(schema.todos, schema.todos.priority);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// #enddocregion stepbystep
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
import 'package:drift/internal/versioned_schema.dart' as i0;
|
||||||
|
import 'package:drift/drift.dart' as i1;
|
||||||
|
import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import
|
||||||
|
|
||||||
|
// GENERATED BY drift_dev, DO NOT MODIFY.
|
||||||
|
final class _S2 extends i0.VersionedSchema {
|
||||||
|
_S2({required super.database}) : super(version: 2);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
todos,
|
||||||
|
];
|
||||||
|
late final Shape0 todos = Shape0(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'todos',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
_column_4,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape0 extends i0.VersionedTable {
|
||||||
|
Shape0({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get title =>
|
||||||
|
columnsByName['title']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get content =>
|
||||||
|
columnsByName['body']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<int> get category =>
|
||||||
|
columnsByName['category']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<DateTime> get dueDate =>
|
||||||
|
columnsByName['due_date']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_0(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('id', aliasedName, false,
|
||||||
|
hasAutoIncrement: true,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
defaultConstraints:
|
||||||
|
i1.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||||
|
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('title', aliasedName, false,
|
||||||
|
additionalChecks: i1.GeneratedColumn.checkTextLength(
|
||||||
|
minTextLength: 6, maxTextLength: 10),
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<String> _column_2(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('body', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<int> _column_3(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('category', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
i1.GeneratedColumn<DateTime> _column_4(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<DateTime>('due_date', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.dateTime);
|
||||||
|
|
||||||
|
final class _S3 extends i0.VersionedSchema {
|
||||||
|
_S3({required super.database}) : super(version: 3);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
todos,
|
||||||
|
];
|
||||||
|
late final Shape1 todos = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'todos',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
_column_4,
|
||||||
|
_column_5,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape1 extends i0.VersionedTable {
|
||||||
|
Shape1({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get title =>
|
||||||
|
columnsByName['title']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get content =>
|
||||||
|
columnsByName['body']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<int> get category =>
|
||||||
|
columnsByName['category']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<DateTime> get dueDate =>
|
||||||
|
columnsByName['due_date']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
i1.GeneratedColumn<int> get priority =>
|
||||||
|
columnsByName['priority']! as i1.GeneratedColumn<int>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_5(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('priority', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
i1.OnUpgrade stepByStep({
|
||||||
|
required Future<void> Function(i1.Migrator m, _S2 schema) from1To2,
|
||||||
|
required Future<void> Function(i1.Migrator m, _S3 schema) from2To3,
|
||||||
|
}) {
|
||||||
|
return i1.Migrator.stepByStepHelper(step: (currentVersion, database) async {
|
||||||
|
switch (currentVersion) {
|
||||||
|
case 1:
|
||||||
|
final schema = _S2(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from1To2(migrator, schema);
|
||||||
|
return 2;
|
||||||
|
case 2:
|
||||||
|
final schema = _S3(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from2To3(migrator, schema);
|
||||||
|
return 3;
|
||||||
|
default:
|
||||||
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -13,11 +13,13 @@ New features need new columns or tables, and outdated columns may have to be alt
|
||||||
removed altogether.
|
removed altogether.
|
||||||
When making changes to your database schema, you need to write migrations enabling users with
|
When making changes to your database schema, you need to write migrations enabling users with
|
||||||
an old version of your app to convert to the database expected by the latest version.
|
an old version of your app to convert to the database expected by the latest version.
|
||||||
Drift provides a set of APIs to make writing migrations easy.
|
With incorrect migrations, your database ends up in an inconsistent state which can cause crashes
|
||||||
|
or data loss. This is why drift provides dedicated test tools and APIs to make writing migrations
|
||||||
|
easy and safe.
|
||||||
|
|
||||||
{% assign snippets = 'package:drift_docs/snippets/migrations/migrations.dart.excerpt.json' | readString | json_decode %}
|
{% assign snippets = 'package:drift_docs/snippets/migrations/migrations.dart.excerpt.json' | readString | json_decode %}
|
||||||
|
|
||||||
## Basics
|
## Manual setup {#basics}
|
||||||
|
|
||||||
Drift provides a migration API that can be used to gradually apply schema changes after bumping
|
Drift provides a migration API that can be used to gradually apply schema changes after bumping
|
||||||
the `schemaVersion` getter inside the `Database` class. To use it, override the `migration`
|
the `schemaVersion` getter inside the `Database` class. To use it, override the `migration`
|
||||||
|
@ -43,7 +45,7 @@ you've actually added the column. In general, try to avoid running queries in mi
|
||||||
`sqlite` can feel a bit limiting when it comes to migrations - there only are methods to create tables and columns.
|
`sqlite` can feel a bit limiting when it comes to migrations - there only are methods to create tables and columns.
|
||||||
Existing columns can't be altered or removed. A workaround is described [here](https://stackoverflow.com/a/805508), it
|
Existing columns can't be altered or removed. A workaround is described [here](https://stackoverflow.com/a/805508), it
|
||||||
can be used together with `customStatement` to run the statements.
|
can be used together with `customStatement` to run the statements.
|
||||||
Alternatively, [complex migrations](#complex-migrations) help automating this.
|
Alternatively, [complex migrations](#complex-migrations) described on this page help automating this.
|
||||||
|
|
||||||
### Tips
|
### Tips
|
||||||
|
|
||||||
|
@ -60,6 +62,241 @@ With all of this combined, a migration callback can look like this:
|
||||||
|
|
||||||
{% include "blocks/snippet" snippets = snippets name = 'structured' %}
|
{% include "blocks/snippet" snippets = snippets name = 'structured' %}
|
||||||
|
|
||||||
|
## Migration workflow
|
||||||
|
|
||||||
|
While migrations can be written manually without additional help from drift, dedicated tools testing your migrations help
|
||||||
|
to ensure that they are correct and aren't loosing any data.
|
||||||
|
|
||||||
|
Drift's migration tooling consists of the following steps:
|
||||||
|
|
||||||
|
1. After each change to your schema, use a tool to export the current schema into a separate file.
|
||||||
|
2. Use a drift tool to generate test code able to verify that your migrations are bringing the database
|
||||||
|
into the expected schema.
|
||||||
|
3. Use generated code to make writing schema migrations easier.
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
As described by the first step, you can export the schema of your database into a JSON file.
|
||||||
|
It is recommended to do this once intially, and then again each time you change your schema
|
||||||
|
and increase the `schemaVersion` getter in the database.
|
||||||
|
|
||||||
|
You should store these exported files in your repository and include them in source control.
|
||||||
|
This guide assumes a top-level `drift_schemas/` folder in your project, like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
my_app
|
||||||
|
.../
|
||||||
|
lib/
|
||||||
|
database/
|
||||||
|
database.dart
|
||||||
|
database.g.dart
|
||||||
|
test/
|
||||||
|
generated_migrations/
|
||||||
|
schema.dart
|
||||||
|
schema_v1.dart
|
||||||
|
schema_v2.dart
|
||||||
|
drift_schemas/
|
||||||
|
drift_schema_v1.json
|
||||||
|
drift_schema_v2.json
|
||||||
|
pubspec.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Of course, you can also use another folder or a subfolder somewhere if that suits your workflow
|
||||||
|
better.
|
||||||
|
|
||||||
|
{% block "blocks/alert" title="Examples available" %}
|
||||||
|
Exporting schemas and generating code for them can't be done with `build_runner` alone, which is
|
||||||
|
why this setup described here is necessary.
|
||||||
|
|
||||||
|
We hope it's worth it though! Verifying migrations can give you confidence that you won't run
|
||||||
|
into issues after changing your database.
|
||||||
|
If you get stuck along the way, don't hesitate to [open a discussion about it](https://github.com/simolus3/drift/discussions).
|
||||||
|
|
||||||
|
Also there are two examples in the drift repository which may be useful as a reference:
|
||||||
|
|
||||||
|
- A [Flutter app](https://github.com/simolus3/drift/tree/latest-release/examples/app)
|
||||||
|
- An [example specific to migrations](https://github.com/simolus3/drift/tree/latest-release/examples/migrations_example).
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
#### Exporting the schema
|
||||||
|
|
||||||
|
To begin, lets create the first schema representation:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mkdir drift_schemas
|
||||||
|
$ 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`. To dump the new schema, just run the
|
||||||
|
command again:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ 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`.
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
{% block "blocks/alert" title='<i class="fas fa-lightbulb"></i> Dumping a database' color="success" %}
|
||||||
|
If, instead of exporting the schema of a database class, you want to export the schema of an existing sqlite3
|
||||||
|
database file, you can do that as well! `drift_dev schema dump` recognizes a sqlite3 database file as its first
|
||||||
|
argument and can extract the relevant schema from there.
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
### Generating step-by-step migrations {#step-by-step}
|
||||||
|
|
||||||
|
With all your database schemas exported into a folder, drift can generate code that makes it much
|
||||||
|
easier to write schema migrations "step-by-step" (incrementally from each version to the next one).
|
||||||
|
|
||||||
|
This code is stored in a single-file, which you can generate like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dart run drift_dev schema steps drift_schemas/ lib/database/schema_versions.dart
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated code contains a `stepByStep` method which you can use as a callback to the `onUpgrade`
|
||||||
|
parameter of your `MigrationStrategy`.
|
||||||
|
As an example, here is the [initial](#basics) migration shown at the top of this page, but rewritten using
|
||||||
|
the generated `stepByStep` function:
|
||||||
|
|
||||||
|
{% include "blocks/snippet" snippets = snippets name = 'stepbystep' %}
|
||||||
|
|
||||||
|
`stepByStep` expects a callback for each schema upgrade responsible for running the partial migration.
|
||||||
|
That callback receives two parameters: A migrator `m` (similar to the regular migrator you'd get for
|
||||||
|
`onUpgrade` callbacks) and a `schema` parameter that gives you access to the schema at the version you're
|
||||||
|
migrating to.
|
||||||
|
For instance, in the `from1To2` function, `schema` provides getters for the database schema at version 2.
|
||||||
|
The migrator passed to the function is also set up to consider that specific version by default.
|
||||||
|
A call to `m.recreateAllViews()` would re-create views at the expected state of schema version 2, for instance.
|
||||||
|
|
||||||
|
### Writing tests
|
||||||
|
|
||||||
|
After you've exported the database schemas into a folder, you can generate old versions of your database class
|
||||||
|
based on those schema files.
|
||||||
|
For verifications, drift will generate a much smaller database implementation that can only be used to
|
||||||
|
test migrations.
|
||||||
|
|
||||||
|
You can put this test code whereever you want, but it makes sense to put it in a subfolder of `test/`.
|
||||||
|
If we wanted to write them to `test/generated_migrations/`, we could use
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dart run drift_dev schema generate drift_schemas/ test/generated_migrations/
|
||||||
|
```
|
||||||
|
|
||||||
|
After that setup, it's finally time to write some tests! For instance, a test could look like this:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:my_app/database/database.dart';
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'package:drift_dev/api/migrations.dart';
|
||||||
|
|
||||||
|
// The generated directory from before.
|
||||||
|
import 'generated_migrations/schema.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late SchemaVerifier verifier;
|
||||||
|
|
||||||
|
setUpAll(() {
|
||||||
|
// GeneratedHelper() was generated by drift, the verifier is an api
|
||||||
|
// provided by drift_dev.
|
||||||
|
verifier = SchemaVerifier(GeneratedHelper());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('upgrade from v1 to v2', () async {
|
||||||
|
// Use startAt(1) to obtain a database connection with all tables
|
||||||
|
// from the v1 schema.
|
||||||
|
final connection = await verifier.startAt(1);
|
||||||
|
final db = MyDatabase(connection);
|
||||||
|
|
||||||
|
// Use this to run a migration to v2 and then validate that the
|
||||||
|
// database has the expected schema.
|
||||||
|
await verifier.migrateAndValidate(db, 2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In general, a test looks like this:
|
||||||
|
|
||||||
|
1. Use `verifier.startAt()` to obtain a [connection](https://drift.simonbinder.eu/api/drift/databaseconnection-class)
|
||||||
|
to a database with an initial schema.
|
||||||
|
This database contains all your tables, indices and triggers from that version, created by using `Migrator.createAll`.
|
||||||
|
2. Create your application database with that connection - you can forward the `DatabaseConnection` to the
|
||||||
|
`GeneratedDatabase.connect()` constructor on the parent class for this.
|
||||||
|
3. Call `verifier.migrateAndValidate(db, version)`. This will initiate a migration towards the target version (here, `2`).
|
||||||
|
Unlike the database created by `startAt`, this uses the migration logic you wrote for your database.
|
||||||
|
|
||||||
|
`migrateAndValidate` will extract all `CREATE` statement from the `sqlite_schema` table and semantically compare them.
|
||||||
|
If it sees anything unexpected, it will throw a `SchemaMismatch` exception to fail your test.
|
||||||
|
|
||||||
|
{% block "blocks/alert" title="Writing testable migrations" %}
|
||||||
|
To test migrations _towards_ an old schema version (e.g. from `v1` to `v2` if your current version is `v3`),
|
||||||
|
you're `onUpgrade` handler must be capable of upgrading to a version older than the current `schemaVersion`.
|
||||||
|
For this, check the `to` parameter of the `onUpgrade` callback to run a different migration if necessary.
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
#### Verifying data integrity
|
||||||
|
|
||||||
|
In addition to the changes made in your table structure, its useful to ensure that data that was present before a migration
|
||||||
|
is still there after it ran.
|
||||||
|
You can use `schemaAt` to obtain a raw `Database` from the `sqlite3` package in addition to a connection.
|
||||||
|
This can be used to insert data before a migration. After the migration ran, you can then check that the data is still there.
|
||||||
|
|
||||||
|
Note that you can't use the regular database class from you app for this, since its data classes always expect the latest
|
||||||
|
schema. However, you can instruct drift to generate older snapshots of your data classes and companions for this purpose.
|
||||||
|
To enable this feature, pass the `--data-classes` and `--companions` command-line arguments to the `drift_dev schema generate`
|
||||||
|
command:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dart run drift_dev schema generate --data-classes --companions drift_schemas/ test/generated_migrations/
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, you can import the generated classes with an alias:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'generated_migrations/schema_v1.dart' as v1;
|
||||||
|
import 'generated_migrations/schema_v2.dart' as v2;
|
||||||
|
```
|
||||||
|
|
||||||
|
This can then be used to manually create and verify data at a specific version:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
void main() {
|
||||||
|
// ...
|
||||||
|
test('upgrade from v1 to v2', () async {
|
||||||
|
final schema = await verifier.schemaAt(1);
|
||||||
|
|
||||||
|
// Add some data to the users table, which only has an id column at v1
|
||||||
|
final oldDb = v1.DatabaseAtV1.connect(schema.newConnection());
|
||||||
|
await oldDb.into(oldDb.users).insert(const v1.UsersCompanion(id: Value(1)));
|
||||||
|
await oldDb.close();
|
||||||
|
|
||||||
|
// Run the migration and verify that it adds the name column.
|
||||||
|
final db = Database(schema.newConnection());
|
||||||
|
await verifier.migrateAndValidate(db, 2);
|
||||||
|
await db.close();
|
||||||
|
|
||||||
|
// Make sure the user is still here
|
||||||
|
final migratedDb = v2.DatabaseAtV2.connect(schema.newConnection());
|
||||||
|
final user = await migratedDb.select(migratedDb.users).getSingle();
|
||||||
|
expect(user.id, 1);
|
||||||
|
expect(user.name, 'no name'); // default from the migration
|
||||||
|
await migratedDb.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Complex migrations
|
## Complex migrations
|
||||||
|
|
||||||
Sqlite has builtin statements for simple changes, like adding columns or dropping entire tables.
|
Sqlite has builtin statements for simple changes, like adding columns or dropping entire tables.
|
||||||
|
@ -235,208 +472,6 @@ the database file and will re-create it when installing the app again.
|
||||||
You can also delete and re-create all tables every time your app is opened, see [this comment](https://github.com/simolus3/drift/issues/188#issuecomment-542682912)
|
You can also delete and re-create all tables every time your app is opened, see [this comment](https://github.com/simolus3/drift/issues/188#issuecomment-542682912)
|
||||||
on how that can be achieved.
|
on how that can be achieved.
|
||||||
|
|
||||||
## Verifying migrations
|
|
||||||
|
|
||||||
Drift contains **experimental** support to verify the integrity of your migrations in unit tests.
|
|
||||||
|
|
||||||
To support this feature, drift can help you generate
|
|
||||||
|
|
||||||
- a json representation of your database schema
|
|
||||||
- test databases operating on an older schema version
|
|
||||||
|
|
||||||
By using those test databases, drift can help you test migrations from and to any schema version.
|
|
||||||
|
|
||||||
{% block "blocks/alert" title="Complex topic ahead" %}
|
|
||||||
> Writing schema tests is an advanced topic that requires a fairly complex setup described here.
|
|
||||||
If you get stuck along the way, don't hesitate to [open a discussion about it](https://github.com/simolus3/drift/discussions).
|
|
||||||
Also, there's a working example [in the drift repository](https://github.com/simolus3/drift/tree/latest-release/examples/migrations_example).
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
### Setup
|
|
||||||
|
|
||||||
To use this feature, drift needs to know all schemas of your database. A schema is the set of all tables, triggers
|
|
||||||
and indices that you use in your database.
|
|
||||||
|
|
||||||
You can use the [CLI tools]({{ "../CLI.md" | pageUrl }}) to export a json representation of your schema.
|
|
||||||
In this guide, we'll assume a file layout like the following, where `my_app` is the root folder of your project:
|
|
||||||
|
|
||||||
```
|
|
||||||
my_app
|
|
||||||
.../
|
|
||||||
lib/
|
|
||||||
database/
|
|
||||||
database.dart
|
|
||||||
database.g.dart
|
|
||||||
test/
|
|
||||||
generated_migrations/
|
|
||||||
schema.dart
|
|
||||||
schema_v1.dart
|
|
||||||
schema_v2.dart
|
|
||||||
drift_schemas/
|
|
||||||
drift_schema_v1.json
|
|
||||||
drift_schema_v2.json
|
|
||||||
pubspec.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
The generated migrations implementation and the schema jsons will be generated by drift.
|
|
||||||
To start writing schemas, create an empty folder named `drift_schemas` in your project.
|
|
||||||
Of course, you can also choose a different name or use a nested subfolder if you want to.
|
|
||||||
|
|
||||||
#### Exporting the schema
|
|
||||||
|
|
||||||
To begin, let's create the first schema representation:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ mkdir drift_schemas
|
|
||||||
$ 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`. To dump the new schema, just run the
|
|
||||||
command again:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ 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`.
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
{% block "blocks/alert" title='<i class="fas fa-lightbulb"></i> Dumping a database' color="success" %}
|
|
||||||
If, instead of exporting the schema of a database class, you want to export the schema of an existing sqlite3
|
|
||||||
database file, you can do that as well! `drift_dev schema dump` recognizes a sqlite3 database file as its first
|
|
||||||
argument and can extract the relevant schema from there.
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
#### Generating test code
|
|
||||||
|
|
||||||
After you exported the database schema into a folder, you can generate old versions of your database class
|
|
||||||
based on those schema files.
|
|
||||||
For verifications, drift will generate a much smaller database implementation that can only be used to
|
|
||||||
test migrations.
|
|
||||||
|
|
||||||
You can put this test code whereever you want, but it makes sense to put it in a subfolder of `test/`.
|
|
||||||
If we wanted to write them to `test/generated_migrations/`, we could use
|
|
||||||
|
|
||||||
```
|
|
||||||
$ dart run drift_dev schema generate drift_schemas/ test/generated_migrations/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Writing tests
|
|
||||||
|
|
||||||
After that setup, it's finally time to write some tests! For instance, a test could look like this:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'package:my_app/database/database.dart';
|
|
||||||
|
|
||||||
import 'package:test/test.dart';
|
|
||||||
import 'package:drift_dev/api/migrations.dart';
|
|
||||||
|
|
||||||
// The generated directory from before.
|
|
||||||
import 'generated_migrations/schema.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
late SchemaVerifier verifier;
|
|
||||||
|
|
||||||
setUpAll(() {
|
|
||||||
// GeneratedHelper() was generated by drift, the verifier is an api
|
|
||||||
// provided by drift_dev.
|
|
||||||
verifier = SchemaVerifier(GeneratedHelper());
|
|
||||||
});
|
|
||||||
|
|
||||||
test('upgrade from v1 to v2', () async {
|
|
||||||
// Use startAt(1) to obtain a database connection with all tables
|
|
||||||
// from the v1 schema.
|
|
||||||
final connection = await verifier.startAt(1);
|
|
||||||
final db = MyDatabase.connect(connection);
|
|
||||||
|
|
||||||
// Use this to run a migration to v2 and then validate that the
|
|
||||||
// database has the expected schema.
|
|
||||||
await verifier.migrateAndValidate(db, 2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In general, a test looks like this:
|
|
||||||
|
|
||||||
1. Use `verifier.startAt()` to obtain a [connection](https://drift.simonbinder.eu/api/drift/databaseconnection-class)
|
|
||||||
to a database with an initial schema.
|
|
||||||
This database contains all your tables, indices and triggers from that version, created by using `Migrator.createAll`.
|
|
||||||
2. Create your application database with that connection - you can forward the `DatabaseConnection` to the
|
|
||||||
`GeneratedDatabase.connect()` constructor on the parent class for this.
|
|
||||||
3. Call `verifier.migrateAndValidate(db, version)`. This will initiate a migration towards the target version (here, `2`).
|
|
||||||
Unlike the database created by `startAt`, this uses the migration logic you wrote for your database.
|
|
||||||
|
|
||||||
`migrateAndValidate` will extract all `CREATE` statement from the `sqlite_schema` table and semantically compare them.
|
|
||||||
If it sees anything unexpected, it will throw a `SchemaMismatch` exception to fail your test.
|
|
||||||
|
|
||||||
{% block "blocks/alert" title="Writing testable migrations" %}
|
|
||||||
To test migrations _towards_ an old schema version (e.g. from `v1` to `v2` if your current version is `v3`),
|
|
||||||
you're `onUpgrade` handler must be capable of upgrading to a version older than the current `schemaVersion`.
|
|
||||||
For this, check the `to` parameter of the `onUpgrade` callback to run a different migration if necessary.
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
#### Verifying data integrity
|
|
||||||
|
|
||||||
In addition to the changes made in your table structure, its useful to ensure that data that was present before a migration
|
|
||||||
is still there after it ran.
|
|
||||||
You can use `schemaAt` to obtain a raw `Database` from the `sqlite3` package in addition to a connection.
|
|
||||||
This can be used to insert data before a migration. After the migration ran, you can then check that the data is still there.
|
|
||||||
|
|
||||||
Note that you can't use the regular database class from you app for this, since its data classes always expect the latest
|
|
||||||
schema. However, you can instruct drift to generate older snapshots of your data classes and companions for this purpose.
|
|
||||||
To enable this feature, pass the `--data-classes` and `--companions` command-line arguments to the `drift_dev schema generate`
|
|
||||||
command:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ dart run drift_dev schema generate --data-classes --companions drift_schemas/ test/generated_migrations/
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, you can import the generated classes with an alias:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'generated_migrations/schema_v1.dart' as v1;
|
|
||||||
import 'generated_migrations/schema_v2.dart' as v2;
|
|
||||||
```
|
|
||||||
|
|
||||||
This can then be used to manually create and verify data at a specific version:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
void main() {
|
|
||||||
// ...
|
|
||||||
test('upgrade from v1 to v2', () async {
|
|
||||||
final schema = await verifier.schemaAt(1);
|
|
||||||
|
|
||||||
// Add some data to the users table, which only has an id column at v1
|
|
||||||
final oldDb = v1.DatabaseAtV1.connect(schema.newConnection());
|
|
||||||
await oldDb.into(oldDb.users).insert(const v1.UsersCompanion(id: Value(1)));
|
|
||||||
await oldDb.close();
|
|
||||||
|
|
||||||
// Run the migration and verify that it adds the name column.
|
|
||||||
final db = Database(schema.newConnection());
|
|
||||||
await verifier.migrateAndValidate(db, 2);
|
|
||||||
await db.close();
|
|
||||||
|
|
||||||
// Make sure the user is still here
|
|
||||||
final migratedDb = v2.DatabaseAtV2.connect(schema.newConnection());
|
|
||||||
final user = await migratedDb.select(migratedDb.users).getSingle();
|
|
||||||
expect(user.id, 1);
|
|
||||||
expect(user.name, 'no name'); // default from the migration
|
|
||||||
await migratedDb.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verifying a database schema at runtime
|
## Verifying a database schema at runtime
|
||||||
|
|
||||||
Instead (or in addition to) [writing tests](#verifying-migrations) to ensure your migrations work as they should,
|
Instead (or in addition to) [writing tests](#verifying-migrations) to ensure your migrations work as they should,
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
## 2.10.0
|
||||||
|
|
||||||
|
- Adds the `schema steps` command to `drift_dev`. It generates an API making it
|
||||||
|
easier to write safe schema migrations ([docs](https://drift.simonbinder.eu/docs/advanced-features/migrations/#step-by-step)).
|
||||||
|
|
||||||
## 2.9.0
|
## 2.9.0
|
||||||
|
|
||||||
- Forbid `schemaVersion` returning `0`, as this causes issues in the migrator.
|
- Forbid `schemaVersion` returning `0`, as this causes issues in the migrator.
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
/// Defines base classes to generate lightweight tables and views. This library
|
||||||
|
/// is used by code generated via `drift_dev schema steps` to generate snapshots
|
||||||
|
/// of every schema version of your database without much overhead.
|
||||||
|
///
|
||||||
|
/// For more information on how to use that feature, see
|
||||||
|
/// https://drift.simonbinder.eu/docs/advanced-features/migrations/#step-by-step
|
||||||
|
///
|
||||||
|
/// __Warning:__ This library is not meant to be imported into user-written
|
||||||
|
/// code, and classes defined in this library are not part of drift's stable
|
||||||
|
/// API.
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
/// A snapshot of a database schema at a previous version.
|
||||||
|
///
|
||||||
|
/// This class is meant to be extended by generated code.
|
||||||
|
abstract base class VersionedSchema {
|
||||||
|
/// The generated database instance, used to create [TableInfo] instances.
|
||||||
|
final DatabaseConnectionUser database;
|
||||||
|
|
||||||
|
/// The [GeneratedDatabase.schemaVersion] at the time this schema was active.
|
||||||
|
final int version;
|
||||||
|
|
||||||
|
/// Default constructor taking the database and the schema version.
|
||||||
|
VersionedSchema({required this.database, required this.version});
|
||||||
|
|
||||||
|
/// All drift schema entities at the time of the set [version].
|
||||||
|
Iterable<DatabaseSchemaEntity> get entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A drift table implementation that, instead of being generated, is constructed
|
||||||
|
/// from individual fields
|
||||||
|
///
|
||||||
|
/// This allows the code generated for step-by-step migrations to be a lot
|
||||||
|
/// smaller than the code typically generated by drift. Features like type
|
||||||
|
/// converters or information about unique/primary keys are not present in these
|
||||||
|
/// tables.
|
||||||
|
class VersionedTable extends Table with TableInfo<Table, QueryRow> {
|
||||||
|
@override
|
||||||
|
final String entityName;
|
||||||
|
final String? _alias;
|
||||||
|
@override
|
||||||
|
final bool isStrict;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool withoutRowId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DatabaseConnectionUser attachedDatabase;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final List<GeneratedColumn> $columns;
|
||||||
|
|
||||||
|
/// List of columns, represented as a function that returns the generated
|
||||||
|
/// column when given the resolved table name.
|
||||||
|
final List<GeneratedColumn Function(String)> _columnFactories;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final List<String> customConstraints;
|
||||||
|
|
||||||
|
/// Create a table from the individual fields.
|
||||||
|
///
|
||||||
|
/// [columns] is a list of functions returning a [GeneratedColumn] when given
|
||||||
|
/// the alias (or original name) of this table.
|
||||||
|
VersionedTable({
|
||||||
|
required this.entityName,
|
||||||
|
required this.isStrict,
|
||||||
|
required this.withoutRowId,
|
||||||
|
required this.attachedDatabase,
|
||||||
|
required List<GeneratedColumn Function(String)> columns,
|
||||||
|
required List<String> tableConstraints,
|
||||||
|
String? alias,
|
||||||
|
}) : _columnFactories = columns,
|
||||||
|
customConstraints = tableConstraints,
|
||||||
|
$columns = [for (final column in columns) column(alias ?? entityName)],
|
||||||
|
_alias = alias;
|
||||||
|
|
||||||
|
/// Create a table by copying fields from [source] and applying an [alias].
|
||||||
|
VersionedTable.aliased({
|
||||||
|
required VersionedTable source,
|
||||||
|
required String? alias,
|
||||||
|
}) : entityName = source.entityName,
|
||||||
|
isStrict = source.isStrict,
|
||||||
|
withoutRowId = source.withoutRowId,
|
||||||
|
attachedDatabase = source.attachedDatabase,
|
||||||
|
customConstraints = source.customConstraints,
|
||||||
|
_columnFactories = source._columnFactories,
|
||||||
|
$columns = [
|
||||||
|
for (final column in source._columnFactories)
|
||||||
|
column(alias ?? source.entityName)
|
||||||
|
],
|
||||||
|
_alias = alias;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get actualTableName => entityName;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? entityName;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get dontWriteConstraints => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
QueryRow map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
|
return QueryRow(data, attachedDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
VersionedTable createAlias(String alias) {
|
||||||
|
return VersionedTable.aliased(source: this, alias: alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The version of [VersionedTable] for virtual tables.
|
||||||
|
class VersionedVirtualTable extends VersionedTable
|
||||||
|
with VirtualTableInfo<Table, QueryRow> {
|
||||||
|
@override
|
||||||
|
final String moduleAndArgs;
|
||||||
|
|
||||||
|
/// Create a small virtual table from the individual fields.
|
||||||
|
VersionedVirtualTable({
|
||||||
|
required super.entityName,
|
||||||
|
required super.attachedDatabase,
|
||||||
|
required super.columns,
|
||||||
|
required this.moduleAndArgs,
|
||||||
|
super.alias,
|
||||||
|
}) : super(
|
||||||
|
isStrict: false,
|
||||||
|
withoutRowId: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Create a virtual table by copying fields from [source] and applying a
|
||||||
|
/// [alias] to columns.
|
||||||
|
VersionedVirtualTable.aliased(
|
||||||
|
{required VersionedVirtualTable source, required String? alias})
|
||||||
|
: moduleAndArgs = source.moduleAndArgs,
|
||||||
|
super.aliased(source: source, alias: alias);
|
||||||
|
|
||||||
|
@override
|
||||||
|
VersionedVirtualTable createAlias(String alias) {
|
||||||
|
return VersionedVirtualTable.aliased(
|
||||||
|
source: this,
|
||||||
|
alias: alias,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A constructed from individual fields instead of being generated with a
|
||||||
|
/// dedicated class.
|
||||||
|
class VersionedView implements ViewInfo<HasResultSet, QueryRow>, HasResultSet {
|
||||||
|
@override
|
||||||
|
final String entityName;
|
||||||
|
final String? _alias;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String createViewStmt;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final List<GeneratedColumn> $columns;
|
||||||
|
|
||||||
|
@override
|
||||||
|
late final Map<String, GeneratedColumn> columnsByName = {
|
||||||
|
for (final column in $columns) column.name: column,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// List of columns, represented as a function that returns the generated
|
||||||
|
/// column when given the resolved table name.
|
||||||
|
final List<GeneratedColumn Function(String)> _columnFactories;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DatabaseConnectionUser attachedDatabase;
|
||||||
|
|
||||||
|
/// Create a view from the individual fields on [ViewInfo].
|
||||||
|
VersionedView({
|
||||||
|
required this.entityName,
|
||||||
|
required this.attachedDatabase,
|
||||||
|
required this.createViewStmt,
|
||||||
|
required List<GeneratedColumn Function(String)> columns,
|
||||||
|
String? alias,
|
||||||
|
}) : _columnFactories = columns,
|
||||||
|
$columns = [for (final column in columns) column(alias ?? entityName)],
|
||||||
|
_alias = alias;
|
||||||
|
|
||||||
|
/// Copy an alias to a [source] view.
|
||||||
|
VersionedView.aliased({required VersionedView source, required String? alias})
|
||||||
|
: entityName = source.entityName,
|
||||||
|
attachedDatabase = source.attachedDatabase,
|
||||||
|
createViewStmt = source.createViewStmt,
|
||||||
|
_columnFactories = source._columnFactories,
|
||||||
|
$columns = [
|
||||||
|
for (final column in source._columnFactories)
|
||||||
|
column(alias ?? source.entityName)
|
||||||
|
],
|
||||||
|
_alias = alias;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? entityName;
|
||||||
|
|
||||||
|
@override
|
||||||
|
HasResultSet get asDslTable => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
VersionedView createAlias(String alias) {
|
||||||
|
return VersionedView.aliased(source: this, alias: alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
QueryRow map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
|
return QueryRow(data, attachedDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query<HasResultSet, dynamic>? get query => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<String> get readTables => const {};
|
||||||
|
}
|
|
@ -49,14 +49,22 @@ class MigrationStrategy {
|
||||||
/// Runs migrations declared by a [MigrationStrategy].
|
/// Runs migrations declared by a [MigrationStrategy].
|
||||||
class Migrator {
|
class Migrator {
|
||||||
final GeneratedDatabase _db;
|
final GeneratedDatabase _db;
|
||||||
|
final VersionedSchema? _fixedVersion;
|
||||||
|
|
||||||
/// Used internally by drift when opening the database.
|
/// Used internally by drift when opening the database.
|
||||||
Migrator(this._db);
|
Migrator(this._db, [this._fixedVersion]);
|
||||||
|
|
||||||
|
Iterable<DatabaseSchemaEntity> get _allSchemaEntities {
|
||||||
|
return switch (_fixedVersion) {
|
||||||
|
null => _db.allSchemaEntities,
|
||||||
|
var fixed => fixed.entities,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates all tables specified for the database, if they don't exist
|
/// Creates all tables specified for the database, if they don't exist
|
||||||
@Deprecated('Use createAll() instead')
|
@Deprecated('Use createAll() instead')
|
||||||
Future<void> createAllTables() async {
|
Future<void> createAllTables() async {
|
||||||
for (final table in _db.allTables) {
|
for (final table in _allSchemaEntities.whereType<TableInfo>()) {
|
||||||
await createTable(table);
|
await createTable(table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +72,7 @@ class Migrator {
|
||||||
/// Creates all tables, triggers, views, indexes and everything else defined
|
/// Creates all tables, triggers, views, indexes and everything else defined
|
||||||
/// in the database, if they don't exist.
|
/// in the database, if they don't exist.
|
||||||
Future<void> createAll() async {
|
Future<void> createAll() async {
|
||||||
for (final entity in _db.allSchemaEntities) {
|
for (final entity in _allSchemaEntities) {
|
||||||
await create(entity);
|
await create(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +102,7 @@ class Migrator {
|
||||||
/// a view reads from may also warrant re-creating the view to make sure it's
|
/// a view reads from may also warrant re-creating the view to make sure it's
|
||||||
/// still valid.
|
/// still valid.
|
||||||
Future<void> recreateAllViews() async {
|
Future<void> recreateAllViews() async {
|
||||||
for (final entity in _db.allSchemaEntities) {
|
for (final entity in _allSchemaEntities) {
|
||||||
if (entity is ViewInfo) {
|
if (entity is ViewInfo) {
|
||||||
await drop(entity);
|
await drop(entity);
|
||||||
await createView(entity);
|
await createView(entity);
|
||||||
|
@ -119,7 +127,7 @@ class Migrator {
|
||||||
return _issueCustomQuery(context.sql, context.boundVariables);
|
return _issueCustomQuery(context.sql, context.boundVariables);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Experimental utility method to alter columns of an existing table.
|
/// Alter columns of an existing tabe.
|
||||||
///
|
///
|
||||||
/// Since sqlite does not provide a way to alter the type or constraint of an
|
/// Since sqlite does not provide a way to alter the type or constraint of an
|
||||||
/// individual column, one needs to write a fairly complex migration procedure
|
/// individual column, one needs to write a fairly complex migration procedure
|
||||||
|
@ -141,7 +149,6 @@ class Migrator {
|
||||||
///
|
///
|
||||||
/// [other alter]: https://www.sqlite.org/lang_altertable.html#otheralter
|
/// [other alter]: https://www.sqlite.org/lang_altertable.html#otheralter
|
||||||
/// [drift docs]: https://drift.simonbinder.eu/docs/advanced-features/migrations/#complex-migrations
|
/// [drift docs]: https://drift.simonbinder.eu/docs/advanced-features/migrations/#complex-migrations
|
||||||
@experimental
|
|
||||||
Future<void> alterTable(TableMigration migration) async {
|
Future<void> alterTable(TableMigration migration) async {
|
||||||
final foreignKeysEnabled =
|
final foreignKeysEnabled =
|
||||||
(await _db.customSelect('PRAGMA foreign_keys').getSingle())
|
(await _db.customSelect('PRAGMA foreign_keys').getSingle())
|
||||||
|
@ -474,6 +481,31 @@ class Migrator {
|
||||||
Future<void> _issueCustomQuery(String sql, [List<dynamic>? args]) {
|
Future<void> _issueCustomQuery(String sql, [List<dynamic>? args]) {
|
||||||
return _db.customStatement(sql, args);
|
return _db.customStatement(sql, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A helper used by drift internally to implement the [step-by-step](https://drift.simonbinder.eu/docs/advanced-features/migrations/#step-by-step)
|
||||||
|
/// migration feature.
|
||||||
|
///
|
||||||
|
/// This method implements an [OnUpgrade] callback by repeatedly invoking
|
||||||
|
/// [step] with the current version, assuming that [step] will perform an
|
||||||
|
/// upgrade from that version to the version returned by the callback.
|
||||||
|
@experimental
|
||||||
|
static OnUpgrade stepByStepHelper({
|
||||||
|
required Future<int> Function(
|
||||||
|
int currentVersion,
|
||||||
|
GeneratedDatabase database,
|
||||||
|
) step,
|
||||||
|
}) {
|
||||||
|
return (m, from, to) async {
|
||||||
|
final database = m._db;
|
||||||
|
|
||||||
|
for (var target = from; target < to;) {
|
||||||
|
final newVersion = await step(target, database);
|
||||||
|
assert(newVersion > target);
|
||||||
|
|
||||||
|
target = newVersion;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides information about whether migrations ran before opening the
|
/// Provides information about whether migrations ran before opening the
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'dart:collection';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:drift/internal/versioned_schema.dart';
|
||||||
import 'package:drift/src/dsl/dsl.dart';
|
import 'package:drift/src/dsl/dsl.dart';
|
||||||
import 'package:drift/src/runtime/api/options.dart';
|
import 'package:drift/src/runtime/api/options.dart';
|
||||||
import 'package:drift/src/runtime/api/runtime_api.dart';
|
import 'package:drift/src/runtime/api/runtime_api.dart';
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:drift/internal/versioned_schema.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
@ -211,6 +212,78 @@ void main() {
|
||||||
// https://github.com/simolus3/drift/discussions/1936
|
// https://github.com/simolus3/drift/discussions/1936
|
||||||
verify(executor.runCustom(argThat(contains('CHECK("foo" < 3)')), []));
|
verify(executor.runCustom(argThat(contains('CHECK("foo" < 3)')), []));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('respects schema version', () {
|
||||||
|
late MockExecutor executor;
|
||||||
|
late _DefaultDb db;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
executor = MockExecutor();
|
||||||
|
db = _DefaultDb(executor);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
db.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('in createAll', () async {
|
||||||
|
final defaultMigrator = db.createMigrator();
|
||||||
|
await defaultMigrator.createAll();
|
||||||
|
verifyNever(executor.runCustom(any));
|
||||||
|
|
||||||
|
final fixedMigrator =
|
||||||
|
Migrator(db, _FakeSchemaVersion(database: db, version: 2));
|
||||||
|
await fixedMigrator.createAll();
|
||||||
|
verify(executor.runCustom(
|
||||||
|
'CREATE TABLE IF NOT EXISTS "my_table" ("foo" INTEGER NOT NULL);',
|
||||||
|
[],
|
||||||
|
));
|
||||||
|
verify(executor.runCustom(
|
||||||
|
'CREATE VIEW my_view AS SELECT 2',
|
||||||
|
[],
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('in recreateViews', () async {
|
||||||
|
final defaultMigrator = db.createMigrator();
|
||||||
|
await defaultMigrator.recreateAllViews();
|
||||||
|
verifyNever(executor.runCustom(any));
|
||||||
|
|
||||||
|
final fixedMigrator =
|
||||||
|
Migrator(db, _FakeSchemaVersion(database: db, version: 2));
|
||||||
|
await fixedMigrator.recreateAllViews();
|
||||||
|
|
||||||
|
verify(executor.runCustom(
|
||||||
|
'CREATE VIEW my_view AS SELECT 2',
|
||||||
|
[],
|
||||||
|
));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeSchemaVersion extends VersionedSchema {
|
||||||
|
_FakeSchemaVersion({required super.database, required super.version});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<DatabaseSchemaEntity> get entities => [
|
||||||
|
VersionedTable(
|
||||||
|
entityName: 'my_table',
|
||||||
|
attachedDatabase: database,
|
||||||
|
columns: [
|
||||||
|
(name) => GeneratedColumn<int>('foo', name, false,
|
||||||
|
type: DriftSqlType.int),
|
||||||
|
],
|
||||||
|
tableConstraints: [],
|
||||||
|
isStrict: false,
|
||||||
|
withoutRowId: false,
|
||||||
|
),
|
||||||
|
VersionedView(
|
||||||
|
entityName: 'my_view',
|
||||||
|
attachedDatabase: database,
|
||||||
|
createViewStmt: 'CREATE VIEW my_view AS SELECT $version',
|
||||||
|
columns: [],
|
||||||
|
),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DefaultDb extends GeneratedDatabase {
|
class _DefaultDb extends GeneratedDatabase {
|
||||||
|
@ -219,6 +292,9 @@ class _DefaultDb extends GeneratedDatabase {
|
||||||
@override
|
@override
|
||||||
List<TableInfo<Table, DataClass>> get allTables => [];
|
List<TableInfo<Table, DataClass>> get allTables => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<DatabaseSchemaEntity> get allSchemaEntities => [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 2;
|
int get schemaVersion => 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ class DriftTable extends DriftElementWithResultSet {
|
||||||
/// when creating the `CREATE TABLE` statement at runtime.
|
/// when creating the `CREATE TABLE` statement at runtime.
|
||||||
final bool writeDefaultConstraints;
|
final bool writeDefaultConstraints;
|
||||||
|
|
||||||
/// When non-null, the generated table class will override the
|
/// When non-empty, the generated table class will override the
|
||||||
/// `customConstraints` getter in the table class with this value.
|
/// `customConstraints` getter in the table class with this value.
|
||||||
final List<String> overrideTableConstraints;
|
final List<String> overrideTableConstraints;
|
||||||
|
|
||||||
|
@ -169,21 +169,21 @@ class DriftTable extends DriftElementWithResultSet {
|
||||||
'\$${className}Table';
|
'\$${className}Table';
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class DriftTableConstraint {}
|
sealed class DriftTableConstraint {}
|
||||||
|
|
||||||
class UniqueColumns extends DriftTableConstraint {
|
final class UniqueColumns extends DriftTableConstraint {
|
||||||
final Set<DriftColumn> uniqueSet;
|
final Set<DriftColumn> uniqueSet;
|
||||||
|
|
||||||
UniqueColumns(this.uniqueSet);
|
UniqueColumns(this.uniqueSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
class PrimaryKeyColumns extends DriftTableConstraint {
|
final class PrimaryKeyColumns extends DriftTableConstraint {
|
||||||
final Set<DriftColumn> primaryKey;
|
final Set<DriftColumn> primaryKey;
|
||||||
|
|
||||||
PrimaryKeyColumns(this.primaryKey);
|
PrimaryKeyColumns(this.primaryKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ForeignKeyTable extends DriftTableConstraint {
|
final class ForeignKeyTable extends DriftTableConstraint {
|
||||||
final List<DriftColumn> localColumns;
|
final List<DriftColumn> localColumns;
|
||||||
final DriftTable otherTable;
|
final DriftTable otherTable;
|
||||||
|
|
||||||
|
@ -214,6 +214,12 @@ class VirtualTableData {
|
||||||
|
|
||||||
final RecognizedVirtualTableModule? recognized;
|
final RecognizedVirtualTableModule? recognized;
|
||||||
|
|
||||||
|
/// The module and the arguments in a single string, suitable for `CREATE
|
||||||
|
/// VIRTUAL TABLE` statements.
|
||||||
|
String get moduleAndArgs {
|
||||||
|
return '$module(${moduleArguments.join(', ')})';
|
||||||
|
}
|
||||||
|
|
||||||
VirtualTableData(this.module, this.moduleArguments, this.recognized);
|
VirtualTableData(this.module, this.moduleArguments, this.recognized);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import 'commands/schema.dart';
|
||||||
import 'logging.dart';
|
import 'logging.dart';
|
||||||
|
|
||||||
Future run(List<String> args) async {
|
Future run(List<String> args) async {
|
||||||
final cli = MoorCli();
|
final cli = DriftDevCli();
|
||||||
try {
|
try {
|
||||||
return await cli.run(args);
|
return await cli.run(args);
|
||||||
} on UsageException catch (e) {
|
} on UsageException catch (e) {
|
||||||
|
@ -21,14 +21,14 @@ Future run(List<String> args) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoorCli {
|
class DriftDevCli {
|
||||||
Logger get logger => Logger.root;
|
Logger get logger => Logger.root;
|
||||||
late final CommandRunner _runner;
|
late final CommandRunner _runner;
|
||||||
late final MoorProject project;
|
late final MoorProject project;
|
||||||
|
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
|
||||||
MoorCli() {
|
DriftDevCli() {
|
||||||
_runner = CommandRunner(
|
_runner = CommandRunner(
|
||||||
'dart run drift_dev',
|
'dart run drift_dev',
|
||||||
'CLI utilities for the drift package, currently in an experimental state.',
|
'CLI utilities for the drift package, currently in an experimental state.',
|
||||||
|
@ -72,7 +72,7 @@ class MoorCli {
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class MoorCommand extends Command {
|
abstract class MoorCommand extends Command {
|
||||||
final MoorCli cli;
|
final DriftDevCli cli;
|
||||||
|
|
||||||
MoorCommand(this.cli);
|
MoorCommand(this.cli);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:io';
|
||||||
import '../cli.dart';
|
import '../cli.dart';
|
||||||
|
|
||||||
class AnalyzeCommand extends MoorCommand {
|
class AnalyzeCommand extends MoorCommand {
|
||||||
AnalyzeCommand(MoorCli cli) : super(cli);
|
AnalyzeCommand(DriftDevCli cli) : super(cli);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get description => 'Analyze and lint drift database code';
|
String get description => 'Analyze and lint drift database code';
|
||||||
|
|
|
@ -7,7 +7,7 @@ import '../../analysis/results/results.dart';
|
||||||
import '../cli.dart';
|
import '../cli.dart';
|
||||||
|
|
||||||
class IdentifyDatabases extends MoorCommand {
|
class IdentifyDatabases extends MoorCommand {
|
||||||
IdentifyDatabases(MoorCli cli) : super(cli);
|
IdentifyDatabases(DriftDevCli cli) : super(cli);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get description =>
|
String get description =>
|
||||||
|
|
|
@ -23,7 +23,7 @@ class MigrateCommand extends MoorCommand {
|
||||||
|
|
||||||
late final AnalysisContext context;
|
late final AnalysisContext context;
|
||||||
|
|
||||||
MigrateCommand(MoorCli cli) : super(cli);
|
MigrateCommand(DriftDevCli cli) : super(cli);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get description => 'Migrate a project from moor to drift';
|
String get description => 'Migrate a project from moor to drift';
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
import 'package:args/command_runner.dart';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:args/command_runner.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
|
import '../../analysis/results/results.dart';
|
||||||
|
import '../../services/schema/schema_files.dart';
|
||||||
import 'schema/dump.dart';
|
import 'schema/dump.dart';
|
||||||
import 'schema/generate_utils.dart';
|
import 'schema/generate_utils.dart';
|
||||||
|
|
||||||
import '../cli.dart';
|
import '../cli.dart';
|
||||||
|
import 'schema/steps.dart';
|
||||||
|
|
||||||
class SchemaCommand extends Command {
|
class SchemaCommand extends Command {
|
||||||
@override
|
@override
|
||||||
|
@ -12,8 +19,37 @@ class SchemaCommand extends Command {
|
||||||
@override
|
@override
|
||||||
String get name => 'schema';
|
String get name => 'schema';
|
||||||
|
|
||||||
SchemaCommand(MoorCli cli) {
|
SchemaCommand(DriftDevCli cli) {
|
||||||
addSubcommand(DumpSchemaCommand(cli));
|
addSubcommand(DumpSchemaCommand(cli));
|
||||||
addSubcommand(GenerateUtilsCommand(cli));
|
addSubcommand(GenerateUtilsCommand(cli));
|
||||||
|
addSubcommand(WriteVersions(cli));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ExportedSchema {
|
||||||
|
final List<DriftElement> schema;
|
||||||
|
final Map<String, Object?> options;
|
||||||
|
|
||||||
|
ExportedSchema(this.schema, this.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
final _filenames = RegExp(r'(?:moor|drift)_schema_v(\d+)\.json');
|
||||||
|
|
||||||
|
Future<Map<int, ExportedSchema>> parseSchema(Directory directory) async {
|
||||||
|
final results = <int, ExportedSchema>{};
|
||||||
|
|
||||||
|
await for (final entity in directory.list()) {
|
||||||
|
final basename = p.basename(entity.path);
|
||||||
|
final match = _filenames.firstMatch(basename);
|
||||||
|
|
||||||
|
if (match == null || entity is! File) continue;
|
||||||
|
|
||||||
|
final version = int.parse(match.group(1)!);
|
||||||
|
final rawData = json.decode(await entity.readAsString());
|
||||||
|
|
||||||
|
final schema = SchemaReader.readJson(rawData as Map<String, dynamic>);
|
||||||
|
results[version] = ExportedSchema(schema.entities.toList(), schema.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ class DumpSchemaCommand extends Command {
|
||||||
return '${runner!.executableName} schema dump [arguments] <input> <output>';
|
return '${runner!.executableName} schema dump [arguments] <input> <output>';
|
||||||
}
|
}
|
||||||
|
|
||||||
final MoorCli cli;
|
final DriftDevCli cli;
|
||||||
|
|
||||||
DumpSchemaCommand(this.cli) {
|
DumpSchemaCommand(this.cli) {
|
||||||
argParser.addSeparator("It's recommended to run this commend from the "
|
argParser.addSeparator("It's recommended to run this commend from the "
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
|
@ -13,9 +12,10 @@ import '../../../writer/database_writer.dart';
|
||||||
import '../../../writer/import_manager.dart';
|
import '../../../writer/import_manager.dart';
|
||||||
import '../../../writer/writer.dart';
|
import '../../../writer/writer.dart';
|
||||||
import '../../cli.dart';
|
import '../../cli.dart';
|
||||||
|
import '../schema.dart';
|
||||||
|
|
||||||
class GenerateUtilsCommand extends Command {
|
class GenerateUtilsCommand extends Command {
|
||||||
final MoorCli cli;
|
final DriftDevCli cli;
|
||||||
|
|
||||||
GenerateUtilsCommand(this.cli) {
|
GenerateUtilsCommand(this.cli) {
|
||||||
argParser.addFlag(
|
argParser.addFlag(
|
||||||
|
@ -61,7 +61,7 @@ class GenerateUtilsCommand extends Command {
|
||||||
await outputDir.create();
|
await outputDir.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
final schema = await _parseSchema(inputDir);
|
final schema = await parseSchema(inputDir);
|
||||||
for (final versionAndEntities in schema.entries) {
|
for (final versionAndEntities in schema.entries) {
|
||||||
final version = versionAndEntities.key;
|
final version = versionAndEntities.key;
|
||||||
final entities = versionAndEntities.value;
|
final entities = versionAndEntities.value;
|
||||||
|
@ -81,30 +81,10 @@ class GenerateUtilsCommand extends Command {
|
||||||
'Wrote ${schema.length + 1} files into ${p.relative(outputDir.path)}');
|
'Wrote ${schema.length + 1} files into ${p.relative(outputDir.path)}');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<int, _ExportedSchema>> _parseSchema(Directory directory) async {
|
|
||||||
final results = <int, _ExportedSchema>{};
|
|
||||||
|
|
||||||
await for (final entity in directory.list()) {
|
|
||||||
final basename = p.basename(entity.path);
|
|
||||||
final match = _filenames.firstMatch(basename);
|
|
||||||
|
|
||||||
if (match == null || entity is! File) continue;
|
|
||||||
|
|
||||||
final version = int.parse(match.group(1)!);
|
|
||||||
final rawData = json.decode(await entity.readAsString());
|
|
||||||
|
|
||||||
final schema = SchemaReader.readJson(rawData as Map<String, dynamic>);
|
|
||||||
results[version] =
|
|
||||||
_ExportedSchema(schema.entities.toList(), schema.options);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _writeSchemaFile(
|
Future<void> _writeSchemaFile(
|
||||||
Directory output,
|
Directory output,
|
||||||
int version,
|
int version,
|
||||||
_ExportedSchema schema,
|
ExportedSchema schema,
|
||||||
bool dataClasses,
|
bool dataClasses,
|
||||||
bool companions,
|
bool companions,
|
||||||
) {
|
) {
|
||||||
|
@ -184,15 +164,7 @@ class GenerateUtilsCommand extends Command {
|
||||||
|
|
||||||
String _filenameForVersion(int version) => 'schema_v$version.dart';
|
String _filenameForVersion(int version) => 'schema_v$version.dart';
|
||||||
|
|
||||||
static final _filenames = RegExp(r'(?:moor|drift)_schema_v(\d+)\.json');
|
|
||||||
static final _dartfmt = DartFormatter();
|
static final _dartfmt = DartFormatter();
|
||||||
static const _prefix = '// GENERATED CODE, DO NOT EDIT BY HAND.\n'
|
static const _prefix = '// GENERATED CODE, DO NOT EDIT BY HAND.\n'
|
||||||
'// ignore_for_file: type=lint';
|
'// ignore_for_file: type=lint';
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ExportedSchema {
|
|
||||||
final List<DriftElement> schema;
|
|
||||||
final Map<String, Object?> options;
|
|
||||||
|
|
||||||
_ExportedSchema(this.schema, this.options);
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:args/command_runner.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:dart_style/dart_style.dart';
|
||||||
|
|
||||||
|
import '../../../analysis/options.dart';
|
||||||
|
import '../../../analysis/results/element.dart';
|
||||||
|
import '../../../writer/import_manager.dart';
|
||||||
|
import '../../../writer/schema_version_writer.dart';
|
||||||
|
import '../../../writer/writer.dart';
|
||||||
|
import '../../cli.dart';
|
||||||
|
import '../schema.dart';
|
||||||
|
|
||||||
|
class WriteVersions extends Command {
|
||||||
|
final DriftDevCli cli;
|
||||||
|
|
||||||
|
WriteVersions(this.cli);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => 'steps';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get description =>
|
||||||
|
'Write a Dart file helping with incremental migrations between schema versions.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get invocation {
|
||||||
|
return '${runner!.executableName} schema steps <schema directory> <output file>';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> run() async {
|
||||||
|
final rest = argResults!.rest;
|
||||||
|
if (rest.length != 2) {
|
||||||
|
usageException('Expected input and output directories');
|
||||||
|
}
|
||||||
|
|
||||||
|
final inputDirectory = Directory(rest[0]);
|
||||||
|
final outputFile = File(rest[1]);
|
||||||
|
final outputDirectory = outputFile.parent;
|
||||||
|
|
||||||
|
if (!await inputDirectory.exists()) {
|
||||||
|
cli.exit('The provided input directory does not exist.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await outputDirectory.exists()) {
|
||||||
|
await outputDirectory.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
final imports = LibraryImportManager();
|
||||||
|
final writer = Writer(
|
||||||
|
const DriftOptions.defaults(),
|
||||||
|
generationOptions: GenerationOptions(imports: imports),
|
||||||
|
);
|
||||||
|
imports.linkToWriter(writer);
|
||||||
|
|
||||||
|
final schema = await parseSchema(inputDirectory);
|
||||||
|
final byVersion = [
|
||||||
|
for (final MapEntry(key: version, value: schema) in schema.entries)
|
||||||
|
SchemaVersion(
|
||||||
|
version,
|
||||||
|
schema.schema.whereType<DriftSchemaElement>().toList(),
|
||||||
|
schema.options,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
byVersion.sortBy<num>((s) => s.version);
|
||||||
|
|
||||||
|
writer.leaf().write("import 'package:drift/drift.dart';");
|
||||||
|
SchemaVersionWriter(byVersion, writer.child()).write();
|
||||||
|
|
||||||
|
var code = writer.writeGenerated();
|
||||||
|
try {
|
||||||
|
code = DartFormatter().format(code);
|
||||||
|
} on FormatterException {
|
||||||
|
// Ignore. Probably a bug in drift_dev, the user will notice.
|
||||||
|
}
|
||||||
|
|
||||||
|
await outputFile.writeAsString(code);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,398 @@
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:drift/drift.dart' show DriftSqlType;
|
||||||
|
import 'package:sqlparser/sqlparser.dart' as sql;
|
||||||
|
import 'package:sqlparser/utils/node_to_text.dart';
|
||||||
|
|
||||||
|
import '../analysis/results/results.dart';
|
||||||
|
import '../utils/string_escaper.dart';
|
||||||
|
import 'tables/table_writer.dart';
|
||||||
|
import 'writer.dart';
|
||||||
|
|
||||||
|
class SchemaVersion {
|
||||||
|
final int version;
|
||||||
|
final List<DriftSchemaElement> schema;
|
||||||
|
final Map<String, Object?> options;
|
||||||
|
|
||||||
|
SchemaVersion(this.version, this.schema, this.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _ResultSetKind {
|
||||||
|
table,
|
||||||
|
virtualTable,
|
||||||
|
view,
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _TableShape {
|
||||||
|
final _ResultSetKind kind;
|
||||||
|
|
||||||
|
// Map from Dart getter names to column names in SQL and the SQL type.
|
||||||
|
final Map<String, (String, DriftSqlType)> columnTypes;
|
||||||
|
|
||||||
|
_TableShape(this.kind, this.columnTypes);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(kind, _equality.hash(columnTypes));
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is _TableShape &&
|
||||||
|
other.kind == kind &&
|
||||||
|
_equality.equals(other.columnTypes, columnTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const _equality = MapEquality<String, (String, DriftSqlType)>();
|
||||||
|
|
||||||
|
static Map<String, (String, DriftSqlType)> columnsFrom(
|
||||||
|
DriftElementWithResultSet e) {
|
||||||
|
return {
|
||||||
|
for (final column in e.columns)
|
||||||
|
column.nameInDart: (column.nameInSql, column.sqlType),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A writer that writes schema code for all schema versions known to us.
|
||||||
|
///
|
||||||
|
/// While other tools to generate code for a specific schema version exists
|
||||||
|
/// (we use it to generate test code), the generated code is very large since
|
||||||
|
/// it can contain data classes and all information known to drift.
|
||||||
|
/// Code generated by this writer is optimized to be compact by hiding table
|
||||||
|
/// metadata not strictly necessary for migrations and by re-using column
|
||||||
|
/// definitions where possible.
|
||||||
|
class SchemaVersionWriter {
|
||||||
|
static final Uri _schemaLibrary =
|
||||||
|
Uri.parse('package:drift/internal/versioned_schema.dart');
|
||||||
|
|
||||||
|
/// All schema versions, sorted by [SchemaVersion.version].
|
||||||
|
final List<SchemaVersion> versions;
|
||||||
|
final Scope libraryScope;
|
||||||
|
|
||||||
|
final Map<String, String> _columnCodeToFactory = {};
|
||||||
|
final Map<_TableShape, String> _shapes = {};
|
||||||
|
|
||||||
|
SchemaVersionWriter(this.versions, this.libraryScope) {
|
||||||
|
assert(versions.isSortedBy<num>((element) => element.version));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Since not every column changes in every schema version, we prefer to re-use
|
||||||
|
/// columns with an identical definition across tables and schema versions.
|
||||||
|
///
|
||||||
|
/// We do this by generating a method constructing the column which can be
|
||||||
|
/// called in different places. This method looks up or creates a method for
|
||||||
|
/// the given [column], returning it if doesn't exist.
|
||||||
|
String _referenceColumn(DriftColumn column) {
|
||||||
|
final text = libraryScope.leaf();
|
||||||
|
final (type, code) = TableOrViewWriter.instantiateColumn(column, text);
|
||||||
|
|
||||||
|
return _columnCodeToFactory.putIfAbsent(code, () {
|
||||||
|
final methodName = '_column_${_columnCodeToFactory.length}';
|
||||||
|
text.writeln('$type $methodName(String aliasedName) => $code;');
|
||||||
|
return methodName;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _writeColumnsArgument(List<DriftColumn> columns, TextEmitter writer) {
|
||||||
|
writer.write('columns: [');
|
||||||
|
|
||||||
|
for (final column in columns) {
|
||||||
|
writer
|
||||||
|
..write(_referenceColumn(column))
|
||||||
|
..write(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write('],');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds a class to use for [resultSet].
|
||||||
|
///
|
||||||
|
/// When only minor details like column or table constraints change, we don't
|
||||||
|
/// want to introduce a new class. The interface of a class is only determined
|
||||||
|
/// by its kind (since we need to subclass from VersionedTable,
|
||||||
|
/// VersionedVirtualTable or VersionedView) and its public getters used to
|
||||||
|
/// access columns.
|
||||||
|
///
|
||||||
|
/// This looks up a suitable class for the existing [resultSet] or creates a
|
||||||
|
/// new one, returning its name.
|
||||||
|
String _shapeClass(DriftElementWithResultSet resultSet) {
|
||||||
|
final (kind, superclass) = switch (resultSet) {
|
||||||
|
DriftTable(virtualTableData: null) => (
|
||||||
|
_ResultSetKind.table,
|
||||||
|
'VersionedTable'
|
||||||
|
),
|
||||||
|
DriftTable() => (_ResultSetKind.virtualTable, 'VersionedVirtualTable'),
|
||||||
|
DriftView() => (_ResultSetKind.view, 'VersionedView'),
|
||||||
|
_ => throw ArgumentError.value(resultSet, 'resultSet', 'Unknown type'),
|
||||||
|
};
|
||||||
|
|
||||||
|
final shape = _TableShape(kind, _TableShape.columnsFrom(resultSet));
|
||||||
|
return _shapes.putIfAbsent(shape, () {
|
||||||
|
final className = 'Shape${_shapes.length}';
|
||||||
|
final classWriter = libraryScope.leaf();
|
||||||
|
|
||||||
|
classWriter
|
||||||
|
..write('class $className extends ')
|
||||||
|
..writeUriRef(_schemaLibrary, superclass)
|
||||||
|
..writeln('{')
|
||||||
|
..writeln(
|
||||||
|
'$className({required super.source, required super.alias}) : super.aliased();');
|
||||||
|
|
||||||
|
for (final MapEntry(key: getterName, value: (sqlName, type))
|
||||||
|
in shape.columnTypes.entries) {
|
||||||
|
final columnType = AnnotatedDartCode([dartTypeNames[type]!]);
|
||||||
|
|
||||||
|
classWriter
|
||||||
|
..writeDriftRef('GeneratedColumn<')
|
||||||
|
..writeDart(columnType)
|
||||||
|
..write('> get ')
|
||||||
|
..write(getterName)
|
||||||
|
..write(' => columnsByName[${asDartLiteral(sqlName)}]! as ')
|
||||||
|
..writeDriftRef('GeneratedColumn<')
|
||||||
|
..writeDart(columnType)
|
||||||
|
..writeln('>;');
|
||||||
|
}
|
||||||
|
|
||||||
|
classWriter.writeln('}');
|
||||||
|
|
||||||
|
return className;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
String _writeWithResultSet(
|
||||||
|
DriftElementWithResultSet entity, TextEmitter writer) {
|
||||||
|
final getterName = entity.dbGetterName;
|
||||||
|
final shape = _shapeClass(entity);
|
||||||
|
writer
|
||||||
|
..write('late final $shape $getterName = ')
|
||||||
|
..write('$shape(source: ');
|
||||||
|
|
||||||
|
switch (entity) {
|
||||||
|
case DriftTable():
|
||||||
|
if (entity.isVirtual) {
|
||||||
|
final info = entity.virtualTableData!;
|
||||||
|
|
||||||
|
writer
|
||||||
|
..writeUriRef(_schemaLibrary, 'VersionedVirtualTable(')
|
||||||
|
..write('entityName: ${asDartLiteral(entity.schemaName)},')
|
||||||
|
..write('moduleAndArgs: ${asDartLiteral(info.moduleAndArgs)},');
|
||||||
|
} else {
|
||||||
|
final tableConstraints = <String>[];
|
||||||
|
|
||||||
|
if (entity.writeDefaultConstraints) {
|
||||||
|
// We don't override primaryKey and uniqueKey in generated table
|
||||||
|
// classes to keep the code shorter. The migrator would use those
|
||||||
|
// getters to generate SQL at runtime, which means that this burden
|
||||||
|
// now falls onto the generator.
|
||||||
|
for (final constraint in entity.tableConstraints) {
|
||||||
|
final astNode = switch (constraint) {
|
||||||
|
PrimaryKeyColumns(primaryKey: var columns) => sql.KeyClause(
|
||||||
|
null,
|
||||||
|
isPrimaryKey: true,
|
||||||
|
columns: [
|
||||||
|
for (final column in columns)
|
||||||
|
sql.IndexedColumn(
|
||||||
|
sql.Reference(columnName: column.nameInSql))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
UniqueColumns(uniqueSet: var columns) => sql.KeyClause(
|
||||||
|
null,
|
||||||
|
isPrimaryKey: false,
|
||||||
|
columns: [
|
||||||
|
for (final column in columns)
|
||||||
|
sql.IndexedColumn(
|
||||||
|
sql.Reference(columnName: column.nameInSql))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ForeignKeyTable() => sql.ForeignKeyTableConstraint(
|
||||||
|
null,
|
||||||
|
columns: [
|
||||||
|
for (final column in constraint.localColumns)
|
||||||
|
sql.Reference(columnName: column.nameInSql)
|
||||||
|
],
|
||||||
|
clause: sql.ForeignKeyClause(
|
||||||
|
foreignTable:
|
||||||
|
sql.TableReference(constraint.otherTable.schemaName),
|
||||||
|
columnNames: [
|
||||||
|
for (final column in constraint.otherColumns)
|
||||||
|
sql.Reference(columnName: column.nameInSql)
|
||||||
|
],
|
||||||
|
onUpdate: constraint.onUpdate,
|
||||||
|
onDelete: constraint.onDelete,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
tableConstraints.add(astNode.toSql());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableConstraints.addAll(entity.overrideTableConstraints.toList());
|
||||||
|
|
||||||
|
writer
|
||||||
|
..writeUriRef(_schemaLibrary, 'VersionedTable(')
|
||||||
|
..write('entityName: ${asDartLiteral(entity.schemaName)},')
|
||||||
|
..write('withoutRowId: ${entity.withoutRowId},')
|
||||||
|
..write('isStrict: ${entity.strict},')
|
||||||
|
..write('tableConstraints: [');
|
||||||
|
|
||||||
|
for (final constraint in tableConstraints) {
|
||||||
|
writer
|
||||||
|
..write(asDartLiteral(constraint))
|
||||||
|
..write(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write('],');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DriftView():
|
||||||
|
final source = entity.source as SqlViewSource;
|
||||||
|
|
||||||
|
writer
|
||||||
|
..writeUriRef(_schemaLibrary, 'VersionedView(')
|
||||||
|
..write('entityName: ${asDartLiteral(entity.schemaName)},')
|
||||||
|
..write(
|
||||||
|
'createViewStmt: ${asDartLiteral(source.sqlCreateViewStmt)},');
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_writeColumnsArgument(entity.columns, writer);
|
||||||
|
writer.write('attachedDatabase: database,');
|
||||||
|
writer.write('), alias: null)');
|
||||||
|
|
||||||
|
return getterName!;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _writeEntity({
|
||||||
|
required DriftSchemaElement element,
|
||||||
|
required TextEmitter definition,
|
||||||
|
}) {
|
||||||
|
String name;
|
||||||
|
|
||||||
|
if (element is DriftElementWithResultSet) {
|
||||||
|
name = _writeWithResultSet(element, definition);
|
||||||
|
} else if (element is DriftIndex) {
|
||||||
|
name = element.dbGetterName;
|
||||||
|
final index = definition.drift('Index');
|
||||||
|
|
||||||
|
definition
|
||||||
|
..write('final $index $name = $index(')
|
||||||
|
..write(asDartLiteral(element.schemaName))
|
||||||
|
..write(',')
|
||||||
|
..write(asDartLiteral(element.createStmt))
|
||||||
|
..write(')');
|
||||||
|
} else if (element is DriftTrigger) {
|
||||||
|
name = element.dbGetterName;
|
||||||
|
final trigger = definition.drift('Trigger');
|
||||||
|
|
||||||
|
definition
|
||||||
|
..write('final $trigger $name = $trigger(')
|
||||||
|
..write(asDartLiteral(element.createStmt))
|
||||||
|
..write(',')
|
||||||
|
..write(asDartLiteral(element.schemaName))
|
||||||
|
..write(')');
|
||||||
|
} else {
|
||||||
|
throw ArgumentError('Unhandled element type $element');
|
||||||
|
}
|
||||||
|
|
||||||
|
definition.write(';');
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write() {
|
||||||
|
libraryScope.leaf()
|
||||||
|
..writeln('// ignore_for_file: type=lint,unused_import')
|
||||||
|
..writeln('// GENERATED BY drift_dev, DO NOT MODIFY.');
|
||||||
|
|
||||||
|
// There is no need to generate schema classes for the first version, we
|
||||||
|
// only need them for versions targeted by migrations.
|
||||||
|
for (final version in versions.skip(1)) {
|
||||||
|
final versionNo = version.version;
|
||||||
|
final versionClass = '_S$versionNo';
|
||||||
|
final versionScope = libraryScope.child();
|
||||||
|
|
||||||
|
// Write an _S<x> class for each schema version x.
|
||||||
|
versionScope.leaf()
|
||||||
|
..write('final class $versionClass extends ')
|
||||||
|
..writeUriRef(_schemaLibrary, 'VersionedSchema')
|
||||||
|
..writeln('{')
|
||||||
|
..writeln('$versionClass({required super.database}): '
|
||||||
|
'super(version: $versionNo);');
|
||||||
|
|
||||||
|
// Override the allEntities getters by VersionedSchema
|
||||||
|
final allEntitiesWriter = versionScope.leaf()
|
||||||
|
..write('@override late final ')
|
||||||
|
..writeUriRef(AnnotatedDartCode.dartCore, 'List')
|
||||||
|
..write('<')
|
||||||
|
..writeDriftRef('DatabaseSchemaEntity')
|
||||||
|
..write('> entities = [');
|
||||||
|
|
||||||
|
for (final entity in version.schema) {
|
||||||
|
// Creata field for the entity and include it in the list
|
||||||
|
final fieldName =
|
||||||
|
_writeEntity(element: entity, definition: versionScope.leaf());
|
||||||
|
|
||||||
|
allEntitiesWriter.write('$fieldName,');
|
||||||
|
}
|
||||||
|
|
||||||
|
allEntitiesWriter.write('];');
|
||||||
|
versionScope.leaf().writeln('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a stepByStep migration function that takes a callback doing a step
|
||||||
|
// for each schema to the next. We supply a special migrator that only
|
||||||
|
// considers entities from that version, as well as a typed reference to the
|
||||||
|
// _S<x> class used to lookup elements.
|
||||||
|
final stepByStep = libraryScope.leaf()
|
||||||
|
..writeDriftRef('OnUpgrade')
|
||||||
|
..write(' stepByStep({');
|
||||||
|
|
||||||
|
for (final (current, next) in versions.withNext) {
|
||||||
|
stepByStep
|
||||||
|
..write('required Future<void> Function(')
|
||||||
|
..writeDriftRef('Migrator')
|
||||||
|
..write(' m, _S${next.version} schema)')
|
||||||
|
..writeln('from${current.version}To${next.version},');
|
||||||
|
}
|
||||||
|
|
||||||
|
stepByStep
|
||||||
|
..writeln('}) {')
|
||||||
|
..write('return ')
|
||||||
|
..writeDriftRef('Migrator')
|
||||||
|
..writeln('.stepByStepHelper(step: (currentVersion, database) async {')
|
||||||
|
..writeln('switch (currentVersion) {');
|
||||||
|
|
||||||
|
for (final (current, next) in versions.withNext) {
|
||||||
|
stepByStep
|
||||||
|
..writeln('case ${current.version}:')
|
||||||
|
..write('final schema = _S${next.version}(database: database);')
|
||||||
|
..write('final migrator = ')
|
||||||
|
..writeDriftRef('Migrator')
|
||||||
|
..writeln('(database, schema);')
|
||||||
|
..writeln(
|
||||||
|
'await from${current.version}To${next.version}(migrator, schema);')
|
||||||
|
..writeln('return ${next.version};');
|
||||||
|
}
|
||||||
|
|
||||||
|
stepByStep
|
||||||
|
..writeln(
|
||||||
|
r"default: throw ArgumentError.value('Unknown migration from $currentVersion');")
|
||||||
|
..writeln('}') // End of switch
|
||||||
|
..writeln('}') // End of stepByStepHelper function
|
||||||
|
..writeln(');') // End of stepByStepHelper call
|
||||||
|
..writeln('}'); // End of method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension<T> on Iterable<T> {
|
||||||
|
Iterable<(T, T)> get withNext sync* {
|
||||||
|
final iterator = this.iterator;
|
||||||
|
if (!iterator.moveNext()) return;
|
||||||
|
|
||||||
|
var a = iterator.current;
|
||||||
|
while (iterator.moveNext()) {
|
||||||
|
var b = iterator.current;
|
||||||
|
yield (a, b);
|
||||||
|
|
||||||
|
a = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,140 +16,24 @@ abstract class TableOrViewWriter {
|
||||||
StringBuffer get buffer => emitter.buffer;
|
StringBuffer get buffer => emitter.buffer;
|
||||||
|
|
||||||
void writeColumnGetter(DriftColumn column, bool isOverride) {
|
void writeColumnGetter(DriftColumn column, bool isOverride) {
|
||||||
final isNullable = column.nullable;
|
bool? isRequiredForInsert;
|
||||||
final additionalParams = <String, String>{};
|
|
||||||
final expressionBuffer = StringBuffer();
|
|
||||||
final constraints = defaultConstraints(column);
|
|
||||||
|
|
||||||
for (final constraint in column.constraints) {
|
|
||||||
if (constraint is LimitingTextLength) {
|
|
||||||
final buffer =
|
|
||||||
StringBuffer(emitter.drift('GeneratedColumn.checkTextLength('));
|
|
||||||
|
|
||||||
if (constraint.minLength != null) {
|
|
||||||
buffer.write('minTextLength: ${constraint.minLength},');
|
|
||||||
}
|
|
||||||
if (constraint.maxLength != null) {
|
|
||||||
buffer.write('maxTextLength: ${constraint.maxLength}');
|
|
||||||
}
|
|
||||||
buffer.write(')');
|
|
||||||
|
|
||||||
additionalParams['additionalChecks'] = buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (constraint is DartCheckExpression) {
|
|
||||||
final dartCheck = emitter.dartCode(constraint.dartExpression);
|
|
||||||
additionalParams['check'] = '() => $dartCheck';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (constraint is ColumnGeneratedAs) {
|
|
||||||
final dartCode = emitter.dartCode(constraint.dartExpression);
|
|
||||||
|
|
||||||
additionalParams['generatedAs'] =
|
|
||||||
'${emitter.drift('GeneratedAs')}($dartCode, ${constraint.stored})';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (constraint is PrimaryKeyColumn && constraint.isAutoIncrement) {
|
|
||||||
additionalParams['hasAutoIncrement'] = 'true';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
additionalParams['type'] = emitter.drift(column.sqlType.toString());
|
|
||||||
|
|
||||||
if (tableOrView is DriftTable) {
|
if (tableOrView is DriftTable) {
|
||||||
additionalParams['requiredDuringInsert'] = (tableOrView as DriftTable)
|
isRequiredForInsert =
|
||||||
.isColumnRequiredForInsert(column)
|
(tableOrView as DriftTable).isColumnRequiredForInsert(column);
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (column.customConstraints != null) {
|
final (type, expression) = instantiateColumn(
|
||||||
additionalParams['\$customConstraints'] =
|
column,
|
||||||
asDartLiteral(column.customConstraints!);
|
emitter,
|
||||||
} else if (constraints.values.any((constraint) => constraint.isNotEmpty)) {
|
isRequiredForInsert: isRequiredForInsert,
|
||||||
// Use the default constraints supported by drift
|
);
|
||||||
|
|
||||||
if (constraints.values.any(
|
|
||||||
(value) => value != constraints.values.first,
|
|
||||||
)) {
|
|
||||||
// One or more constraints are different depending on dialect, generate
|
|
||||||
// per-dialect constraints
|
|
||||||
|
|
||||||
final literalEntries = [
|
|
||||||
for (final entry in constraints.entries)
|
|
||||||
'${emitter.drift('SqlDialect.${entry.key.name}')}: ${asDartLiteral(entry.value)},',
|
|
||||||
];
|
|
||||||
|
|
||||||
additionalParams['defaultConstraints'] =
|
|
||||||
'${emitter.drift('GeneratedColumn.constraintsDependsOnDialect')}({${literalEntries.join('\n')}})';
|
|
||||||
} else {
|
|
||||||
// Constraints are the same regardless of dialect, only generate one set
|
|
||||||
// of them
|
|
||||||
|
|
||||||
final constraint = asDartLiteral(constraints.values.first);
|
|
||||||
|
|
||||||
additionalParams['defaultConstraints'] =
|
|
||||||
'${emitter.drift('GeneratedColumn.constraintIsAlways')}($constraint)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (column.defaultArgument != null) {
|
|
||||||
additionalParams['defaultValue'] =
|
|
||||||
emitter.dartCode(column.defaultArgument!);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (column.clientDefaultCode != null) {
|
|
||||||
additionalParams['clientDefault'] =
|
|
||||||
emitter.dartCode(column.clientDefaultCode!);
|
|
||||||
}
|
|
||||||
|
|
||||||
final innerType = emitter.innerColumnType(column);
|
|
||||||
var type =
|
|
||||||
'${emitter.drift('GeneratedColumn')}<${emitter.dartCode(innerType)}>';
|
|
||||||
expressionBuffer
|
|
||||||
..write(type)
|
|
||||||
..write(
|
|
||||||
'(${asDartLiteral(column.nameInSql)}, aliasedName, $isNullable, ');
|
|
||||||
|
|
||||||
var first = true;
|
|
||||||
additionalParams.forEach((name, value) {
|
|
||||||
if (!first) {
|
|
||||||
expressionBuffer.write(', ');
|
|
||||||
} else {
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
expressionBuffer
|
|
||||||
..write(name)
|
|
||||||
..write(': ')
|
|
||||||
..write(value);
|
|
||||||
});
|
|
||||||
|
|
||||||
expressionBuffer.write(')');
|
|
||||||
|
|
||||||
final converter = column.typeConverter;
|
|
||||||
if (converter != null) {
|
|
||||||
// Generate a GeneratedColumnWithTypeConverter instance, as it has
|
|
||||||
// additional methods to check for equality against a mapped value.
|
|
||||||
final mappedType = emitter.dartCode(emitter.writer.dartType(column));
|
|
||||||
|
|
||||||
final converterCode = emitter.dartCode(emitter.writer
|
|
||||||
.readConverter(converter, forNullable: column.nullable));
|
|
||||||
|
|
||||||
type = '${emitter.drift('GeneratedColumnWithTypeConverter')}'
|
|
||||||
'<$mappedType, ${emitter.dartCode(innerType)}>';
|
|
||||||
expressionBuffer
|
|
||||||
..write('.withConverter<')
|
|
||||||
..write(mappedType)
|
|
||||||
..write('>(')
|
|
||||||
..write(converterCode)
|
|
||||||
..write(')');
|
|
||||||
}
|
|
||||||
|
|
||||||
writeMemoizedGetter(
|
writeMemoizedGetter(
|
||||||
buffer: buffer,
|
buffer: buffer,
|
||||||
getterName: column.nameInDart,
|
getterName: column.nameInDart,
|
||||||
returnType: type,
|
returnType: type,
|
||||||
code: expressionBuffer.toString(),
|
code: expression,
|
||||||
hasOverride: isOverride,
|
hasOverride: isOverride,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -276,6 +160,143 @@ abstract class TableOrViewWriter {
|
||||||
buffer.write(
|
buffer.write(
|
||||||
'@override\n${tableOrView.entityInfoName} get asDslTable => this;\n');
|
'@override\n${tableOrView.entityInfoName} get asDslTable => this;\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the Dart type and the Dart expression creating a `GeneratedColumn`
|
||||||
|
/// instance in drift for the givne [column].
|
||||||
|
static (String, String) instantiateColumn(
|
||||||
|
DriftColumn column,
|
||||||
|
TextEmitter emitter, {
|
||||||
|
bool? isRequiredForInsert,
|
||||||
|
}) {
|
||||||
|
final isNullable = column.nullable;
|
||||||
|
final additionalParams = <String, String>{};
|
||||||
|
final expressionBuffer = StringBuffer();
|
||||||
|
final constraints = defaultConstraints(column);
|
||||||
|
|
||||||
|
for (final constraint in column.constraints) {
|
||||||
|
if (constraint is LimitingTextLength) {
|
||||||
|
final buffer =
|
||||||
|
StringBuffer(emitter.drift('GeneratedColumn.checkTextLength('));
|
||||||
|
|
||||||
|
if (constraint.minLength != null) {
|
||||||
|
buffer.write('minTextLength: ${constraint.minLength},');
|
||||||
|
}
|
||||||
|
if (constraint.maxLength != null) {
|
||||||
|
buffer.write('maxTextLength: ${constraint.maxLength}');
|
||||||
|
}
|
||||||
|
buffer.write(')');
|
||||||
|
|
||||||
|
additionalParams['additionalChecks'] = buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constraint is DartCheckExpression) {
|
||||||
|
final dartCheck = emitter.dartCode(constraint.dartExpression);
|
||||||
|
additionalParams['check'] = '() => $dartCheck';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constraint is ColumnGeneratedAs) {
|
||||||
|
final dartCode = emitter.dartCode(constraint.dartExpression);
|
||||||
|
|
||||||
|
additionalParams['generatedAs'] =
|
||||||
|
'${emitter.drift('GeneratedAs')}($dartCode, ${constraint.stored})';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constraint is PrimaryKeyColumn && constraint.isAutoIncrement) {
|
||||||
|
additionalParams['hasAutoIncrement'] = 'true';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
additionalParams['type'] = emitter.drift(column.sqlType.toString());
|
||||||
|
|
||||||
|
if (isRequiredForInsert != null) {
|
||||||
|
additionalParams['requiredDuringInsert'] = isRequiredForInsert.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.customConstraints != null) {
|
||||||
|
additionalParams['\$customConstraints'] =
|
||||||
|
asDartLiteral(column.customConstraints!);
|
||||||
|
} else if (constraints.values.any((constraint) => constraint.isNotEmpty)) {
|
||||||
|
// Use the default constraints supported by drift
|
||||||
|
|
||||||
|
if (constraints.values.any(
|
||||||
|
(value) => value != constraints.values.first,
|
||||||
|
)) {
|
||||||
|
// One or more constraints are different depending on dialect, generate
|
||||||
|
// per-dialect constraints
|
||||||
|
|
||||||
|
final literalEntries = [
|
||||||
|
for (final entry in constraints.entries)
|
||||||
|
'${emitter.drift('SqlDialect.${entry.key.name}')}: ${asDartLiteral(entry.value)},',
|
||||||
|
];
|
||||||
|
|
||||||
|
additionalParams['defaultConstraints'] =
|
||||||
|
'${emitter.drift('GeneratedColumn.constraintsDependsOnDialect')}({${literalEntries.join('\n')}})';
|
||||||
|
} else {
|
||||||
|
// Constraints are the same regardless of dialect, only generate one set
|
||||||
|
// of them
|
||||||
|
|
||||||
|
final constraint = asDartLiteral(constraints.values.first);
|
||||||
|
|
||||||
|
additionalParams['defaultConstraints'] =
|
||||||
|
'${emitter.drift('GeneratedColumn.constraintIsAlways')}($constraint)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.defaultArgument != null) {
|
||||||
|
additionalParams['defaultValue'] =
|
||||||
|
emitter.dartCode(column.defaultArgument!);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.clientDefaultCode != null) {
|
||||||
|
additionalParams['clientDefault'] =
|
||||||
|
emitter.dartCode(column.clientDefaultCode!);
|
||||||
|
}
|
||||||
|
|
||||||
|
final innerType = emitter.innerColumnType(column);
|
||||||
|
var type =
|
||||||
|
'${emitter.drift('GeneratedColumn')}<${emitter.dartCode(innerType)}>';
|
||||||
|
expressionBuffer
|
||||||
|
..write(type)
|
||||||
|
..write(
|
||||||
|
'(${asDartLiteral(column.nameInSql)}, aliasedName, $isNullable, ');
|
||||||
|
|
||||||
|
var first = true;
|
||||||
|
additionalParams.forEach((name, value) {
|
||||||
|
if (!first) {
|
||||||
|
expressionBuffer.write(', ');
|
||||||
|
} else {
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
expressionBuffer
|
||||||
|
..write(name)
|
||||||
|
..write(': ')
|
||||||
|
..write(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
expressionBuffer.write(')');
|
||||||
|
|
||||||
|
final converter = column.typeConverter;
|
||||||
|
if (converter != null) {
|
||||||
|
// Generate a GeneratedColumnWithTypeConverter instance, as it has
|
||||||
|
// additional methods to check for equality against a mapped value.
|
||||||
|
final mappedType = emitter.dartCode(emitter.writer.dartType(column));
|
||||||
|
|
||||||
|
final converterCode = emitter.dartCode(emitter.writer
|
||||||
|
.readConverter(converter, forNullable: column.nullable));
|
||||||
|
|
||||||
|
type = '${emitter.drift('GeneratedColumnWithTypeConverter')}'
|
||||||
|
'<$mappedType, ${emitter.dartCode(innerType)}>';
|
||||||
|
expressionBuffer
|
||||||
|
..write('.withConverter<')
|
||||||
|
..write(mappedType)
|
||||||
|
..write('>(')
|
||||||
|
..write(converterCode)
|
||||||
|
..write(')');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (type, expressionBuffer.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TableWriter extends TableOrViewWriter {
|
class TableWriter extends TableOrViewWriter {
|
||||||
|
@ -577,8 +598,7 @@ class TableWriter extends TableOrViewWriter {
|
||||||
|
|
||||||
if (table.isVirtual) {
|
if (table.isVirtual) {
|
||||||
final stmt = table.virtualTableData!;
|
final stmt = table.virtualTableData!;
|
||||||
final moduleAndArgs =
|
final moduleAndArgs = asDartLiteral(stmt.moduleAndArgs);
|
||||||
asDartLiteral('${stmt.module}(${stmt.moduleArguments.join(', ')})');
|
|
||||||
buffer
|
buffer
|
||||||
..write('@override\n')
|
..write('@override\n')
|
||||||
..write('String get moduleAndArgs => $moduleAndArgs;\n');
|
..write('String get moduleAndArgs => $moduleAndArgs;\n');
|
||||||
|
|
|
@ -11,7 +11,7 @@ topics:
|
||||||
- database
|
- database
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.17.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
charcode: ^1.2.0
|
charcode: ^1.2.0
|
||||||
|
|
|
@ -13,7 +13,7 @@ class TestDriftProject {
|
||||||
|
|
||||||
Future<void> runDriftCli(Iterable<String> args) {
|
Future<void> runDriftCli(Iterable<String> args) {
|
||||||
return IOOverrides.runZoned(
|
return IOOverrides.runZoned(
|
||||||
() => MoorCli().run(args),
|
() => DriftDevCli().run(args),
|
||||||
getCurrentDirectory: () => root,
|
getCurrentDirectory: () => root,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ Drift databases don't depend on platform-channels or Flutter-specific features
|
||||||
by default. This means that they can easily be used in unit tests.
|
by default. This means that they can easily be used in unit tests.
|
||||||
One such test is in `test/database_test.dart`
|
One such test is in `test/database_test.dart`
|
||||||
|
|
||||||
#### Testing migrations
|
### Migration
|
||||||
|
|
||||||
After changing the structure of your database schema, for instance by adding
|
After changing the structure of your database schema, for instance by adding
|
||||||
new tables or altering columns, you need to write a migration to ensure that
|
new tables or altering columns, you need to write a migration to ensure that
|
||||||
|
@ -57,4 +57,11 @@ you can use to write unit tests for schema migrations:
|
||||||
dart run drift_dev schema generate drift_schemas/ test/generated_migrations/
|
dart run drift_dev schema generate drift_schemas/ test/generated_migrations/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To make migrations easier, this command updates the `lib/database/schema_versions.dart`
|
||||||
|
file containing snapshots of older database schema:
|
||||||
|
|
||||||
|
```
|
||||||
|
dart run drift_dev schema steps drift_schemas/ lib/database/schema_versions.dart
|
||||||
|
```
|
||||||
|
|
||||||
An example for a schema test is in `test/migration_test.dart`.
|
An example for a schema test is in `test/migration_test.dart`.
|
||||||
|
|
|
@ -4,6 +4,10 @@ import 'package:riverpod/riverpod.dart';
|
||||||
|
|
||||||
import 'connection/connection.dart' as impl;
|
import 'connection/connection.dart' as impl;
|
||||||
import 'tables.dart';
|
import 'tables.dart';
|
||||||
|
// Manually generated by `drift_dev schema steps` - this file makes writing
|
||||||
|
// migrations easier. See this for details:
|
||||||
|
// https://drift.simonbinder.eu/docs/advanced-features/migrations/#step-by-step
|
||||||
|
import 'schema_versions.dart';
|
||||||
|
|
||||||
// Generated by drift_dev when running `build_runner build`
|
// Generated by drift_dev when running `build_runner build`
|
||||||
part 'database.g.dart';
|
part 'database.g.dart';
|
||||||
|
@ -20,26 +24,22 @@ class AppDatabase extends _$AppDatabase {
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration {
|
MigrationStrategy get migration {
|
||||||
return MigrationStrategy(
|
return MigrationStrategy(
|
||||||
onUpgrade: ((m, from, to) async {
|
onUpgrade: stepByStep(
|
||||||
for (var step = from + 1; step <= to; step++) {
|
from1To2: (m, schema) async {
|
||||||
switch (step) {
|
// The todoEntries.dueDate column was added in version 2.
|
||||||
case 2:
|
await m.addColumn(schema.todoEntries, schema.todoEntries.dueDate);
|
||||||
// The todoEntries.dueDate column was added in version 2.
|
},
|
||||||
await m.addColumn(todoEntries, todoEntries.dueDate);
|
from2To3: (m, schema) async {
|
||||||
break;
|
// New triggers were added in version 3:
|
||||||
case 3:
|
await m.create(schema.todosDelete);
|
||||||
// New triggers were added in version 3:
|
await m.create(schema.todosUpdate);
|
||||||
await m.create(todosDelete);
|
|
||||||
await m.create(todosUpdate);
|
|
||||||
|
|
||||||
// Also, the `REFERENCES` constraint was added to
|
// Also, the `REFERENCES` constraint was added to
|
||||||
// [TodoEntries.category]. Run a table migration to rebuild all
|
// [TodoEntries.category]. Run a table migration to rebuild all
|
||||||
// column constraints without loosing data.
|
// column constraints without loosing data.
|
||||||
await m.alterTable(TableMigration(todoEntries));
|
await m.alterTable(TableMigration(schema.todoEntries));
|
||||||
break;
|
},
|
||||||
}
|
),
|
||||||
}
|
|
||||||
}),
|
|
||||||
beforeOpen: (details) async {
|
beforeOpen: (details) async {
|
||||||
// Make sure that foreign keys are enabled
|
// Make sure that foreign keys are enabled
|
||||||
await customStatement('PRAGMA foreign_keys = ON');
|
await customStatement('PRAGMA foreign_keys = ON');
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
import 'package:drift/internal/versioned_schema.dart' as i0;
|
||||||
|
import 'package:drift/drift.dart' as i1;
|
||||||
|
import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import
|
||||||
|
|
||||||
|
// GENERATED BY drift_dev, DO NOT MODIFY.
|
||||||
|
final class _S2 extends i0.VersionedSchema {
|
||||||
|
_S2({required super.database}) : super(version: 2);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
categories,
|
||||||
|
todoEntries,
|
||||||
|
textEntries,
|
||||||
|
todosInsert,
|
||||||
|
];
|
||||||
|
late final Shape0 categories = Shape0(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'categories',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
_column_2,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape1 todoEntries = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'todo_entries',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_3,
|
||||||
|
_column_4,
|
||||||
|
_column_5,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape2 textEntries = Shape2(
|
||||||
|
source: i0.VersionedVirtualTable(
|
||||||
|
entityName: 'text_entries',
|
||||||
|
moduleAndArgs:
|
||||||
|
'fts5(description, content=todo_entries, content_rowid=id)',
|
||||||
|
columns: [
|
||||||
|
_column_6,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
final i1.Trigger todosInsert = i1.Trigger(
|
||||||
|
'CREATE TRIGGER todos_insert AFTER INSERT ON todo_entries BEGIN\n INSERT INTO text_entries(rowid, description) VALUES (new.id, new.description);\nEND;',
|
||||||
|
'todos_insert');
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape0 extends i0.VersionedTable {
|
||||||
|
Shape0({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get name =>
|
||||||
|
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<int> get color =>
|
||||||
|
columnsByName['color']! as i1.GeneratedColumn<int>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_0(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('id', aliasedName, false,
|
||||||
|
hasAutoIncrement: true,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
defaultConstraints:
|
||||||
|
i1.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||||
|
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('name', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<int> _column_2(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('color', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
|
||||||
|
class Shape1 extends i0.VersionedTable {
|
||||||
|
Shape1({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get description =>
|
||||||
|
columnsByName['description']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<int> get category =>
|
||||||
|
columnsByName['category']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<DateTime> get dueDate =>
|
||||||
|
columnsByName['due_date']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_3(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('description', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
i1.GeneratedColumn<int> _column_4(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('category', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
i1.GeneratedColumn<DateTime> _column_5(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<DateTime>('due_date', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.dateTime);
|
||||||
|
|
||||||
|
class Shape2 extends i0.VersionedVirtualTable {
|
||||||
|
Shape2({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<String> get description =>
|
||||||
|
columnsByName['description']! as i1.GeneratedColumn<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_6(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('description', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string, $customConstraints: '');
|
||||||
|
|
||||||
|
final class _S3 extends i0.VersionedSchema {
|
||||||
|
_S3({required super.database}) : super(version: 3);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
categories,
|
||||||
|
todoEntries,
|
||||||
|
textEntries,
|
||||||
|
todosInsert,
|
||||||
|
todosDelete,
|
||||||
|
todosUpdate,
|
||||||
|
];
|
||||||
|
late final Shape0 categories = Shape0(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'categories',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
_column_2,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape1 todoEntries = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'todo_entries',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_3,
|
||||||
|
_column_7,
|
||||||
|
_column_5,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape2 textEntries = Shape2(
|
||||||
|
source: i0.VersionedVirtualTable(
|
||||||
|
entityName: 'text_entries',
|
||||||
|
moduleAndArgs:
|
||||||
|
'fts5(description, content=todo_entries, content_rowid=id)',
|
||||||
|
columns: [
|
||||||
|
_column_6,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
final i1.Trigger todosInsert = i1.Trigger(
|
||||||
|
'CREATE TRIGGER todos_insert AFTER INSERT ON todo_entries BEGIN INSERT INTO text_entries ("rowid", description) VALUES (new.id, new.description);END',
|
||||||
|
'todos_insert');
|
||||||
|
final i1.Trigger todosDelete = i1.Trigger(
|
||||||
|
'CREATE TRIGGER todos_delete AFTER DELETE ON todo_entries BEGIN INSERT INTO text_entries (text_entries, "rowid", description) VALUES (\'delete\', old.id, old.description);END',
|
||||||
|
'todos_delete');
|
||||||
|
final i1.Trigger todosUpdate = i1.Trigger(
|
||||||
|
'CREATE TRIGGER todos_update AFTER UPDATE ON todo_entries BEGIN INSERT INTO text_entries (text_entries, "rowid", description) VALUES (\'delete\', new.id, new.description);INSERT INTO text_entries ("rowid", description) VALUES (new.id, new.description);END',
|
||||||
|
'todos_update');
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_7(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('category', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||||
|
'REFERENCES categories (id)'));
|
||||||
|
i1.OnUpgrade stepByStep({
|
||||||
|
required Future<void> Function(i1.Migrator m, _S2 schema) from1To2,
|
||||||
|
required Future<void> Function(i1.Migrator m, _S3 schema) from2To3,
|
||||||
|
}) {
|
||||||
|
return i1.Migrator.stepByStepHelper(step: (currentVersion, database) async {
|
||||||
|
switch (currentVersion) {
|
||||||
|
case 1:
|
||||||
|
final schema = _S2(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from1To2(migrator, schema);
|
||||||
|
return 2;
|
||||||
|
case 2:
|
||||||
|
final schema = _S3(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from2To3(migrator, schema);
|
||||||
|
return 3;
|
||||||
|
default:
|
||||||
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ publish_to: 'none'
|
||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.16.1 <3.0.0"
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
|
@ -15,9 +15,8 @@ dependencies:
|
||||||
file_picker: ^5.2.5
|
file_picker: ^5.2.5
|
||||||
flutter_colorpicker: ^1.0.3
|
flutter_colorpicker: ^1.0.3
|
||||||
flutter_riverpod: ^2.3.0
|
flutter_riverpod: ^2.3.0
|
||||||
go_router: ^6.2.0
|
go_router: ^9.0.0
|
||||||
intl: ^0.18.0
|
intl: ^0.18.0
|
||||||
http: ^0.13.4 # used to load sqlite3 wasm files on the web
|
|
||||||
sqlite3_flutter_libs: ^0.5.5
|
sqlite3_flutter_libs: ^0.5.5
|
||||||
sqlite3: ^2.0.0
|
sqlite3: ^2.0.0
|
||||||
path_provider: ^2.0.9
|
path_provider: ^2.0.9
|
||||||
|
@ -34,7 +33,7 @@ dev_dependencies:
|
||||||
# web workers for drift AND want to compile those web workers through `build_runner`.
|
# web workers for drift AND want to compile those web workers through `build_runner`.
|
||||||
# Either way, using this package with Flutter requires a delicate setup described in
|
# Either way, using this package with Flutter requires a delicate setup described in
|
||||||
# `build.yaml`.
|
# `build.yaml`.
|
||||||
build_web_compilers: ^3.2.2
|
build_web_compilers: ^4.0.0
|
||||||
build: ^2.2.1
|
build: ^2.2.1
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -20,9 +20,9 @@ Run
|
||||||
dart run drift_dev schema generate drift_migrations/ test/generated/ --data-classes --companions
|
dart run drift_dev schema generate drift_migrations/ test/generated/ --data-classes --companions
|
||||||
```
|
```
|
||||||
|
|
||||||
We're also using test code inside `lib/` to run migrations with older definitions of tables.
|
Since we're using the step-by-step generator to make writing migrations easier, this command
|
||||||
This isn't required for all migrations, but can be useful in some cases.
|
is used to generate a helper file in `lib/`:
|
||||||
|
|
||||||
```
|
```
|
||||||
dart run drift_dev schema generate drift_migrations/ lib/src/generated
|
dart run drift_dev schema steps drift_migrations/ lib/src/versions.dart
|
||||||
```
|
```
|
||||||
|
|
|
@ -2,9 +2,7 @@ import 'package:drift/drift.dart';
|
||||||
import 'package:drift_dev/api/migrations.dart';
|
import 'package:drift_dev/api/migrations.dart';
|
||||||
|
|
||||||
import 'tables.dart';
|
import 'tables.dart';
|
||||||
import 'src/generated/schema_v2.dart' as v2;
|
import 'src/versions.dart';
|
||||||
import 'src/generated/schema_v4.dart' as v4;
|
|
||||||
import 'src/generated/schema_v8.dart' as v8;
|
|
||||||
|
|
||||||
part 'database.g.dart';
|
part 'database.g.dart';
|
||||||
|
|
||||||
|
@ -20,59 +18,7 @@ class Database extends _$Database {
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration {
|
MigrationStrategy get migration {
|
||||||
return MigrationStrategy(
|
return MigrationStrategy(
|
||||||
onUpgrade: (m, before, now) async {
|
onUpgrade: _upgrade,
|
||||||
for (var target = before + 1; target <= now; target++) {
|
|
||||||
switch (target) {
|
|
||||||
case 2:
|
|
||||||
// Migration from 1 to 2: Add name column in users. Use "no name"
|
|
||||||
// as a default value.
|
|
||||||
final usersAtV2 = v2.Users(this);
|
|
||||||
|
|
||||||
await m.alterTable(
|
|
||||||
TableMigration(
|
|
||||||
usersAtV2,
|
|
||||||
columnTransformer: {
|
|
||||||
users.name: const Constant<String>('no name'),
|
|
||||||
},
|
|
||||||
newColumns: [usersAtV2.name],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
// Migration from 2 to 3: We added the groups table
|
|
||||||
await m.createTable(groups);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
// Migration from 3 to 4: users.name now has a default value
|
|
||||||
// No need to transform any data, just re-create the table
|
|
||||||
final usersAtV4 = v4.Users(this);
|
|
||||||
|
|
||||||
await m.alterTable(TableMigration(usersAtV4));
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
// Just add a new column that was added in version 5;
|
|
||||||
await m.addColumn(users, users.nextUser);
|
|
||||||
|
|
||||||
// And create the view on users
|
|
||||||
await m.createView(groupCount);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
await m.addColumn(users, users.birthday);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
await m.createTable(notes);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
// Added a unique key to the users table
|
|
||||||
await m.alterTable(TableMigration(v8.Users(this)));
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
// Added a check to the users table
|
|
||||||
await m.alterTable(TableMigration(users));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeOpen: (details) async {
|
beforeOpen: (details) async {
|
||||||
// For Flutter apps, this should be wrapped in an if (kDebugMode) as
|
// For Flutter apps, this should be wrapped in an if (kDebugMode) as
|
||||||
// suggested here: https://drift.simonbinder.eu/docs/advanced-features/migrations/#verifying-a-database-schema-at-runtime
|
// suggested here: https://drift.simonbinder.eu/docs/advanced-features/migrations/#verifying-a-database-schema-at-runtime
|
||||||
|
@ -80,4 +26,50 @@ class Database extends _$Database {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final _upgrade = stepByStep(
|
||||||
|
from1To2: (m, schema) async {
|
||||||
|
// Migration from 1 to 2: Add name column in users. Use "no name"
|
||||||
|
// as a default value.
|
||||||
|
|
||||||
|
await m.alterTable(
|
||||||
|
TableMigration(
|
||||||
|
schema.users,
|
||||||
|
columnTransformer: {
|
||||||
|
schema.users.name: const Constant<String>('no name'),
|
||||||
|
},
|
||||||
|
newColumns: [schema.users.name],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
from2To3: (m, schema) async => m.createTable(schema.groups),
|
||||||
|
from3To4: (m, schema) async {
|
||||||
|
// Migration from 3 to 4: users.name now has a default value
|
||||||
|
// No need to transform any data, just re-create the table
|
||||||
|
final usersAtV4 = schema.users;
|
||||||
|
|
||||||
|
await m.alterTable(TableMigration(usersAtV4));
|
||||||
|
},
|
||||||
|
from4To5: (m, schema) async {
|
||||||
|
// Just add a new column that was added in version 5;
|
||||||
|
await m.addColumn(schema.users, schema.users.nextUser);
|
||||||
|
|
||||||
|
// And create the view on users
|
||||||
|
await m.createView(schema.groupCount);
|
||||||
|
},
|
||||||
|
from5To6: (m, schema) async {
|
||||||
|
await m.addColumn(schema.users, schema.users.birthday);
|
||||||
|
},
|
||||||
|
from6To7: (m, schema) async {
|
||||||
|
await m.createTable(schema.notes);
|
||||||
|
},
|
||||||
|
from7To8: (m, schema) async {
|
||||||
|
// Added a unique key to the users table
|
||||||
|
await m.alterTable(TableMigration(schema.users));
|
||||||
|
},
|
||||||
|
from8To9: (m, schema) async {
|
||||||
|
// Added a check to the users table
|
||||||
|
await m.alterTable(TableMigration(schema.users));
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -874,7 +874,7 @@ class GroupCount extends ViewInfo<GroupCount, GroupCountData>
|
||||||
@override
|
@override
|
||||||
Query? get query => null;
|
Query? get query => null;
|
||||||
@override
|
@override
|
||||||
Set<String> get readTables => const {'groups', 'users'};
|
Set<String> get readTables => const {'users', 'groups'};
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _$Database extends GeneratedDatabase {
|
abstract class _$Database extends GeneratedDatabase {
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
//@dart=2.12
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:drift/internal/migrations.dart';
|
|
||||||
import 'schema_v1.dart' as v1;
|
|
||||||
import 'schema_v2.dart' as v2;
|
|
||||||
import 'schema_v3.dart' as v3;
|
|
||||||
import 'schema_v4.dart' as v4;
|
|
||||||
import 'schema_v5.dart' as v5;
|
|
||||||
import 'schema_v6.dart' as v6;
|
|
||||||
import 'schema_v7.dart' as v7;
|
|
||||||
import 'schema_v8.dart' as v8;
|
|
||||||
import 'schema_v9.dart' as v9;
|
|
||||||
|
|
||||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
|
||||||
@override
|
|
||||||
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
|
|
||||||
switch (version) {
|
|
||||||
case 1:
|
|
||||||
return v1.DatabaseAtV1(db);
|
|
||||||
case 2:
|
|
||||||
return v2.DatabaseAtV2(db);
|
|
||||||
case 3:
|
|
||||||
return v3.DatabaseAtV3(db);
|
|
||||||
case 4:
|
|
||||||
return v4.DatabaseAtV4(db);
|
|
||||||
case 5:
|
|
||||||
return v5.DatabaseAtV5(db);
|
|
||||||
case 6:
|
|
||||||
return v6.DatabaseAtV6(db);
|
|
||||||
case 7:
|
|
||||||
return v7.DatabaseAtV7(db);
|
|
||||||
case 8:
|
|
||||||
return v8.DatabaseAtV8(db);
|
|
||||||
case 9:
|
|
||||||
return v9.DatabaseAtV9(db);
|
|
||||||
default:
|
|
||||||
throw MissingSchemaException(
|
|
||||||
version, const {1, 2, 3, 4, 5, 6, 7, 8, 9});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
//@dart=2.12
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
|
|
||||||
class Users extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Users(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
hasAutoIncrement: true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'users';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'users';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Users createAlias(String alias) {
|
|
||||||
return Users(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DatabaseAtV1 extends GeneratedDatabase {
|
|
||||||
DatabaseAtV1(QueryExecutor e) : super(e);
|
|
||||||
late final Users users = Users(this);
|
|
||||||
@override
|
|
||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
|
||||||
@override
|
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities => [users];
|
|
||||||
@override
|
|
||||||
int get schemaVersion => 1;
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
//@dart=2.12
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
|
|
||||||
class Users extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Users(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
hasAutoIncrement: true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string, requiredDuringInsert: true);
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, name];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'users';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'users';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Users createAlias(String alias) {
|
|
||||||
return Users(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DatabaseAtV2 extends GeneratedDatabase {
|
|
||||||
DatabaseAtV2(QueryExecutor e) : super(e);
|
|
||||||
late final Users users = Users(this);
|
|
||||||
@override
|
|
||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
|
||||||
@override
|
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities => [users];
|
|
||||||
@override
|
|
||||||
int get schemaVersion => 2;
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
//@dart=2.12
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
|
|
||||||
class Users extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Users(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
hasAutoIncrement: true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string, requiredDuringInsert: true);
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, name];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'users';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'users';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Users createAlias(String alias) {
|
|
||||||
return Users(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Groups extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Groups(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
|
||||||
'title', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<bool> deleted = GeneratedColumn<bool>(
|
|
||||||
'deleted', aliasedName, true,
|
|
||||||
type: DriftSqlType.bool,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'DEFAULT FALSE',
|
|
||||||
defaultValue: const CustomExpression<bool>('FALSE'));
|
|
||||||
late final GeneratedColumn<int> owner = GeneratedColumn<int>(
|
|
||||||
'owner', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL REFERENCES users (id)');
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, title, deleted, owner];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'groups';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'groups';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Groups createAlias(String alias) {
|
|
||||||
return Groups(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<String> get customConstraints => const ['PRIMARY KEY (id)'];
|
|
||||||
@override
|
|
||||||
bool get dontWriteConstraints => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DatabaseAtV3 extends GeneratedDatabase {
|
|
||||||
DatabaseAtV3(QueryExecutor e) : super(e);
|
|
||||||
late final Users users = Users(this);
|
|
||||||
late final Groups groups = Groups(this);
|
|
||||||
@override
|
|
||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
|
||||||
@override
|
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities => [users, groups];
|
|
||||||
@override
|
|
||||||
int get schemaVersion => 3;
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
//@dart=2.12
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
|
|
||||||
class Users extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Users(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
hasAutoIncrement: true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultValue: const Constant('name'));
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, name];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'users';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'users';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Users createAlias(String alias) {
|
|
||||||
return Users(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Groups extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Groups(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
|
||||||
'title', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<bool> deleted = GeneratedColumn<bool>(
|
|
||||||
'deleted', aliasedName, true,
|
|
||||||
type: DriftSqlType.bool,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'DEFAULT FALSE',
|
|
||||||
defaultValue: const CustomExpression<bool>('FALSE'));
|
|
||||||
late final GeneratedColumn<int> owner = GeneratedColumn<int>(
|
|
||||||
'owner', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL REFERENCES users (id)');
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, title, deleted, owner];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'groups';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'groups';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Groups createAlias(String alias) {
|
|
||||||
return Groups(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<String> get customConstraints => const ['PRIMARY KEY (id)'];
|
|
||||||
@override
|
|
||||||
bool get dontWriteConstraints => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DatabaseAtV4 extends GeneratedDatabase {
|
|
||||||
DatabaseAtV4(QueryExecutor e) : super(e);
|
|
||||||
late final Users users = Users(this);
|
|
||||||
late final Groups groups = Groups(this);
|
|
||||||
@override
|
|
||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
|
||||||
@override
|
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities => [users, groups];
|
|
||||||
@override
|
|
||||||
int get schemaVersion => 4;
|
|
||||||
}
|
|
|
@ -1,154 +0,0 @@
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
//@dart=2.12
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
|
|
||||||
class Users extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Users(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
hasAutoIncrement: true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultValue: const Constant('name'));
|
|
||||||
late final GeneratedColumn<int> nextUser = GeneratedColumn<int>(
|
|
||||||
'next_user', aliasedName, true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('REFERENCES users (id)'));
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, name, nextUser];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'users';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'users';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Users createAlias(String alias) {
|
|
||||||
return Users(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Groups extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Groups(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
|
||||||
'title', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<bool> deleted = GeneratedColumn<bool>(
|
|
||||||
'deleted', aliasedName, true,
|
|
||||||
type: DriftSqlType.bool,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'DEFAULT FALSE',
|
|
||||||
defaultValue: const CustomExpression<bool>('FALSE'));
|
|
||||||
late final GeneratedColumn<int> owner = GeneratedColumn<int>(
|
|
||||||
'owner', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL REFERENCES users (id)');
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, title, deleted, owner];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'groups';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'groups';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Groups createAlias(String alias) {
|
|
||||||
return Groups(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<String> get customConstraints => const ['PRIMARY KEY (id)'];
|
|
||||||
@override
|
|
||||||
bool get dontWriteConstraints => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupCount extends ViewInfo<GroupCount, Never> implements HasResultSet {
|
|
||||||
final String? _alias;
|
|
||||||
@override
|
|
||||||
final DatabaseAtV5 attachedDatabase;
|
|
||||||
GroupCount(this.attachedDatabase, [this._alias]);
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, name, nextUser, groupCount];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? entityName;
|
|
||||||
@override
|
|
||||||
String get entityName => 'group_count';
|
|
||||||
@override
|
|
||||||
String get createViewStmt =>
|
|
||||||
'CREATE VIEW group_count AS SELECT users.*, (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count FROM users';
|
|
||||||
@override
|
|
||||||
GroupCount get asDslTable => this;
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
late final GeneratedColumn<int> id =
|
|
||||||
GeneratedColumn<int>('id', aliasedName, false, type: DriftSqlType.int);
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string);
|
|
||||||
late final GeneratedColumn<int> nextUser = GeneratedColumn<int>(
|
|
||||||
'next_user', aliasedName, true,
|
|
||||||
type: DriftSqlType.int);
|
|
||||||
late final GeneratedColumn<int> groupCount = GeneratedColumn<int>(
|
|
||||||
'group_count', aliasedName, false,
|
|
||||||
type: DriftSqlType.int);
|
|
||||||
@override
|
|
||||||
GroupCount createAlias(String alias) {
|
|
||||||
return GroupCount(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Query? get query => null;
|
|
||||||
@override
|
|
||||||
Set<String> get readTables => const {};
|
|
||||||
}
|
|
||||||
|
|
||||||
class DatabaseAtV5 extends GeneratedDatabase {
|
|
||||||
DatabaseAtV5(QueryExecutor e) : super(e);
|
|
||||||
late final Users users = Users(this);
|
|
||||||
late final Groups groups = Groups(this);
|
|
||||||
late final GroupCount groupCount = GroupCount(this);
|
|
||||||
@override
|
|
||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
|
||||||
@override
|
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities =>
|
|
||||||
[users, groups, groupCount];
|
|
||||||
@override
|
|
||||||
int get schemaVersion => 5;
|
|
||||||
}
|
|
|
@ -1,164 +0,0 @@
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
//@dart=2.12
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
|
|
||||||
class Users extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Users(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
hasAutoIncrement: true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultValue: const Constant('name'));
|
|
||||||
late final GeneratedColumn<DateTime> birthday = GeneratedColumn<DateTime>(
|
|
||||||
'birthday', aliasedName, true,
|
|
||||||
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
|
||||||
late final GeneratedColumn<int> nextUser = GeneratedColumn<int>(
|
|
||||||
'next_user', aliasedName, true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('REFERENCES users (id)'));
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, name, birthday, nextUser];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'users';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'users';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Users createAlias(String alias) {
|
|
||||||
return Users(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Groups extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Groups(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
|
||||||
'title', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<bool> deleted = GeneratedColumn<bool>(
|
|
||||||
'deleted', aliasedName, true,
|
|
||||||
type: DriftSqlType.bool,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'DEFAULT FALSE',
|
|
||||||
defaultValue: const CustomExpression<bool>('FALSE'));
|
|
||||||
late final GeneratedColumn<int> owner = GeneratedColumn<int>(
|
|
||||||
'owner', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL REFERENCES users (id)');
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, title, deleted, owner];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'groups';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'groups';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Groups createAlias(String alias) {
|
|
||||||
return Groups(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<String> get customConstraints => const ['PRIMARY KEY (id)'];
|
|
||||||
@override
|
|
||||||
bool get dontWriteConstraints => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupCount extends ViewInfo<GroupCount, Never> implements HasResultSet {
|
|
||||||
final String? _alias;
|
|
||||||
@override
|
|
||||||
final DatabaseAtV6 attachedDatabase;
|
|
||||||
GroupCount(this.attachedDatabase, [this._alias]);
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns =>
|
|
||||||
[id, name, birthday, nextUser, groupCount];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? entityName;
|
|
||||||
@override
|
|
||||||
String get entityName => 'group_count';
|
|
||||||
@override
|
|
||||||
String get createViewStmt =>
|
|
||||||
'CREATE VIEW group_count AS SELECT users.*, (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count FROM users';
|
|
||||||
@override
|
|
||||||
GroupCount get asDslTable => this;
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
late final GeneratedColumn<int> id =
|
|
||||||
GeneratedColumn<int>('id', aliasedName, false, type: DriftSqlType.int);
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string);
|
|
||||||
late final GeneratedColumn<DateTime> birthday = GeneratedColumn<DateTime>(
|
|
||||||
'birthday', aliasedName, true,
|
|
||||||
type: DriftSqlType.dateTime);
|
|
||||||
late final GeneratedColumn<int> nextUser = GeneratedColumn<int>(
|
|
||||||
'next_user', aliasedName, true,
|
|
||||||
type: DriftSqlType.int);
|
|
||||||
late final GeneratedColumn<int> groupCount = GeneratedColumn<int>(
|
|
||||||
'group_count', aliasedName, false,
|
|
||||||
type: DriftSqlType.int);
|
|
||||||
@override
|
|
||||||
GroupCount createAlias(String alias) {
|
|
||||||
return GroupCount(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Query? get query => null;
|
|
||||||
@override
|
|
||||||
Set<String> get readTables => const {};
|
|
||||||
}
|
|
||||||
|
|
||||||
class DatabaseAtV6 extends GeneratedDatabase {
|
|
||||||
DatabaseAtV6(QueryExecutor e) : super(e);
|
|
||||||
late final Users users = Users(this);
|
|
||||||
late final Groups groups = Groups(this);
|
|
||||||
late final GroupCount groupCount = GroupCount(this);
|
|
||||||
@override
|
|
||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
|
||||||
@override
|
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities =>
|
|
||||||
[users, groups, groupCount];
|
|
||||||
@override
|
|
||||||
int get schemaVersion => 6;
|
|
||||||
@override
|
|
||||||
DriftDatabaseOptions get options =>
|
|
||||||
const DriftDatabaseOptions(storeDateTimeAsText: true);
|
|
||||||
}
|
|
|
@ -1,208 +0,0 @@
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
//@dart=2.12
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
|
|
||||||
class Users extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Users(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
hasAutoIncrement: true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultValue: const Constant('name'));
|
|
||||||
late final GeneratedColumn<DateTime> birthday = GeneratedColumn<DateTime>(
|
|
||||||
'birthday', aliasedName, true,
|
|
||||||
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
|
||||||
late final GeneratedColumn<int> nextUser = GeneratedColumn<int>(
|
|
||||||
'next_user', aliasedName, true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('REFERENCES users (id)'));
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, name, birthday, nextUser];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'users';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'users';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Users createAlias(String alias) {
|
|
||||||
return Users(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Groups extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Groups(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
|
||||||
'title', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<bool> deleted = GeneratedColumn<bool>(
|
|
||||||
'deleted', aliasedName, true,
|
|
||||||
type: DriftSqlType.bool,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'DEFAULT FALSE',
|
|
||||||
defaultValue: const CustomExpression<bool>('FALSE'));
|
|
||||||
late final GeneratedColumn<int> owner = GeneratedColumn<int>(
|
|
||||||
'owner', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL REFERENCES users (id)');
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, title, deleted, owner];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'groups';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'groups';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Groups createAlias(String alias) {
|
|
||||||
return Groups(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<String> get customConstraints => const ['PRIMARY KEY (id)'];
|
|
||||||
@override
|
|
||||||
bool get dontWriteConstraints => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupCount extends ViewInfo<GroupCount, Never> implements HasResultSet {
|
|
||||||
final String? _alias;
|
|
||||||
@override
|
|
||||||
final DatabaseAtV7 attachedDatabase;
|
|
||||||
GroupCount(this.attachedDatabase, [this._alias]);
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns =>
|
|
||||||
[id, name, birthday, nextUser, groupCount];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? entityName;
|
|
||||||
@override
|
|
||||||
String get entityName => 'group_count';
|
|
||||||
@override
|
|
||||||
String get createViewStmt =>
|
|
||||||
'CREATE VIEW group_count AS SELECT users.*, (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count FROM users';
|
|
||||||
@override
|
|
||||||
GroupCount get asDslTable => this;
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
late final GeneratedColumn<int> id =
|
|
||||||
GeneratedColumn<int>('id', aliasedName, false, type: DriftSqlType.int);
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string);
|
|
||||||
late final GeneratedColumn<DateTime> birthday = GeneratedColumn<DateTime>(
|
|
||||||
'birthday', aliasedName, true,
|
|
||||||
type: DriftSqlType.dateTime);
|
|
||||||
late final GeneratedColumn<int> nextUser = GeneratedColumn<int>(
|
|
||||||
'next_user', aliasedName, true,
|
|
||||||
type: DriftSqlType.int);
|
|
||||||
late final GeneratedColumn<int> groupCount = GeneratedColumn<int>(
|
|
||||||
'group_count', aliasedName, false,
|
|
||||||
type: DriftSqlType.int);
|
|
||||||
@override
|
|
||||||
GroupCount createAlias(String alias) {
|
|
||||||
return GroupCount(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Query? get query => null;
|
|
||||||
@override
|
|
||||||
Set<String> get readTables => const {};
|
|
||||||
}
|
|
||||||
|
|
||||||
class Notes extends Table with TableInfo, VirtualTableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Notes(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
|
||||||
'title', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: '');
|
|
||||||
late final GeneratedColumn<String> content = GeneratedColumn<String>(
|
|
||||||
'content', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: '');
|
|
||||||
late final GeneratedColumn<String> searchTerms = GeneratedColumn<String>(
|
|
||||||
'search_terms', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: '');
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [title, content, searchTerms];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'notes';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'notes';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => const {};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Notes createAlias(String alias) {
|
|
||||||
return Notes(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get moduleAndArgs =>
|
|
||||||
'fts5(title, content, search_terms, tokenize = "unicode61 tokenchars \'.\'")';
|
|
||||||
}
|
|
||||||
|
|
||||||
class DatabaseAtV7 extends GeneratedDatabase {
|
|
||||||
DatabaseAtV7(QueryExecutor e) : super(e);
|
|
||||||
late final Users users = Users(this);
|
|
||||||
late final Groups groups = Groups(this);
|
|
||||||
late final GroupCount groupCount = GroupCount(this);
|
|
||||||
late final Notes notes = Notes(this);
|
|
||||||
@override
|
|
||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
|
||||||
@override
|
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities =>
|
|
||||||
[users, groups, groupCount, notes];
|
|
||||||
@override
|
|
||||||
int get schemaVersion => 7;
|
|
||||||
@override
|
|
||||||
DriftDatabaseOptions get options =>
|
|
||||||
const DriftDatabaseOptions(storeDateTimeAsText: true);
|
|
||||||
}
|
|
|
@ -1,212 +0,0 @@
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
//@dart=2.12
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
|
|
||||||
class Users extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Users(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
hasAutoIncrement: true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultValue: const Constant('name'));
|
|
||||||
late final GeneratedColumn<DateTime> birthday = GeneratedColumn<DateTime>(
|
|
||||||
'birthday', aliasedName, true,
|
|
||||||
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
|
||||||
late final GeneratedColumn<int> nextUser = GeneratedColumn<int>(
|
|
||||||
'next_user', aliasedName, true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('REFERENCES "users" ("id")'));
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, name, birthday, nextUser];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'users';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'users';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
List<Set<GeneratedColumn>> get uniqueKeys => [
|
|
||||||
{name, birthday},
|
|
||||||
];
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Users createAlias(String alias) {
|
|
||||||
return Users(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Groups extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Groups(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
|
||||||
'title', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<bool> deleted = GeneratedColumn<bool>(
|
|
||||||
'deleted', aliasedName, true,
|
|
||||||
type: DriftSqlType.bool,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'DEFAULT FALSE',
|
|
||||||
defaultValue: const CustomExpression<bool>('FALSE'));
|
|
||||||
late final GeneratedColumn<int> owner = GeneratedColumn<int>(
|
|
||||||
'owner', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL REFERENCES users (id)');
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, title, deleted, owner];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'groups';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'groups';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Groups createAlias(String alias) {
|
|
||||||
return Groups(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<String> get customConstraints => const ['PRIMARY KEY (id)'];
|
|
||||||
@override
|
|
||||||
bool get dontWriteConstraints => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupCount extends ViewInfo<GroupCount, Never> implements HasResultSet {
|
|
||||||
final String? _alias;
|
|
||||||
@override
|
|
||||||
final DatabaseAtV8 attachedDatabase;
|
|
||||||
GroupCount(this.attachedDatabase, [this._alias]);
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns =>
|
|
||||||
[id, name, birthday, nextUser, groupCount];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? entityName;
|
|
||||||
@override
|
|
||||||
String get entityName => 'group_count';
|
|
||||||
@override
|
|
||||||
String get createViewStmt =>
|
|
||||||
'CREATE VIEW group_count AS SELECT users.*, (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count FROM users';
|
|
||||||
@override
|
|
||||||
GroupCount get asDslTable => this;
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
late final GeneratedColumn<int> id =
|
|
||||||
GeneratedColumn<int>('id', aliasedName, false, type: DriftSqlType.int);
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string);
|
|
||||||
late final GeneratedColumn<DateTime> birthday = GeneratedColumn<DateTime>(
|
|
||||||
'birthday', aliasedName, true,
|
|
||||||
type: DriftSqlType.dateTime);
|
|
||||||
late final GeneratedColumn<int> nextUser = GeneratedColumn<int>(
|
|
||||||
'next_user', aliasedName, true,
|
|
||||||
type: DriftSqlType.int);
|
|
||||||
late final GeneratedColumn<int> groupCount = GeneratedColumn<int>(
|
|
||||||
'group_count', aliasedName, false,
|
|
||||||
type: DriftSqlType.int);
|
|
||||||
@override
|
|
||||||
GroupCount createAlias(String alias) {
|
|
||||||
return GroupCount(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Query? get query => null;
|
|
||||||
@override
|
|
||||||
Set<String> get readTables => const {};
|
|
||||||
}
|
|
||||||
|
|
||||||
class Notes extends Table with TableInfo, VirtualTableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Notes(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
|
||||||
'title', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: '');
|
|
||||||
late final GeneratedColumn<String> content = GeneratedColumn<String>(
|
|
||||||
'content', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: '');
|
|
||||||
late final GeneratedColumn<String> searchTerms = GeneratedColumn<String>(
|
|
||||||
'search_terms', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: '');
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [title, content, searchTerms];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'notes';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'notes';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => const {};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Notes createAlias(String alias) {
|
|
||||||
return Notes(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get moduleAndArgs =>
|
|
||||||
'fts5(title, content, search_terms, tokenize = "unicode61 tokenchars \'.\'")';
|
|
||||||
}
|
|
||||||
|
|
||||||
class DatabaseAtV8 extends GeneratedDatabase {
|
|
||||||
DatabaseAtV8(QueryExecutor e) : super(e);
|
|
||||||
late final Users users = Users(this);
|
|
||||||
late final Groups groups = Groups(this);
|
|
||||||
late final GroupCount groupCount = GroupCount(this);
|
|
||||||
late final Notes notes = Notes(this);
|
|
||||||
@override
|
|
||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
|
||||||
@override
|
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities =>
|
|
||||||
[users, groups, groupCount, notes];
|
|
||||||
@override
|
|
||||||
int get schemaVersion => 8;
|
|
||||||
@override
|
|
||||||
DriftDatabaseOptions get options =>
|
|
||||||
const DriftDatabaseOptions(storeDateTimeAsText: true);
|
|
||||||
}
|
|
|
@ -1,215 +0,0 @@
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
//@dart=2.12
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
|
|
||||||
class Users extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Users(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
hasAutoIncrement: true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultValue: const Constant('name'));
|
|
||||||
late final GeneratedColumn<DateTime> birthday = GeneratedColumn<DateTime>(
|
|
||||||
'birthday', aliasedName, true,
|
|
||||||
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
|
||||||
late final GeneratedColumn<int> nextUser = GeneratedColumn<int>(
|
|
||||||
'next_user', aliasedName, true,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints:
|
|
||||||
GeneratedColumn.constraintIsAlways('REFERENCES users (id)'));
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, name, birthday, nextUser];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'users';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'users';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
List<Set<GeneratedColumn>> get uniqueKeys => [
|
|
||||||
{name, birthday},
|
|
||||||
];
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Users createAlias(String alias) {
|
|
||||||
return Users(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<String> get customConstraints => const ['CHECK (LENGTH(name) < 10)'];
|
|
||||||
}
|
|
||||||
|
|
||||||
class Groups extends Table with TableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Groups(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
|
||||||
'title', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL');
|
|
||||||
late final GeneratedColumn<bool> deleted = GeneratedColumn<bool>(
|
|
||||||
'deleted', aliasedName, true,
|
|
||||||
type: DriftSqlType.bool,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
$customConstraints: 'DEFAULT FALSE',
|
|
||||||
defaultValue: const CustomExpression('FALSE'));
|
|
||||||
late final GeneratedColumn<int> owner = GeneratedColumn<int>(
|
|
||||||
'owner', aliasedName, false,
|
|
||||||
type: DriftSqlType.int,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: 'NOT NULL REFERENCES users(id)');
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [id, title, deleted, owner];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'groups';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'groups';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Groups createAlias(String alias) {
|
|
||||||
return Groups(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<String> get customConstraints => const ['PRIMARY KEY(id)'];
|
|
||||||
@override
|
|
||||||
bool get dontWriteConstraints => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Notes extends Table with TableInfo, VirtualTableInfo {
|
|
||||||
@override
|
|
||||||
final GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
Notes(this.attachedDatabase, [this._alias]);
|
|
||||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
|
||||||
'title', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: '');
|
|
||||||
late final GeneratedColumn<String> content = GeneratedColumn<String>(
|
|
||||||
'content', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: '');
|
|
||||||
late final GeneratedColumn<String> searchTerms = GeneratedColumn<String>(
|
|
||||||
'search_terms', aliasedName, false,
|
|
||||||
type: DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
$customConstraints: '');
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns => [title, content, searchTerms];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? 'notes';
|
|
||||||
@override
|
|
||||||
String get actualTableName => 'notes';
|
|
||||||
@override
|
|
||||||
Set<GeneratedColumn> get $primaryKey => const {};
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Notes createAlias(String alias) {
|
|
||||||
return Notes(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get moduleAndArgs =>
|
|
||||||
'fts5(title, content, search_terms, tokenize = "unicode61 tokenchars \'.\'")';
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupCount extends ViewInfo<GroupCount, Never> implements HasResultSet {
|
|
||||||
final String? _alias;
|
|
||||||
@override
|
|
||||||
final DatabaseAtV9 attachedDatabase;
|
|
||||||
GroupCount(this.attachedDatabase, [this._alias]);
|
|
||||||
@override
|
|
||||||
List<GeneratedColumn> get $columns =>
|
|
||||||
[id, name, birthday, nextUser, groupCount];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? entityName;
|
|
||||||
@override
|
|
||||||
String get entityName => 'group_count';
|
|
||||||
@override
|
|
||||||
String get createViewStmt =>
|
|
||||||
'CREATE VIEW group_count AS SELECT\n users.*,\n (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count\n FROM users;';
|
|
||||||
@override
|
|
||||||
GroupCount get asDslTable => this;
|
|
||||||
@override
|
|
||||||
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
|
||||||
}
|
|
||||||
|
|
||||||
late final GeneratedColumn<int> id =
|
|
||||||
GeneratedColumn<int>('id', aliasedName, false, type: DriftSqlType.int);
|
|
||||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: DriftSqlType.string);
|
|
||||||
late final GeneratedColumn<DateTime> birthday = GeneratedColumn<DateTime>(
|
|
||||||
'birthday', aliasedName, true,
|
|
||||||
type: DriftSqlType.dateTime);
|
|
||||||
late final GeneratedColumn<int> nextUser = GeneratedColumn<int>(
|
|
||||||
'next_user', aliasedName, true,
|
|
||||||
type: DriftSqlType.int);
|
|
||||||
late final GeneratedColumn<int> groupCount = GeneratedColumn<int>(
|
|
||||||
'group_count', aliasedName, false,
|
|
||||||
type: DriftSqlType.int);
|
|
||||||
@override
|
|
||||||
GroupCount createAlias(String alias) {
|
|
||||||
return GroupCount(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Query? get query => null;
|
|
||||||
@override
|
|
||||||
Set<String> get readTables => const {};
|
|
||||||
}
|
|
||||||
|
|
||||||
class DatabaseAtV9 extends GeneratedDatabase {
|
|
||||||
DatabaseAtV9(QueryExecutor e) : super(e);
|
|
||||||
late final Users users = Users(this);
|
|
||||||
late final Groups groups = Groups(this);
|
|
||||||
late final Notes notes = Notes(this);
|
|
||||||
late final GroupCount groupCount = GroupCount(this);
|
|
||||||
@override
|
|
||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
|
||||||
@override
|
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities =>
|
|
||||||
[users, groups, notes, groupCount];
|
|
||||||
@override
|
|
||||||
int get schemaVersion => 9;
|
|
||||||
@override
|
|
||||||
DriftDatabaseOptions get options =>
|
|
||||||
const DriftDatabaseOptions(storeDateTimeAsText: true);
|
|
||||||
}
|
|
|
@ -0,0 +1,644 @@
|
||||||
|
import 'package:drift/internal/versioned_schema.dart' as i0;
|
||||||
|
import 'package:drift/drift.dart' as i1;
|
||||||
|
import 'package:drift/drift.dart'; // ignore_for_file: type=lint
|
||||||
|
|
||||||
|
// GENERATED BY drift_dev, DO NOT MODIFY.
|
||||||
|
final class _S2 extends i0.VersionedSchema {
|
||||||
|
_S2({required super.database}) : super(version: 2);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
users,
|
||||||
|
];
|
||||||
|
late final Shape0 users = Shape0(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'users',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape0 extends i0.VersionedTable {
|
||||||
|
Shape0({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get name =>
|
||||||
|
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_0(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('id', aliasedName, false,
|
||||||
|
hasAutoIncrement: true,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
defaultConstraints:
|
||||||
|
i1.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||||
|
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('name', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string);
|
||||||
|
|
||||||
|
final class _S3 extends i0.VersionedSchema {
|
||||||
|
_S3({required super.database}) : super(version: 3);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
users,
|
||||||
|
groups,
|
||||||
|
];
|
||||||
|
late final Shape0 users = Shape0(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'users',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape1 groups = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'groups',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY (id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
_column_4,
|
||||||
|
_column_5,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape1 extends i0.VersionedTable {
|
||||||
|
Shape1({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get title =>
|
||||||
|
columnsByName['title']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<bool> get deleted =>
|
||||||
|
columnsByName['deleted']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<int> get owner =>
|
||||||
|
columnsByName['owner']! as i1.GeneratedColumn<int>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_2(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('id', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int, $customConstraints: 'NOT NULL');
|
||||||
|
i1.GeneratedColumn<String> _column_3(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('title', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string, $customConstraints: 'NOT NULL');
|
||||||
|
i1.GeneratedColumn<bool> _column_4(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('deleted', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
$customConstraints: 'DEFAULT FALSE',
|
||||||
|
defaultValue: const CustomExpression<bool>('FALSE'));
|
||||||
|
i1.GeneratedColumn<int> _column_5(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('owner', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
$customConstraints: 'NOT NULL REFERENCES users (id)');
|
||||||
|
|
||||||
|
final class _S4 extends i0.VersionedSchema {
|
||||||
|
_S4({required super.database}) : super(version: 4);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
users,
|
||||||
|
groups,
|
||||||
|
];
|
||||||
|
late final Shape0 users = Shape0(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'users',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_6,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape1 groups = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'groups',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY (id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
_column_4,
|
||||||
|
_column_5,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_6(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('name', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string, defaultValue: const Constant('name'));
|
||||||
|
|
||||||
|
final class _S5 extends i0.VersionedSchema {
|
||||||
|
_S5({required super.database}) : super(version: 5);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
users,
|
||||||
|
groups,
|
||||||
|
groupCount,
|
||||||
|
];
|
||||||
|
late final Shape2 users = Shape2(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'users',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_6,
|
||||||
|
_column_7,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape1 groups = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'groups',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY (id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
_column_4,
|
||||||
|
_column_5,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape3 groupCount = Shape3(
|
||||||
|
source: i0.VersionedView(
|
||||||
|
entityName: 'group_count',
|
||||||
|
createViewStmt:
|
||||||
|
'CREATE VIEW group_count AS SELECT users.*, (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count FROM users',
|
||||||
|
columns: [
|
||||||
|
_column_8,
|
||||||
|
_column_1,
|
||||||
|
_column_9,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape2 extends i0.VersionedTable {
|
||||||
|
Shape2({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get name =>
|
||||||
|
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<int> get nextUser =>
|
||||||
|
columnsByName['next_user']! as i1.GeneratedColumn<int>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_7(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('next_user', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
defaultConstraints:
|
||||||
|
i1.GeneratedColumn.constraintIsAlways('REFERENCES users (id)'));
|
||||||
|
|
||||||
|
class Shape3 extends i0.VersionedView {
|
||||||
|
Shape3({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get name =>
|
||||||
|
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<int> get nextUser =>
|
||||||
|
columnsByName['next_user']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get groupCount =>
|
||||||
|
columnsByName['group_count']! as i1.GeneratedColumn<int>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_8(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('id', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
i1.GeneratedColumn<int> _column_9(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('next_user', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
i1.GeneratedColumn<int> _column_10(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('group_count', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int);
|
||||||
|
|
||||||
|
final class _S6 extends i0.VersionedSchema {
|
||||||
|
_S6({required super.database}) : super(version: 6);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
users,
|
||||||
|
groups,
|
||||||
|
groupCount,
|
||||||
|
];
|
||||||
|
late final Shape4 users = Shape4(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'users',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_6,
|
||||||
|
_column_11,
|
||||||
|
_column_7,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape1 groups = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'groups',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY (id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
_column_4,
|
||||||
|
_column_5,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape5 groupCount = Shape5(
|
||||||
|
source: i0.VersionedView(
|
||||||
|
entityName: 'group_count',
|
||||||
|
createViewStmt:
|
||||||
|
'CREATE VIEW group_count AS SELECT users.*, (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count FROM users',
|
||||||
|
columns: [
|
||||||
|
_column_8,
|
||||||
|
_column_1,
|
||||||
|
_column_11,
|
||||||
|
_column_9,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape4 extends i0.VersionedTable {
|
||||||
|
Shape4({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get name =>
|
||||||
|
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<DateTime> get birthday =>
|
||||||
|
columnsByName['birthday']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
i1.GeneratedColumn<int> get nextUser =>
|
||||||
|
columnsByName['next_user']! as i1.GeneratedColumn<int>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<DateTime> _column_11(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<DateTime>('birthday', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.dateTime);
|
||||||
|
|
||||||
|
class Shape5 extends i0.VersionedView {
|
||||||
|
Shape5({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get name =>
|
||||||
|
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<DateTime> get birthday =>
|
||||||
|
columnsByName['birthday']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
i1.GeneratedColumn<int> get nextUser =>
|
||||||
|
columnsByName['next_user']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get groupCount =>
|
||||||
|
columnsByName['group_count']! as i1.GeneratedColumn<int>;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _S7 extends i0.VersionedSchema {
|
||||||
|
_S7({required super.database}) : super(version: 7);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
users,
|
||||||
|
groups,
|
||||||
|
groupCount,
|
||||||
|
notes,
|
||||||
|
];
|
||||||
|
late final Shape4 users = Shape4(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'users',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_6,
|
||||||
|
_column_11,
|
||||||
|
_column_7,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape1 groups = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'groups',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY (id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
_column_4,
|
||||||
|
_column_5,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape5 groupCount = Shape5(
|
||||||
|
source: i0.VersionedView(
|
||||||
|
entityName: 'group_count',
|
||||||
|
createViewStmt:
|
||||||
|
'CREATE VIEW group_count AS SELECT users.*, (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count FROM users',
|
||||||
|
columns: [
|
||||||
|
_column_8,
|
||||||
|
_column_1,
|
||||||
|
_column_11,
|
||||||
|
_column_9,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape6 notes = Shape6(
|
||||||
|
source: i0.VersionedVirtualTable(
|
||||||
|
entityName: 'notes',
|
||||||
|
moduleAndArgs:
|
||||||
|
'fts5(title, content, search_terms, tokenize = "unicode61 tokenchars \'.\'")',
|
||||||
|
columns: [
|
||||||
|
_column_12,
|
||||||
|
_column_13,
|
||||||
|
_column_14,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape6 extends i0.VersionedVirtualTable {
|
||||||
|
Shape6({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<String> get title =>
|
||||||
|
columnsByName['title']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get content =>
|
||||||
|
columnsByName['content']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get searchTerms =>
|
||||||
|
columnsByName['search_terms']! as i1.GeneratedColumn<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_12(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('title', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string, $customConstraints: '');
|
||||||
|
i1.GeneratedColumn<String> _column_13(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('content', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string, $customConstraints: '');
|
||||||
|
i1.GeneratedColumn<String> _column_14(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('search_terms', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string, $customConstraints: '');
|
||||||
|
|
||||||
|
final class _S8 extends i0.VersionedSchema {
|
||||||
|
_S8({required super.database}) : super(version: 8);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
users,
|
||||||
|
groups,
|
||||||
|
groupCount,
|
||||||
|
notes,
|
||||||
|
];
|
||||||
|
late final Shape4 users = Shape4(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'users',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'UNIQUE(name, birthday)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_6,
|
||||||
|
_column_11,
|
||||||
|
_column_15,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape1 groups = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'groups',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY (id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
_column_4,
|
||||||
|
_column_5,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape5 groupCount = Shape5(
|
||||||
|
source: i0.VersionedView(
|
||||||
|
entityName: 'group_count',
|
||||||
|
createViewStmt:
|
||||||
|
'CREATE VIEW group_count AS SELECT users.*, (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count FROM users',
|
||||||
|
columns: [
|
||||||
|
_column_8,
|
||||||
|
_column_1,
|
||||||
|
_column_11,
|
||||||
|
_column_9,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape6 notes = Shape6(
|
||||||
|
source: i0.VersionedVirtualTable(
|
||||||
|
entityName: 'notes',
|
||||||
|
moduleAndArgs:
|
||||||
|
'fts5(title, content, search_terms, tokenize = "unicode61 tokenchars \'.\'")',
|
||||||
|
columns: [
|
||||||
|
_column_12,
|
||||||
|
_column_13,
|
||||||
|
_column_14,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_15(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('next_user', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
defaultConstraints:
|
||||||
|
i1.GeneratedColumn.constraintIsAlways('REFERENCES "users" ("id")'));
|
||||||
|
|
||||||
|
final class _S9 extends i0.VersionedSchema {
|
||||||
|
_S9({required super.database}) : super(version: 9);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
users,
|
||||||
|
groups,
|
||||||
|
notes,
|
||||||
|
groupCount,
|
||||||
|
];
|
||||||
|
late final Shape4 users = Shape4(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'users',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'UNIQUE(name, birthday)',
|
||||||
|
'CHECK (LENGTH(name) < 10)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_6,
|
||||||
|
_column_11,
|
||||||
|
_column_7,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape1 groups = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'groups',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [
|
||||||
|
'PRIMARY KEY(id)',
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
_column_16,
|
||||||
|
_column_17,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape6 notes = Shape6(
|
||||||
|
source: i0.VersionedVirtualTable(
|
||||||
|
entityName: 'notes',
|
||||||
|
moduleAndArgs:
|
||||||
|
'fts5(title, content, search_terms, tokenize = "unicode61 tokenchars \'.\'")',
|
||||||
|
columns: [
|
||||||
|
_column_12,
|
||||||
|
_column_13,
|
||||||
|
_column_14,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape5 groupCount = Shape5(
|
||||||
|
source: i0.VersionedView(
|
||||||
|
entityName: 'group_count',
|
||||||
|
createViewStmt:
|
||||||
|
'CREATE VIEW group_count AS SELECT\n users.*,\n (SELECT COUNT(*) FROM "groups" WHERE owner = users.id) AS group_count\n FROM users;',
|
||||||
|
columns: [
|
||||||
|
_column_8,
|
||||||
|
_column_1,
|
||||||
|
_column_11,
|
||||||
|
_column_9,
|
||||||
|
_column_10,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<bool> _column_16(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<bool>('deleted', aliasedName, true,
|
||||||
|
type: i1.DriftSqlType.bool,
|
||||||
|
$customConstraints: 'DEFAULT FALSE',
|
||||||
|
defaultValue: const CustomExpression('FALSE'));
|
||||||
|
i1.GeneratedColumn<int> _column_17(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>('owner', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
$customConstraints: 'NOT NULL REFERENCES users(id)');
|
||||||
|
i1.OnUpgrade stepByStep({
|
||||||
|
required Future<void> Function(i1.Migrator m, _S2 schema) from1To2,
|
||||||
|
required Future<void> Function(i1.Migrator m, _S3 schema) from2To3,
|
||||||
|
required Future<void> Function(i1.Migrator m, _S4 schema) from3To4,
|
||||||
|
required Future<void> Function(i1.Migrator m, _S5 schema) from4To5,
|
||||||
|
required Future<void> Function(i1.Migrator m, _S6 schema) from5To6,
|
||||||
|
required Future<void> Function(i1.Migrator m, _S7 schema) from6To7,
|
||||||
|
required Future<void> Function(i1.Migrator m, _S8 schema) from7To8,
|
||||||
|
required Future<void> Function(i1.Migrator m, _S9 schema) from8To9,
|
||||||
|
}) {
|
||||||
|
return i1.Migrator.stepByStepHelper(step: (currentVersion, database) async {
|
||||||
|
switch (currentVersion) {
|
||||||
|
case 1:
|
||||||
|
final schema = _S2(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from1To2(migrator, schema);
|
||||||
|
return 2;
|
||||||
|
case 2:
|
||||||
|
final schema = _S3(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from2To3(migrator, schema);
|
||||||
|
return 3;
|
||||||
|
case 3:
|
||||||
|
final schema = _S4(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from3To4(migrator, schema);
|
||||||
|
return 4;
|
||||||
|
case 4:
|
||||||
|
final schema = _S5(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from4To5(migrator, schema);
|
||||||
|
return 5;
|
||||||
|
case 5:
|
||||||
|
final schema = _S6(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from5To6(migrator, schema);
|
||||||
|
return 6;
|
||||||
|
case 6:
|
||||||
|
final schema = _S7(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from6To7(migrator, schema);
|
||||||
|
return 7;
|
||||||
|
case 7:
|
||||||
|
final schema = _S8(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from7To8(migrator, schema);
|
||||||
|
return 8;
|
||||||
|
case 8:
|
||||||
|
final schema = _S9(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from8To9(migrator, schema);
|
||||||
|
return 9;
|
||||||
|
default:
|
||||||
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ publish_to: none
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.12.0 <3.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
drift:
|
drift:
|
||||||
|
|
32
pubspec.lock
32
pubspec.lock
|
@ -13,10 +13,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a
|
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.2"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -53,10 +53,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cli_util
|
name: cli_util
|
||||||
sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c"
|
sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.5"
|
version: "0.4.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -85,18 +85,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: glob
|
name: glob
|
||||||
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
|
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: graphs
|
name: graphs
|
||||||
sha256: "772db3d53d23361d4ffcf5a9bb091cf3ee9b22f2be52cd107cd7a2683a89ba0e"
|
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.1"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -133,18 +133,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.15"
|
version: "0.12.16"
|
||||||
melos:
|
melos:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: melos
|
name: melos
|
||||||
sha256: "993ac467e7a36bd832a6cdabbe18a0487c30bc52b5cca14e476a824679ebdce0"
|
sha256: ccbb6ecd8bb3f08ae8f9ce22920d816bff325a98940c845eda0257cd395503ac
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.1.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -253,10 +253,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -277,10 +277,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: daadc9baabec998b062c9091525aa95786508b1c48e9c30f1f891b8bf6ff2e64
|
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.2"
|
version: "0.6.0"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
Loading…
Reference in New Issue