add LIST parsing to the sqlparser

This commit is contained in:
Daniel Brauner 2022-01-17 15:43:36 +01:00
parent 0e331933af
commit ca8482be71
7 changed files with 121 additions and 0 deletions

View File

@ -320,6 +320,8 @@ class ColumnResolver extends RecursiveVisitor<void, void> {
});
if (target != null) resultColumn.resultSet = target.resultSet.resultSet;
} else if (resultColumn is NestedQueryColumn) {
_resolveSelect(resultColumn.select);
}
}

View File

@ -18,6 +18,7 @@ export 'moor/declared_statement.dart';
export 'moor/import_statement.dart';
export 'moor/inline_dart.dart';
export 'moor/moor_file.dart';
export 'moor/nested_query_column.dart';
export 'moor/nested_star_result_column.dart';
export 'node.dart';
export 'statements/block.dart';

View File

@ -0,0 +1,38 @@
import '../../../sqlparser.dart';
import '../ast.dart' show ResultColumn, Renamable;
import '../node.dart';
import '../visitor.dart';
import 'moor_file.dart';
/// A nested query column, denoted by `LIST(...)` in user queries.
///
/// Nested query columns take a select query and execute it for every result
/// returned from the main query. Nested query columns can only be added to a
/// top level select query, because the result of them can only be computed
/// in dart.
class NestedQueryColumn extends ResultColumn
implements MoorSpecificNode, Renamable, Referencable {
@override
final String? as;
SelectStatement select;
NestedQueryColumn({required this.select, this.as});
@override
Iterable<AstNode> get childNodes => [select];
@override
void transformChildren<A>(Transformer<A> transformer, A arg) {
select = transformer.transformChild(select, this, arg);
}
@override
R accept<A, R>(AstVisitor<A, R> visitor, A arg) {
return visitor.visitMoorSpecificNode(this, arg);
}
// idk is this required?
@override
bool get visibleToChildren => false;
}

View File

@ -1933,6 +1933,34 @@ class Parser {
_current = positionBefore;
}
// parsing for the nested query column
if (enableMoorExtensions && _matchOne(TokenType.list)) {
final list = _previous;
_consume(
TokenType.leftParen,
'Expected opening parenthesis after LIST',
);
// or _fullSelect but I don't think with support is required here because
// with statements can be added to the main select statement
final statement = select();
if (statement == null || statement is! SelectStatement) {
_error(
'Expected a select statement but found ${statement?.toString()}',
);
}
_consume(
TokenType.rightParen,
'Expected closing parenthesis to finish LIST expression',
);
final as = _as();
return NestedQueryColumn(select: statement, as: as?.identifier)
..setSpan(list, _previous);
}
final tokenBefore = _peek;
final expr = expression();

View File

@ -218,6 +218,7 @@ enum TokenType {
import,
json,
required,
list,
/// A `**` token. This is only scanned when scanning for moor tokens.
doubleStar,
@ -413,6 +414,7 @@ const Map<String, TokenType> moorKeywords = {
'JSON': TokenType.json,
'MAPPED': TokenType.mapped,
'REQUIRED': TokenType.required,
'LIST': TokenType.list,
};
/// A set of [TokenType]s that can be parsed as an identifier.

View File

@ -443,6 +443,12 @@ class EqualityEnforcingVisitor implements AstVisitor<void, void> {
_checkChildren(e);
}
void visitMoorNestedQueryColumn(NestedQueryColumn e, void arg) {
final current = _currentAs<NestedQueryColumn>(e);
_assert(current.as == e.as, e);
_checkChildren(e);
}
@override
void visitMoorSpecificNode(MoorSpecificNode e, void arg) {
if (e is DartPlaceholder) {
@ -459,6 +465,8 @@ class EqualityEnforcingVisitor implements AstVisitor<void, void> {
return visitMoorStatementParameter(e, arg);
} else if (e is MoorTableName) {
return visitMoorTableName(e, arg);
} else if (e is NestedQueryColumn) {
return visitMoorNestedQueryColumn(e, arg);
}
}

View File

@ -0,0 +1,42 @@
import 'package:sqlparser/sqlparser.dart';
import 'package:sqlparser/src/utils/ast_equality.dart';
import 'package:test/test.dart';
import '../utils.dart';
void main() {
test('parses nested query statements', () {
final stmt = SqlEngine(EngineOptions(useMoorExtensions: true))
.parse('SELECT LIST(SELECT * FROM test) FROM test')
.rootNode as SelectStatement;
enforceHasSpan(stmt);
return enforceEqual(
stmt.columns[0],
NestedQueryColumn(
select: SelectStatement(
columns: [StarResultColumn(null)],
from: TableReference('test'),
),
),
);
});
test('parses nested query statements with as', () {
final stmt = SqlEngine(EngineOptions(useMoorExtensions: true))
.parse('SELECT LIST(SELECT * FROM test) AS newname FROM test')
.rootNode as SelectStatement;
enforceHasSpan(stmt);
return enforceEqual(
stmt.columns[0],
NestedQueryColumn(
as: 'newname',
select: SelectStatement(
columns: [StarResultColumn(null)],
from: TableReference('test'),
),
),
);
});
}