diff --git a/extras/migrations_example/lib/database.dart b/extras/migrations_example/lib/database.dart index 0cc9c35c..44cc3a78 100644 --- a/extras/migrations_example/lib/database.dart +++ b/extras/migrations_example/lib/database.dart @@ -2,19 +2,12 @@ import 'package:moor/moor.dart'; part 'database.g.dart'; -class Users extends Table { - IntColumn get id => integer().autoIncrement()(); - - TextColumn get name => text()(); // added in schema version 2 -} - -@UseMoor(tables: [Users]) +@UseMoor(include: {'tables.moor'}) class Database extends _$Database { @override - final int schemaVersion; + int get schemaVersion => 3; - Database(this.schemaVersion, DatabaseConnection connection) - : super.connect(connection); + Database(DatabaseConnection connection) : super.connect(connection); @override MigrationStrategy get migration { @@ -24,6 +17,9 @@ class Database extends _$Database { if (target == 2) { // Migration from 1 to 2: Add name column in users await m.addColumn(users, users.name); + } else if (target == 3) { + // Migration from 2 to 3: We added the groups table + await m.createTable(groups); } } }, diff --git a/extras/migrations_example/lib/database.g.dart b/extras/migrations_example/lib/database.g.dart index 9beb6f4a..07ade9ef 100644 --- a/extras/migrations_example/lib/database.g.dart +++ b/extras/migrations_example/lib/database.g.dart @@ -192,13 +192,288 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> { } } +class Group extends DataClass implements Insertable { + final int id; + final String title; + final bool deleted; + final int owner; + Group( + {@required this.id, + @required this.title, + this.deleted, + @required this.owner}); + factory Group.fromData(Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + final boolType = db.typeSystem.forDartType(); + return Group( + id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']), + title: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}title']), + deleted: + boolType.mapFromDatabaseResponse(data['${effectivePrefix}deleted']), + owner: intType.mapFromDatabaseResponse(data['${effectivePrefix}owner']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || id != null) { + map['id'] = Variable(id); + } + if (!nullToAbsent || title != null) { + map['title'] = Variable(title); + } + if (!nullToAbsent || deleted != null) { + map['deleted'] = Variable(deleted); + } + if (!nullToAbsent || owner != null) { + map['owner'] = Variable(owner); + } + return map; + } + + GroupsCompanion toCompanion(bool nullToAbsent) { + return GroupsCompanion( + id: id == null && nullToAbsent ? const Value.absent() : Value(id), + title: + title == null && nullToAbsent ? const Value.absent() : Value(title), + deleted: deleted == null && nullToAbsent + ? const Value.absent() + : Value(deleted), + owner: + owner == null && nullToAbsent ? const Value.absent() : Value(owner), + ); + } + + factory Group.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return Group( + id: serializer.fromJson(json['id']), + title: serializer.fromJson(json['title']), + deleted: serializer.fromJson(json['deleted']), + owner: serializer.fromJson(json['owner']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'title': serializer.toJson(title), + 'deleted': serializer.toJson(deleted), + 'owner': serializer.toJson(owner), + }; + } + + Group copyWith({int id, String title, bool deleted, int owner}) => Group( + id: id ?? this.id, + title: title ?? this.title, + deleted: deleted ?? this.deleted, + owner: owner ?? this.owner, + ); + @override + String toString() { + return (StringBuffer('Group(') + ..write('id: $id, ') + ..write('title: $title, ') + ..write('deleted: $deleted, ') + ..write('owner: $owner') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc(id.hashCode, + $mrjc(title.hashCode, $mrjc(deleted.hashCode, owner.hashCode)))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is Group && + other.id == this.id && + other.title == this.title && + other.deleted == this.deleted && + other.owner == this.owner); +} + +class GroupsCompanion extends UpdateCompanion { + final Value id; + final Value title; + final Value deleted; + final Value owner; + const GroupsCompanion({ + this.id = const Value.absent(), + this.title = const Value.absent(), + this.deleted = const Value.absent(), + this.owner = const Value.absent(), + }); + GroupsCompanion.insert({ + this.id = const Value.absent(), + @required String title, + this.deleted = const Value.absent(), + @required int owner, + }) : title = Value(title), + owner = Value(owner); + static Insertable custom({ + Expression id, + Expression title, + Expression deleted, + Expression owner, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (title != null) 'title': title, + if (deleted != null) 'deleted': deleted, + if (owner != null) 'owner': owner, + }); + } + + GroupsCompanion copyWith( + {Value id, + Value title, + Value deleted, + Value owner}) { + return GroupsCompanion( + id: id ?? this.id, + title: title ?? this.title, + deleted: deleted ?? this.deleted, + owner: owner ?? this.owner, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (title.present) { + map['title'] = Variable(title.value); + } + if (deleted.present) { + map['deleted'] = Variable(deleted.value); + } + if (owner.present) { + map['owner'] = Variable(owner.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('GroupsCompanion(') + ..write('id: $id, ') + ..write('title: $title, ') + ..write('deleted: $deleted, ') + ..write('owner: $owner') + ..write(')')) + .toString(); + } +} + +class Groups extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + Groups(this._db, [this._alias]); + final VerificationMeta _idMeta = const VerificationMeta('id'); + GeneratedIntColumn _id; + GeneratedIntColumn get id => _id ??= _constructId(); + GeneratedIntColumn _constructId() { + return GeneratedIntColumn('id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _titleMeta = const VerificationMeta('title'); + GeneratedTextColumn _title; + GeneratedTextColumn get title => _title ??= _constructTitle(); + GeneratedTextColumn _constructTitle() { + return GeneratedTextColumn('title', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _deletedMeta = const VerificationMeta('deleted'); + GeneratedBoolColumn _deleted; + GeneratedBoolColumn get deleted => _deleted ??= _constructDeleted(); + GeneratedBoolColumn _constructDeleted() { + return GeneratedBoolColumn('deleted', $tableName, true, + $customConstraints: 'DEFAULT FALSE', + defaultValue: const CustomExpression('FALSE')); + } + + final VerificationMeta _ownerMeta = const VerificationMeta('owner'); + GeneratedIntColumn _owner; + GeneratedIntColumn get owner => _owner ??= _constructOwner(); + GeneratedIntColumn _constructOwner() { + return GeneratedIntColumn('owner', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES users (id)'); + } + + @override + List get $columns => [id, title, deleted, owner]; + @override + Groups get asDslTable => this; + @override + String get $tableName => _alias ?? 'groups'; + @override + final String actualTableName = 'groups'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id'], _idMeta)); + } + if (data.containsKey('title')) { + context.handle( + _titleMeta, title.isAcceptableOrUnknown(data['title'], _titleMeta)); + } else if (isInserting) { + context.missing(_titleMeta); + } + if (data.containsKey('deleted')) { + context.handle(_deletedMeta, + deleted.isAcceptableOrUnknown(data['deleted'], _deletedMeta)); + } + if (data.containsKey('owner')) { + context.handle( + _ownerMeta, owner.isAcceptableOrUnknown(data['owner'], _ownerMeta)); + } else if (isInserting) { + context.missing(_ownerMeta); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + Group map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return Group.fromData(data, _db, prefix: effectivePrefix); + } + + @override + Groups createAlias(String alias) { + return Groups(_db, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY (id)']; + @override + bool get dontWriteConstraints => true; +} + abstract class _$Database extends GeneratedDatabase { _$Database(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e); _$Database.connect(DatabaseConnection c) : super.connect(c); $UsersTable _users; $UsersTable get users => _users ??= $UsersTable(this); + Groups _groups; + Groups get groups => _groups ??= Groups(this); @override Iterable get allTables => allSchemaEntities.whereType(); @override - List get allSchemaEntities => [users]; + List get allSchemaEntities => [users, groups]; } diff --git a/extras/migrations_example/lib/tables.dart b/extras/migrations_example/lib/tables.dart new file mode 100644 index 00000000..681282c6 --- /dev/null +++ b/extras/migrations_example/lib/tables.dart @@ -0,0 +1,7 @@ +import 'package:moor/moor.dart'; + +class Users extends Table { + IntColumn get id => integer().autoIncrement()(); + + TextColumn get name => text()(); // added in schema version 2 +} diff --git a/extras/migrations_example/lib/tables.moor b/extras/migrations_example/lib/tables.moor new file mode 100644 index 00000000..ea890305 --- /dev/null +++ b/extras/migrations_example/lib/tables.moor @@ -0,0 +1,11 @@ +import 'tables.dart'; + +-- This table was added in schema version 3 +CREATE TABLE "groups" ( + id INTEGER NOT NULL, + title TEXT NOT NULL, + deleted BOOLEAN DEFAULT FALSE, + owner INTEGER NOT NULL REFERENCES users (id), + + PRIMARY KEY (id) +); \ No newline at end of file diff --git a/extras/migrations_example/moor_migrations/moor_schema_v3.json b/extras/migrations_example/moor_migrations/moor_schema_v3.json new file mode 100644 index 00000000..3339871b --- /dev/null +++ b/extras/migrations_example/moor_migrations/moor_schema_v3.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for moor.","version":"0.1.0-dev-preview"},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"users","was_declared_in_moor":false,"columns":[{"name":"id","moor_type":"ColumnType.integer","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment","primary-key"]},{"name":"name","moor_type":"ColumnType.text","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false}},{"id":1,"references":[0],"type":"table","data":{"name":"groups","was_declared_in_moor":true,"columns":[{"name":"id","moor_type":"ColumnType.integer","nullable":false,"customConstraints":"NOT NULL","default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"title","moor_type":"ColumnType.text","nullable":false,"customConstraints":"NOT NULL","default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted","moor_type":"ColumnType.boolean","nullable":true,"customConstraints":"DEFAULT FALSE","default_dart":"const CustomExpression('FALSE')","default_client_dart":null,"dsl_features":[]},{"name":"owner","moor_type":"ColumnType.integer","nullable":false,"customConstraints":"NOT NULL REFERENCES users (id)","default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"constraints":["PRIMARY KEY (id)"],"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/extras/migrations_example/test/generated/schema.dart b/extras/migrations_example/test/generated/schema.dart index 23a90024..42acc140 100644 --- a/extras/migrations_example/test/generated/schema.dart +++ b/extras/migrations_example/test/generated/schema.dart @@ -3,6 +3,7 @@ import 'package:moor/moor.dart'; import 'package:moor_generator/api/migrations.dart'; import 'schema_v1.dart' as v1; import 'schema_v2.dart' as v2; +import 'schema_v3.dart' as v3; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -12,8 +13,10 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v1.DatabaseAtV1(db); case 2: return v2.DatabaseAtV2(db); + case 3: + return v3.DatabaseAtV3(db); default: - throw MissingSchemaException(version, const {1, 2}); + throw MissingSchemaException(version, const {1, 2, 3}); } } } diff --git a/extras/migrations_example/test/generated/schema_v1.dart b/extras/migrations_example/test/generated/schema_v1.dart index ae628dfa..adf8e4af 100644 --- a/extras/migrations_example/test/generated/schema_v1.dart +++ b/extras/migrations_example/test/generated/schema_v1.dart @@ -31,6 +31,9 @@ class _Users extends Table with TableInfo { _Users createAlias(String alias) { return _Users(_db, alias); } + + @override + bool get dontWriteConstraints => false; } class DatabaseAtV1 extends GeneratedDatabase { diff --git a/extras/migrations_example/test/generated/schema_v2.dart b/extras/migrations_example/test/generated/schema_v2.dart index 2472ac77..3f17d033 100644 --- a/extras/migrations_example/test/generated/schema_v2.dart +++ b/extras/migrations_example/test/generated/schema_v2.dart @@ -41,6 +41,9 @@ class _Users extends Table with TableInfo { _Users createAlias(String alias) { return _Users(_db, alias); } + + @override + bool get dontWriteConstraints => false; } class DatabaseAtV2 extends GeneratedDatabase { diff --git a/extras/migrations_example/test/generated/schema_v3.dart b/extras/migrations_example/test/generated/schema_v3.dart new file mode 100644 index 00000000..5ed9d3c8 --- /dev/null +++ b/extras/migrations_example/test/generated/schema_v3.dart @@ -0,0 +1,120 @@ +// GENERATED CODE, DO NOT EDIT BY HAND. +import 'package:moor/moor.dart'; + +class _Users extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + _Users(this._db, [this._alias]); + GeneratedIntColumn _id; + GeneratedIntColumn get id => _id ??= _constructId(); + GeneratedIntColumn _constructId() { + return GeneratedIntColumn('id', $tableName, false, + hasAutoIncrement: true, declaredAsPrimaryKey: true); + } + + GeneratedTextColumn _name; + GeneratedTextColumn get name => _name ??= _constructName(); + GeneratedTextColumn _constructName() { + return GeneratedTextColumn( + 'name', + $tableName, + false, + ); + } + + @override + List get $columns => [id, name]; + @override + _Users get asDslTable => this; + @override + String get $tableName => _alias ?? 'users'; + @override + final String actualTableName = 'users'; + @override + Set get $primaryKey => {id}; + @override + Null map(Map data, {String tablePrefix}) { + return null; + } + + @override + _Users createAlias(String alias) { + return _Users(_db, alias); + } + + @override + bool get dontWriteConstraints => false; +} + +class _Groups extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + _Groups(this._db, [this._alias]); + GeneratedIntColumn _id; + GeneratedIntColumn get id => _id ??= _constructId(); + GeneratedIntColumn _constructId() { + return GeneratedIntColumn('id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + GeneratedTextColumn _title; + GeneratedTextColumn get title => _title ??= _constructTitle(); + GeneratedTextColumn _constructTitle() { + return GeneratedTextColumn('title', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + GeneratedBoolColumn _deleted; + GeneratedBoolColumn get deleted => _deleted ??= _constructDeleted(); + GeneratedBoolColumn _constructDeleted() { + return GeneratedBoolColumn('deleted', $tableName, true, + $customConstraints: 'DEFAULT FALSE'); + } + + GeneratedIntColumn _owner; + GeneratedIntColumn get owner => _owner ??= _constructOwner(); + GeneratedIntColumn _constructOwner() { + return GeneratedIntColumn('owner', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES users (id)'); + } + + @override + List get $columns => [id, title, deleted, owner]; + @override + _Groups get asDslTable => this; + @override + String get $tableName => _alias ?? 'groups'; + @override + final String actualTableName = 'groups'; + @override + Set get $primaryKey => {id}; + @override + Null map(Map data, {String tablePrefix}) { + return null; + } + + @override + _Groups createAlias(String alias) { + return _Groups(_db, alias); + } + + @override + List get customConstraints => const ['PRIMARY KEY (id)']; + @override + bool get dontWriteConstraints => true; +} + +class DatabaseAtV3 extends GeneratedDatabase { + DatabaseAtV3(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e); + DatabaseAtV3.connect(DatabaseConnection c) : super.connect(c); + _Users _users; + _Users get users => _users ??= _Users(this); + _Groups _groups; + _Groups get groups => _groups ??= _Groups(this); + @override + Iterable get allTables => allSchemaEntities.whereType(); + @override + List get allSchemaEntities => [users, groups]; + @override + int get schemaVersion => 3; +} diff --git a/extras/migrations_example/test/migration_test.dart b/extras/migrations_example/test/migration_test.dart index 10862ffa..92ee0ebb 100644 --- a/extras/migrations_example/test/migration_test.dart +++ b/extras/migrations_example/test/migration_test.dart @@ -14,8 +14,15 @@ void main() { test('upgrade from v1 to v2', () async { final connection = await verifier.startAt(1); - final db = Database(2, connection); + final db = Database(connection); await verifier.migrateAndValidate(db, 2); }); + + test('upgrade from v2 to v3', () async { + final connection = await verifier.startAt(2); + final db = Database(connection); + + await verifier.migrateAndValidate(db, 3); + }); }