mirror of https://github.com/AMT-Cheif/drift.git
Parse Dart placeholders based on their context
This commit is contained in:
parent
1d7b4d01fe
commit
f171098789
|
@ -193,7 +193,7 @@ abstract class AstVisitor<T> {
|
|||
T visitMoorFile(MoorFile e);
|
||||
T visitMoorImportStatement(ImportStatement e);
|
||||
T visitMoorDeclaredStatement(DeclaredStatement e);
|
||||
T visitInlineDartCode(InlineDart e);
|
||||
T visitDartPlaceholder(DartPlaceholder e);
|
||||
}
|
||||
|
||||
/// Visitor that walks down the entire tree, visiting all children in order.
|
||||
|
@ -316,7 +316,7 @@ class RecursiveVisitor<T> extends AstVisitor<T> {
|
|||
T visitMoorDeclaredStatement(DeclaredStatement e) => visitChildren(e);
|
||||
|
||||
@override
|
||||
T visitInlineDartCode(InlineDart e) => visitChildren(e);
|
||||
T visitDartPlaceholder(DartPlaceholder e) => visitChildren(e);
|
||||
|
||||
@protected
|
||||
T visitChildren(AstNode e) {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
part of '../ast.dart';
|
||||
|
||||
/// Base for limit statements. Without moor extensions, only [Limit] will be
|
||||
/// parsed. With moor extensions, a [DartLimitPlaceholder] can be emitted as
|
||||
/// well.
|
||||
abstract class LimitBase implements AstNode {}
|
||||
|
||||
class Limit extends AstNode implements LimitBase {
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
part of '../ast.dart';
|
||||
|
||||
class OrderBy extends AstNode {
|
||||
final List<OrderingTerm> terms;
|
||||
/// Base for `ORDER BY` clauses. Without moor extensions, ony [OrderBy] will be
|
||||
/// parsed. Otherwise, [DartOrderByPlaceholder] can be parsed as well.
|
||||
abstract class OrderByBase extends AstNode {}
|
||||
|
||||
/// Base for a single ordering term that is a part of a [OrderBy]. Without moor
|
||||
/// extensions, only [OrderingTerm] will be parsed. With moor extensions, a
|
||||
/// [DartOrderingTermPlaceholder] can be parsed as well.
|
||||
abstract class OrderingTermBase extends AstNode {}
|
||||
|
||||
class OrderBy extends AstNode implements OrderByBase {
|
||||
final List<OrderingTermBase> terms;
|
||||
|
||||
OrderBy({this.terms});
|
||||
|
||||
|
@ -19,7 +28,7 @@ class OrderBy extends AstNode {
|
|||
|
||||
enum OrderingMode { ascending, descending }
|
||||
|
||||
class OrderingTerm extends AstNode {
|
||||
class OrderingTerm extends AstNode implements OrderingTermBase {
|
||||
final Expression expression;
|
||||
final OrderingMode orderingMode;
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ class NamedWindowDeclaration with Referencable {
|
|||
class WindowDefinition extends AstNode {
|
||||
final String baseWindowName;
|
||||
final List<Expression> partitionBy;
|
||||
final OrderBy orderBy;
|
||||
final OrderByBase orderBy;
|
||||
final FrameSpec frameSpec;
|
||||
|
||||
WindowDefinition(
|
||||
|
|
|
@ -8,40 +8,45 @@ part of '../ast.dart';
|
|||
/// 1. expressions: Any expression can be used for moor: `SELECT * FROM table
|
||||
/// = $expr`. Generated code will write this as an `Expression` class from
|
||||
/// moor.
|
||||
/// 2. limits
|
||||
/// 3. A single order-by clause
|
||||
/// 4. A list of order-by clauses
|
||||
abstract class InlineDart extends AstNode {
|
||||
/// 2. limits, which will be exposed as a `Limit` component from moor
|
||||
/// 3. A single order-by clause, which will be exposed as a `OrderingTerm` from
|
||||
/// moor.
|
||||
/// 4. A list of order-by clauses, which will be exposed as a `OrderBy` from
|
||||
/// moor.
|
||||
abstract class DartPlaceholder extends AstNode {
|
||||
final String name;
|
||||
|
||||
DollarSignVariableToken token;
|
||||
|
||||
InlineDart._(this.name);
|
||||
DartPlaceholder._(this.name);
|
||||
|
||||
@override
|
||||
final Iterable<AstNode> childNodes = const Iterable.empty();
|
||||
|
||||
@override
|
||||
T accept<T>(AstVisitor<T> visitor) => visitor.visitInlineDartCode(this);
|
||||
T accept<T>(AstVisitor<T> visitor) => visitor.visitDartPlaceholder(this);
|
||||
|
||||
bool _dartEquals(covariant InlineDart other);
|
||||
bool _dartEquals(covariant DartPlaceholder other) => true;
|
||||
|
||||
@override
|
||||
bool contentEquals(InlineDart other) {
|
||||
bool contentEquals(DartPlaceholder other) {
|
||||
return other.name == name && other._dartEquals(other);
|
||||
}
|
||||
}
|
||||
|
||||
class InlineDartExpression extends InlineDart implements Expression {
|
||||
InlineDartExpression({@required String name}) : super._(name);
|
||||
|
||||
@override
|
||||
bool _dartEquals(InlineDartExpression other) => true;
|
||||
class DartExpressionPlaceholder extends DartPlaceholder implements Expression {
|
||||
DartExpressionPlaceholder({@required String name}) : super._(name);
|
||||
}
|
||||
|
||||
class InlineDartLimit extends InlineDart implements LimitBase {
|
||||
InlineDartLimit({@required String name}) : super._(name);
|
||||
|
||||
@override
|
||||
bool _dartEquals(InlineDartLimit other) => true;
|
||||
class DartLimitPlaceholder extends DartPlaceholder implements LimitBase {
|
||||
DartLimitPlaceholder({@required String name}) : super._(name);
|
||||
}
|
||||
|
||||
class DartOrderingTermPlaceholder extends DartPlaceholder
|
||||
implements OrderingTermBase {
|
||||
DartOrderingTermPlaceholder({@required String name}) : super._(name);
|
||||
}
|
||||
|
||||
class DartOrderByPlaceholder extends DartPlaceholder implements OrderByBase {
|
||||
DartOrderByPlaceholder({@required String name}) : super._(name);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ class SelectStatement extends Statement with CrudStatement, ResultSet {
|
|||
final GroupBy groupBy;
|
||||
final List<NamedWindowDeclaration> windowDeclarations;
|
||||
|
||||
final OrderBy orderBy;
|
||||
final OrderByBase orderBy;
|
||||
final LimitBase limit;
|
||||
|
||||
/// The resolved list of columns returned by this select statements. Not
|
||||
|
|
|
@ -272,22 +272,41 @@ mixin CrudParser on ParserBase {
|
|||
return declarations;
|
||||
}
|
||||
|
||||
OrderBy _orderBy() {
|
||||
OrderByBase _orderBy() {
|
||||
if (_matchOne(TokenType.order)) {
|
||||
_consume(TokenType.by, 'Expected "BY" after "ORDER" token');
|
||||
final terms = <OrderingTerm>[];
|
||||
final terms = <OrderingTermBase>[];
|
||||
do {
|
||||
terms.add(_orderingTerm());
|
||||
} while (_matchOne(TokenType.comma));
|
||||
|
||||
// If we only hit a single ordering term and that term is a Dart
|
||||
// placeholder, we can upgrade that term to a full order by placeholder.
|
||||
// This gives users more control at runtime (they can specify multiple
|
||||
// terms).
|
||||
if (terms.length == 1 && terms.single is DartOrderingTermPlaceholder) {
|
||||
final termPlaceholder = terms.single as DartOrderingTermPlaceholder;
|
||||
return DartOrderByPlaceholder(name: termPlaceholder.name);
|
||||
}
|
||||
|
||||
return OrderBy(terms: terms);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
OrderingTerm _orderingTerm() {
|
||||
OrderingTermBase _orderingTerm() {
|
||||
final expr = expression();
|
||||
final mode = _orderingModeOrNull();
|
||||
|
||||
return OrderingTerm(expression: expr, orderingMode: _orderingModeOrNull());
|
||||
// if there is no ASC or DESC after a Dart placeholder, we can upgrade the
|
||||
// expression to an ordering term placeholder and let users define the mode
|
||||
// at runtime.
|
||||
if (mode == null && expr is DartExpressionPlaceholder) {
|
||||
return DartOrderingTermPlaceholder(name: expr.name)
|
||||
..setSpan(expr.first, expr.last);
|
||||
}
|
||||
|
||||
return OrderingTerm(expression: expr, orderingMode: mode);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -326,8 +345,8 @@ mixin CrudParser on ParserBase {
|
|||
// no offset or comma was parsed (so just LIMIT $expr). In that case, we
|
||||
// want to provide additional flexibility to the user by interpreting the
|
||||
// expression as a whole limit clause.
|
||||
if (first is InlineDartExpression) {
|
||||
return InlineDartLimit(name: first.name)
|
||||
if (first is DartExpressionPlaceholder) {
|
||||
return DartLimitPlaceholder(name: first.name)
|
||||
..setSpan(limitToken, _previous);
|
||||
}
|
||||
return Limit(count: first)..setSpan(limitToken, _previous);
|
||||
|
@ -457,7 +476,7 @@ mixin CrudParser on ParserBase {
|
|||
final leftParen = _previous;
|
||||
|
||||
String baseWindowName;
|
||||
OrderBy orderBy;
|
||||
OrderByBase orderBy;
|
||||
|
||||
final partitionBy = <Expression>[];
|
||||
if (_matchOne(TokenType.identifier)) {
|
||||
|
|
|
@ -322,7 +322,7 @@ mixin ExpressionParser on ParserBase {
|
|||
case TokenType.dollarSignVariable:
|
||||
if (enableMoorExtensions) {
|
||||
final typedToken = token as DollarSignVariableToken;
|
||||
return InlineDartExpression(name: typedToken.name)
|
||||
return DartExpressionPlaceholder(name: typedToken.name)
|
||||
..token = typedToken
|
||||
..setSpan(token, token);
|
||||
}
|
||||
|
|
|
@ -46,8 +46,9 @@ void main() {
|
|||
expect(context.errors, isEmpty);
|
||||
|
||||
final select = context.root as SelectStatement;
|
||||
final orderingTerm = select.orderBy.terms.single.expression as Reference;
|
||||
final resolved = orderingTerm.resolved as ExpressionColumn;
|
||||
final term = (select.orderBy as OrderBy).terms.single as OrderingTerm;
|
||||
final expression = term.expression as Reference;
|
||||
final resolved = expression.resolved as ExpressionColumn;
|
||||
|
||||
enforceEqual(
|
||||
resolved.expression,
|
||||
|
|
|
@ -10,20 +10,20 @@ void main() {
|
|||
SelectStatement(
|
||||
columns: [StarResultColumn(null)],
|
||||
from: [TableReference('tbl', null)],
|
||||
limit: InlineDartLimit(name: 'limit'),
|
||||
limit: DartLimitPlaceholder(name: 'limit'),
|
||||
),
|
||||
moorMode: true,
|
||||
);
|
||||
});
|
||||
|
||||
test('parses limit counts as expressions', () {
|
||||
test('parses limit count as expressions', () {
|
||||
testStatement(
|
||||
r'SELECT * FROM tbl LIMIT $amount OFFSET 3',
|
||||
SelectStatement(
|
||||
columns: [StarResultColumn(null)],
|
||||
from: [TableReference('tbl', null)],
|
||||
limit: Limit(
|
||||
count: InlineDartExpression(name: 'amount'),
|
||||
count: DartExpressionPlaceholder(name: 'amount'),
|
||||
offsetSeparator: token(TokenType.offset),
|
||||
offset: NumericLiteral(3, token(TokenType.numberLiteral)),
|
||||
),
|
||||
|
@ -31,4 +31,36 @@ void main() {
|
|||
moorMode: true,
|
||||
);
|
||||
});
|
||||
|
||||
test('parses ordering terms and ordering expressions', () {
|
||||
testStatement(
|
||||
r'SELECT * FROM tbl ORDER BY $term, $expr DESC',
|
||||
SelectStatement(
|
||||
columns: [StarResultColumn(null)],
|
||||
from: [TableReference('tbl', null)],
|
||||
orderBy: OrderBy(
|
||||
terms: [
|
||||
DartOrderingTermPlaceholder(name: 'term'),
|
||||
OrderingTerm(
|
||||
expression: DartExpressionPlaceholder(name: 'expr'),
|
||||
orderingMode: OrderingMode.descending,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
moorMode: true,
|
||||
);
|
||||
});
|
||||
|
||||
test('parses full order by placeholders', () {
|
||||
testStatement(
|
||||
r'SELECT * FROM tbl ORDER BY $order',
|
||||
SelectStatement(
|
||||
columns: [StarResultColumn(null)],
|
||||
from: [TableReference('tbl', null)],
|
||||
orderBy: DartOrderByPlaceholder(name: 'order'),
|
||||
),
|
||||
moorMode: true,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue