mirror of https://github.com/AMT-Cheif/drift.git
Prepare code for table / column creation
This commit is contained in:
parent
8a14b4e8d0
commit
96c2e8641f
|
@ -16,17 +16,17 @@ class _$ProductsTable extends Products implements TableInfo<Products, Product> {
|
|||
final GeneratedDatabase db;
|
||||
_$ProductsTable(this.db);
|
||||
@override
|
||||
IntColumn get id => GeneratedIntColumn('products_id', false);
|
||||
GeneratedIntColumn get id => GeneratedIntColumn('products_id', false);
|
||||
@override
|
||||
TextColumn get name => GeneratedTextColumn('name', false);
|
||||
GeneratedTextColumn get name => GeneratedTextColumn('name', false);
|
||||
@override
|
||||
List<Column> get $columns => [id, name];
|
||||
List<GeneratedColumn> get $columns => [id, name];
|
||||
@override
|
||||
Products get asDslTable => this;
|
||||
@override
|
||||
String get $tableName => 'products';
|
||||
@override
|
||||
Set<Column> get $primaryKey => Set();
|
||||
Set<GeneratedColumn> get $primaryKey => Set();
|
||||
@override
|
||||
Product map(Map<String, dynamic> data) {
|
||||
final intType = db.typeSystem.forDartType<int>();
|
||||
|
@ -48,17 +48,17 @@ class _$UsersTable extends Users implements TableInfo<Users, User> {
|
|||
final GeneratedDatabase db;
|
||||
_$UsersTable(this.db);
|
||||
@override
|
||||
IntColumn get id => GeneratedIntColumn('id', false);
|
||||
GeneratedIntColumn get id => GeneratedIntColumn('id', false);
|
||||
@override
|
||||
TextColumn get name => GeneratedTextColumn('name', false);
|
||||
GeneratedTextColumn get name => GeneratedTextColumn('name', false);
|
||||
@override
|
||||
List<Column> get $columns => [id, name];
|
||||
List<GeneratedColumn> get $columns => [id, name];
|
||||
@override
|
||||
Users get asDslTable => this;
|
||||
@override
|
||||
String get $tableName => 'users';
|
||||
@override
|
||||
Set<Column> get $primaryKey => Set();
|
||||
Set<GeneratedColumn> get $primaryKey => Set();
|
||||
@override
|
||||
User map(Map<String, dynamic> data) {
|
||||
final intType = db.typeSystem.forDartType<int>();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:meta/meta.dart';
|
||||
import 'package:sally/sally.dart';
|
||||
import 'package:sally/src/runtime/executor/type_system.dart';
|
||||
import 'package:sally/src/runtime/migration.dart';
|
||||
|
@ -19,14 +20,15 @@ abstract class GeneratedDatabase {
|
|||
/// Creates a migrator with the provided query executor. We sometimes can't
|
||||
/// use the regular [GeneratedDatabase.executor] because migration happens
|
||||
/// before that executor is ready.
|
||||
Migrator _createMigrator(QueryExecutor executor) => Migrator(this, executor);
|
||||
Migrator _createMigrator(SqlExecutor executor) => Migrator(this, executor);
|
||||
|
||||
Future<void> handleDatabaseCreation(QueryExecutor executor) {
|
||||
Future<void> handleDatabaseCreation({@required SqlExecutor executor}) {
|
||||
final migrator = _createMigrator(executor);
|
||||
return migration.onCreate(migrator);
|
||||
}
|
||||
|
||||
Future<void> handleDatabaseVersionChange(QueryExecutor executor, int from, int to) {
|
||||
Future<void> handleDatabaseVersionChange(
|
||||
{@required SqlExecutor executor, int from, int to}) {
|
||||
final migrator = _createMigrator(executor);
|
||||
return migration.onUpgrade(migrator, from, to);
|
||||
}
|
||||
|
@ -41,7 +43,6 @@ abstract class GeneratedDatabase {
|
|||
}
|
||||
|
||||
abstract class QueryExecutor {
|
||||
|
||||
GeneratedDatabase databaseInfo;
|
||||
|
||||
Future<bool> ensureOpen();
|
||||
|
@ -50,4 +51,5 @@ abstract class QueryExecutor {
|
|||
Future<int> runInsert(String statement, List<dynamic> args);
|
||||
Future<int> runUpdate(String statement, List<dynamic> args);
|
||||
Future<int> runDelete(String statement, List<dynamic> args);
|
||||
Future<void> runCustom(String statement);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ typedef Future<void> OnUpgrade(Migrator m, int from, int to);
|
|||
Future<void> _defaultOnCreate(Migrator m) => m.createAllTables();
|
||||
Future<void> _defaultOnUpdate(Migrator m, int from, int to) async =>
|
||||
throw Exception("You've bumped the schema version for your sally database "
|
||||
"but didn't provide a strategy for schema updates. Please do that by "
|
||||
'adapting the migrations getter in your database class.');
|
||||
"but didn't provide a strategy for schema updates. Please do that by "
|
||||
'adapting the migrations getter in your database class.');
|
||||
|
||||
class MigrationStrategy {
|
||||
final OnCreate onCreate;
|
||||
|
@ -21,22 +21,58 @@ class MigrationStrategy {
|
|||
});
|
||||
}
|
||||
|
||||
/// A function that executes queries and ignores what they return.
|
||||
typedef Future<void> SqlExecutor(String sql);
|
||||
|
||||
class Migrator {
|
||||
|
||||
final GeneratedDatabase _db;
|
||||
final QueryExecutor _customExecutor;
|
||||
final SqlExecutor _executor;
|
||||
|
||||
Migrator(this._db, this._customExecutor);
|
||||
Migrator(this._db, this._executor);
|
||||
|
||||
Future<void> createAllTables() async {}
|
||||
Future<void> createAllTables() async {
|
||||
return Future.wait(_db.allTables.map(createTable));
|
||||
}
|
||||
|
||||
Future<void> createTable(TableInfo table) async {}
|
||||
Future<void> createTable(TableInfo table) async {
|
||||
final sql = StringBuffer();
|
||||
|
||||
Future<void> deleteTable(String name) async {}
|
||||
// todo write primary key
|
||||
|
||||
Future<void> addColumn(TableInfo table, GeneratedColumn column) async {}
|
||||
// ignore: cascade_invocations
|
||||
sql.write('CREATE TABLE IF NOT EXISTS ${table.$tableName} (');
|
||||
|
||||
Future<void> deleteColumn(TableInfo table, String columnName) async {}
|
||||
for (var i = 0; i < table.$columns.length; i++) {
|
||||
final column = table.$columns[i];
|
||||
|
||||
Future<void> issueCustomQuery(String sql) async {}
|
||||
// ignore: cascade_invocations
|
||||
column.writeColumnDefinition(sql);
|
||||
|
||||
if (i < table.$columns.length - 1) sql.write(', ');
|
||||
}
|
||||
|
||||
sql.write(')');
|
||||
|
||||
return issueCustomQuery(sql.toString());
|
||||
}
|
||||
|
||||
/// Deletes the table with the given name. Note that this function does not
|
||||
/// escape the [name] parameter.
|
||||
Future<void> deleteTable(String name) async {
|
||||
return issueCustomQuery('DROP TABLE IF EXISTS $name');
|
||||
}
|
||||
|
||||
Future<void> addColumn(TableInfo table, GeneratedColumn column) async {
|
||||
final sql = StringBuffer();
|
||||
|
||||
// ignore: cascade_invocations
|
||||
sql.write('ALTER TABLE ${table.$tableName} ADD COLUMN');
|
||||
column.writeColumnDefinition(sql);
|
||||
|
||||
return issueCustomQuery(sql.toString());
|
||||
}
|
||||
|
||||
Future<void> issueCustomQuery(String sql) async {
|
||||
return _executor(sql);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:meta/meta.dart';
|
||||
import 'package:sally/sally.dart';
|
||||
import 'package:sally/src/runtime/components/component.dart';
|
||||
import 'package:sally/src/runtime/expressions/expression.dart';
|
||||
|
@ -11,6 +12,22 @@ abstract class GeneratedColumn<T, S extends SqlType<T>> extends Column<T, S> {
|
|||
|
||||
GeneratedColumn(this.$name, this.$nullable);
|
||||
|
||||
/// Writes the definition of this column, as defined
|
||||
/// [here](https://www.sqlite.org/syntax/column-def.html), into the given
|
||||
/// buffer.
|
||||
void writeColumnDefinition(StringBuffer into) {
|
||||
into
|
||||
..write('${$name} $typeName ')
|
||||
..write($nullable ? 'NULL' : 'NOT NULL')
|
||||
..write(' ');
|
||||
writeCustomConstraints(into);
|
||||
}
|
||||
|
||||
@visibleForOverriding
|
||||
void writeCustomConstraints(StringBuffer into) {}
|
||||
@visibleForOverriding
|
||||
String get typeName;
|
||||
|
||||
@override
|
||||
Expression<BoolType> equals(Expression<S> compare) =>
|
||||
Comparison.equal(this, compare);
|
||||
|
@ -31,12 +48,23 @@ class GeneratedTextColumn extends GeneratedColumn<String, StringType>
|
|||
@override
|
||||
Expression<BoolType> like(String regex) =>
|
||||
LikeOperator(this, Variable<String, StringType>(regex));
|
||||
|
||||
@override
|
||||
final String typeName = 'VARCHAR';
|
||||
}
|
||||
|
||||
class GeneratedBoolColumn extends GeneratedColumn<bool, BoolType>
|
||||
implements BoolColumn {
|
||||
GeneratedBoolColumn(String name, bool nullable) : super(name, nullable);
|
||||
|
||||
@override
|
||||
final String typeName = 'BOOLEAN';
|
||||
|
||||
@override
|
||||
void writeCustomConstraints(StringBuffer into) {
|
||||
into.write('CHECK (${$name} in (0, 1))');
|
||||
}
|
||||
|
||||
@override
|
||||
void writeInto(GenerationContext context) {
|
||||
context.buffer.write('(');
|
||||
|
@ -49,6 +77,9 @@ class GeneratedIntColumn extends GeneratedColumn<int, IntType>
|
|||
implements IntColumn {
|
||||
final bool hasAutoIncrement;
|
||||
|
||||
@override
|
||||
final String typeName = 'INTEGER';
|
||||
|
||||
GeneratedIntColumn(String name, bool nullable,
|
||||
{this.hasAutoIncrement = false})
|
||||
: super(name, nullable);
|
||||
|
|
|
@ -6,11 +6,11 @@ abstract class TableInfo<TableDsl, DataClass> {
|
|||
|
||||
/// The primary key of this table. Can be null if no custom primary key has
|
||||
/// been specified
|
||||
Set<Column> get $primaryKey => null;
|
||||
Set<GeneratedColumn> get $primaryKey => null;
|
||||
|
||||
/// The table name in the sql table
|
||||
String get $tableName;
|
||||
List<Column> get $columns;
|
||||
List<GeneratedColumn> get $columns;
|
||||
|
||||
DataClass map(Map<String, dynamic> data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,15 +21,15 @@ class GeneratedUsersTable extends Users with TableInfo<Users, UserDataObject> {
|
|||
GeneratedUsersTable(this.db);
|
||||
|
||||
@override
|
||||
Set<Column> get $primaryKey => Set()..add(id);
|
||||
Set<GeneratedColumn> get $primaryKey => Set()..add(id);
|
||||
@override
|
||||
IntColumn id = GeneratedIntColumn('id', false);
|
||||
GeneratedIntColumn id = GeneratedIntColumn('id', false);
|
||||
@override
|
||||
TextColumn name = GeneratedTextColumn('name', false);
|
||||
GeneratedTextColumn name = GeneratedTextColumn('name', false);
|
||||
@override
|
||||
BoolColumn isAwesome = GeneratedBoolColumn('is_awesome', true);
|
||||
GeneratedBoolColumn isAwesome = GeneratedBoolColumn('is_awesome', true);
|
||||
@override
|
||||
List<Column<dynamic, SqlType>> get $columns => [id, name, isAwesome];
|
||||
List<GeneratedColumn<dynamic, SqlType>> get $columns => [id, name, isAwesome];
|
||||
@override
|
||||
String get $tableName => 'users';
|
||||
@override
|
||||
|
|
|
@ -36,11 +36,13 @@ class FlutterQueryExecutor extends QueryExecutor {
|
|||
resolvedPath,
|
||||
version: databaseInfo.schemaVersion,
|
||||
onCreate: (db, version) {
|
||||
return databaseInfo.handleDatabaseCreation(_SqfliteExecutor(db));
|
||||
return databaseInfo.handleDatabaseCreation(
|
||||
executor: (sql) => db.execute(sql),
|
||||
);
|
||||
},
|
||||
onUpgrade: (db, from, to) {
|
||||
return databaseInfo.handleDatabaseVersionChange(
|
||||
_SqfliteExecutor(db), from, to);
|
||||
executor: (sql) => db.execute(sql), from: from, to: to);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -66,35 +68,9 @@ class FlutterQueryExecutor extends QueryExecutor {
|
|||
Future<int> runUpdate(String statement, List args) {
|
||||
return _db.rawUpdate(statement, args);
|
||||
}
|
||||
}
|
||||
|
||||
class _SqfliteExecutor extends QueryExecutor {
|
||||
final Database _db;
|
||||
|
||||
_SqfliteExecutor(this._db);
|
||||
|
||||
@override
|
||||
Future<bool> ensureOpen() async {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runDelete(String statement, List args) {
|
||||
return _db.rawDelete(statement, args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runInsert(String statement, List args) {
|
||||
return _db.rawInsert(statement, args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Map<String, dynamic>>> runSelect(String statement, List args) {
|
||||
return _db.rawQuery(statement, args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runUpdate(String statement, List args) {
|
||||
return _db.rawUpdate(statement, args);
|
||||
Future<void> runCustom(String statement) {
|
||||
return _db.execute(statement);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,16 +45,16 @@ class SpecifiedColumn {
|
|||
}[type];
|
||||
|
||||
String get dslColumnTypeName => {
|
||||
ColumnType.boolean: 'BoolColumn',
|
||||
ColumnType.text: 'TextColumn',
|
||||
ColumnType.integer: 'IntColumn'
|
||||
}[type];
|
||||
ColumnType.boolean: 'BoolColumn',
|
||||
ColumnType.text: 'TextColumn',
|
||||
ColumnType.integer: 'IntColumn'
|
||||
}[type];
|
||||
|
||||
String get implColumnTypeName => {
|
||||
ColumnType.boolean: 'GeneratedBoolColumn',
|
||||
ColumnType.text: 'GeneratedTextColumn',
|
||||
ColumnType.integer: 'GeneratedIntColumn'
|
||||
}[type];
|
||||
ColumnType.boolean: 'GeneratedBoolColumn',
|
||||
ColumnType.text: 'GeneratedTextColumn',
|
||||
ColumnType.integer: 'GeneratedIntColumn'
|
||||
}[type];
|
||||
|
||||
const SpecifiedColumn(
|
||||
{this.type,
|
||||
|
|
|
@ -2,10 +2,8 @@ import 'package:analyzer/dart/element/element.dart';
|
|||
import 'package:sally_generator/src/model/specified_table.dart';
|
||||
|
||||
class SpecifiedDatabase {
|
||||
|
||||
final ClassElement fromClass;
|
||||
final List<SpecifiedTable> tables;
|
||||
|
||||
SpecifiedDatabase(this.fromClass, this.tables);
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ class SpecifiedTable {
|
|||
final ClassElement fromClass;
|
||||
final List<SpecifiedColumn> columns;
|
||||
final String sqlName;
|
||||
|
||||
/// The name for the data class associated with this table
|
||||
final String dartTypeName;
|
||||
|
||||
|
|
|
@ -19,8 +19,7 @@ class TableParser extends ParserBase {
|
|||
fromClass: element,
|
||||
columns: _parseColumns(element),
|
||||
sqlName: sqlName,
|
||||
dartTypeName: dataClassNameForClassName(element.name)
|
||||
);
|
||||
dartTypeName: dataClassNameForClassName(element.name));
|
||||
}
|
||||
|
||||
String _parseTableName(ClassElement element) {
|
||||
|
|
|
@ -56,10 +56,10 @@ class SallyGenerator extends GeneratorForAnnotation<UseSally> {
|
|||
}
|
||||
}
|
||||
|
||||
if (_foundTables.isEmpty)
|
||||
return '';
|
||||
if (_foundTables.isEmpty) return '';
|
||||
|
||||
final specifiedDb = SpecifiedDatabase(element as ClassElement, tablesForThisDb);
|
||||
final specifiedDb =
|
||||
SpecifiedDatabase(element as ClassElement, tablesForThisDb);
|
||||
|
||||
final buffer = StringBuffer();
|
||||
DatabaseWriter(specifiedDb).write(buffer);
|
||||
|
|
|
@ -13,4 +13,4 @@ String dataClassNameForClassName(String tableName) {
|
|||
|
||||
// Default behavior if the table name is not a valid plural.
|
||||
return '${tableName}Data';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import 'package:sally_generator/src/model/specified_database.dart';
|
|||
import 'package:sally_generator/src/writer/table_writer.dart';
|
||||
|
||||
class DatabaseWriter {
|
||||
|
||||
final SpecifiedDatabase db;
|
||||
|
||||
DatabaseWriter(this.db);
|
||||
|
@ -17,7 +16,7 @@ class DatabaseWriter {
|
|||
// Write the database class
|
||||
final className = '_\$${db.fromClass.name}';
|
||||
buffer.write('abstract class $className extends GeneratedDatabase {\n'
|
||||
'$className() : super(const SqlTypeSystem.withDefaults(), null); \n');
|
||||
'$className() : super(const SqlTypeSystem.withDefaults(), null); \n');
|
||||
|
||||
final tableGetters = <String>[];
|
||||
|
||||
|
@ -26,7 +25,8 @@ class DatabaseWriter {
|
|||
tableGetters.add(tableFieldName);
|
||||
final tableClassName = table.tableInfoName;
|
||||
|
||||
buffer.write('$tableClassName get $tableFieldName => $tableClassName(this);');
|
||||
buffer.write(
|
||||
'$tableClassName get $tableFieldName => $tableClassName(this);');
|
||||
}
|
||||
|
||||
// Write List of tables, close bracket for class
|
||||
|
@ -35,4 +35,4 @@ class DatabaseWriter {
|
|||
..write(tableGetters.join(','))
|
||||
..write('];\n}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,25 +47,27 @@ class TableWriter {
|
|||
final isNullable = false;
|
||||
|
||||
// @override
|
||||
// IntColumn get id => GeneratedIntColumn('sql_name', isNullable);
|
||||
// GeneratedIntColumn get id => GeneratedIntColumn('sql_name', isNullable);
|
||||
buffer
|
||||
..write('@override \n')
|
||||
..write('${column.dslColumnTypeName} get ${column.dartGetterName} => '
|
||||
'${column.implColumnTypeName}(\'${column.name.name}\', $isNullable);\n');
|
||||
..write('${column.implColumnTypeName} get ${column.dartGetterName} => '
|
||||
'${column.implColumnTypeName}(\'${column.name.name}\', $isNullable);\n');
|
||||
}
|
||||
|
||||
// Generate $columns, $tableName, asDslTable getters
|
||||
final columnsWithGetters = table.columns.map((c) => c.dartGetterName).join(', ');
|
||||
final columnsWithGetters =
|
||||
table.columns.map((c) => c.dartGetterName).join(', ');
|
||||
|
||||
buffer
|
||||
..write('@override\nList<Column> get \$columns => [$columnsWithGetters];\n')
|
||||
..write('@override\n$tableDslName get asDslTable => this;\n')
|
||||
..write('@override\nString get \$tableName => \'${table.sqlName}\';\n');
|
||||
..write(
|
||||
'@override\nList<GeneratedColumn> get \$columns => [$columnsWithGetters];\n')
|
||||
..write('@override\n$tableDslName get asDslTable => this;\n')
|
||||
..write('@override\nString get \$tableName => \'${table.sqlName}\';\n');
|
||||
|
||||
// todo replace set syntax with literal once dart supports it
|
||||
// write primary key getter: Set<Column> get $primaryKey => Set().add(id);
|
||||
final primaryKeyColumns = table.primaryKey.map((c) => c.dartGetterName);
|
||||
buffer.write('@override\nSet<Column> get \$primaryKey => Set()');
|
||||
buffer.write('@override\nSet<GeneratedColumn> get \$primaryKey => Set()');
|
||||
for (var pkColumn in primaryKeyColumns) {
|
||||
buffer.write('..add($pkColumn)');
|
||||
}
|
||||
|
@ -80,7 +82,8 @@ class TableWriter {
|
|||
void _writeMappingMethod(StringBuffer buffer) {
|
||||
final dataClassName = table.dartTypeName;
|
||||
|
||||
buffer.write('@override\n$dataClassName map(Map<String, dynamic> data) {\n');
|
||||
buffer
|
||||
.write('@override\n$dataClassName map(Map<String, dynamic> data) {\n');
|
||||
|
||||
final dartTypeToResolver = <String, String>{};
|
||||
|
||||
|
@ -90,7 +93,8 @@ class TableWriter {
|
|||
final resolver = '${ReCase(usedType).camelCase}Type';
|
||||
dartTypeToResolver[usedType] = resolver;
|
||||
|
||||
buffer.write('final $resolver = db.typeSystem.forDartType<$usedType>();\n');
|
||||
buffer
|
||||
.write('final $resolver = db.typeSystem.forDartType<$usedType>();\n');
|
||||
}
|
||||
|
||||
// finally, the mighty constructor invocation:
|
||||
|
@ -100,7 +104,8 @@ class TableWriter {
|
|||
// id: intType.mapFromDatabaseResponse(data["id])
|
||||
final getter = column.dartGetterName;
|
||||
final resolver = dartTypeToResolver[column.dartTypeName];
|
||||
final typeParser = '$resolver.mapFromDatabaseResponse(data[\'${column.name.name}\'])';
|
||||
final typeParser =
|
||||
'$resolver.mapFromDatabaseResponse(data[\'${column.name.name}\'])';
|
||||
|
||||
buffer.write('$getter: $typeParser,');
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue