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';
|
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
|
/// - `-`, `+`, `~`, unary not
|
||||||
/// - `||` (concatenation)
|
/// - `||` (concatenation)
|
||||||
/// - `*`, '/', '%'
|
/// - `*`, '/', '%'
|
||||||
|
@ -11,40 +15,9 @@ part of 'parser.dart';
|
||||||
/// `REGEXP`
|
/// `REGEXP`
|
||||||
/// - `AND`
|
/// - `AND`
|
||||||
/// - `OR`
|
/// - `OR`
|
||||||
/// - Case expressions
|
|
||||||
mixin ExpressionParser on ParserBase {
|
mixin ExpressionParser on ParserBase {
|
||||||
@override
|
@override
|
||||||
Expression expression() {
|
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();
|
return _or();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +178,7 @@ mixin ExpressionParser on ParserBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression _postfix() {
|
Expression _postfix() {
|
||||||
var expression = _primary();
|
var expression = _case();
|
||||||
|
|
||||||
// todo we don't currently parse "NOT NULL" (2 tokens) because of ambiguity
|
// todo we don't currently parse "NOT NULL" (2 tokens) because of ambiguity
|
||||||
// with NOT BETWEEN / NOT IN / ... expressions
|
// with NOT BETWEEN / NOT IN / ... expressions
|
||||||
|
@ -245,6 +218,36 @@ mixin ExpressionParser on ParserBase {
|
||||||
return expression;
|
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
|
@override
|
||||||
Literal _literalOrNull() {
|
Literal _literalOrNull() {
|
||||||
final token = _peek;
|
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