mirror of https://github.com/AMT-Cheif/drift.git
Migrate some trigger code to refactorings on develop
This commit is contained in:
parent
ba603f22cc
commit
04f75d11d3
|
@ -31,6 +31,12 @@ abstract class GeneratedDatabase extends DatabaseConnectionUser
|
|||
/// A list of tables specified in this database.
|
||||
List<TableInfo> get allTables;
|
||||
|
||||
/// A list of all [DatabaseSchemaEntity] that are specified in this database.
|
||||
///
|
||||
/// This contains [allTables], but also advanced entities like triggers.
|
||||
// return allTables for backwards compatibility
|
||||
List<DatabaseSchemaEntity> get allSchemaEntities => allTables;
|
||||
|
||||
/// A [Type] can't be sent across isolates. Instances of this class shouldn't
|
||||
/// be sent over isolates either, so let's keep a reference to a [Type] that
|
||||
/// definitely prohibits this.
|
||||
|
|
|
@ -69,7 +69,7 @@ class Migrator {
|
|||
/// Creates all tables, triggers, views, indexes and everything else defined
|
||||
/// in the database, if they don't exist.
|
||||
Future<void> createAll() async {
|
||||
for (var entity in _db.allEntities) {
|
||||
for (final entity in _db.allSchemaEntities) {
|
||||
if (entity is TableInfo) {
|
||||
await createTable(entity);
|
||||
} else if (entity is Trigger) {
|
||||
|
|
|
@ -28,6 +28,7 @@ part 'expressions/text.dart';
|
|||
part 'expressions/variables.dart';
|
||||
|
||||
part 'schema/columns.dart';
|
||||
part 'schema/entities.dart';
|
||||
part 'schema/table_info.dart';
|
||||
|
||||
part 'statements/select/custom_select.dart';
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
part of '../query_builder.dart';
|
||||
|
||||
/// Some abstract schema entity that can be stored in a database. This includes
|
||||
/// tables, triggers, views, indexes, etc.
|
||||
abstract class DatabaseSchemaEntity {
|
||||
|
@ -10,10 +12,11 @@ abstract class DatabaseSchemaEntity {
|
|||
/// In moor, triggers can only be declared in `.moor` files.
|
||||
///
|
||||
/// For more information on triggers, see the [CREATE TRIGGER][sqlite-docs]
|
||||
/// documentation from sqlite, or the [entry on sqlitetutorial.net][sql-tutorial].
|
||||
/// documentation from sqlite, or the [entry on sqlitetutorial.net][sql-tut].
|
||||
///
|
||||
/// [sqlite-docs]: (https://sqlite.org/lang_createtrigger.html)
|
||||
/// [sql-tutorial]: (https://www.sqlitetutorial.net/sqlite-trigger/)
|
||||
///
|
||||
/// [sqlite-docs]: https://sqlite.org/lang_createtrigger.html
|
||||
/// [sql-tut]: https://www.sqlitetutorial.net/sqlite-trigger/
|
||||
class Trigger extends DatabaseSchemaEntity {
|
||||
/// The `CREATE TRIGGER` sql statement that can be used to create this
|
||||
/// trigger.
|
||||
|
|
|
@ -35,7 +35,7 @@ void main() {
|
|||
final mockExecutor = MockExecutor();
|
||||
final mockQueryExecutor = MockQueryExecutor();
|
||||
final db = CustomTablesDb(mockExecutor);
|
||||
await Migrator(db, mockQueryExecutor).createAllTables();
|
||||
await Migrator(db, mockQueryExecutor).createAll();
|
||||
|
||||
verify(mockQueryExecutor.call(_createNoIds, []));
|
||||
verify(mockQueryExecutor.call(_createWithDefaults, []));
|
||||
|
|
|
@ -65,7 +65,7 @@ class Database extends _$Database {
|
|||
MigrationStrategy get migration {
|
||||
return MigrationStrategy(
|
||||
onCreate: (Migrator m) {
|
||||
return m.createAllTables();
|
||||
return m.createAll();
|
||||
},
|
||||
onUpgrade: (Migrator m, int from, int to) async {
|
||||
if (from == 1) {
|
||||
|
|
|
@ -7,12 +7,12 @@ import 'package:sqlparser/sqlparser.dart';
|
|||
/// Handles `REFERENCES` clauses in tables by resolving their columns and
|
||||
/// reporting errors if they don't exist. Further, sets the
|
||||
/// [MoorTable.references] field for tables declared in moor.
|
||||
class TableHandler {
|
||||
class EntityHandler {
|
||||
final AnalyzeMoorStep step;
|
||||
final ParsedMoorFile file;
|
||||
final List<MoorTable> availableTables;
|
||||
|
||||
TableHandler(this.step, this.file, this.availableTables);
|
||||
EntityHandler(this.step, this.file, this.availableTables);
|
||||
|
||||
void handle() {
|
||||
for (final table in file.declaredTables) {
|
||||
|
@ -25,7 +25,7 @@ class TableHandler {
|
|||
}
|
||||
|
||||
class _ReferenceResolvingVisitor extends RecursiveVisitor<void, void> {
|
||||
final TableHandler handler;
|
||||
final EntityHandler handler;
|
||||
|
||||
_ReferenceResolvingVisitor(this.handler);
|
||||
|
|
@ -6,9 +6,11 @@ import 'package:moor_generator/src/model/sql_query.dart';
|
|||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
abstract class FileResult {
|
||||
final List<MoorTable> declaredTables;
|
||||
final List<MoorSchemaEntity> declaredEntities;
|
||||
|
||||
FileResult(this.declaredTables);
|
||||
Iterable<MoorTable> get declaredTables => declaredEntities.whereType();
|
||||
|
||||
FileResult(this.declaredEntities);
|
||||
}
|
||||
|
||||
class ParsedDartFile extends FileResult {
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:moor/moor.dart';
|
|||
import 'package:moor_generator/moor_generator.dart';
|
||||
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';
|
||||
import 'package:moor_generator/src/analyzer/moor/entity_handler.dart';
|
||||
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';
|
||||
|
@ -14,7 +14,8 @@ 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/sql_query.dart';
|
||||
import 'package:moor_generator/src/utils/table_reference_sorter.dart';
|
||||
import 'package:moor_generator/src/utils/entity_reference_sorter.dart';
|
||||
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
|
||||
part 'steps/analyze_dart.dart';
|
||||
|
@ -53,8 +54,12 @@ abstract class AnalyzingStep extends Step {
|
|||
.toList();
|
||||
}
|
||||
|
||||
Iterable<MoorSchemaEntity> _availableEntities(List<FoundFile> imports) {
|
||||
return imports.expand<MoorSchemaEntity>((file) =>
|
||||
file.currentResult?.declaredEntities ?? const Iterable.empty());
|
||||
}
|
||||
|
||||
Iterable<MoorTable> _availableTables(List<FoundFile> imports) {
|
||||
return imports.expand<MoorTable>(
|
||||
(file) => file.currentResult?.declaredTables ?? const Iterable.empty());
|
||||
return _availableEntities(imports).whereType();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@ class AnalyzeDartStep extends AnalyzingStep {
|
|||
for (final accessor in parseResult.dbAccessors) {
|
||||
final transitiveImports = _transitiveImports(accessor.imports);
|
||||
|
||||
var availableTables = _availableTables(transitiveImports)
|
||||
var availableEntities = _availableEntities(transitiveImports)
|
||||
.followedBy(accessor.declaredTables)
|
||||
.toList();
|
||||
|
||||
try {
|
||||
availableTables = sortTablesTopologically(availableTables);
|
||||
availableEntities = sortEntitiesTopologically(availableEntities);
|
||||
} on CircularReferenceException catch (e) {
|
||||
final msg = StringBuffer(
|
||||
'Found a circular reference in your database. This can cause '
|
||||
|
@ -39,6 +39,7 @@ class AnalyzeDartStep extends AnalyzingStep {
|
|||
.whereType<ParsedMoorFile>()
|
||||
.expand((f) => f.resolvedQueries);
|
||||
|
||||
final availableTables = availableEntities.whereType<MoorTable>().toList();
|
||||
final parser = SqlParser(this, availableTables, accessor.declaredQueries);
|
||||
parser.parse();
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class AnalyzeMoorStep extends AnalyzingStep {
|
|||
final parser = SqlParser(this, availableTables, parseResult.queries)
|
||||
..parse();
|
||||
|
||||
TableHandler(this, parseResult, availableTables).handle();
|
||||
EntityHandler(this, parseResult, availableTables).handle();
|
||||
|
||||
parseResult.resolvedQueries = parser.foundQueries;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/// Some schema entity found.
|
||||
///
|
||||
/// Most commonly a table, but it can also be a trigger.
|
||||
abstract class MoorSchemaEntity {
|
||||
/// All entities that have to be created before this entity can be created.
|
||||
///
|
||||
/// For tables, this can be contents of a `REFERENCES` clause. For triggers,
|
||||
/// it would be the tables watched.
|
||||
///
|
||||
/// The generator will verify that the graph of entities and [references]
|
||||
/// is acyclic and sort them topologically.
|
||||
Iterable<MoorSchemaEntity> get references;
|
||||
|
||||
/// A human readable name of this entity, like the table name.
|
||||
String get displayName;
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
export 'base_entity.dart';
|
||||
export 'column.dart';
|
||||
export 'database.dart';
|
||||
export 'declarations/declaration.dart';
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
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);
|
||||
}
|
|
@ -4,12 +4,13 @@ import 'package:moor_generator/src/model/used_type_converter.dart';
|
|||
import 'package:recase/recase.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
import 'base_entity.dart';
|
||||
import 'column.dart';
|
||||
import 'declarations/declaration.dart';
|
||||
|
||||
/// A parsed table, declared in code by extending `Table` and referencing that
|
||||
/// table in `@UseMoor` or `@UseDao`.
|
||||
class MoorTable implements HasDeclaration {
|
||||
class MoorTable implements HasDeclaration, MoorSchemaEntity {
|
||||
/// The [ClassElement] for the class that declares this table or null if
|
||||
/// the table was inferred from a `CREATE TABLE` statement.
|
||||
final ClassElement fromClass;
|
||||
|
@ -80,8 +81,7 @@ class MoorTable implements HasDeclaration {
|
|||
/// `customConstraints` getter in the table class with this value.
|
||||
final List<String> overrideTableConstraints;
|
||||
|
||||
/// The set of tables referenced somewhere in the declaration of this table,
|
||||
/// for instance by using a `REFERENCES` column constraint.
|
||||
@override
|
||||
final Set<MoorTable> references = {};
|
||||
|
||||
/// Returns whether this table was created from a `CREATE VIRTUAL TABLE`
|
||||
|
@ -116,6 +116,7 @@ class MoorTable implements HasDeclaration {
|
|||
Iterable<UsedTypeConverter> get converters =>
|
||||
columns.map((c) => c.typeConverter).where((t) => t != null);
|
||||
|
||||
@override
|
||||
String get displayName {
|
||||
if (isFromSql) {
|
||||
return sqlName;
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
import 'package:moor_generator/moor_generator.dart';
|
||||
|
||||
/// Topologically sorts a list of [MoorTable]s by their
|
||||
/// [MoorTable.references] relationship: Tables appearing first in the
|
||||
/// Topologically sorts a list of [MoorSchemaEntity]s by their
|
||||
/// [MoorSchemaEntity.references] relationship: Tables appearing first in the
|
||||
/// output have to be created first so the table creation script doesn't crash
|
||||
/// because of tables not existing.
|
||||
///
|
||||
/// If there is a circular reference between [MoorTable]s, an error will
|
||||
/// be added that contains the name of the tables in question.
|
||||
List<MoorTable> sortTablesTopologically(Iterable<MoorTable> tables) {
|
||||
List<MoorSchemaEntity> sortEntitiesTopologically(
|
||||
Iterable<MoorSchemaEntity> tables) {
|
||||
final run = _SortRun();
|
||||
|
||||
for (final table in tables) {
|
||||
if (!run.didVisitAlready(table)) {
|
||||
run.previous[table] = null;
|
||||
_visit(table, run);
|
||||
for (final entity in tables) {
|
||||
if (!run.didVisitAlready(entity)) {
|
||||
run.previous[entity] = null;
|
||||
_visit(entity, run);
|
||||
}
|
||||
}
|
||||
|
||||
return run.result;
|
||||
}
|
||||
|
||||
void _visit(MoorTable table, _SortRun run) {
|
||||
void _visit(MoorSchemaEntity table, _SortRun run) {
|
||||
for (final reference in table.references) {
|
||||
if (run.result.contains(reference)) {
|
||||
// already handled, nothing to do
|
||||
|
@ -38,34 +39,34 @@ void _visit(MoorTable table, _SortRun run) {
|
|||
}
|
||||
|
||||
class _SortRun {
|
||||
final Map<MoorTable, MoorTable> previous = {};
|
||||
final List<MoorTable> result = [];
|
||||
final Map<MoorSchemaEntity, MoorSchemaEntity> previous = {};
|
||||
final List<MoorSchemaEntity> result = [];
|
||||
|
||||
/// Throws a [CircularReferenceException] because the [last] table depends on
|
||||
/// [first], which (transitively) depends on [last] as well. The path in the
|
||||
/// thrown exception will go from [first] to [last].
|
||||
void throwCircularException(MoorTable last, MoorTable first) {
|
||||
final constructedPath = <MoorTable>[];
|
||||
void throwCircularException(MoorSchemaEntity last, MoorSchemaEntity first) {
|
||||
final constructedPath = <MoorSchemaEntity>[];
|
||||
for (var current = last; current != first; current = previous[current]) {
|
||||
constructedPath.insert(0, current);
|
||||
}
|
||||
constructedPath.insert(0, first);
|
||||
|
||||
throw CircularReferenceException(constructedPath);
|
||||
throw CircularReferenceException._(constructedPath);
|
||||
}
|
||||
|
||||
bool didVisitAlready(MoorTable table) {
|
||||
bool didVisitAlready(MoorSchemaEntity table) {
|
||||
return previous[table] != null || result.contains(table);
|
||||
}
|
||||
}
|
||||
|
||||
/// Thrown by [sortTablesTopologically] when the graph formed by
|
||||
/// [MoorTable]s and their [MoorTable.references] is not acyclic.
|
||||
/// Thrown by [sortEntitiesTopologically] when the graph formed by
|
||||
/// [MoorSchemaEntity.references] is not acyclic.
|
||||
class CircularReferenceException implements Exception {
|
||||
/// The list of tables forming a circular reference, so that the first table
|
||||
/// in this list references the second one and so on. The last table in this
|
||||
/// list references the first one.
|
||||
final List<MoorTable> affected;
|
||||
/// The list of entities forming a circular reference, so that the first
|
||||
/// entity in this list references the second one and so on. The last entity
|
||||
/// in this list references the first one, thus forming a cycle.
|
||||
final List<MoorSchemaEntity> affected;
|
||||
|
||||
CircularReferenceException(this.affected);
|
||||
CircularReferenceException._(this.affected);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor_generator/moor_generator.dart';
|
||||
import 'package:moor_generator/src/utils/table_reference_sorter.dart';
|
||||
import 'package:moor_generator/src/utils/entity_reference_sorter.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
@ -39,14 +39,14 @@ void main() {
|
|||
a.references.add(b);
|
||||
b.references.add(c);
|
||||
|
||||
final sorted = sortTablesTopologically([a, b, c, d]);
|
||||
final sorted = sortEntitiesTopologically([a, b, c, d]);
|
||||
expect(sorted, [c, b, a, d]);
|
||||
});
|
||||
}
|
||||
|
||||
CircularReferenceException _expectFails(Iterable<MoorTable> table) {
|
||||
try {
|
||||
sortTablesTopologically(table);
|
||||
sortEntitiesTopologically(table);
|
||||
fail('Expected sortTablesTopologically to throw here');
|
||||
} on CircularReferenceException catch (e) {
|
||||
return e;
|
|
@ -9,7 +9,9 @@ class Block extends AstNode {
|
|||
Block(this.statements);
|
||||
|
||||
@override
|
||||
T accept<T>(AstVisitor<T> visitor) => visitor.visitBlock(this);
|
||||
R accept<A, R>(AstVisitor<A, R> visitor, A arg) {
|
||||
return visitor.visitBlock(this, arg);
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<AstNode> get childNodes => statements;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
part of '../ast.dart';
|
||||
|
||||
abstract class TableInducingStatement extends Statement
|
||||
implements PartOfMoorFile, SchemaStatement {
|
||||
implements SchemaStatement {
|
||||
final bool ifNotExists;
|
||||
final String tableName;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
part of '../ast.dart';
|
||||
|
||||
/// A "CREATE TRIGGER" statement, see https://sqlite.org/lang_createtrigger.html
|
||||
class CreateTriggerStatement extends Statement with SchemaStatement {
|
||||
class CreateTriggerStatement extends Statement implements SchemaStatement {
|
||||
final bool ifNotExists;
|
||||
final String triggerName;
|
||||
IdentifierToken triggerNameToken;
|
||||
|
@ -24,8 +24,8 @@ class CreateTriggerStatement extends Statement with SchemaStatement {
|
|||
@required this.action});
|
||||
|
||||
@override
|
||||
T accept<T>(AstVisitor<T> visitor) {
|
||||
return visitor.visitCreateTriggerStatement(this);
|
||||
R accept<A, R>(AstVisitor<A, R> visitor, A arg) {
|
||||
return visitor.visitCreateTriggerStatement(this, arg);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -53,7 +53,7 @@ abstract class TriggerTarget {
|
|||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(other) => other.runtimeType == runtimeType;
|
||||
bool operator ==(dynamic other) => other.runtimeType == runtimeType;
|
||||
}
|
||||
|
||||
class DeleteTarget extends TriggerTarget {
|
||||
|
|
|
@ -10,6 +10,7 @@ abstract class AstVisitor<A, R> {
|
|||
R visitUpdateStatement(UpdateStatement e, A arg);
|
||||
R visitCreateTableStatement(CreateTableStatement e, A arg);
|
||||
R visitCreateVirtualTableStatement(CreateVirtualTableStatement e, A arg);
|
||||
R visitCreateTriggerStatement(CreateTriggerStatement e, A arg);
|
||||
|
||||
R visitWithClause(WithClause e, A arg);
|
||||
R visitCommonTableExpression(CommonTableExpression e, A arg);
|
||||
|
@ -50,6 +51,8 @@ abstract class AstVisitor<A, R> {
|
|||
R visitNumberedVariable(NumberedVariable e, A arg);
|
||||
R visitNamedVariable(ColonNamedVariable e, A arg);
|
||||
|
||||
R visitBlock(Block block, A arg);
|
||||
|
||||
R visitMoorFile(MoorFile e, A arg);
|
||||
R visitMoorImportStatement(ImportStatement e, A arg);
|
||||
R visitMoorDeclaredStatement(DeclaredStatement e, A arg);
|
||||
|
@ -91,12 +94,16 @@ class RecursiveVisitor<A, R> implements AstVisitor<A, R> {
|
|||
return visitTableInducingStatement(e, arg);
|
||||
}
|
||||
|
||||
@override
|
||||
@override
|
||||
R visitCreateVirtualTableStatement(CreateVirtualTableStatement e, A arg) {
|
||||
return visitTableInducingStatement(e, arg);
|
||||
}
|
||||
|
||||
@override
|
||||
R visitCreateTriggerStatement(CreateTriggerStatement e, A arg) {
|
||||
return visitCreateTriggerStatement(e, arg);
|
||||
}
|
||||
|
||||
R visitBaseSelectStatement(BaseSelectStatement stmt, A arg) {
|
||||
return visitCrudStatement(stmt, arg);
|
||||
}
|
||||
|
@ -204,6 +211,11 @@ class RecursiveVisitor<A, R> implements AstVisitor<A, R> {
|
|||
return visitChildren(e, arg);
|
||||
}
|
||||
|
||||
@override
|
||||
R visitBlock(Block e, A arg) {
|
||||
return visitChildren(e, arg);
|
||||
}
|
||||
|
||||
// Moor-specific additions
|
||||
@override
|
||||
R visitMoorFile(MoorFile e, A arg) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:test/test.dart';
|
|||
|
||||
import 'utils.dart';
|
||||
|
||||
final block = Block([
|
||||
final _block = Block([
|
||||
UpdateStatement(table: TableReference('tbl'), set: [
|
||||
SetComponent(
|
||||
column: Reference(columnName: 'foo'),
|
||||
|
@ -36,7 +36,7 @@ void main() {
|
|||
mode: TriggerMode.after,
|
||||
target: const DeleteTarget(),
|
||||
onTable: TableReference('tbl'),
|
||||
action: block,
|
||||
action: _block,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
@ -61,7 +61,7 @@ void main() {
|
|||
Reference(columnName: 'bar'),
|
||||
]),
|
||||
onTable: TableReference('tbl'),
|
||||
action: block,
|
||||
action: _block,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
@ -89,7 +89,7 @@ void main() {
|
|||
Reference(tableName: 'new', columnName: 'foo'),
|
||||
NullLiteral(token(TokenType.$null)),
|
||||
),
|
||||
action: block,
|
||||
action: _block,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue