mirror of https://github.com/AMT-Cheif/drift.git
Infer types for insert statements
This commit is contained in:
parent
2f8dc6d68e
commit
dd8b4ab03a
|
@ -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.
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -22,6 +22,14 @@ class ColumnResolver extends RecursiveVisitor<void> {
|
|||
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);
|
||||
|
|
|
@ -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<void> {
|
||||
final AnalysisContext context;
|
||||
TypeResolver get types => context.types;
|
||||
|
@ -19,4 +20,29 @@ class TypeResolvingVisitor extends RecursiveVisitor<void> {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -16,6 +16,15 @@ class InsertStatement extends Statement with CrudStatement {
|
|||
final List<Reference> targetColumns;
|
||||
final InsertSource source;
|
||||
|
||||
List<Column> 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(
|
||||
|
|
|
@ -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<Variable>().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<Variable>().iterator;
|
||||
|
|
Loading…
Reference in New Issue