Add simple tests for reference resolution

This commit is contained in:
Simon Binder 2019-06-23 16:35:28 +02:00
parent 8b5539bb03
commit 62c20d0202
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
8 changed files with 77 additions and 5 deletions

View File

@ -3,6 +3,7 @@
/// More dartdocs go here.
library sqlparser;
export 'src/analysis/analysis.dart';
export 'src/ast/ast.dart';
export 'src/engine/sql_engine.dart';
export 'src/reader/tokenizer/token.dart';

View File

@ -2,6 +2,9 @@ part of 'analysis.dart';
class AnalysisContext {
final List<AnalysisError> errors = [];
final AstNode root;
AnalysisContext(this.root);
void reportError(AnalysisError error) {
errors.add(error);

View File

@ -1,7 +1,7 @@
part of '../analysis.dart';
// https://www.sqlite.org/lang_corefunc.html
final abs = SqlFunction('abs');
final abs = SqlFunction('ABS');
final coreFunctions = [
abs,

View File

@ -23,7 +23,7 @@ class ReferenceScope {
}
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].
@ -51,7 +51,8 @@ class ReferenceScope {
final collected = <T>[];
while (scope != null) {
collected.addAll(scope._references.values.whereType<T>());
collected.addAll(
scope._references.values.expand((list) => list).whereType<T>());
scope = scope.parent;
}
return collected;

View File

@ -17,7 +17,7 @@ abstract class ResultSet implements ResolvesToResultSet {
}
}
class Table with ResultSet implements ResolvesToResultSet {
class Table with ResultSet {
final String name;
@override

View File

@ -39,7 +39,7 @@ class ReferenceResolver extends NoopVisitor<void> {
final columns = tables
.map((t) => t.resultSet.findColumn(e.columnName))
.where((c) => c != null)
.toList();
.toSet();
if (columns.isEmpty) {
context.reportError(AnalysisError(

View File

@ -7,6 +7,28 @@ class SqlEngine {
final List<Table> knownTables = [];
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
/// supported.
AstNode parse(String sql) {
@ -17,4 +39,14 @@ class SqlEngine {
final parser = Parser(tokens);
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;
}
}

View File

@ -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);
});
}