mirror of https://github.com/AMT-Cheif/drift.git
Better column name prediction for expressions
This commit is contained in:
parent
6d54a21091
commit
db92059610
|
@ -12,6 +12,7 @@ part 'steps/column_resolver.dart';
|
|||
part 'steps/reference_finder.dart';
|
||||
part 'steps/reference_resolver.dart';
|
||||
part 'steps/set_parent_visitor.dart';
|
||||
part 'steps/type_resolver.dart';
|
||||
|
||||
part 'types/data.dart';
|
||||
part 'types/resolution.dart';
|
||||
|
|
|
@ -3,8 +3,9 @@ part of 'analysis.dart';
|
|||
class AnalysisContext {
|
||||
final List<AnalysisError> errors = [];
|
||||
final AstNode root;
|
||||
final String sql;
|
||||
|
||||
AnalysisContext(this.root);
|
||||
AnalysisContext(this.root, this.sql);
|
||||
|
||||
void reportError(AnalysisError error) {
|
||||
errors.add(error);
|
||||
|
|
|
@ -75,8 +75,12 @@ class ColumnResolver extends RecursiveVisitor<void> {
|
|||
return (c.expression as Reference).columnName;
|
||||
}
|
||||
|
||||
// todo I think in this case it's just the literal lexeme?
|
||||
return 'TODO';
|
||||
// in this case it's just the literal expression. So for instance,
|
||||
// "SELECT 3+ 5" has a result column called "3+ 5" (consecutive whitespace
|
||||
// is removed).
|
||||
final span = context.sql.substring(c.firstPosition, c.lastPosition);
|
||||
// todo remove consecutive whitespace
|
||||
return span;
|
||||
}
|
||||
|
||||
void _resolveTableReference(TableReference r) {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
part of '../analysis.dart';
|
||||
|
||||
class TypeResolver extends RecursiveVisitor<void> {}
|
|
@ -24,6 +24,20 @@ abstract class AstNode {
|
|||
/// by the analyzer after the tree has been parsed.
|
||||
AstNode parent;
|
||||
|
||||
Token first;
|
||||
Token last;
|
||||
|
||||
/// The first index in the source that belongs to this node
|
||||
int get firstPosition => first.span.start.offset;
|
||||
|
||||
/// The last position that belongs to node, exclusive
|
||||
int get lastPosition => last.span.end.offset;
|
||||
|
||||
void setSpan(Token first, Token last) {
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
Iterable<AstNode> get parents sync* {
|
||||
var node = parent;
|
||||
while (node != null) {
|
||||
|
|
|
@ -44,7 +44,7 @@ class SqlEngine {
|
|||
final node = parse(sql);
|
||||
const SetParentVisitor().startAtRoot(node);
|
||||
|
||||
final context = AnalysisContext(node);
|
||||
final context = AnalysisContext(node, sql);
|
||||
final scope = _constructRootScope();
|
||||
|
||||
ReferenceFinder(globalScope: scope).start(node);
|
||||
|
|
|
@ -99,6 +99,7 @@ class Parser {
|
|||
/// https://www.sqlite.org/lang_select.html
|
||||
SelectStatement select() {
|
||||
if (!_match(const [TokenType.select])) return null;
|
||||
final selectToken = _previous;
|
||||
|
||||
var distinct = false;
|
||||
if (_matchOne(TokenType.distinct)) {
|
||||
|
@ -127,14 +128,14 @@ class Parser {
|
|||
groupBy: groupBy,
|
||||
orderBy: orderBy,
|
||||
limit: limit,
|
||||
);
|
||||
)..setSpan(selectToken, _previous);
|
||||
}
|
||||
|
||||
/// Parses a [ResultColumn] or throws if none is found.
|
||||
/// https://www.sqlite.org/syntax/result-column.html
|
||||
ResultColumn _resultColumn() {
|
||||
if (_match(const [TokenType.star])) {
|
||||
return StarResultColumn(null);
|
||||
return StarResultColumn(null)..setSpan(_previous, _previous);
|
||||
}
|
||||
|
||||
final positionBefore = _current;
|
||||
|
@ -146,7 +147,8 @@ class Parser {
|
|||
final identifier = _previous;
|
||||
|
||||
if (_match(const [TokenType.dot]) && _match(const [TokenType.star])) {
|
||||
return StarResultColumn((identifier as IdentifierToken).identifier);
|
||||
return StarResultColumn((identifier as IdentifierToken).identifier)
|
||||
..setSpan(identifier, _previous);
|
||||
}
|
||||
|
||||
// not a star result column. go back and parse the expression.
|
||||
|
@ -155,10 +157,13 @@ class Parser {
|
|||
_current = positionBefore;
|
||||
}
|
||||
|
||||
final tokenBefore = _peek;
|
||||
|
||||
final expr = expression();
|
||||
final as = _as();
|
||||
|
||||
return ExpressionResultColumn(expression: expr, as: as?.identifier);
|
||||
return ExpressionResultColumn(expression: expr, as: as?.identifier)
|
||||
..setSpan(tokenBefore, _previous);
|
||||
}
|
||||
|
||||
/// Returns an identifier followed after an optional "AS" token in sql.
|
||||
|
|
|
@ -21,13 +21,14 @@ void main() {
|
|||
);
|
||||
final engine = SqlEngine()..registerTable(demoTable);
|
||||
|
||||
final context = engine.analyze('SELECT id, d.content, * FROM demo AS d');
|
||||
final context =
|
||||
engine.analyze('SELECT id, d.content, *, 3 + 4 FROM demo AS d');
|
||||
|
||||
final select = context.root as SelectStatement;
|
||||
final resolvedColumns = select.resolvedColumns;
|
||||
|
||||
expect(
|
||||
resolvedColumns.map((c) => c.name), ['id', 'content', 'id', 'content']);
|
||||
expect(resolvedColumns.map((c) => c.name),
|
||||
['id', 'content', 'id', 'content', '3 + 4']);
|
||||
|
||||
final firstColumn = select.columns[0] as ExpressionResultColumn;
|
||||
final secondColumn = select.columns[1] as ExpressionResultColumn;
|
||||
|
|
Loading…
Reference in New Issue