mirror of https://github.com/AMT-Cheif/drift.git
Add simple tests for reference resolution
This commit is contained in:
parent
8b5539bb03
commit
62c20d0202
|
@ -3,6 +3,7 @@
|
||||||
/// More dartdocs go here.
|
/// More dartdocs go here.
|
||||||
library sqlparser;
|
library sqlparser;
|
||||||
|
|
||||||
|
export 'src/analysis/analysis.dart';
|
||||||
export 'src/ast/ast.dart';
|
export 'src/ast/ast.dart';
|
||||||
export 'src/engine/sql_engine.dart';
|
export 'src/engine/sql_engine.dart';
|
||||||
export 'src/reader/tokenizer/token.dart';
|
export 'src/reader/tokenizer/token.dart';
|
||||||
|
|
|
@ -2,6 +2,9 @@ part of 'analysis.dart';
|
||||||
|
|
||||||
class AnalysisContext {
|
class AnalysisContext {
|
||||||
final List<AnalysisError> errors = [];
|
final List<AnalysisError> errors = [];
|
||||||
|
final AstNode root;
|
||||||
|
|
||||||
|
AnalysisContext(this.root);
|
||||||
|
|
||||||
void reportError(AnalysisError error) {
|
void reportError(AnalysisError error) {
|
||||||
errors.add(error);
|
errors.add(error);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
part of '../analysis.dart';
|
part of '../analysis.dart';
|
||||||
|
|
||||||
// https://www.sqlite.org/lang_corefunc.html
|
// https://www.sqlite.org/lang_corefunc.html
|
||||||
final abs = SqlFunction('abs');
|
final abs = SqlFunction('ABS');
|
||||||
|
|
||||||
final coreFunctions = [
|
final coreFunctions = [
|
||||||
abs,
|
abs,
|
||||||
|
|
|
@ -23,7 +23,7 @@ class ReferenceScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
void register(String identifier, Referencable ref) {
|
void register(String identifier, Referencable ref) {
|
||||||
_references.putIfAbsent(identifier, () => []).add(ref);
|
_references.putIfAbsent(identifier.toUpperCase(), () => []).add(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves to a [Referencable] with the given [name] and of the type [T].
|
/// Resolves to a [Referencable] with the given [name] and of the type [T].
|
||||||
|
@ -51,7 +51,8 @@ class ReferenceScope {
|
||||||
final collected = <T>[];
|
final collected = <T>[];
|
||||||
|
|
||||||
while (scope != null) {
|
while (scope != null) {
|
||||||
collected.addAll(scope._references.values.whereType<T>());
|
collected.addAll(
|
||||||
|
scope._references.values.expand((list) => list).whereType<T>());
|
||||||
scope = scope.parent;
|
scope = scope.parent;
|
||||||
}
|
}
|
||||||
return collected;
|
return collected;
|
||||||
|
|
|
@ -17,7 +17,7 @@ abstract class ResultSet implements ResolvesToResultSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Table with ResultSet implements ResolvesToResultSet {
|
class Table with ResultSet {
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -39,7 +39,7 @@ class ReferenceResolver extends NoopVisitor<void> {
|
||||||
final columns = tables
|
final columns = tables
|
||||||
.map((t) => t.resultSet.findColumn(e.columnName))
|
.map((t) => t.resultSet.findColumn(e.columnName))
|
||||||
.where((c) => c != null)
|
.where((c) => c != null)
|
||||||
.toList();
|
.toSet();
|
||||||
|
|
||||||
if (columns.isEmpty) {
|
if (columns.isEmpty) {
|
||||||
context.reportError(AnalysisError(
|
context.reportError(AnalysisError(
|
||||||
|
|
|
@ -7,6 +7,28 @@ class SqlEngine {
|
||||||
final List<Table> knownTables = [];
|
final List<Table> knownTables = [];
|
||||||
final List<SqlFunction> knownFunctions = [];
|
final List<SqlFunction> knownFunctions = [];
|
||||||
|
|
||||||
|
SqlEngine({bool includeDefaults = true}) {
|
||||||
|
if (includeDefaults) {
|
||||||
|
knownFunctions.addAll(coreFunctions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerTable(Table table) {
|
||||||
|
knownTables.add(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReferenceScope _constructRootScope() {
|
||||||
|
final scope = ReferenceScope(null);
|
||||||
|
for (var table in knownTables) {
|
||||||
|
scope.register(table.name, table);
|
||||||
|
}
|
||||||
|
for (var function in knownFunctions) {
|
||||||
|
scope.register(function.name, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses the [sql] statement. At the moment, only SELECT statements are
|
/// Parses the [sql] statement. At the moment, only SELECT statements are
|
||||||
/// supported.
|
/// supported.
|
||||||
AstNode parse(String sql) {
|
AstNode parse(String sql) {
|
||||||
|
@ -17,4 +39,14 @@ class SqlEngine {
|
||||||
final parser = Parser(tokens);
|
final parser = Parser(tokens);
|
||||||
return parser.select();
|
return parser.select();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AnalysisContext analyze(String sql) {
|
||||||
|
final node = parse(sql);
|
||||||
|
final context = AnalysisContext(node);
|
||||||
|
final scope = _constructRootScope();
|
||||||
|
|
||||||
|
node.accept(ReferenceResolver(scope, context));
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'package:sqlparser/sqlparser.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('resolves simple functions', () {
|
||||||
|
final context = SqlEngine().analyze('SELECT ABS(-3)');
|
||||||
|
|
||||||
|
final select = context.root as SelectStatement;
|
||||||
|
final column = select.columns.single as ExpressionResultColumn;
|
||||||
|
|
||||||
|
expect((column.expression as FunctionExpression).resolved, abs);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolves table names and aliases', () {
|
||||||
|
final id = Column('id');
|
||||||
|
final content = Column('content');
|
||||||
|
|
||||||
|
final demoTable = Table(
|
||||||
|
name: 'demo',
|
||||||
|
resolvedColumns: [id, content],
|
||||||
|
);
|
||||||
|
final engine = SqlEngine()..registerTable(demoTable);
|
||||||
|
|
||||||
|
final context = engine.analyze('SELECT id, d.content FROM demo AS d');
|
||||||
|
|
||||||
|
final select = context.root as SelectStatement;
|
||||||
|
final firstColumn = select.columns[0] as ExpressionResultColumn;
|
||||||
|
final secondColumn = select.columns[1] as ExpressionResultColumn;
|
||||||
|
final from = select.from[0] as TableReference;
|
||||||
|
|
||||||
|
expect((firstColumn.expression as Reference).resolved, id);
|
||||||
|
expect((secondColumn.expression as Reference).resolved, content);
|
||||||
|
expect(from.resolved, demoTable);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue