mirror of https://github.com/AMT-Cheif/drift.git
sqlparser: Give CASE a higher precedence
This commit is contained in:
parent
85206d2fd7
commit
732369c25c
|
@ -1,6 +1,10 @@
|
|||
part of 'parser.dart';
|
||||
|
||||
/// Parses expressions. Expressions have the following precedence:
|
||||
/// Parses expressions. Expressions have the following precedence, in descending
|
||||
/// order
|
||||
/// - primary expressions: Parenthesis, etc.
|
||||
/// - Case expressions
|
||||
/// - postfix expressions: NOT NULL, etc.
|
||||
/// - `-`, `+`, `~`, unary not
|
||||
/// - `||` (concatenation)
|
||||
/// - `*`, '/', '%'
|
||||
|
@ -11,40 +15,9 @@ part of 'parser.dart';
|
|||
/// `REGEXP`
|
||||
/// - `AND`
|
||||
/// - `OR`
|
||||
/// - Case expressions
|
||||
mixin ExpressionParser on ParserBase {
|
||||
@override
|
||||
Expression expression() {
|
||||
return _case();
|
||||
}
|
||||
|
||||
Expression _case() {
|
||||
if (_matchOne(TokenType.$case)) {
|
||||
final caseToken = _previous;
|
||||
|
||||
final base = _check(TokenType.when) ? null : _or();
|
||||
final whens = <WhenComponent>[];
|
||||
Expression $else;
|
||||
|
||||
while (_matchOne(TokenType.when)) {
|
||||
final whenToken = _previous;
|
||||
|
||||
final whenExpr = _or();
|
||||
_consume(TokenType.then, 'Expected THEN');
|
||||
final then = expression();
|
||||
whens.add(WhenComponent(when: whenExpr, then: then)
|
||||
..setSpan(whenToken, _previous));
|
||||
}
|
||||
|
||||
if (_matchOne(TokenType.$else)) {
|
||||
$else = expression();
|
||||
}
|
||||
|
||||
_consume(TokenType.end, 'Expected END to finish the case operator');
|
||||
return CaseExpression(whens: whens, base: base, elseExpr: $else)
|
||||
..setSpan(caseToken, _previous);
|
||||
}
|
||||
|
||||
return _or();
|
||||
}
|
||||
|
||||
|
@ -205,7 +178,7 @@ mixin ExpressionParser on ParserBase {
|
|||
}
|
||||
|
||||
Expression _postfix() {
|
||||
var expression = _primary();
|
||||
var expression = _case();
|
||||
|
||||
// todo we don't currently parse "NOT NULL" (2 tokens) because of ambiguity
|
||||
// with NOT BETWEEN / NOT IN / ... expressions
|
||||
|
@ -245,6 +218,36 @@ mixin ExpressionParser on ParserBase {
|
|||
return expression;
|
||||
}
|
||||
|
||||
Expression _case() {
|
||||
if (_matchOne(TokenType.$case)) {
|
||||
final caseToken = _previous;
|
||||
|
||||
final base = _check(TokenType.when) ? null : _primary();
|
||||
final whens = <WhenComponent>[];
|
||||
Expression $else;
|
||||
|
||||
while (_matchOne(TokenType.when)) {
|
||||
final whenToken = _previous;
|
||||
|
||||
final whenExpr = _or();
|
||||
_consume(TokenType.then, 'Expected THEN');
|
||||
final then = expression();
|
||||
whens.add(WhenComponent(when: whenExpr, then: then)
|
||||
..setSpan(whenToken, _previous));
|
||||
}
|
||||
|
||||
if (_matchOne(TokenType.$else)) {
|
||||
$else = expression();
|
||||
}
|
||||
|
||||
_consume(TokenType.end, 'Expected END to finish the case operator');
|
||||
return CaseExpression(whens: whens, base: base, elseExpr: $else)
|
||||
..setSpan(caseToken, _previous);
|
||||
}
|
||||
|
||||
return _primary();
|
||||
}
|
||||
|
||||
@override
|
||||
Literal _literalOrNull() {
|
||||
final token = _peek;
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
void main() {
|
||||
final caseExpr = CaseExpression(
|
||||
whens: [
|
||||
WhenComponent(
|
||||
when: BinaryExpression(
|
||||
NumericLiteral(1, token(TokenType.numberLiteral)),
|
||||
token(TokenType.equal),
|
||||
ColonNamedVariable(
|
||||
ColonVariableToken(null, ':isReviewFolderSelected'),
|
||||
),
|
||||
),
|
||||
then: IsExpression(
|
||||
true,
|
||||
Reference(tableName: 'n', columnName: 'nextReviewTime'),
|
||||
NullLiteral(token(TokenType.$null)),
|
||||
),
|
||||
),
|
||||
],
|
||||
elseExpr: IsExpression(
|
||||
false,
|
||||
Reference(tableName: 'n', columnName: 'nextReviewTime'),
|
||||
NullLiteral(token(TokenType.$null)),
|
||||
),
|
||||
);
|
||||
|
||||
final folderExpr = BinaryExpression(
|
||||
Reference(tableName: 'n', columnName: 'folderId'),
|
||||
token(TokenType.equal),
|
||||
ColonNamedVariable(
|
||||
ColonVariableToken(null, ':selectedFolderId'),
|
||||
),
|
||||
);
|
||||
|
||||
test('repro 1', () {
|
||||
testStatement(
|
||||
'''
|
||||
SELECT * FROM notes n WHERE
|
||||
CASE
|
||||
WHEN 1 = :isReviewFolderSelected THEN n.nextReviewTime IS NOT NULL
|
||||
ELSE n.nextReviewTime IS NULL
|
||||
END
|
||||
and n.folderId = :selectedFolderId;
|
||||
''',
|
||||
SelectStatement(
|
||||
from: TableReference('notes', 'n'),
|
||||
columns: [StarResultColumn()],
|
||||
where: BinaryExpression(
|
||||
caseExpr,
|
||||
token(TokenType.and),
|
||||
folderExpr,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('repro 2', () {
|
||||
testStatement(
|
||||
'''
|
||||
SELECT * FROM notes n WHERE
|
||||
n.folderId = :selectedFolderId and
|
||||
CASE
|
||||
WHEN 1 = :isReviewFolderSelected THEN n.nextReviewTime IS NOT NULL
|
||||
ELSE n.nextReviewTime IS NULL
|
||||
END;
|
||||
''',
|
||||
SelectStatement(
|
||||
from: TableReference('notes', 'n'),
|
||||
columns: [StarResultColumn()],
|
||||
where: BinaryExpression(
|
||||
folderExpr,
|
||||
token(TokenType.and),
|
||||
caseExpr,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue