mirror of https://github.com/AMT-Cheif/drift.git
Move referenced table finder to sqlparser package (#537)
This commit is contained in:
parent
f336668c8a
commit
8b6abd7140
|
@ -16,12 +16,14 @@ class CreateTableReader {
|
||||||
final TableInducingStatement stmt;
|
final TableInducingStatement stmt;
|
||||||
final Step step;
|
final Step step;
|
||||||
|
|
||||||
|
static const _schemaReader = SchemaFromCreateTable(moorExtensions: true);
|
||||||
|
|
||||||
CreateTableReader(this.stmt, this.step);
|
CreateTableReader(this.stmt, this.step);
|
||||||
|
|
||||||
Future<MoorTable> extractTable(TypeMapper mapper) async {
|
Future<MoorTable> extractTable(TypeMapper mapper) async {
|
||||||
Table table;
|
Table table;
|
||||||
try {
|
try {
|
||||||
table = SchemaFromCreateTable(moorExtensions: true).read(stmt);
|
table = _schemaReader.read(stmt);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
step.reportError(ErrorInMoorFile(
|
step.reportError(ErrorInMoorFile(
|
||||||
span: stmt.tableNameToken.span,
|
span: stmt.tableNameToken.span,
|
||||||
|
|
|
@ -2,10 +2,10 @@ import 'package:moor_generator/moor_generator.dart';
|
||||||
import 'package:moor_generator/src/analyzer/errors.dart';
|
import 'package:moor_generator/src/analyzer/errors.dart';
|
||||||
import 'package:moor_generator/src/analyzer/runner/results.dart';
|
import 'package:moor_generator/src/analyzer/runner/results.dart';
|
||||||
import 'package:moor_generator/src/analyzer/runner/steps.dart';
|
import 'package:moor_generator/src/analyzer/runner/steps.dart';
|
||||||
import 'package:moor_generator/src/analyzer/sql_queries/affected_tables_visitor.dart';
|
|
||||||
import 'package:moor_generator/src/analyzer/sql_queries/lints/linter.dart';
|
import 'package:moor_generator/src/analyzer/sql_queries/lints/linter.dart';
|
||||||
import 'package:moor_generator/src/analyzer/sql_queries/query_analyzer.dart';
|
import 'package:moor_generator/src/analyzer/sql_queries/query_analyzer.dart';
|
||||||
import 'package:sqlparser/sqlparser.dart';
|
import 'package:sqlparser/sqlparser.dart';
|
||||||
|
import 'package:sqlparser/utils/find_referenced_tables.dart';
|
||||||
|
|
||||||
/// Handles `REFERENCES` clauses in tables by resolving their columns and
|
/// Handles `REFERENCES` clauses in tables by resolving their columns and
|
||||||
/// reporting errors if they don't exist. Further, sets the
|
/// reporting errors if they don't exist. Further, sets the
|
||||||
|
|
|
@ -3,8 +3,8 @@ import 'package:moor_generator/src/model/used_type_converter.dart';
|
||||||
import 'package:moor_generator/src/analyzer/sql_queries/type_mapping.dart';
|
import 'package:moor_generator/src/analyzer/sql_queries/type_mapping.dart';
|
||||||
import 'package:moor_generator/src/utils/type_converter_hint.dart';
|
import 'package:moor_generator/src/utils/type_converter_hint.dart';
|
||||||
import 'package:sqlparser/sqlparser.dart' hide ResultColumn;
|
import 'package:sqlparser/sqlparser.dart' hide ResultColumn;
|
||||||
|
import 'package:sqlparser/utils/find_referenced_tables.dart';
|
||||||
|
|
||||||
import 'affected_tables_visitor.dart';
|
|
||||||
import 'lints/linter.dart';
|
import 'lints/linter.dart';
|
||||||
|
|
||||||
/// Maps an [AnalysisContext] from the sqlparser to a [SqlQuery] from this
|
/// Maps an [AnalysisContext] from the sqlparser to a [SqlQuery] from this
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
import 'package:moor/moor.dart' as m;
|
||||||
import 'package:moor_generator/moor_generator.dart';
|
import 'package:moor_generator/moor_generator.dart';
|
||||||
import 'package:moor_generator/src/analyzer/sql_queries/affected_tables_visitor.dart';
|
|
||||||
import 'package:moor_generator/src/model/sql_query.dart';
|
import 'package:moor_generator/src/model/sql_query.dart';
|
||||||
import 'package:moor_generator/src/utils/type_converter_hint.dart';
|
import 'package:moor_generator/src/utils/type_converter_hint.dart';
|
||||||
import 'package:sqlparser/sqlparser.dart';
|
import 'package:sqlparser/sqlparser.dart';
|
||||||
|
import 'package:sqlparser/utils/find_referenced_tables.dart' as s;
|
||||||
|
|
||||||
/// Converts tables and types between the moor_generator and the sqlparser
|
/// Converts tables and types between the moor_generator and the sqlparser
|
||||||
/// library.
|
/// library.
|
||||||
|
@ -224,7 +225,13 @@ class TypeMapper {
|
||||||
return _engineTablesToSpecified[table];
|
return _engineTablesToSpecified[table];
|
||||||
}
|
}
|
||||||
|
|
||||||
WrittenMoorTable writtenToMoor(WrittenTable table) {
|
WrittenMoorTable writtenToMoor(s.TableWrite table) {
|
||||||
return WrittenMoorTable(tableToMoor(table.table), table.kind);
|
final moorKind = const {
|
||||||
|
s.UpdateKind.insert: m.UpdateKind.insert,
|
||||||
|
s.UpdateKind.update: m.UpdateKind.update,
|
||||||
|
s.UpdateKind.delete: m.UpdateKind.delete,
|
||||||
|
}[table.kind];
|
||||||
|
|
||||||
|
return WrittenMoorTable(tableToMoor(table.table), moorKind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: moor_generator
|
name: moor_generator
|
||||||
description: Dev-dependency to generate table and dataclasses together with the moor package.
|
description: Dev-dependency to generate table and dataclasses together with the moor package.
|
||||||
version: 3.0.0
|
version: 3.1.0-dev
|
||||||
repository: https://github.com/simolus3/moor
|
repository: https://github.com/simolus3/moor
|
||||||
homepage: https://moor.simonbinder.eu/
|
homepage: https://moor.simonbinder.eu/
|
||||||
issue_tracker: https://github.com/simolus3/moor/issues
|
issue_tracker: https://github.com/simolus3/moor/issues
|
||||||
|
@ -23,7 +23,7 @@ dependencies:
|
||||||
|
|
||||||
# Moor-specific analysis
|
# Moor-specific analysis
|
||||||
moor: ^3.0.0
|
moor: ^3.0.0
|
||||||
sqlparser: ^0.8.0
|
sqlparser: ^0.9.0
|
||||||
|
|
||||||
# Dart analysis
|
# Dart analysis
|
||||||
analyzer: '^0.39.0'
|
analyzer: '^0.39.0'
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
## 0.9.0
|
||||||
|
|
||||||
|
- New `package:sqlparser/utils/find_referenced_tables.dart` library. Use it to easily find all referenced tables
|
||||||
|
in a query.
|
||||||
|
|
||||||
## 0.8.1
|
## 0.8.1
|
||||||
|
|
||||||
- Support collate expressions in the new type inference ([#533](https://github.com/simolus3/moor/issues/533))
|
- Support collate expressions in the new type inference ([#533](https://github.com/simolus3/moor/issues/533))
|
||||||
|
|
|
@ -6,7 +6,7 @@ class SchemaFromCreateTable {
|
||||||
/// and `DATETIME` columns.
|
/// and `DATETIME` columns.
|
||||||
final bool moorExtensions;
|
final bool moorExtensions;
|
||||||
|
|
||||||
SchemaFromCreateTable({this.moorExtensions = false});
|
const SchemaFromCreateTable({this.moorExtensions = false});
|
||||||
|
|
||||||
Table read(TableInducingStatement stmt) {
|
Table read(TableInducingStatement stmt) {
|
||||||
if (stmt is CreateTableStatement) {
|
if (stmt is CreateTableStatement) {
|
||||||
|
|
|
@ -105,6 +105,10 @@ class AstPreparingVisitor extends RecursiveVisitor<void, void> {
|
||||||
// acts like a table for expressions in the same scope, so let's
|
// acts like a table for expressions in the same scope, so let's
|
||||||
// register it.
|
// register it.
|
||||||
if (table.as != null) {
|
if (table.as != null) {
|
||||||
|
// todo should we register a TableAlias instead? Some parts of this
|
||||||
|
// package and moor_generator might depend on this being a table
|
||||||
|
// directly (e.g. nested result sets in moor).
|
||||||
|
// Same for nested selects, joins and table-valued functions below.
|
||||||
scope.register(table.as, table);
|
scope.register(table.as, table);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:moor/moor.dart' show UpdateKind;
|
library utils.find_referenced_tables;
|
||||||
|
|
||||||
import 'package:sqlparser/sqlparser.dart';
|
import 'package:sqlparser/sqlparser.dart';
|
||||||
|
|
||||||
/// An AST-visitor that walks sql statements and finds all tables referenced in
|
/// An AST-visitor that walks sql statements and finds all tables referenced in
|
||||||
|
@ -40,11 +41,25 @@ class ReferencedTablesVisitor extends RecursiveVisitor<void, void> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WrittenTable {
|
enum UpdateKind { insert, update, delete }
|
||||||
|
|
||||||
|
/// A write to a table as found while analyzing a statement.
|
||||||
|
class TableWrite {
|
||||||
|
/// The table that a statement might write to when run.
|
||||||
final Table table;
|
final Table table;
|
||||||
|
|
||||||
|
/// What kind of update was found (e.g. insert, update or delete).
|
||||||
final UpdateKind kind;
|
final UpdateKind kind;
|
||||||
|
|
||||||
WrittenTable(this.table, this.kind);
|
TableWrite(this.table, this.kind);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => 37 * table.hashCode + kind.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return other is TableWrite && other.table == table && other.kind == kind;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds all tables that could be affected when executing a query. In
|
/// Finds all tables that could be affected when executing a query. In
|
||||||
|
@ -56,12 +71,12 @@ class UpdatedTablesVisitor extends ReferencedTablesVisitor {
|
||||||
/// Note that this is a subset of [foundTables], since an updating tables
|
/// Note that this is a subset of [foundTables], since an updating tables
|
||||||
/// could reference tables it's not updating (e.g. with `INSERT INTO foo
|
/// could reference tables it's not updating (e.g. with `INSERT INTO foo
|
||||||
/// SELECT * FROM bar`).
|
/// SELECT * FROM bar`).
|
||||||
final Set<WrittenTable> writtenTables = {};
|
final Set<TableWrite> writtenTables = {};
|
||||||
|
|
||||||
void _addIfResolved(ResolvesToResultSet r, UpdateKind kind) {
|
void _addIfResolved(ResolvesToResultSet r, UpdateKind kind) {
|
||||||
final resolved = _toTableOrNull(r);
|
final resolved = _toTableOrNull(r);
|
||||||
if (resolved != null) {
|
if (resolved != null) {
|
||||||
writtenTables.add(WrittenTable(resolved, kind));
|
writtenTables.add(TableWrite(resolved, kind));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,3 +98,34 @@ class UpdatedTablesVisitor extends ReferencedTablesVisitor {
|
||||||
visitChildren(e, arg);
|
visitChildren(e, arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds all writes to a table that occur anywhere inside the [root] node or a
|
||||||
|
/// descendant.
|
||||||
|
///
|
||||||
|
/// The [root] node must have all its references resolved. This means that using
|
||||||
|
/// a node obtained via [SqlEngine.parse] directly won't report meaningful
|
||||||
|
/// results. Instead, use [SqlEngine.analyze] or [SqlEngine.analyzeParsed].
|
||||||
|
///
|
||||||
|
/// If you want to find all referenced tables, use [findReferencedTables]. If
|
||||||
|
/// you want to find writes (including their [UpdateKind]) and referenced
|
||||||
|
/// tables, constrct a [UpdatedTablesVisitor] manually.
|
||||||
|
/// Then, let it [RecursiveVisitor.visit] the [root] node. You can now use
|
||||||
|
/// [UpdatedTablesVisitor.writtenTables] and
|
||||||
|
/// [ReferencedTablesVisitor.foundTables]. This will only walk the ast once,
|
||||||
|
/// whereas calling this and [findReferencedTables] will require two walks.
|
||||||
|
///
|
||||||
|
Set<TableWrite> findWrittenTables(AstNode root) {
|
||||||
|
return (UpdatedTablesVisitor()..visit(root, null)).writtenTables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds all tables referenced in [root] or a descendant.
|
||||||
|
///
|
||||||
|
/// The [root] node must have all its references resolved. This means that using
|
||||||
|
/// a node obtained via [SqlEngine.parse] directly won't report meaningful
|
||||||
|
/// results. Instead, use [SqlEngine.analyze] or [SqlEngine.analyzeParsed].
|
||||||
|
///
|
||||||
|
/// If you want to use both [findWrittenTables] and this on the same ast node,
|
||||||
|
/// follow the advice on [findWrittenTables] to only walk the ast once.
|
||||||
|
Set<Table> findReferencedTables(AstNode root) {
|
||||||
|
return (ReferencedTablesVisitor()..visit(root, null)).foundTables;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
name: sqlparser
|
name: sqlparser
|
||||||
description: Parses sqlite statements and performs static analysis on them
|
description: Parses sqlite statements and performs static analysis on them
|
||||||
version: 0.8.1
|
version: 0.9.0-dev
|
||||||
homepage: https://github.com/simolus3/moor/tree/develop/sqlparser
|
homepage: https://github.com/simolus3/moor/tree/develop/sqlparser
|
||||||
#homepage: https://moor.simonbinder.eu/
|
#homepage: https://moor.simonbinder.eu/
|
||||||
issue_tracker: https://github.com/simolus3/moor/issues
|
issue_tracker: https://github.com/simolus3/moor/issues
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:test/test.dart';
|
||||||
void main() {
|
void main() {
|
||||||
test('isAliasForRowId', () {
|
test('isAliasForRowId', () {
|
||||||
final engine = SqlEngine();
|
final engine = SqlEngine();
|
||||||
final schemaParser = SchemaFromCreateTable();
|
const schemaParser = SchemaFromCreateTable();
|
||||||
|
|
||||||
final isAlias = {
|
final isAlias = {
|
||||||
'CREATE TABLE x (id INTEGER PRIMARY KEY)': true,
|
'CREATE TABLE x (id INTEGER PRIMARY KEY)': true,
|
||||||
|
|
|
@ -36,7 +36,7 @@ const _affinityTests = {
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('affinity from typename', () {
|
test('affinity from typename', () {
|
||||||
final resolver = SchemaFromCreateTable();
|
const resolver = SchemaFromCreateTable();
|
||||||
|
|
||||||
_affinityTests.forEach((key, value) {
|
_affinityTests.forEach((key, value) {
|
||||||
expect(resolver.columnAffinity(key), equals(value),
|
expect(resolver.columnAffinity(key), equals(value),
|
||||||
|
@ -48,7 +48,8 @@ void main() {
|
||||||
final engine = SqlEngine();
|
final engine = SqlEngine();
|
||||||
final stmt = engine.parse(createTableStmt).rootNode;
|
final stmt = engine.parse(createTableStmt).rootNode;
|
||||||
|
|
||||||
final table = SchemaFromCreateTable().read(stmt as CreateTableStatement);
|
final table =
|
||||||
|
const SchemaFromCreateTable().read(stmt as CreateTableStatement);
|
||||||
|
|
||||||
expect(table.resolvedColumns.map((c) => c.name),
|
expect(table.resolvedColumns.map((c) => c.name),
|
||||||
['id', 'email', 'score', 'display_name']);
|
['id', 'email', 'score', 'display_name']);
|
||||||
|
@ -70,7 +71,7 @@ void main() {
|
||||||
)
|
)
|
||||||
''').rootNode;
|
''').rootNode;
|
||||||
|
|
||||||
final table = SchemaFromCreateTable(moorExtensions: true)
|
final table = const SchemaFromCreateTable(moorExtensions: true)
|
||||||
.read(stmt as CreateTableStatement);
|
.read(stmt as CreateTableStatement);
|
||||||
expect(table.resolvedColumns.map((c) => c.type), const [
|
expect(table.resolvedColumns.map((c) => c.type), const [
|
||||||
ResolvedType(type: BasicType.int, hint: IsBoolean(), nullable: true),
|
ResolvedType(type: BasicType.int, hint: IsBoolean(), nullable: true),
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:test/test.dart';
|
||||||
void main() {
|
void main() {
|
||||||
group('finds columns', () {
|
group('finds columns', () {
|
||||||
final engine = SqlEngine();
|
final engine = SqlEngine();
|
||||||
final schemaParser = SchemaFromCreateTable();
|
const schemaParser = SchemaFromCreateTable();
|
||||||
|
|
||||||
Column findWith(String createTbl, String columnName) {
|
Column findWith(String createTbl, String columnName) {
|
||||||
final stmt = engine.parse(createTbl).rootNode as CreateTableStatement;
|
final stmt = engine.parse(createTbl).rootNode as CreateTableStatement;
|
||||||
|
|
|
@ -11,8 +11,8 @@ void main() {
|
||||||
final result = engine.analyze('CREATE VIRTUAL TABLE foo USING '
|
final result = engine.analyze('CREATE VIRTUAL TABLE foo USING '
|
||||||
"fts5(bar , tokenize = 'porter ascii')");
|
"fts5(bar , tokenize = 'porter ascii')");
|
||||||
|
|
||||||
final table =
|
final table = const SchemaFromCreateTable()
|
||||||
SchemaFromCreateTable().read(result.root as TableInducingStatement);
|
.read(result.root as TableInducingStatement);
|
||||||
|
|
||||||
expect(table.name, 'foo');
|
expect(table.name, 'foo');
|
||||||
final columns = table.resultColumns;
|
final columns = table.resultColumns;
|
||||||
|
@ -24,8 +24,8 @@ void main() {
|
||||||
final result = engine
|
final result = engine
|
||||||
.analyze('CREATE VIRTUAL TABLE foo USING fts5(bar, baz UNINDEXED)');
|
.analyze('CREATE VIRTUAL TABLE foo USING fts5(bar, baz UNINDEXED)');
|
||||||
|
|
||||||
final table =
|
final table = const SchemaFromCreateTable()
|
||||||
SchemaFromCreateTable().read(result.root as TableInducingStatement);
|
.read(result.root as TableInducingStatement);
|
||||||
|
|
||||||
expect(table.name, 'foo');
|
expect(table.name, 'foo');
|
||||||
expect(table.resultColumns.map((c) => c.name), ['bar', 'baz']);
|
expect(table.resultColumns.map((c) => c.name), ['bar', 'baz']);
|
||||||
|
@ -39,7 +39,7 @@ void main() {
|
||||||
// add an fts5 table for the following queries
|
// add an fts5 table for the following queries
|
||||||
final fts5Result = engine.analyze('CREATE VIRTUAL TABLE foo USING '
|
final fts5Result = engine.analyze('CREATE VIRTUAL TABLE foo USING '
|
||||||
'fts5(bar, baz);');
|
'fts5(bar, baz);');
|
||||||
engine.registerTable(SchemaFromCreateTable()
|
engine.registerTable(const SchemaFromCreateTable()
|
||||||
.read(fts5Result.root as TableInducingStatement));
|
.read(fts5Result.root as TableInducingStatement));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ void main() {
|
||||||
// add an fts5 table for the following queries
|
// add an fts5 table for the following queries
|
||||||
final fts5Result = engine.analyze('CREATE VIRTUAL TABLE foo USING '
|
final fts5Result = engine.analyze('CREATE VIRTUAL TABLE foo USING '
|
||||||
'fts5(bar, baz);');
|
'fts5(bar, baz);');
|
||||||
engine.registerTable(SchemaFromCreateTable()
|
engine.registerTable(const SchemaFromCreateTable()
|
||||||
.read(fts5Result.root as TableInducingStatement));
|
.read(fts5Result.root as TableInducingStatement));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -129,11 +129,11 @@ void main() {
|
||||||
// add an fts5 table for the following queries
|
// add an fts5 table for the following queries
|
||||||
final fts5Result = engine.analyze('CREATE VIRTUAL TABLE foo USING '
|
final fts5Result = engine.analyze('CREATE VIRTUAL TABLE foo USING '
|
||||||
'fts5(bar, baz);');
|
'fts5(bar, baz);');
|
||||||
engine.registerTable(SchemaFromCreateTable()
|
engine.registerTable(const SchemaFromCreateTable()
|
||||||
.read(fts5Result.root as TableInducingStatement));
|
.read(fts5Result.root as TableInducingStatement));
|
||||||
|
|
||||||
final normalResult = engine.analyze('CREATE TABLE other (bar TEXT);');
|
final normalResult = engine.analyze('CREATE TABLE other (bar TEXT);');
|
||||||
engine.registerTable(SchemaFromCreateTable()
|
engine.registerTable(const SchemaFromCreateTable()
|
||||||
.read(normalResult.root as TableInducingStatement));
|
.read(normalResult.root as TableInducingStatement));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import 'package:sqlparser/sqlparser.dart';
|
||||||
|
import 'package:sqlparser/utils/find_referenced_tables.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
SqlEngine engine;
|
||||||
|
const schemaReader = SchemaFromCreateTable();
|
||||||
|
Table users, logins;
|
||||||
|
|
||||||
|
setUpAll(() {
|
||||||
|
engine = SqlEngine();
|
||||||
|
|
||||||
|
Table addTableFromStmt(String create) {
|
||||||
|
final parsed = engine.parse(create);
|
||||||
|
final table = schemaReader.read(parsed.rootNode as CreateTableStatement);
|
||||||
|
|
||||||
|
engine.registerTable(table);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
users = addTableFromStmt('''
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
);
|
||||||
|
''');
|
||||||
|
|
||||||
|
logins = addTableFromStmt('''
|
||||||
|
CREATE TABLE logins (
|
||||||
|
user INTEGER NOT NULL REFERENCES users (id),
|
||||||
|
timestamp INT
|
||||||
|
);
|
||||||
|
''');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('recognizes read tables', () {
|
||||||
|
final ctx = engine.analyze('SELECT * FROM logins INNER JOIN users u '
|
||||||
|
'ON u.id = logins.user;');
|
||||||
|
expect(findReferencedTables(ctx.root), {users, logins});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolves aliased tables', () {
|
||||||
|
final ctx = engine.analyze('''
|
||||||
|
CREATE TRIGGER foo AFTER INSERT ON users BEGIN
|
||||||
|
INSERT INTO logins (user, timestamp) VALUES (new.id, 0);
|
||||||
|
END;
|
||||||
|
''');
|
||||||
|
final body = (ctx.root as CreateTriggerStatement).action;
|
||||||
|
|
||||||
|
// Users referenced via "new" in body.
|
||||||
|
expect(findReferencedTables(body), contains(users));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('recognizes written tables', () {
|
||||||
|
final ctx = engine.analyze('INSERT INTO logins '
|
||||||
|
'SELECT id, CURRENT_TIME FROM users;');
|
||||||
|
expect(
|
||||||
|
findWrittenTables(ctx.root), {TableWrite(logins, UpdateKind.insert)});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue