diff --git a/sqlparser/README.md b/sqlparser/README.md index 3c9f23cf..728977b3 100644 --- a/sqlparser/README.md +++ b/sqlparser/README.md @@ -65,10 +65,10 @@ package to generate type-safe methods from sql. Most on this list is just not supported yet because I didn't found a use case for them yet. If you need them, just leave an issue and I'll try to implement them soon. -- For now, `INSERT` statements are not supported, but they will be soon. - Compound select statements (`UNION` / `INTERSECT`) are not supported yet - Common table expressions are not supported - Some advanced expressions, like `CAST`s aren't supported yet. +- An `UPSERT` clause is not yet supported on insert statements If you run into parsing errors with what you think is valid sql, please create an issue. diff --git a/sqlparser/lib/src/analysis/analysis.dart b/sqlparser/lib/src/analysis/analysis.dart index b52b5ea2..af770458 100644 --- a/sqlparser/lib/src/analysis/analysis.dart +++ b/sqlparser/lib/src/analysis/analysis.dart @@ -1,4 +1,4 @@ -import 'dart:math'; +import 'dart:math' show min, max; import 'package:meta/meta.dart'; import 'package:source_span/source_span.dart'; diff --git a/sqlparser/lib/src/analysis/steps/column_resolver.dart b/sqlparser/lib/src/analysis/steps/column_resolver.dart index 3d38d9c5..1f00ae02 100644 --- a/sqlparser/lib/src/analysis/steps/column_resolver.dart +++ b/sqlparser/lib/src/analysis/steps/column_resolver.dart @@ -22,6 +22,14 @@ class ColumnResolver extends RecursiveVisitor { visitChildren(e); } + @override + void visitInsertStatement(InsertStatement e) { + final table = _resolveTableReference(e.table); + visitChildren(e); + e.scope.availableColumns = table.resolvedColumns; + visitChildren(e); + } + @override void visitDeleteStatement(DeleteStatement e) { final table = _resolveTableReference(e.from); diff --git a/sqlparser/lib/src/analysis/steps/type_resolver.dart b/sqlparser/lib/src/analysis/steps/type_resolver.dart index 154040d9..987cce4b 100644 --- a/sqlparser/lib/src/analysis/steps/type_resolver.dart +++ b/sqlparser/lib/src/analysis/steps/type_resolver.dart @@ -1,7 +1,8 @@ part of '../analysis.dart'; -/// Resolves the type of columns in a select statement and the type of -/// expressions appearing in a select statement. +/// Resolves types for all nodes in the AST which can have a type. This includes +/// expressions, variables and so on. For select statements, we also try to +/// figure out what types they return. class TypeResolvingVisitor extends RecursiveVisitor { final AnalysisContext context; TypeResolver get types => context.types; @@ -19,4 +20,29 @@ class TypeResolvingVisitor extends RecursiveVisitor { super.visitChildren(e); } + + @override + void visitInsertStatement(InsertStatement e) { + // resolve target columns - this is easy, as we should have the table + // structure available. + e.targetColumns.forEach(types.resolveExpression); + + // if the insert statement has a VALUES source, we can now infer the type + // for those expressions by comparing with the target column. + if (e.source is ValuesSource) { + final targetTypes = e.resolvedTargetColumns.map(context.typeOf).toList(); + final source = e.source as ValuesSource; + + for (var tuple in source.values) { + final expressions = tuple.expressions; + for (var i = 0; i < min(expressions.length, targetTypes.length); i++) { + if (i < targetTypes.length) { + context.types.markResult(expressions[i], targetTypes[i]); + } + } + } + } + + visitChildren(e); + } } diff --git a/sqlparser/lib/src/analysis/types/resolver.dart b/sqlparser/lib/src/analysis/types/resolver.dart index 9ba8dab7..4a5cc35e 100644 --- a/sqlparser/lib/src/analysis/types/resolver.dart +++ b/sqlparser/lib/src/analysis/types/resolver.dart @@ -29,6 +29,11 @@ class TypeResolver { return calculated; } + /// Manually writes the [result] for the [Typeable] [t]. + void markResult(Typeable t, ResolveResult result) { + _results.putIfAbsent(t, () => result); + } + ResolveResult resolveOrInfer(Typeable t) { if (t is Column) { return resolveColumn(t); diff --git a/sqlparser/lib/src/ast/expressions/reference.dart b/sqlparser/lib/src/ast/expressions/reference.dart index bba0a6d4..66b94d73 100644 --- a/sqlparser/lib/src/ast/expressions/reference.dart +++ b/sqlparser/lib/src/ast/expressions/reference.dart @@ -11,6 +11,8 @@ class Reference extends Expression with ReferenceOwner { final String tableName; final String columnName; + Column get resolvedColumn => resolved as Column; + Reference({this.tableName, this.columnName}); @override diff --git a/sqlparser/lib/src/ast/statements/insert.dart b/sqlparser/lib/src/ast/statements/insert.dart index d7550fd9..f4385526 100644 --- a/sqlparser/lib/src/ast/statements/insert.dart +++ b/sqlparser/lib/src/ast/statements/insert.dart @@ -16,6 +16,15 @@ class InsertStatement extends Statement with CrudStatement { final List targetColumns; final InsertSource source; + List get resolvedTargetColumns { + if (targetColumns.isNotEmpty) { + return targetColumns.map((c) => c.resolvedColumn).toList(); + } else { + // no columns declared - assume all columns from the table + return table.resultSet?.resolvedColumns; + } + } + // todo parse upsert clauses InsertStatement( diff --git a/sqlparser/test/analysis/type_resolver_test.dart b/sqlparser/test/analysis/type_resolver_test.dart index f820e5f2..0e1a2f1d 100644 --- a/sqlparser/test/analysis/type_resolver_test.dart +++ b/sqlparser/test/analysis/type_resolver_test.dart @@ -46,6 +46,19 @@ void main() { }); }); + test('handles VALUES clause in insert statements', () { + final engine = SqlEngine()..registerTable(demoTable); + final context = engine.analyze('INSERT INTO demo VALUES (?, ?), (?, ?)'); + + final variables = + context.root.allDescendants.whereType().toList(); + + expect(context.typeOf(variables[0]), ResolveResult(id.type)); + expect(context.typeOf(variables[1]), ResolveResult(content.type)); + expect(context.typeOf(variables[2]), ResolveResult(id.type)); + expect(context.typeOf(variables[3]), ResolveResult(content.type)); + }); + test('handles nth_value', () { final ctx = SqlEngine().analyze("SELECT nth_value('string', ?1) = ?2"); final variables = ctx.root.allDescendants.whereType().iterator;