// Stripped down version of the SQL grammar focusing on Selection
// From https://raw.githubusercontent.com/pest-parser/pest/refs/heads/master/grammars/src/grammars/sql.pest
Selection = _{ SOI ~ Expr ~ OrderByClause? ~ LimitClause? ~ EOI }
Expr = { ExprAtomValue ~ (ExprInfixOp ~ ExprAtomValue)* }
ExprInfixOp = _{ Between | ArithInfixOp | CmpInfixOp | And | Or }
Between = { NotFlag? ~ ^"between" }
And = @{ ^"and" ~ !IDENT_CONT }
Or = @{ ^"or" ~ !IDENT_CONT }
ArithInfixOp = _{ Add | Subtract | Multiply | Divide }
Add = { "+" }
Subtract = { "-" }
Multiply = { "*" }
Divide = { "/" }
CmpInfixOp = _{ NotEq | GtEq | Gt | LtEq | Lt | Eq | Lt | In }
Eq = { "=" }
Gt = { ">" }
GtEq = { ">=" }
Lt = { "<" }
LtEq = { "<=" }
NotEq = { "<>" | "!=" }
In = { NotFlag? ~ ^"in" }
ExprAtomValue = _{ UnaryNot* ~ AtomicExpr ~ IsNullPostfix? }
UnaryNot = @{ NotFlag }
IsNullPostfix = { ^"is" ~ NotFlag? ~ ^"null" }
AtomicExpr = _{ Literal | QuestionParameter | PathExpr | ExpressionInParentheses | Row }
Literal = _{ True | False | Null | Double | Decimal | Unsigned | Integer | SingleQuotedString }
True = @{ ^"true" ~ !IDENT_CONT }
False = @{ ^"false" ~ !IDENT_CONT }
Null = @{ ^"null" ~ !IDENT_CONT }
Decimal = @{ Integer ~ ("." ~ ASCII_DIGIT*) }
Double = @{ Integer ~ ("." ~ ASCII_DIGIT*)? ~ (^"e" ~ Integer) }
Integer = @{ ("+" | "-")? ~ ASCII_DIGIT+ }
Unsigned = @{ ASCII_DIGIT+ }
SingleQuotedString = @{ OnlyQuotesSequence | AnythingButQuotesSequence }
OnlyQuotesSequence = @{ ("'" ~ "'")+ }
AnythingButQuotesSequence = @{ "'" ~ (!("'") ~ ANY)* ~ "'" }
QuestionParameter = @{ "?" }
PathExpr = { Identifier ~ ("." ~ Identifier)* }
ExpressionInParentheses = { "(" ~ Expr ~ ")" }
Row = { "(" ~ Expr ~ ("," ~ Expr)* ~ ")" }
Identifier = @{ DoubleQuotedIdentifier | IdentifierInner }
DoubleQuotedIdentifier = @{ ("\"" ~ IdentifierInner ~ "\"") }
IdentifierInner = @{ !(Reserved ~ ("(" | WHITESPACE | "," | EOF)) ~ (IdentifierNonDigit ~ (IdentifierNonDigit | ASCII_DIGIT)*) }
IdentifierNonDigit = _{ ('a'..'z' | 'A' .. 'Z' | 'А' .. 'Я' | 'а' .. 'я' | "-" | "_") }
Reserved = { ^"left" | ^"having" | ^"not" | ^"inner" | ^"group"
| ^"on" | ^"join" | ^"from" | ^"exists" | ^"except"
| ^"union" | ^"where" | ^"distinct" | ^"between" | ^"option"
| ^"values" | LimitClause | OrderByClause }
OrderByClause = ${ ^"order" ~ WS+ ~ ^"by" ~ WS+ ~ (OrderByItem ~ WS* ~ ("," ~ WS* ~ OrderByItem)*)? }
OrderByItem = ${ Identifier ~ (WS+ ~ OrderDirection)? }
OrderDirection = { ^"asc" | ^"desc" }
LimitClause = ${ ^"limit" ~ WS+ ~ Unsigned }
NotFlag = { ^"not" }
EOF = { EOI | ";" }
WS = _{ WHITESPACE }
WHITESPACE = _{ " " | "\t" | "\n" | "\r\n" }
IDENT_CONT = _{ ASCII_ALPHANUMERIC | "_" }