From d2b70e69dc927df5561036ebf49c325519cc9d8d Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 25 Dec 2019 21:07:08 +0100 Subject: [PATCH] Support explicit type arguments in moor_generator --- moor_generator/CHANGELOG.md | 6 ++++ .../src/analyzer/sql_queries/sql_parser.dart | 29 ++++++++++++++++-- moor_generator/lib/src/model/sql_query.dart | 8 ++--- .../integration/dao_inheritance_test.dart | 2 +- .../integration/integration_test.dart | 2 +- .../analyzer/sql_queries/sql_parser_test.dart | 30 +++++++++++++++++++ .../analyzer/{integration => }/utils.dart | 2 +- .../lib/src/ast/statements/create_table.dart | 3 +- .../lib/src/ast/statements/statement.dart | 4 +-- sqlparser/lib/src/engine/sql_engine.dart | 12 ++++++++ 10 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 moor_generator/test/analyzer/sql_queries/sql_parser_test.dart rename moor_generator/test/analyzer/{integration => }/utils.dart (95%) diff --git a/moor_generator/CHANGELOG.md b/moor_generator/CHANGELOG.md index ab3241ee..61925191 100644 --- a/moor_generator/CHANGELOG.md +++ b/moor_generator/CHANGELOG.md @@ -1,3 +1,9 @@ +## unreleased + +- Support explicit type arguments for queries in moor files. In + `foo(:bar AS TEXT, :baz AS INT): SELECT :bar, :baz;`, the column type can now be inferred. + Previously, the query would fail because of an unknown type. + ## 2.2.0 - Experimental new CLI tool (`pub run moor_generator`). Not useful at the moment diff --git a/moor_generator/lib/src/analyzer/sql_queries/sql_parser.dart b/moor_generator/lib/src/analyzer/sql_queries/sql_parser.dart index 7d9ddc5a..b931d769 100644 --- a/moor_generator/lib/src/analyzer/sql_queries/sql_parser.dart +++ b/moor_generator/lib/src/analyzer/sql_queries/sql_parser.dart @@ -40,8 +40,11 @@ class SqlParser { final sql = query.sql; context = _engine.analyze(sql); } else if (query is DeclaredMoorQuery) { - context = - _engine.analyzeNode(query.query, query.file.parseResult.sql); + context = _engine.analyzeNode( + query.query, + query.file.parseResult.sql, + stmtOptions: _createOptions(query.astNode), + ); declaredInMoor = true; } } catch (e, s) { @@ -89,4 +92,26 @@ class SqlParser { )); } } + + AnalyzeStatementOptions _createOptions(DeclaredStatement stmt) { + final reader = _engine.schemaReader; + final indexedHints = {}; + final namedHints = {}; + + for (final hint in stmt.parameters.whereType()) { + final variable = hint.variable; + final type = reader.resolveColumnType(hint.typeName); + + if (variable is ColonNamedVariable) { + namedHints[variable.name] = type; + } else if (variable is NumberedVariable) { + indexedHints[variable.resolvedIndex] = type; + } + } + + return AnalyzeStatementOptions( + indexedVariableTypes: indexedHints, + namedVariableTypes: namedHints, + ); + } } diff --git a/moor_generator/lib/src/model/sql_query.dart b/moor_generator/lib/src/model/sql_query.dart index ec32c291..e87be5ee 100644 --- a/moor_generator/lib/src/model/sql_query.dart +++ b/moor_generator/lib/src/model/sql_query.dart @@ -35,16 +35,16 @@ class DeclaredDartQuery extends DeclaredQuery { /// A [DeclaredQuery] read from a `.moor` file, where the AST is already /// available. class DeclaredMoorQuery extends DeclaredQuery { - final AstNode query; + final DeclaredStatement astNode; + CrudStatement get query => astNode.statement; ParsedMoorFile file; - DeclaredMoorQuery(String name, this.query) : super(name); + DeclaredMoorQuery(String name, this.astNode) : super(name); factory DeclaredMoorQuery.fromStatement(DeclaredStatement stmt) { assert(stmt.identifier is SimpleName); final name = (stmt.identifier as SimpleName).name; - final query = stmt.statement; - return DeclaredMoorQuery(name, query); + return DeclaredMoorQuery(name, stmt); } } diff --git a/moor_generator/test/analyzer/integration/dao_inheritance_test.dart b/moor_generator/test/analyzer/integration/dao_inheritance_test.dart index ba5f45d4..0436cbd9 100644 --- a/moor_generator/test/analyzer/integration/dao_inheritance_test.dart +++ b/moor_generator/test/analyzer/integration/dao_inheritance_test.dart @@ -2,7 +2,7 @@ import 'package:moor_generator/src/analyzer/runner/results.dart'; import 'package:test/test.dart'; -import 'utils.dart'; +import '../utils.dart'; void main() { test('supports inheritance for daos', () async { diff --git a/moor_generator/test/analyzer/integration/integration_test.dart b/moor_generator/test/analyzer/integration/integration_test.dart index da520837..fc1125a4 100644 --- a/moor_generator/test/analyzer/integration/integration_test.dart +++ b/moor_generator/test/analyzer/integration/integration_test.dart @@ -5,7 +5,7 @@ import 'package:moor_generator/src/analyzer/runner/results.dart'; import 'package:moor_generator/src/model/sql_query.dart'; import 'package:test/test.dart'; -import 'utils.dart'; +import '../utils.dart'; void main() { TestState state; diff --git a/moor_generator/test/analyzer/sql_queries/sql_parser_test.dart b/moor_generator/test/analyzer/sql_queries/sql_parser_test.dart new file mode 100644 index 00000000..2c6d4f98 --- /dev/null +++ b/moor_generator/test/analyzer/sql_queries/sql_parser_test.dart @@ -0,0 +1,30 @@ +import 'package:moor_generator/moor_generator.dart'; +import 'package:moor_generator/src/analyzer/runner/results.dart'; +import 'package:test/test.dart'; + +import '../utils.dart'; + +void main() { + test('respects explicit type arguments', () async { + final state = TestState.withContent({ + 'foo|lib/main.moor': ''' +bar(?1 AS TEXT, :foo AS BOOLEAN): SELECT ?, :foo; + ''', + }); + + await state.runTask('package:foo/main.moor'); + final file = state.file('package:foo/main.moor'); + + expect(file.errors.errors, isEmpty); + final content = file.currentResult as ParsedMoorFile; + + final query = content.resolvedQueries.single; + expect(query, const TypeMatcher()); + + final resultSet = (query as SqlSelectQuery).resultSet; + expect(resultSet.matchingTable, isNull); + expect(resultSet.columns.map((c) => c.name), ['?', ':foo']); + expect(resultSet.columns.map((c) => c.type), + [ColumnType.text, ColumnType.boolean]); + }); +} diff --git a/moor_generator/test/analyzer/integration/utils.dart b/moor_generator/test/analyzer/utils.dart similarity index 95% rename from moor_generator/test/analyzer/integration/utils.dart rename to moor_generator/test/analyzer/utils.dart index b32f6b09..040d6dbc 100644 --- a/moor_generator/test/analyzer/integration/utils.dart +++ b/moor_generator/test/analyzer/utils.dart @@ -3,7 +3,7 @@ import 'package:moor_generator/src/analyzer/runner/file_graph.dart'; import 'package:moor_generator/src/analyzer/runner/task.dart'; import 'package:moor_generator/src/analyzer/session.dart'; -import '../../utils/test_backend.dart'; +import '../utils/test_backend.dart'; class TestState { TestBackend backend; diff --git a/sqlparser/lib/src/ast/statements/create_table.dart b/sqlparser/lib/src/ast/statements/create_table.dart index e5ad3e04..19e3e1d2 100644 --- a/sqlparser/lib/src/ast/statements/create_table.dart +++ b/sqlparser/lib/src/ast/statements/create_table.dart @@ -1,8 +1,7 @@ part of '../ast.dart'; abstract class TableInducingStatement extends Statement - with SchemaStatement - implements PartOfMoorFile { + implements PartOfMoorFile, SchemaStatement { final bool ifNotExists; final String tableName; diff --git a/sqlparser/lib/src/ast/statements/statement.dart b/sqlparser/lib/src/ast/statements/statement.dart index ef498e57..6c6008f5 100644 --- a/sqlparser/lib/src/ast/statements/statement.dart +++ b/sqlparser/lib/src/ast/statements/statement.dart @@ -18,5 +18,5 @@ abstract class HasWhereClause extends Statement { Expression get where; } -/// Marker mixin for statements that change the table structure. -mixin SchemaStatement on Statement {} +/// Marker interface for statements that change the table structure. +abstract class SchemaStatement extends Statement {} diff --git a/sqlparser/lib/src/engine/sql_engine.dart b/sqlparser/lib/src/engine/sql_engine.dart index 488ddc8e..a41d99d4 100644 --- a/sqlparser/lib/src/engine/sql_engine.dart +++ b/sqlparser/lib/src/engine/sql_engine.dart @@ -18,6 +18,8 @@ class SqlEngine { /// Internal options for this sql engine. final EngineOptions options; + SchemaFromCreateTable _schemaReader; + SqlEngine( {bool useMoorExtensions = false, bool enableJson1Module = false, @@ -34,6 +36,16 @@ class SqlEngine { registerTable(sqliteSequence); } + /// Obtain a [SchemaFromCreateTable] instance compatible with the + /// configuration of this engine. + /// + /// The returned reader can be used to read the table structure from a + /// [TableInducingStatement] by using [SchemaFromCreateTable.read]. + SchemaFromCreateTable get schemaReader { + return _schemaReader ??= + SchemaFromCreateTable(moorExtensions: options.useMoorExtensions); + } + /// Registers the [table], which means that it can later be used in sql /// statements. void registerTable(Table table) {