mirror of https://github.com/AMT-Cheif/drift.git
Parse <expression> IN (<select-stmt>) again
This commit is contained in:
parent
9f8ccd08d0
commit
5f2d5d3258
|
@ -1,21 +1,23 @@
|
|||
# moor_ffi
|
||||
|
||||
Moor backend that uses the new `dart:ffi` apis. Note that, while we have integration tests
|
||||
Moor backend that uses `dart:ffi`. Note that, while we have integration tests
|
||||
on this package, it depends on the `dart:ffi` apis, which are in "preview" status at the moment.
|
||||
Thus, this library is not suited for production use.
|
||||
|
||||
If you want to use moor on Android or iOS, see the [getting started guide](https://moor.simonbinder.eu/docs/getting-started/)
|
||||
which recommends to use the [moor_flutter](https://pub.dev/packages/moor_flutter) package.
|
||||
At the moment, this library is targeted for advanced moor users who want to try out the `ffi`
|
||||
At the moment, this library is targeted at advanced moor users who want to try out the `ffi`
|
||||
backend.
|
||||
|
||||
## Supported platforms
|
||||
At the moment, this plugin supports Android natively. However, it's also going to run on all
|
||||
platforms that expose `sqlite3` as a shared native library (macOS and virtually all Linux
|
||||
distros, I'm not sure about Windows). Native iOS and macOS support is planned.
|
||||
At the moment, this plugin only supports Android without further work. However, it's also going
|
||||
to run on all platforms that expose `sqlite3` as a shared native library (macOS and virtually
|
||||
all Linux distros, I'm not sure about Windows). Native iOS and macOS support is planned.
|
||||
As Flutter desktop doesn't support plugins on Windows and Linux yet, we can't bundle the
|
||||
sqlite library on those platforms.
|
||||
|
||||
## Migrating from moor_flutter
|
||||
Add both `moor` and `moor_ffi` to your pubspec.
|
||||
Add both `moor` and `moor_ffi` to your pubspec, the `moor_flutter` dependency can be dropped.
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
|
|
|
@ -122,10 +122,15 @@ class BetweenExpression extends Expression {
|
|||
class InExpression extends Expression {
|
||||
final bool not;
|
||||
final Expression left;
|
||||
|
||||
/// The right-hand part: Contains the set of values [left] will be tested
|
||||
/// against. From the sqlite grammar, we support [Tuple] and a [SubQuery].
|
||||
/// We also support a [Variable] as syntax sugar - it will be expanded into a
|
||||
/// tuple of variables at runtime.
|
||||
final Expression inside;
|
||||
|
||||
InExpression({this.not = false, @required this.left, @required this.inside}) {
|
||||
assert(inside is Tuple || inside is Variable);
|
||||
assert(inside is Tuple || inside is Variable || inside is SubQuery);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -460,7 +460,8 @@ mixin CrudParser on ParserBase {
|
|||
if (_matchOne(TokenType.$values)) {
|
||||
final values = <Tuple>[];
|
||||
do {
|
||||
values.add(_consumeTuple());
|
||||
// it will be a tuple, we don't turn on "orSubQuery"
|
||||
values.add(_consumeTuple() as Tuple);
|
||||
} while (_matchOne(TokenType.comma));
|
||||
return ValuesSource(values);
|
||||
} else if (_matchOne(TokenType.$default)) {
|
||||
|
|
|
@ -67,7 +67,7 @@ mixin ExpressionParser on ParserBase {
|
|||
final not = _matchOne(TokenType.not);
|
||||
_matchOne(TokenType.$in);
|
||||
|
||||
final inside = _variableOrNull() ?? _consumeTuple();
|
||||
final inside = _variableOrNull() ?? _consumeTuple(orSubQuery: true);
|
||||
return InExpression(left: left, inside: inside, not: not);
|
||||
}
|
||||
|
||||
|
@ -289,12 +289,6 @@ mixin ExpressionParser on ParserBase {
|
|||
return Reference(columnName: first.identifier)..setSpan(first, first);
|
||||
}
|
||||
break;
|
||||
case TokenType.questionMarkVariable:
|
||||
return NumberedVariable(token as QuestionMarkVariableToken)
|
||||
..setSpan(token, token);
|
||||
case TokenType.colonVariable:
|
||||
return ColonNamedVariable(token as ColonVariableToken)
|
||||
..setSpan(token, token);
|
||||
case TokenType.dollarSignVariable:
|
||||
if (enableMoorExtensions) {
|
||||
final typedToken = token as DollarSignVariableToken;
|
||||
|
@ -377,20 +371,28 @@ mixin ExpressionParser on ParserBase {
|
|||
}
|
||||
|
||||
@override
|
||||
Tuple _consumeTuple() {
|
||||
Expression _consumeTuple({bool orSubQuery = false}) {
|
||||
final firstToken =
|
||||
_consume(TokenType.leftParen, 'Expected opening parenthesis for tuple');
|
||||
final expressions = <Expression>[];
|
||||
|
||||
// tuples can be empty `()`, so only start parsing values when it's not
|
||||
if (_peek.type != TokenType.rightParen) {
|
||||
do {
|
||||
expressions.add(expression());
|
||||
} while (_matchOne(TokenType.comma));
|
||||
final subQuery = select();
|
||||
if (subQuery == null) {
|
||||
// no sub query found. read expressions that form the tuple.
|
||||
// tuples can be empty `()`, so only start parsing values when it's not
|
||||
if (_peek.type != TokenType.rightParen) {
|
||||
do {
|
||||
expressions.add(expression());
|
||||
} while (_matchOne(TokenType.comma));
|
||||
}
|
||||
|
||||
_consume(
|
||||
TokenType.rightParen, 'Expected right parenthesis to close tuple');
|
||||
return Tuple(expressions: expressions)..setSpan(firstToken, _previous);
|
||||
} else {
|
||||
_consume(TokenType.rightParen,
|
||||
'Expected right parenthesis to finish subquery');
|
||||
return SubQuery(select: subQuery)..setSpan(firstToken, _previous);
|
||||
}
|
||||
|
||||
_consume(TokenType.rightParen, 'Expected right parenthesis to close tuple');
|
||||
|
||||
return Tuple(expressions: expressions)..setSpan(firstToken, _previous);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,7 +142,10 @@ abstract class ParserBase {
|
|||
|
||||
// Common operations that we are referenced very often
|
||||
Expression expression();
|
||||
Tuple _consumeTuple();
|
||||
|
||||
/// Parses a [Tuple]. If [orSubQuery] is set (defaults to false), a [SubQuery]
|
||||
/// (in brackets) will be accepted as well.
|
||||
Expression _consumeTuple({bool orSubQuery = false});
|
||||
|
||||
/// Parses a [SelectStatement], or returns null if there is no select token
|
||||
/// after the current position.
|
||||
|
|
|
@ -83,6 +83,15 @@ final Map<String, Expression> _testCases = {
|
|||
),
|
||||
),
|
||||
),
|
||||
'(SELECT x)': SubQuery(
|
||||
select: SelectStatement(
|
||||
columns: [
|
||||
ExpressionResultColumn(
|
||||
expression: Reference(columnName: 'x'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
"'hello' || 'world' COLLATE NOCASE": BinaryExpression(
|
||||
StringLiteral.from(token(TokenType.stringLiteral), 'hello'),
|
||||
token(TokenType.doublePipe),
|
||||
|
@ -92,6 +101,41 @@ final Map<String, Expression> _testCases = {
|
|||
collateFunction: token(TokenType.identifier),
|
||||
),
|
||||
),
|
||||
'x in ?': InExpression(
|
||||
left: Reference(columnName: 'x'),
|
||||
inside: NumberedVariable(QuestionMarkVariableToken(fakeSpan('?'), null)),
|
||||
),
|
||||
'x IN (SELECT col FROM tbl)': InExpression(
|
||||
left: Reference(columnName: 'x'),
|
||||
inside: SubQuery(
|
||||
select: SelectStatement(
|
||||
columns: [
|
||||
ExpressionResultColumn(
|
||||
expression: Reference(columnName: 'col'),
|
||||
)
|
||||
],
|
||||
from: [
|
||||
TableReference('tbl', null),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
'x IN (1, 2, (SELECT 3))': InExpression(
|
||||
left: Reference(columnName: 'x'),
|
||||
inside: Tuple(
|
||||
expressions: [
|
||||
NumericLiteral(1.0, token(TokenType.numberLiteral)),
|
||||
NumericLiteral(2.0, token(TokenType.numberLiteral)),
|
||||
SubQuery(
|
||||
select: SelectStatement(columns: [
|
||||
ExpressionResultColumn(
|
||||
expression: NumericLiteral(3.0, token(TokenType.numberLiteral)),
|
||||
),
|
||||
]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
void main() {
|
||||
|
|
Loading…
Reference in New Issue