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.
|
||||
library sqlparser;
|
||||
|
||||
export 'src/analysis/analysis.dart';
|
||||
export 'src/ast/ast.dart';
|
||||
export 'src/engine/sql_engine.dart';
|
||||
export 'src/reader/tokenizer/token.dart';
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -17,7 +17,7 @@ abstract class ResultSet implements ResolvesToResultSet {
|
|||
}
|
||||
}
|
||||
|
||||
class Table with ResultSet implements ResolvesToResultSet {
|
||||
class Table with ResultSet {
|
||||
final String name;
|
||||
|
||||
@override
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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