From 8a54fd47292aeaa25ac1e5c6a22a62ebb3a94802 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 21 Oct 2019 18:13:28 +0200 Subject: [PATCH] Generate code for CREATE TRIGGER statements --- moor/example/example.g.dart | 3 +++ moor/lib/src/runtime/database.dart | 1 + moor/test/data/tables/custom_tables.g.dart | 7 ++++++ moor/test/data/tables/tables.moor | 4 ++++ moor/test/data/tables/todos.g.dart | 9 +++++++ .../lib/src/analyzer/moor/parser.dart | 4 ++++ .../lib/src/analyzer/runner/results.dart | 5 ++++ .../lib/src/analyzer/runner/steps.dart | 5 +++- .../analyzer/runner/steps/analyze_dart.dart | 23 +++++++++++++++--- .../lib/src/model/specified_db_classes.dart | 15 +++++++++--- .../lib/src/model/specified_entities.dart | 24 +++++++++++++++++++ .../lib/src/writer/database_writer.dart | 21 ++++++++++++++++ sqlparser/lib/src/reader/parser/parser.dart | 2 +- 13 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 moor_generator/lib/src/model/specified_entities.dart diff --git a/moor/example/example.g.dart b/moor/example/example.g.dart index 64b3caa2..74902da1 100644 --- a/moor/example/example.g.dart +++ b/moor/example/example.g.dart @@ -866,6 +866,9 @@ abstract class _$Database extends GeneratedDatabase { @override List get allTables => [categories, recipes, ingredients, ingredientInRecipes]; + @override + List get allEntities => + [categories, recipes, ingredients, ingredientInRecipes]; } class TotalWeightResult { diff --git a/moor/lib/src/runtime/database.dart b/moor/lib/src/runtime/database.dart index 6c53990d..618952af 100644 --- a/moor/lib/src/runtime/database.dart +++ b/moor/lib/src/runtime/database.dart @@ -369,6 +369,7 @@ abstract class GeneratedDatabase extends DatabaseConnectionUser MigrationStrategy get _resolvedMigration => _cachedMigration ??= migration; /// A list of tables specified in this database. + // todo: Replace all usages with allEntities.whereType() List get allTables; /// All entities (tables, views, triggers, indexes) that are declared in this diff --git a/moor/test/data/tables/custom_tables.g.dart b/moor/test/data/tables/custom_tables.g.dart index 94764a6d..10e63eb8 100644 --- a/moor/test/data/tables/custom_tables.g.dart +++ b/moor/test/data/tables/custom_tables.g.dart @@ -888,6 +888,10 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { ConfigTable get config => _config ??= ConfigTable(this); Mytable _mytable; Mytable get mytable => _mytable ??= Mytable(this); + Trigger _myTrigger; + Trigger get myTrigger => _myTrigger ??= Trigger( + 'CREATE TRIGGER my_trigger AFTER INSERT ON config BEGIN\n INSERT INTO with_defaults VALUES (new.config_key, LENGTH(new.config_value));\nEND;', + 'my_trigger'); Config _rowToConfig(QueryRow row) { return Config( configKey: row.readString('config_key'), @@ -953,6 +957,9 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { @override List get allTables => [noIds, withDefaults, withConstraints, config, mytable]; + @override + List get allEntities => + [noIds, withDefaults, withConstraints, config, mytable, myTrigger]; } class ReadRowIdResult { diff --git a/moor/test/data/tables/tables.moor b/moor/test/data/tables/tables.moor index 972fecb8..f8f96140 100644 --- a/moor/test/data/tables/tables.moor +++ b/moor/test/data/tables/tables.moor @@ -27,6 +27,10 @@ CREATE TABLE mytable ( somedate DATETIME ); +CREATE TRIGGER my_trigger AFTER INSERT ON config BEGIN + INSERT INTO with_defaults VALUES (new.config_key, LENGTH(new.config_value)); +END; + readConfig: SELECT * FROM config WHERE config_key = ?; readMultiple: SELECT * FROM config WHERE config_key IN ? ORDER BY $clause; readDynamic: SELECT * FROM config WHERE $predicate; diff --git a/moor/test/data/tables/todos.g.dart b/moor/test/data/tables/todos.g.dart index 57d1e58e..6cca93e8 100644 --- a/moor/test/data/tables/todos.g.dart +++ b/moor/test/data/tables/todos.g.dart @@ -1417,6 +1417,15 @@ abstract class _$TodoDb extends GeneratedDatabase { tableWithoutPK, pureDefaults ]; + @override + List get allEntities => [ + todosTable, + categories, + users, + sharedTodos, + tableWithoutPK, + pureDefaults + ]; } class AllTodosWithCategoryResult { diff --git a/moor_generator/lib/src/analyzer/moor/parser.dart b/moor_generator/lib/src/analyzer/moor/parser.dart index 8ac459d4..2bc1bc3d 100644 --- a/moor_generator/lib/src/analyzer/moor/parser.dart +++ b/moor_generator/lib/src/analyzer/moor/parser.dart @@ -19,6 +19,7 @@ class MoorParser { final createdReaders = []; final queryDeclarations = []; final importStatements = []; + final otherComponents = []; for (var parsedStmt in parsedFile.statements) { if (parsedStmt is ImportStatement) { @@ -29,6 +30,8 @@ class MoorParser { createdReaders.add(CreateTableReader(parsedStmt, step)); } else if (parsedStmt is DeclaredStatement) { queryDeclarations.add(DeclaredMoorQuery.fromStatement(parsedStmt)); + } else if (parsedStmt is CreateTriggerStatement) { + otherComponents.add(parsedStmt); } } @@ -54,6 +57,7 @@ class MoorParser { queries: queryDeclarations, imports: importStatements, tableDeclarations: tableDeclarations, + otherComponents: otherComponents, ); for (var decl in queryDeclarations) { decl.file = analyzedFile; diff --git a/moor_generator/lib/src/analyzer/runner/results.dart b/moor_generator/lib/src/analyzer/runner/results.dart index 5ee8934f..2d52ac7d 100644 --- a/moor_generator/lib/src/analyzer/runner/results.dart +++ b/moor_generator/lib/src/analyzer/runner/results.dart @@ -36,6 +36,10 @@ class ParsedMoorFile extends FileResult { final List imports; final List queries; + /// Schema component that are neither tables nor queries. This can include + /// triggers or indexes. + final List otherComponents; + List resolvedQueries; Map tableDeclarations; Map resolvedImports; @@ -44,6 +48,7 @@ class ParsedMoorFile extends FileResult { {List declaredTables = const [], this.queries = const [], this.imports = const [], + this.otherComponents = const [], this.tableDeclarations = const {}}) : super(declaredTables); } diff --git a/moor_generator/lib/src/analyzer/runner/steps.dart b/moor_generator/lib/src/analyzer/runner/steps.dart index 44ae604b..c4f74b85 100644 --- a/moor_generator/lib/src/analyzer/runner/steps.dart +++ b/moor_generator/lib/src/analyzer/runner/steps.dart @@ -1,7 +1,7 @@ import 'package:analyzer/dart/constant/value.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; -import 'package:moor/moor.dart'; +import 'package:moor/moor.dart' show Table, UseMoor, UseDao; import 'package:moor_generator/src/analyzer/dart/parser.dart'; import 'package:moor_generator/src/analyzer/errors.dart'; import 'package:moor_generator/src/analyzer/moor/table_handler.dart'; @@ -9,14 +9,17 @@ import 'package:moor_generator/src/analyzer/runner/file_graph.dart'; import 'package:moor_generator/src/analyzer/runner/results.dart'; import 'package:moor_generator/src/analyzer/moor/inline_dart_resolver.dart'; import 'package:moor_generator/src/analyzer/moor/parser.dart'; +import 'package:moor_generator/src/analyzer/sql_queries/meta/declarations.dart'; import 'package:moor_generator/src/analyzer/sql_queries/sql_parser.dart'; import 'package:moor_generator/src/analyzer/sql_queries/type_mapping.dart'; import 'package:moor_generator/src/analyzer/runner/task.dart'; import 'package:moor_generator/src/model/specified_db_classes.dart'; +import 'package:moor_generator/src/model/specified_entities.dart'; import 'package:moor_generator/src/model/specified_table.dart'; import 'package:moor_generator/src/model/sql_query.dart'; import 'package:moor_generator/src/utils/table_reference_sorter.dart'; import 'package:source_gen/source_gen.dart'; +import 'package:sqlparser/sqlparser.dart' hide Table; part 'steps/analyze_dart.dart'; part 'steps/analyze_moor.dart'; diff --git a/moor_generator/lib/src/analyzer/runner/steps/analyze_dart.dart b/moor_generator/lib/src/analyzer/runner/steps/analyze_dart.dart index ac44fdd4..42ca89c1 100644 --- a/moor_generator/lib/src/analyzer/runner/steps/analyze_dart.dart +++ b/moor_generator/lib/src/analyzer/runner/steps/analyze_dart.dart @@ -34,10 +34,12 @@ class AnalyzeDartStep extends AnalyzingStep { )); } - final availableQueries = transitiveImports + final transitiveMoorFiles = transitiveImports .map((f) => f.currentResult) - .whereType() - .expand((f) => f.resolvedQueries); + .whereType(); + + final availableQueries = + transitiveMoorFiles.expand((f) => f.resolvedQueries); final parser = SqlParser(this, availableTables, accessor.queries); parser.parse(); @@ -46,6 +48,21 @@ class AnalyzeDartStep extends AnalyzingStep { accessor.resolvedQueries = availableQueries.followedBy(parser.foundQueries).toList(); + + if (accessor is SpecifiedDatabase) { + accessor.otherEntities = transitiveMoorFiles.expand((file) { + return file.otherComponents.map((component) { + final declaration = BaseDeclaration(null, null, component); + + if (component is CreateTriggerStatement) { + return SpecifiedTrigger( + component.triggerName, component.span.text, declaration); + } + + throw AssertionError('Unexpected component: $component'); + }); + }).toList(); + } } } } diff --git a/moor_generator/lib/src/model/specified_db_classes.dart b/moor_generator/lib/src/model/specified_db_classes.dart index d07c1d70..6f13b6ac 100644 --- a/moor_generator/lib/src/model/specified_db_classes.dart +++ b/moor_generator/lib/src/model/specified_db_classes.dart @@ -1,6 +1,7 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:moor_generator/src/analyzer/runner/file_graph.dart'; +import 'package:moor_generator/src/model/specified_entities.dart'; import 'package:moor_generator/src/model/specified_table.dart'; import 'package:moor_generator/src/model/sql_query.dart'; @@ -41,7 +42,15 @@ class SpecifiedDao extends SpecifiedDbAccessor { class SpecifiedDatabase extends SpecifiedDbAccessor { final List daos; - SpecifiedDatabase(ClassElement fromClass, List tables, - this.daos, List includes, List queries) - : super(fromClass, tables, includes, queries); + /// Other entities (such as triggers or components) that are available to this + /// database. + List otherEntities; + + SpecifiedDatabase( + ClassElement fromClass, + List tables, + this.daos, + List includes, + List queries, + ) : super(fromClass, tables, includes, queries); } diff --git a/moor_generator/lib/src/model/specified_entities.dart b/moor_generator/lib/src/model/specified_entities.dart new file mode 100644 index 00000000..e91c71f6 --- /dev/null +++ b/moor_generator/lib/src/model/specified_entities.dart @@ -0,0 +1,24 @@ +import 'package:moor_generator/src/analyzer/sql_queries/meta/declarations.dart'; +import 'package:recase/recase.dart'; + +/// An abstract schema entity that isn't a table. +/// +/// This includes triggers or indexes. +abstract class SpecifiedEntity { + final String name; + + String get dartFieldName => ReCase(name).camelCase; + + SpecifiedEntity(this.name); +} + +/// Information about a trigger defined in a `.moor` file. +class SpecifiedTrigger extends SpecifiedEntity { + /// Information on where this trigger was created. + final BaseDeclaration declaration; + + /// The `CREATE TRIGGER` sql statement that creates this trigger. + final String sql; + + SpecifiedTrigger(String name, this.sql, this.declaration) : super(name); +} diff --git a/moor_generator/lib/src/writer/database_writer.dart b/moor_generator/lib/src/writer/database_writer.dart index 5352d30f..022ab2bb 100644 --- a/moor_generator/lib/src/writer/database_writer.dart +++ b/moor_generator/lib/src/writer/database_writer.dart @@ -1,4 +1,6 @@ import 'package:moor_generator/src/model/specified_db_classes.dart'; +import 'package:moor_generator/src/model/specified_entities.dart'; +import 'package:moor_generator/src/utils/string_escaper.dart'; import 'package:moor_generator/src/writer/queries/query_writer.dart'; import 'package:moor_generator/src/writer/tables/table_writer.dart'; import 'package:moor_generator/src/writer/utils/memoized_getter.dart'; @@ -26,6 +28,7 @@ class DatabaseWriter { '$className(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e); \n'); final tableGetters = []; + final entityGetters = []; for (var table in db.allTables) { tableGetters.add(table.tableFieldName); @@ -38,6 +41,21 @@ class DatabaseWriter { code: '$tableClassName(this)', ); } + entityGetters.addAll(tableGetters); + + for (var otherEntity in db.otherEntities) { + entityGetters.add(otherEntity.dartFieldName); + + if (otherEntity is SpecifiedTrigger) { + writeMemoizedGetter( + buffer: dbScope.leaf(), + getterName: otherEntity.dartFieldName, + returnType: 'Trigger', + code: 'Trigger(${asDartLiteral(otherEntity.sql)}, ' + '${asDartLiteral(otherEntity.name)})', + ); + } + } // Write fields to access an dao. We use a lazy getter for that. for (var dao in db.daos) { @@ -63,6 +81,9 @@ class DatabaseWriter { dbScope.leaf() ..write('@override\nList get allTables => [') ..write(tableGetters.join(',')) + ..write('];\n') + ..write('@override\nList get allEntities => [') + ..write(entityGetters.join(',')) ..write('];\n}'); } } diff --git a/sqlparser/lib/src/reader/parser/parser.dart b/sqlparser/lib/src/reader/parser/parser.dart index 9bf42be6..ae9c36c4 100644 --- a/sqlparser/lib/src/reader/parser/parser.dart +++ b/sqlparser/lib/src/reader/parser/parser.dart @@ -332,7 +332,7 @@ class Parser extends ParserBase for (var stmt = _parseAsStatement(_crud); stmt != null || _lastStmtHadParsingError; stmt = _parseAsStatement(_crud)) { - stmts.add(stmt); + if (stmt != null) stmts.add(stmt); } final end = _consume(TokenType.end, 'Expected END');