mirror of https://github.com/AMT-Cheif/drift.git
Parse variable type hints in queries
This commit is contained in:
parent
412e8b4c83
commit
868dde358f
|
@ -197,6 +197,7 @@ abstract class AstVisitor<T> {
|
|||
T visitMoorFile(MoorFile e);
|
||||
T visitMoorImportStatement(ImportStatement e);
|
||||
T visitMoorDeclaredStatement(DeclaredStatement e);
|
||||
T visitMoorStatementParameter(StatementParameter e);
|
||||
T visitDartPlaceholder(DartPlaceholder e);
|
||||
}
|
||||
|
||||
|
@ -335,6 +336,9 @@ class RecursiveVisitor<T> extends AstVisitor<T> {
|
|||
@override
|
||||
T visitMoorDeclaredStatement(DeclaredStatement e) => visitChildren(e);
|
||||
|
||||
@override
|
||||
T visitMoorStatementParameter(StatementParameter e) => visitChildren(e);
|
||||
|
||||
@override
|
||||
T visitDartPlaceholder(DartPlaceholder e) => visitChildren(e);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ part of '../ast.dart';
|
|||
class DeclaredStatement extends Statement implements PartOfMoorFile {
|
||||
final DeclaredStatementIdentifier identifier;
|
||||
final CrudStatement statement;
|
||||
final List<StatementParameter> parameters;
|
||||
|
||||
Token colon;
|
||||
|
||||
|
@ -13,14 +14,15 @@ class DeclaredStatement extends Statement implements PartOfMoorFile {
|
|||
/// meaning.
|
||||
bool get isRegularQuery => identifier is SimpleName;
|
||||
|
||||
DeclaredStatement(this.identifier, this.statement);
|
||||
DeclaredStatement(this.identifier, this.statement, {this.parameters});
|
||||
|
||||
@override
|
||||
T accept<T>(AstVisitor<T> visitor) =>
|
||||
visitor.visitMoorDeclaredStatement(this);
|
||||
|
||||
@override
|
||||
Iterable<AstNode> get childNodes => [statement];
|
||||
Iterable<AstNode> get childNodes =>
|
||||
[statement, if (parameters != null) ...parameters];
|
||||
|
||||
@override
|
||||
bool contentEquals(DeclaredStatement other) {
|
||||
|
@ -77,3 +79,35 @@ class SpecialStatementIdentifier extends DeclaredStatementIdentifier {
|
|||
other.specialName == specialName);
|
||||
}
|
||||
}
|
||||
|
||||
/// A statement parameter, which appears between brackets after the statement
|
||||
/// identifier.
|
||||
/// In `selectString(:name AS TEXT): SELECT :name`, `:name AS TEXT` is a
|
||||
abstract class StatementParameter extends AstNode {
|
||||
@override
|
||||
T accept<T>(AstVisitor<T> visitor) {
|
||||
return visitor.visitMoorStatementParameter(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct to explicitly set a variable type.
|
||||
///
|
||||
/// Users can use `:name AS TYPE` as a statement parameter. Any use of `:name`
|
||||
/// in the query will then be resolved to the type set here. This is useful for
|
||||
/// cases in which the resolver doesn't yield acceptable results.
|
||||
class VariableTypeHint extends StatementParameter {
|
||||
final Variable variable;
|
||||
final String typeName;
|
||||
|
||||
Token as;
|
||||
|
||||
VariableTypeHint(this.variable, this.typeName);
|
||||
|
||||
@override
|
||||
Iterable<AstNode> get childNodes => [variable];
|
||||
|
||||
@override
|
||||
bool contentEquals(VariableTypeHint other) {
|
||||
return other.typeName == typeName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -311,6 +311,7 @@ mixin ExpressionParser on ParserBase {
|
|||
_error('Could not parse this expression');
|
||||
}
|
||||
|
||||
@override
|
||||
Variable _variableOrNull() {
|
||||
if (_matchOne(TokenType.questionMarkVariable)) {
|
||||
return NumberedVariable(_previous as QuestionMarkVariableToken)
|
||||
|
|
|
@ -162,6 +162,8 @@ abstract class ParserBase {
|
|||
// Common operations that we are referenced very often
|
||||
Expression expression();
|
||||
|
||||
List<Token> _typeName();
|
||||
|
||||
/// Parses a [Tuple]. If [orSubQuery] is set (defaults to false), a [SubQuery]
|
||||
/// (in brackets) will be accepted as well.
|
||||
Expression _consumeTuple({bool orSubQuery = false});
|
||||
|
@ -183,6 +185,7 @@ abstract class ParserBase {
|
|||
/// [s-d]: https://sqlite.org/syntax/select-stmt.html
|
||||
BaseSelectStatement _fullSelect();
|
||||
|
||||
Variable _variableOrNull();
|
||||
Literal _literalOrNull();
|
||||
OrderingMode _orderingModeOrNull();
|
||||
|
||||
|
@ -295,11 +298,40 @@ class Parser extends ParserBase
|
|||
return null;
|
||||
}
|
||||
|
||||
final parameters = <StatementParameter>[];
|
||||
if (_matchOne(TokenType.leftParen)) {
|
||||
do {
|
||||
final first = _peek;
|
||||
final variable = _variableOrNull();
|
||||
if (variable == null) {
|
||||
_error('Expected a variable here');
|
||||
}
|
||||
final as = _consume(TokenType.as, 'Expected AS followed by a type');
|
||||
|
||||
final typeNameTokens = _typeName();
|
||||
if (typeNameTokens == null) {
|
||||
_error('Expected a type name here');
|
||||
}
|
||||
|
||||
final typeName =
|
||||
typeNameTokens.first.span.expand(typeNameTokens.last.span).text;
|
||||
parameters.add(VariableTypeHint(variable, typeName)
|
||||
..as = as
|
||||
..setSpan(first, _previous));
|
||||
} while (_matchOne(TokenType.comma));
|
||||
|
||||
_consume(TokenType.rightParen, 'Expected closing parenthesis');
|
||||
}
|
||||
|
||||
final colon =
|
||||
_consume(TokenType.colon, 'Expected a colon (:) followed by a query');
|
||||
final stmt = _crud();
|
||||
|
||||
return DeclaredStatement(identifier, stmt)..colon = colon;
|
||||
return DeclaredStatement(
|
||||
identifier,
|
||||
stmt,
|
||||
parameters: parameters,
|
||||
)..colon = colon;
|
||||
}
|
||||
|
||||
/// Invokes [parser], sets the appropriate source span and attaches a
|
||||
|
|
|
@ -178,6 +178,7 @@ mixin SchemaParser on ParserBase {
|
|||
..nameToken = name;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Token> _typeName() {
|
||||
// sqlite doesn't really define what a type name is and has very loose rules
|
||||
// at turning them into a type affinity. We support this pattern:
|
||||
|
@ -199,7 +200,7 @@ mixin SchemaParser on ParserBase {
|
|||
}
|
||||
|
||||
_consume(TokenType.rightParen,
|
||||
'Expected closing paranthesis to finish type name');
|
||||
'Expected closing parenthesis to finish type name');
|
||||
typeNames.add(_previous);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ CREATE TABLE tbl (
|
|||
|
||||
all: SELECT /* COUNT(*), */ * FROM tbl WHERE $predicate;
|
||||
@special: SELECT * FROM tbl;
|
||||
typeHints(:foo AS TEXT): SELECT :foo;
|
||||
''';
|
||||
|
||||
void main() {
|
||||
|
@ -68,6 +69,24 @@ void main() {
|
|||
from: [TableReference('tbl', null)],
|
||||
),
|
||||
),
|
||||
DeclaredStatement(
|
||||
SimpleName('typeHints'),
|
||||
SelectStatement(columns: [
|
||||
ExpressionResultColumn(
|
||||
expression: ColonNamedVariable(
|
||||
ColonVariableToken(fakeSpan(':foo'), ':foo'),
|
||||
),
|
||||
),
|
||||
]),
|
||||
parameters: [
|
||||
VariableTypeHint(
|
||||
ColonNamedVariable(
|
||||
ColonVariableToken(fakeSpan(':foo'), ':foo'),
|
||||
),
|
||||
'TEXT',
|
||||
)
|
||||
],
|
||||
),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue