use crate::{
DataType, Identifier, QualifiedName, SString, Span, Spanned, Statement,
data_type::{DataTypeContext, parse_data_type},
function_expression::{
AggregateFunctionCallExpression, CharFunctionExpression, Function, FunctionCallExpression,
WindowFunctionCallExpression, is_aggregate_function_ident, parse_aggregate_function,
parse_char_function, parse_function, parse_function_call,
},
keywords::{Keyword, Restrict},
lexer::Token,
operator::parse_operator_name,
parser::{ParseError, Parser},
span::OptSpanned,
statement::parse_compound_query,
};
use alloc::string::ToString;
use alloc::vec;
use alloc::{boxed::Box, vec::Vec};
#[derive(Debug, Clone)]
pub enum Variable<'a> {
TimeZone,
Other(&'a str),
}
#[derive(Debug, Clone)]
pub enum BinaryOperator<'a> {
Or(Span),
Xor(Span),
And(Span),
Eq(Span),
NullSafeEq(Span),
GtEq(Span),
Gt(Span),
LtEq(Span),
Lt(Span),
Neq(Span),
ShiftLeft(Span),
ShiftRight(Span),
BitAnd(Span),
BitOr(Span),
BitXor(Span),
Add(Span),
Subtract(Span),
Divide(Span),
Div(Span),
Mod(Span),
Mult(Span),
Like(Span),
NotLike(Span),
Regexp(Span),
NotRegexp(Span),
Rlike(Span),
NotRlike(Span),
Collate(Span),
JsonExtract(Span),
JsonExtractUnquote(Span),
Assignment(Span),
Concat(Span),
Contains(Span),
ContainedBy(Span),
JsonPathMatch(Span),
JsonPathExists(Span),
JsonbKeyExists(Span),
JsonbAnyKeyExists(Span),
JsonbAllKeyExists(Span),
JsonGetPath(Span),
JsonGetPathText(Span),
JsonDeletePath(Span),
RegexMatch(Span),
RegexIMatch(Span),
NotRegexMatch(Span),
NotRegexIMatch(Span),
User(&'a str, Span),
Operator(QualifiedName<'a>, Span),
}
impl<'a> Spanned for BinaryOperator<'a> {
fn span(&self) -> Span {
match self {
BinaryOperator::Or(s)
| BinaryOperator::Xor(s)
| BinaryOperator::And(s)
| BinaryOperator::Eq(s)
| BinaryOperator::NullSafeEq(s)
| BinaryOperator::GtEq(s)
| BinaryOperator::Gt(s)
| BinaryOperator::LtEq(s)
| BinaryOperator::Lt(s)
| BinaryOperator::Neq(s)
| BinaryOperator::ShiftLeft(s)
| BinaryOperator::ShiftRight(s)
| BinaryOperator::BitAnd(s)
| BinaryOperator::BitOr(s)
| BinaryOperator::BitXor(s)
| BinaryOperator::Add(s)
| BinaryOperator::Subtract(s)
| BinaryOperator::Divide(s)
| BinaryOperator::Div(s)
| BinaryOperator::Mod(s)
| BinaryOperator::Mult(s)
| BinaryOperator::Like(s)
| BinaryOperator::NotLike(s)
| BinaryOperator::Regexp(s)
| BinaryOperator::NotRegexp(s)
| BinaryOperator::Rlike(s)
| BinaryOperator::NotRlike(s)
| BinaryOperator::Collate(s)
| BinaryOperator::JsonExtract(s)
| BinaryOperator::JsonExtractUnquote(s)
| BinaryOperator::Assignment(s)
| BinaryOperator::Concat(s)
| BinaryOperator::Contains(s)
| BinaryOperator::ContainedBy(s)
| BinaryOperator::JsonPathMatch(s)
| BinaryOperator::JsonPathExists(s)
| BinaryOperator::JsonbKeyExists(s)
| BinaryOperator::JsonbAnyKeyExists(s)
| BinaryOperator::JsonbAllKeyExists(s)
| BinaryOperator::JsonGetPath(s)
| BinaryOperator::JsonGetPathText(s)
| BinaryOperator::JsonDeletePath(s)
| BinaryOperator::RegexMatch(s)
| BinaryOperator::RegexIMatch(s)
| BinaryOperator::NotRegexMatch(s)
| BinaryOperator::NotRegexIMatch(s) => s.clone(),
BinaryOperator::User(_, s) => s.clone(),
BinaryOperator::Operator(_, s) => s.clone(),
}
}
}
#[derive(Debug, Clone)]
pub enum MatchMode {
InBoolean(Span),
InNaturalLanguage(Span),
InNaturalLanguageWithQueryExpansion(Span),
WithQueryExpansion(Span),
}
impl Spanned for MatchMode {
fn span(&self) -> Span {
match self {
MatchMode::InBoolean(s) => s.clone(),
MatchMode::InNaturalLanguage(s) => s.clone(),
MatchMode::InNaturalLanguageWithQueryExpansion(s) => s.clone(),
MatchMode::WithQueryExpansion(s) => s.clone(),
}
}
}
#[derive(Debug, Clone)]
pub enum Is<'a> {
Null,
NotNull,
True,
NotTrue,
False,
NotFalse,
Unknown,
NotUnknown,
DistinctFrom(Expression<'a>),
NotDistinctFrom(Expression<'a>),
}
#[derive(Debug, Clone)]
pub enum UnaryOperator {
Binary(Span),
LogicalNot(Span),
Minus(Span),
Not(Span),
}
impl Spanned for UnaryOperator {
fn span(&self) -> Span {
match self {
UnaryOperator::Binary(s)
| UnaryOperator::LogicalNot(s)
| UnaryOperator::Minus(s)
| UnaryOperator::Not(s) => s.clone(),
}
}
}
#[derive(Debug, Clone)]
pub enum IdentifierPart<'a> {
Name(Identifier<'a>),
Star(Span),
}
impl<'a> Spanned for IdentifierPart<'a> {
fn span(&self) -> Span {
match &self {
IdentifierPart::Name(v) => v.span(),
IdentifierPart::Star(v) => v.span(),
}
}
}
#[derive(Debug, Clone)]
pub struct When<'a> {
pub when_span: Span,
pub when: Expression<'a>,
pub then_span: Span,
pub then: Expression<'a>,
}
impl<'a> Spanned for When<'a> {
fn span(&self) -> Span {
self.when_span
.join_span(&self.when)
.join_span(&self.then_span)
.join_span(&self.then)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TimeUnit {
Microsecond,
Second,
Minute,
Hour,
Day,
Week,
Month,
Quarter,
Year,
SecondMicrosecond,
MinuteMicrosecond,
MinuteSecond,
HourMicrosecond,
HourSecond,
HourMinute,
DayMicrosecond,
DaySecond,
DayMinute,
DayHour,
YearMonth,
Epoch,
Dow,
Doy,
Century,
Decade,
IsoDow,
IsoYear,
Julian,
Millennium,
Timezone,
TimezoneHour,
TimezoneMinute,
}
fn parse_time_unit_from_str(s: &str) -> Option<TimeUnit> {
match s.to_ascii_lowercase().trim_end_matches('s') {
"microsecond" => Some(TimeUnit::Microsecond),
"second" => Some(TimeUnit::Second),
"minute" => Some(TimeUnit::Minute),
"hour" => Some(TimeUnit::Hour),
"day" => Some(TimeUnit::Day),
"week" => Some(TimeUnit::Week),
"month" => Some(TimeUnit::Month),
"quarter" => Some(TimeUnit::Quarter),
"year" => Some(TimeUnit::Year),
"epoch" => Some(TimeUnit::Epoch),
"dow" => Some(TimeUnit::Dow),
"doy" => Some(TimeUnit::Doy),
"century" | "centurie" => Some(TimeUnit::Century),
"decade" => Some(TimeUnit::Decade),
"isodow" => Some(TimeUnit::IsoDow),
"isoyear" => Some(TimeUnit::IsoYear),
"julian" => Some(TimeUnit::Julian),
"millennium" | "millennia" | "millenniu" => Some(TimeUnit::Millennium),
"timezone" => Some(TimeUnit::Timezone),
"timezone_hour" => Some(TimeUnit::TimezoneHour),
"timezone_minute" => Some(TimeUnit::TimezoneMinute),
_ => None,
}
}
fn parse_time_unit(t: &Token<'_>) -> Option<TimeUnit> {
match t {
Token::Ident(_, Keyword::MICROSECOND) => Some(TimeUnit::Microsecond),
Token::Ident(_, Keyword::SECOND) => Some(TimeUnit::Second),
Token::Ident(_, Keyword::MINUTE) => Some(TimeUnit::Minute),
Token::Ident(_, Keyword::HOUR) => Some(TimeUnit::Hour),
Token::Ident(_, Keyword::DAY) => Some(TimeUnit::Day),
Token::Ident(_, Keyword::WEEK) => Some(TimeUnit::Week),
Token::Ident(_, Keyword::MONTH) => Some(TimeUnit::Month),
Token::Ident(_, Keyword::QUARTER) => Some(TimeUnit::Quarter),
Token::Ident(_, Keyword::YEAR) => Some(TimeUnit::Year),
Token::Ident(_, Keyword::SECOND_MICROSECOND) => Some(TimeUnit::SecondMicrosecond),
Token::Ident(_, Keyword::MINUTE_MICROSECOND) => Some(TimeUnit::MinuteMicrosecond),
Token::Ident(_, Keyword::MINUTE_SECOND) => Some(TimeUnit::MinuteSecond),
Token::Ident(_, Keyword::HOUR_MICROSECOND) => Some(TimeUnit::HourMicrosecond),
Token::Ident(_, Keyword::HOUR_SECOND) => Some(TimeUnit::HourSecond),
Token::Ident(_, Keyword::HOUR_MINUTE) => Some(TimeUnit::HourMinute),
Token::Ident(_, Keyword::DAY_MICROSECOND) => Some(TimeUnit::DayMicrosecond),
Token::Ident(_, Keyword::DAY_SECOND) => Some(TimeUnit::DaySecond),
Token::Ident(_, Keyword::DAY_MINUTE) => Some(TimeUnit::DayMinute),
Token::Ident(_, Keyword::DAY_HOUR) => Some(TimeUnit::DayHour),
Token::Ident(_, Keyword::YEAR_MONTH) => Some(TimeUnit::YearMonth),
Token::Ident(s, Keyword::NOT_A_KEYWORD) => parse_time_unit_from_str(s),
_ => None,
}
}
#[derive(Debug, Clone)]
pub struct BinaryExpression<'a> {
pub op: BinaryOperator<'a>,
pub lhs: Expression<'a>,
pub rhs: Expression<'a>,
}
impl Spanned for BinaryExpression<'_> {
fn span(&self) -> Span {
self.op.span().join_span(&self.lhs).join_span(&self.rhs)
}
}
#[derive(Debug, Clone)]
pub struct UnaryExpression<'a> {
pub op: UnaryOperator,
pub operand: Expression<'a>,
}
impl Spanned for UnaryExpression<'_> {
fn span(&self) -> Span {
self.op.span().join_span(&self.operand)
}
}
#[derive(Debug, Clone)]
pub struct IntervalExpression {
pub interval_span: Span,
pub time_interval: (Vec<i64>, Span),
pub time_unit: (TimeUnit, Span),
}
impl Spanned for IntervalExpression {
fn span(&self) -> Span {
self.interval_span
.join_span(&self.time_interval.1)
.join_span(&self.time_unit.1)
}
}
#[derive(Debug, Clone)]
pub struct ExtractExpression<'a> {
pub extract_span: Span,
pub time_unit: (TimeUnit, Span),
pub from_span: Span,
pub date: Expression<'a>,
}
impl Spanned for ExtractExpression<'_> {
fn span(&self) -> Span {
self.extract_span
.join_span(&self.time_unit.1)
.join_span(&self.from_span)
.join_span(&self.date)
}
}
#[derive(Debug, Clone)]
pub enum TrimDirection {
Both(Span),
Leading(Span),
Trailing(Span),
}
impl Spanned for TrimDirection {
fn span(&self) -> Span {
match self {
TrimDirection::Both(s) | TrimDirection::Leading(s) | TrimDirection::Trailing(s) => {
s.clone()
}
}
}
}
#[derive(Debug, Clone)]
pub struct TrimExpression<'a> {
pub trim_span: Span,
pub direction: Option<TrimDirection>,
pub what: Option<Expression<'a>>,
pub from_span: Option<Span>,
pub value: Expression<'a>,
}
impl Spanned for TrimExpression<'_> {
fn span(&self) -> Span {
self.trim_span
.join_span(&self.direction)
.join_span(&self.what)
.join_span(&self.from_span)
.join_span(&self.value)
}
}
#[derive(Debug, Clone)]
pub struct InExpression<'a> {
pub lhs: Expression<'a>,
pub rhs: Vec<Expression<'a>>,
pub in_span: Span,
pub not_in: bool,
}
impl Spanned for InExpression<'_> {
fn span(&self) -> Span {
self.in_span.join_span(&self.lhs).join_span(&self.rhs)
}
}
#[derive(Debug, Clone)]
pub struct BetweenExpression<'a> {
pub lhs: Expression<'a>,
pub low: Expression<'a>,
pub high: Expression<'a>,
pub between_span: Span,
pub not_between: bool,
}
impl Spanned for BetweenExpression<'_> {
fn span(&self) -> Span {
self.between_span
.join_span(&self.lhs)
.join_span(&self.low)
.join_span(&self.high)
}
}
#[derive(Debug, Clone)]
pub struct MemberOfExpression<'a> {
pub lhs: Expression<'a>,
pub rhs: Expression<'a>,
pub member_of_span: Span,
}
impl Spanned for MemberOfExpression<'_> {
fn span(&self) -> Span {
self.member_of_span
.join_span(&self.lhs)
.join_span(&self.rhs)
}
}
#[derive(Debug, Clone)]
pub struct CaseExpression<'a> {
pub case_span: Span,
pub value: Option<Expression<'a>>,
pub whens: Vec<When<'a>>,
pub else_: Option<(Span, Expression<'a>)>,
pub end_span: Span,
}
impl Spanned for CaseExpression<'_> {
fn span(&self) -> Span {
self.case_span
.join_span(&self.value)
.join_span(&self.whens)
.join_span(&self.else_)
.join_span(&self.end_span)
}
}
#[derive(Debug, Clone)]
pub struct CastExpression<'a> {
pub cast_span: Span,
pub expr: Expression<'a>,
pub as_span: Span,
pub type_: DataType<'a>,
}
impl Spanned for CastExpression<'_> {
fn span(&self) -> Span {
self.cast_span
.join_span(&self.expr)
.join_span(&self.as_span)
.join_span(&self.type_)
}
}
#[derive(Debug, Clone)]
pub struct ConvertExpression<'a> {
pub convert_span: Span,
pub expr: Expression<'a>,
pub type_: Option<DataType<'a>>,
pub using_charset: Option<(Span, Identifier<'a>)>,
}
impl Spanned for ConvertExpression<'_> {
fn span(&self) -> Span {
self.convert_span
.join_span(&self.expr)
.join_span(&self.type_)
.join_span(&self.using_charset)
}
}
#[derive(Debug, Clone)]
pub struct TypeCastExpression<'a> {
pub expr: Expression<'a>,
pub doublecolon_span: Span,
pub type_: DataType<'a>,
}
impl Spanned for TypeCastExpression<'_> {
fn span(&self) -> Span {
self.expr
.span()
.join_span(&self.doublecolon_span)
.join_span(&self.type_)
}
}
#[derive(Debug, Clone)]
pub struct ArrayExpression<'a> {
pub array_span: Span,
pub bracket_span: Span,
pub elements: Vec<Expression<'a>>,
}
impl Spanned for ArrayExpression<'_> {
fn span(&self) -> Span {
self.array_span.join_span(&self.bracket_span)
}
}
#[derive(Debug, Clone)]
pub struct ArraySubscriptExpression<'a> {
pub expr: Expression<'a>,
pub bracket_span: Span,
pub lower: Expression<'a>,
pub upper: Option<Expression<'a>>,
}
impl Spanned for ArraySubscriptExpression<'_> {
fn span(&self) -> Span {
self.expr.span().join_span(&self.bracket_span)
}
}
#[derive(Debug, Clone)]
pub struct FieldAccessExpression<'a> {
pub expr: Expression<'a>,
pub dot_span: Span,
pub field: Identifier<'a>,
}
impl Spanned for FieldAccessExpression<'_> {
fn span(&self) -> Span {
self.expr
.span()
.join_span(&self.dot_span)
.join_span(&self.field)
}
}
#[derive(Debug, Clone)]
pub struct GroupConcatExpression<'a> {
pub group_concat_span: Span,
pub distinct_span: Option<Span>,
pub expr: Expression<'a>,
}
impl Spanned for GroupConcatExpression<'_> {
fn span(&self) -> Span {
self.group_concat_span
.join_span(&self.distinct_span)
.join_span(&self.expr)
}
}
#[derive(Debug, Clone)]
pub struct VariableExpression<'a> {
pub global: Option<Span>,
pub session: Option<Span>,
pub dot: Option<Span>,
pub variable: Variable<'a>,
pub variable_span: Span,
}
impl Spanned for VariableExpression<'_> {
fn span(&self) -> Span {
self.variable_span
.join_span(&self.global)
.join_span(&self.session)
.join_span(&self.dot)
}
}
#[derive(Debug, Clone)]
pub struct UserVariableExpression<'a> {
pub name: Identifier<'a>,
pub at_span: Span,
}
impl Spanned for UserVariableExpression<'_> {
fn span(&self) -> Span {
self.at_span.join_span(&self.name)
}
}
#[derive(Debug, Clone)]
pub struct TimestampAddExpression<'a> {
pub timestamp_add_span: Span,
pub unit: (TimeUnit, Span),
pub interval: Expression<'a>,
pub datetime: Expression<'a>,
}
impl Spanned for TimestampAddExpression<'_> {
fn span(&self) -> Span {
self.timestamp_add_span
.join_span(&self.unit.1)
.join_span(&self.interval)
.join_span(&self.datetime)
}
}
#[derive(Debug, Clone)]
pub struct TimestampDiffExpression<'a> {
pub timestamp_diff_span: Span,
pub unit: (TimeUnit, Span),
pub e1: Expression<'a>,
pub e2: Expression<'a>,
}
impl Spanned for TimestampDiffExpression<'_> {
fn span(&self) -> Span {
self.timestamp_diff_span
.join_span(&self.unit.1)
.join_span(&self.e1)
.join_span(&self.e2)
}
}
#[derive(Debug, Clone)]
pub struct MatchAgainstExpression<'a> {
pub match_span: Span,
pub columns: Vec<Expression<'a>>,
pub against_span: Span,
pub expr: Expression<'a>,
pub mode: Option<MatchMode>,
}
impl Spanned for MatchAgainstExpression<'_> {
fn span(&self) -> Span {
self.match_span
.join_span(&self.columns)
.join_span(&self.against_span)
.join_span(&self.expr)
.join_span(&self.mode)
}
}
#[derive(Debug, Clone)]
pub struct IsExpression<'a> {
pub lhs: Expression<'a>,
pub is: Is<'a>,
pub is_span: Span,
}
impl<'a> Spanned for IsExpression<'a> {
fn span(&self) -> Span {
match &self.is {
Is::DistinctFrom(rhs) | Is::NotDistinctFrom(rhs) => {
self.lhs.span().join_span(&self.is_span).join_span(rhs)
}
_ => self.lhs.span().join_span(&self.is_span),
}
}
}
#[derive(Debug, Clone)]
pub struct NullExpression {
pub span: Span,
}
impl Spanned for NullExpression {
fn span(&self) -> Span {
self.span.clone()
}
}
#[derive(Debug, Clone)]
pub struct DefaultExpression {
pub span: Span,
}
impl Spanned for DefaultExpression {
fn span(&self) -> Span {
self.span.clone()
}
}
#[derive(Debug, Clone)]
pub struct BoolExpression {
pub value: bool,
pub span: Span,
}
impl Spanned for BoolExpression {
fn span(&self) -> Span {
self.span.clone()
}
}
#[derive(Debug, Clone)]
pub struct IntegerExpression {
pub value: u64,
pub span: Span,
}
impl Spanned for IntegerExpression {
fn span(&self) -> Span {
self.span.clone()
}
}
#[derive(Debug, Clone)]
pub struct ListHackExpression {
pub index: usize,
pub span: Span,
}
impl Spanned for ListHackExpression {
fn span(&self) -> Span {
self.span.clone()
}
}
#[derive(Debug, Clone)]
pub struct FloatExpression {
pub value: f64,
pub span: Span,
}
impl Spanned for FloatExpression {
fn span(&self) -> Span {
self.span.clone()
}
}
#[derive(Debug, Clone)]
pub struct ArgExpression {
pub index: usize,
pub span: Span,
}
impl Spanned for ArgExpression {
fn span(&self) -> Span {
self.span.clone()
}
}
#[derive(Debug, Clone)]
pub struct InvalidExpression {
pub span: Span,
}
impl Spanned for InvalidExpression {
fn span(&self) -> Span {
self.span.clone()
}
}
#[derive(Debug, Clone)]
pub struct IdentifierExpression<'a> {
pub parts: Vec<IdentifierPart<'a>>,
}
impl<'a> Spanned for IdentifierExpression<'a> {
fn span(&self) -> Span {
self.parts.opt_span().expect("Span of identifier parts")
}
}
#[derive(Debug, Clone)]
pub struct SubqueryExpression<'a> {
pub expression: Statement<'a>,
}
impl Spanned for SubqueryExpression<'_> {
fn span(&self) -> Span {
self.expression.span()
}
}
#[derive(Debug, Clone)]
pub struct ExistsExpression<'a> {
pub exists_span: Span,
pub subquery: Statement<'a>,
}
impl Spanned for ExistsExpression<'_> {
fn span(&self) -> Span {
self.exists_span.join_span(&self.subquery)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Quantifier {
Any(Span),
Some(Span),
All(Span),
}
impl Spanned for Quantifier {
fn span(&self) -> Span {
match self {
Quantifier::Any(s) | Quantifier::Some(s) | Quantifier::All(s) => s.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct QuantifierExpression<'a> {
pub quantifier: Quantifier,
pub operand: Expression<'a>,
}
impl Spanned for QuantifierExpression<'_> {
fn span(&self) -> Span {
self.quantifier.join_span(&self.operand)
}
}
#[derive(Debug, Clone)]
pub struct RowExpression<'a> {
pub paren_span: Span,
pub elements: Vec<Expression<'a>>,
}
impl Spanned for RowExpression<'_> {
fn span(&self) -> Span {
self.paren_span.clone()
}
}
#[derive(Debug, Clone)]
pub enum Expression<'a> {
Binary(Box<BinaryExpression<'a>>),
Unary(Box<UnaryExpression<'a>>),
Subquery(Box<SubqueryExpression<'a>>),
Null(Box<NullExpression>),
Default(Box<DefaultExpression>),
Bool(Box<BoolExpression>),
String(Box<SString<'a>>),
Integer(Box<IntegerExpression>),
ListHack(Box<ListHackExpression>),
Float(Box<FloatExpression>),
Function(Box<FunctionCallExpression<'a>>),
WindowFunction(Box<WindowFunctionCallExpression<'a>>),
AggregateFunction(Box<AggregateFunctionCallExpression<'a>>),
Identifier(Box<IdentifierExpression<'a>>),
Interval(Box<IntervalExpression>),
Arg(Box<ArgExpression>),
Exists(Box<ExistsExpression<'a>>),
Extract(Box<ExtractExpression<'a>>),
Trim(Box<TrimExpression<'a>>),
In(Box<InExpression<'a>>),
Between(Box<BetweenExpression<'a>>),
MemberOf(Box<MemberOfExpression<'a>>),
Is(Box<IsExpression<'a>>),
Invalid(Box<InvalidExpression>),
Case(Box<CaseExpression<'a>>),
Cast(Box<CastExpression<'a>>),
Convert(Box<ConvertExpression<'a>>),
GroupConcat(Box<GroupConcatExpression<'a>>),
Variable(Box<VariableExpression<'a>>),
UserVariable(Box<UserVariableExpression<'a>>),
TimestampAdd(Box<TimestampAddExpression<'a>>),
TimestampDiff(Box<TimestampDiffExpression<'a>>),
TypeCast(Box<TypeCastExpression<'a>>),
MatchAgainst(Box<MatchAgainstExpression<'a>>),
Array(Box<ArrayExpression<'a>>),
ArraySubscript(Box<ArraySubscriptExpression<'a>>),
Quantifier(Box<QuantifierExpression<'a>>),
FieldAccess(Box<FieldAccessExpression<'a>>),
Char(Box<CharFunctionExpression<'a>>),
Row(Box<RowExpression<'a>>),
}
impl<'a> Spanned for Expression<'a> {
fn span(&self) -> Span {
match &self {
Expression::Binary(e) => e.span(),
Expression::Unary(e) => e.span(),
Expression::Subquery(v) => v.span(),
Expression::Null(v) => v.span(),
Expression::Default(v) => v.span(),
Expression::Bool(v) => v.span(),
Expression::String(v) => v.span(),
Expression::Integer(v) => v.span(),
Expression::Float(v) => v.span(),
Expression::ListHack(v) => v.span(),
Expression::Function(e) => e.span(),
Expression::AggregateFunction(e) => e.span(),
Expression::Identifier(e) => e.span(),
Expression::Arg(v) => v.span(),
Expression::Exists(v) => v.span(),
Expression::In(e) => e.span(),
Expression::Between(e) => e.span(),
Expression::MemberOf(e) => e.span(),
Expression::Is(e) => e.span(),
Expression::Invalid(s) => s.span(),
Expression::Case(e) => e.span(),
Expression::Cast(e) => e.span(),
Expression::Convert(e) => e.span(),
Expression::TypeCast(e) => e.span(),
Expression::GroupConcat(e) => e.span(),
Expression::Variable(e) => e.span(),
Expression::UserVariable(e) => e.span(),
Expression::WindowFunction(e) => e.span(),
Expression::Interval(e) => e.span(),
Expression::Extract(e) => e.span(),
Expression::Trim(e) => e.span(),
Expression::TimestampAdd(e) => e.span(),
Expression::TimestampDiff(e) => e.span(),
Expression::MatchAgainst(e) => e.span(),
Expression::Array(e) => e.span(),
Expression::ArraySubscript(e) => e.span(),
Expression::Quantifier(e) => e.span(),
Expression::FieldAccess(e) => e.span(),
Expression::Char(e) => e.span(),
Expression::Row(e) => e.span(),
}
}
}
pub(crate) const PRIORITY_TYPECAST: usize = 10; pub(crate) const PRIORITY_JSON_EXTRACT: usize = 30; pub(crate) const PRIORITY_BITXOR: usize = 50; pub(crate) const PRIORITY_MULT: usize = 60; pub(crate) const PRIORITY_ADD: usize = 70; pub(crate) const PRIORITY_PG_CUSTOM: usize = 75; pub(crate) const PRIORITY_SHIFT: usize = 80; pub(crate) const PRIORITY_BITAND: usize = 90; pub(crate) const PRIORITY_BITOR: usize = 100; pub(crate) const PRIORITY_CMP: usize = 110; pub(crate) const PRIORITY_AND: usize = 140; pub(crate) const PRIORITY_XOR: usize = 150; pub(crate) const PRIORITY_OR: usize = 160; pub(crate) const PRIORITY_ASSIGN: usize = 200;
pub(crate) const PRIORITY_MAX: usize = usize::MAX;
trait Priority {
fn priority(&self) -> usize;
}
impl<'a> Priority for BinaryOperator<'a> {
fn priority(&self) -> usize {
match self {
BinaryOperator::Assignment(_) => PRIORITY_ASSIGN,
BinaryOperator::Or(_) => PRIORITY_OR,
BinaryOperator::Concat(_) => PRIORITY_ADD,
BinaryOperator::Xor(_) => PRIORITY_XOR,
BinaryOperator::And(_) => PRIORITY_AND,
BinaryOperator::Eq(_) => PRIORITY_CMP,
BinaryOperator::NullSafeEq(_) => PRIORITY_CMP,
BinaryOperator::GtEq(_) => PRIORITY_CMP,
BinaryOperator::Gt(_) => PRIORITY_CMP,
BinaryOperator::LtEq(_) => PRIORITY_CMP,
BinaryOperator::Lt(_) => PRIORITY_CMP,
BinaryOperator::Neq(_) => PRIORITY_CMP,
BinaryOperator::Like(_) => PRIORITY_CMP,
BinaryOperator::NotLike(_) => PRIORITY_CMP,
BinaryOperator::Regexp(_) => PRIORITY_CMP,
BinaryOperator::NotRegexp(_) => PRIORITY_CMP,
BinaryOperator::Rlike(_) => PRIORITY_CMP,
BinaryOperator::NotRlike(_) => PRIORITY_CMP,
BinaryOperator::ShiftLeft(_) => PRIORITY_SHIFT,
BinaryOperator::ShiftRight(_) => PRIORITY_SHIFT,
BinaryOperator::BitAnd(_) => PRIORITY_BITAND,
BinaryOperator::BitOr(_) => PRIORITY_BITOR,
BinaryOperator::BitXor(_) => PRIORITY_BITXOR,
BinaryOperator::Add(_) => PRIORITY_ADD,
BinaryOperator::Subtract(_) => PRIORITY_ADD,
BinaryOperator::Divide(_) => PRIORITY_MULT,
BinaryOperator::Div(_) => PRIORITY_MULT,
BinaryOperator::Mod(_) => PRIORITY_MULT,
BinaryOperator::Mult(_) => PRIORITY_MULT,
BinaryOperator::Collate(_) => 20,
BinaryOperator::JsonExtract(_) => PRIORITY_JSON_EXTRACT,
BinaryOperator::JsonExtractUnquote(_) => PRIORITY_JSON_EXTRACT,
BinaryOperator::Contains(_)
| BinaryOperator::ContainedBy(_)
| BinaryOperator::JsonPathMatch(_)
| BinaryOperator::JsonPathExists(_)
| BinaryOperator::JsonbKeyExists(_)
| BinaryOperator::JsonbAnyKeyExists(_)
| BinaryOperator::JsonbAllKeyExists(_)
| BinaryOperator::JsonGetPath(_)
| BinaryOperator::JsonGetPathText(_)
| BinaryOperator::JsonDeletePath(_)
| BinaryOperator::RegexMatch(_)
| BinaryOperator::RegexIMatch(_)
| BinaryOperator::NotRegexMatch(_)
| BinaryOperator::NotRegexIMatch(_)
| BinaryOperator::User(_, _)
| BinaryOperator::Operator(_, _) => PRIORITY_PG_CUSTOM,
}
}
}
impl Priority for UnaryOperator {
fn priority(&self) -> usize {
match self {
UnaryOperator::Binary(_) => 20,
UnaryOperator::LogicalNot(_) => 30,
UnaryOperator::Minus(_) => 40,
UnaryOperator::Not(_) => 130,
}
}
}
#[derive(Debug)]
enum ReduceMember<'a> {
Expression(Expression<'a>),
Binary(BinaryOperator<'a>),
Unary(UnaryOperator),
}
struct Reducer<'a> {
stack: Vec<ReduceMember<'a>>,
}
impl<'a> Reducer<'a> {
fn reduce(&mut self, priority: usize) -> Result<(), &'static str> {
let mut e = match self.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => {
return Err("Expected expression before here");
}
};
loop {
let v = self.stack.pop();
match v {
None => break,
Some(ReduceMember::Expression(_)) => return Err("ICE Reduce stack error 1"),
Some(ReduceMember::Unary(op)) if op.priority() > priority => {
self.stack.push(ReduceMember::Unary(op));
break;
}
Some(ReduceMember::Binary(op)) if op.priority() > priority => {
self.stack.push(ReduceMember::Binary(op));
break;
}
Some(ReduceMember::Unary(op)) => {
e = Expression::Unary(Box::new(UnaryExpression { op, operand: e }));
}
Some(ReduceMember::Binary(op)) => {
let lhs = match self.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => return Err("ICE Reduce stack error 2"),
};
e = Expression::Binary(Box::new(BinaryExpression { op, lhs, rhs: e }));
}
}
}
self.stack.push(ReduceMember::Expression(e));
Ok(())
}
fn shift_binop(&mut self, op: BinaryOperator<'a>) -> Result<(), &'static str> {
self.reduce(op.priority())?;
self.stack.push(ReduceMember::Binary(op));
Ok(())
}
fn shift_unary(&mut self, op: UnaryOperator) -> Result<(), &'static str> {
if matches!(self.stack.last(), Some(ReduceMember::Expression(_))) {
return Err("Unary operator cannot come before expression");
}
self.stack.push(ReduceMember::Unary(op));
Ok(())
}
fn shift_expr(&mut self, e: Expression<'a>) -> Result<(), &'static str> {
if matches!(self.stack.last(), Some(ReduceMember::Expression(_))) {
return Err("Expression should not follow expression");
}
self.stack.push(ReduceMember::Expression(e));
Ok(())
}
}
fn parse_array_element<'a>(parser: &mut Parser<'a, '_>) -> Result<Expression<'a>, ParseError> {
if matches!(parser.token, Token::LBracket) {
let lbracket = parser.consume_token(Token::LBracket)?;
let mut elements = Vec::new();
if !matches!(parser.token, Token::RBracket) {
loop {
parser.recovered(
"']' or ','",
&|t| matches!(t, Token::RBracket | Token::Comma),
|parser| {
elements.push(parse_array_element(parser)?);
Ok(())
},
)?;
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
}
let rbracket = parser.consume_token(Token::RBracket)?;
let bracket_span = lbracket.join_span(&rbracket);
Ok(Expression::Array(Box::new(ArrayExpression {
array_span: bracket_span.clone(),
bracket_span,
elements,
})))
} else {
parse_expression_unreserved(parser, PRIORITY_MAX)
}
}
pub(crate) fn parse_expression_restricted<'a>(
parser: &mut Parser<'a, '_>,
max_priority: usize,
restrict: Restrict,
) -> Result<Expression<'a>, ParseError> {
let mut r = Reducer { stack: Vec::new() };
loop {
if matches!(r.stack.last(), Some(ReduceMember::Expression(_)))
&& matches!(
parser.token,
Token::Ident(
_,
Keyword::NULLS
| Keyword::FIRST
| Keyword::LAST
| Keyword::ROW
| Keyword::ROWS
| Keyword::RANGE
| Keyword::ONLY
| Keyword::FILTER
| Keyword::PRECEDING
| Keyword::FOLLOWING
)
)
{
break;
}
if matches!(r.stack.last(), Some(ReduceMember::Expression(_)))
&& matches!(parser.token, Token::Ident(_, Keyword::NOT))
&& matches!(parser.peek(), Token::Ident(_, Keyword::NULL))
{
break;
}
let e = match parser.token.clone() {
Token::ColonEq if PRIORITY_ASSIGN < max_priority => {
r.shift_binop(BinaryOperator::Assignment(parser.consume()))
}
Token::Ident(_, Keyword::OR) if PRIORITY_OR < max_priority => {
r.shift_binop(BinaryOperator::Or(parser.consume()))
}
Token::DoublePipe if PRIORITY_ADD < max_priority => {
r.shift_binop(BinaryOperator::Concat(parser.consume()))
}
Token::Ident(_, Keyword::XOR) if PRIORITY_XOR < max_priority => {
r.shift_binop(BinaryOperator::Xor(parser.consume()))
}
Token::Ident(_, Keyword::AND) | Token::DoubleAmpersand
if PRIORITY_AND < max_priority =>
{
r.shift_binop(BinaryOperator::And(parser.consume()))
}
Token::Eq if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::Eq(parser.consume()))
}
Token::Spaceship if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::NullSafeEq(parser.consume()))
}
Token::GtEq if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::GtEq(parser.consume()))
}
Token::Gt if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::Gt(parser.consume()))
}
Token::LtEq if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::LtEq(parser.consume()))
}
Token::Lt if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::Lt(parser.consume()))
}
Token::Neq if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::Neq(parser.consume()))
}
Token::ShiftLeft if PRIORITY_SHIFT < max_priority => {
r.shift_binop(BinaryOperator::ShiftLeft(parser.consume()))
}
Token::ShiftRight if PRIORITY_SHIFT < max_priority => {
r.shift_binop(BinaryOperator::ShiftRight(parser.consume()))
}
Token::Ampersand => r.shift_binop(BinaryOperator::BitAnd(parser.consume())),
Token::Pipe if PRIORITY_BITOR < max_priority => {
r.shift_binop(BinaryOperator::BitOr(parser.consume()))
}
Token::Ident(_, Keyword::BINARY) if PRIORITY_JSON_EXTRACT < max_priority => {
r.shift_unary(UnaryOperator::Binary(parser.consume()))
}
Token::Ident(_, Keyword::COLLATE)
if 20 < max_priority
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
let collate_span = parser.consume_keyword(Keyword::COLLATE)?;
let collation = parser.consume_plain_identifier_unreserved()?;
if let Err(e) = r.shift_binop(BinaryOperator::Collate(collate_span)) {
parser.err_here(e)?;
}
r.shift_expr(Expression::Identifier(Box::new(IdentifierExpression {
parts: vec![IdentifierPart::Name(collation)],
})))
}
Token::ExclamationMark if PRIORITY_JSON_EXTRACT < max_priority => {
r.shift_unary(UnaryOperator::LogicalNot(parser.consume()))
}
Token::Minus if !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) => {
r.shift_unary(UnaryOperator::Minus(parser.consume()))
}
Token::Minus
if PRIORITY_ADD < max_priority
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
r.shift_binop(BinaryOperator::Subtract(parser.consume()))
}
Token::Ident(_, Keyword::IN) if PRIORITY_CMP < max_priority => {
if let Err(e) = r.reduce(PRIORITY_CMP) {
parser.err_here(e)?;
}
let lhs = match r.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => parser.err_here("Expected expression before here 3")?,
};
let op = parser.consume_keyword(Keyword::IN)?;
parser.consume_token(Token::LParen)?;
let mut rhs = Vec::new();
loop {
parser.recovered(
"')' or ','",
&|t| matches!(t, Token::RParen | Token::Comma),
|parser| {
rhs.push(parse_expression_paren(parser)?);
Ok(())
},
)?;
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
parser.consume_token(Token::RParen)?;
r.shift_expr(Expression::In(Box::new(InExpression {
lhs,
rhs,
in_span: op,
not_in: false,
})))
}
Token::Ident(_, Keyword::IS) if PRIORITY_CMP < max_priority => {
if let Err(e) = r.reduce(PRIORITY_CMP) {
parser.err_here(e)?;
}
let lhs = match r.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => parser.err_here("Expected expression before here 4")?,
};
let op = parser.consume_keyword(Keyword::IS)?;
let (is, op) = match &parser.token {
Token::Ident(_, Keyword::NOT) => {
parser.consume();
match &parser.token {
Token::Ident(_, Keyword::TRUE) => {
(Is::NotTrue, parser.consume().join_span(&op))
}
Token::Ident(_, Keyword::FALSE) => {
(Is::NotFalse, parser.consume().join_span(&op))
}
Token::Ident(_, Keyword::NULL) => {
(Is::NotNull, parser.consume().join_span(&op))
}
Token::Ident(_, Keyword::UNKNOWN) => {
(Is::NotUnknown, parser.consume().join_span(&op))
}
Token::Ident(_, Keyword::DISTINCT) => {
let op_span = parser
.consume_keywords(&[Keyword::DISTINCT, Keyword::FROM])?
.join_span(&op);
parser.postgres_only(&op_span);
let rhs = parse_expression_unreserved(parser, PRIORITY_AND)?;
(Is::NotDistinctFrom(rhs), op_span)
}
_ => parser.expected_failure(
"'TRUE', 'FALSE', 'UNKNOWN', 'NULL' or 'DISTINCT'",
)?,
}
}
Token::Ident(_, Keyword::TRUE) => (Is::True, parser.consume().join_span(&op)),
Token::Ident(_, Keyword::FALSE) => (Is::False, parser.consume().join_span(&op)),
Token::Ident(_, Keyword::NULL) => (Is::Null, parser.consume().join_span(&op)),
Token::Ident(_, Keyword::DISTINCT) => {
let op_span = parser
.consume_keywords(&[Keyword::DISTINCT, Keyword::FROM])?
.join_span(&op);
parser.postgres_only(&op_span);
let rhs = parse_expression_unreserved(parser, PRIORITY_AND)?;
(Is::DistinctFrom(rhs), op_span)
}
Token::Ident(_, Keyword::UNKNOWN) => {
(Is::Unknown, parser.consume().join_span(&op))
}
_ => parser.expected_failure(
"'NOT', 'TRUE', 'FALSE', 'UNKNOWN', 'NULL' or 'DISTINCT'",
)?,
};
r.shift_expr(Expression::Is(Box::new(IsExpression {
lhs,
is,
is_span: op,
})))
}
Token::DoubleColon
if parser.options.dialect.is_postgresql()
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
if let Err(e) = r.reduce(PRIORITY_TYPECAST) {
parser.err_here(e)?;
}
let expr = match r.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => parser.err_here("Expected expression before '::'")?,
};
let doublecolon_span = parser.consume_token(Token::DoubleColon)?;
let type_ = parse_data_type(parser, DataTypeContext::TypeRef)?;
r.shift_expr(Expression::TypeCast(Box::new(TypeCastExpression {
expr,
doublecolon_span,
type_,
})))
}
Token::LBracket
if parser.options.dialect.is_postgresql()
&& PRIORITY_TYPECAST < max_priority
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
if let Err(e) = r.reduce(PRIORITY_TYPECAST) {
parser.err_here(e)?;
}
let expr = match r.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => parser.err_here("Expected expression before '['")?,
};
let lbracket = parser.consume_token(Token::LBracket)?;
let lower = parse_expression_unreserved(parser, PRIORITY_MAX)?;
let upper = if parser.skip_token(Token::Colon).is_some() {
Some(parse_expression_unreserved(parser, PRIORITY_MAX)?)
} else {
None
};
let rbracket = parser.consume_token(Token::RBracket)?;
let bracket_span = lbracket.join_span(&rbracket);
r.shift_expr(Expression::ArraySubscript(Box::new(
ArraySubscriptExpression {
expr,
bracket_span,
lower,
upper,
},
)))
}
Token::Period
if parser.options.dialect.is_postgresql()
&& PRIORITY_TYPECAST < max_priority
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
if let Err(e) = r.reduce(PRIORITY_TYPECAST) {
parser.err_here(e)?;
}
let expr = match r.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => parser.err_here("Expected expression before '.'")?,
};
let dot_span = parser.consume_token(Token::Period)?;
let field = parser.consume_plain_identifier_unreserved()?;
r.shift_expr(Expression::FieldAccess(Box::new(FieldAccessExpression {
expr,
dot_span,
field,
})))
}
Token::Ident(_, Keyword::NOT)
if !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
r.shift_unary(UnaryOperator::Not(parser.consume()))
}
Token::Ident(_, Keyword::NOT)
if PRIORITY_CMP < max_priority
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
if let Err(e) = r.reduce(PRIORITY_CMP) {
parser.err_here(e)?;
}
let lhs = match r.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => parser.err_here("Expected expression before here 2")?,
};
let op = parser.consume_keyword(Keyword::NOT)?;
match &parser.token {
Token::Ident(_, Keyword::IN) => {
let op = parser.consume_keyword(Keyword::IN)?.join_span(&op);
parser.consume_token(Token::LParen)?;
let mut rhs = Vec::new();
loop {
parser.recovered(
"')' or ','",
&|t| matches!(t, Token::RParen | Token::Comma),
|parser| {
rhs.push(parse_expression_paren(parser)?);
Ok(())
},
)?;
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
parser.consume_token(Token::RParen)?;
r.shift_expr(Expression::In(Box::new(InExpression {
lhs,
rhs,
in_span: op,
not_in: true,
})))
}
Token::Ident(_, Keyword::LIKE) => {
r.stack.push(ReduceMember::Expression(lhs));
r.shift_binop(BinaryOperator::NotLike(parser.consume().join_span(&op)))
}
Token::Ident(_, Keyword::REGEXP) if parser.options.dialect.is_maria() => {
r.stack.push(ReduceMember::Expression(lhs));
r.shift_binop(BinaryOperator::NotRegexp(parser.consume().join_span(&op)))
}
Token::Ident(_, Keyword::RLIKE) if parser.options.dialect.is_maria() => {
r.stack.push(ReduceMember::Expression(lhs));
r.shift_binop(BinaryOperator::NotRlike(parser.consume().join_span(&op)))
}
Token::Ident(_, Keyword::BETWEEN) => {
let between_span = parser.consume_keyword(Keyword::BETWEEN)?.join_span(&op);
let low = parse_expression_unreserved(parser, PRIORITY_AND)?;
let and_span = parser.consume_keyword(Keyword::AND)?;
let high = parse_expression_unreserved(parser, PRIORITY_AND)?;
r.shift_expr(Expression::Between(Box::new(BetweenExpression {
lhs,
low,
high,
between_span: between_span.join_span(&and_span),
not_between: true,
})))
}
_ => parser.expected_failure("'IN', 'LIKE', 'REGEXP', 'RLIKE' or 'BETWEEN'")?,
}
}
Token::Ident(_, Keyword::BETWEEN)
if PRIORITY_CMP < max_priority
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
if let Err(e) = r.reduce(PRIORITY_CMP) {
parser.err_here(e)?;
}
let lhs = match r.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => parser.err_here("Expected expression before BETWEEN")?,
};
let between_span = parser.consume_keyword(Keyword::BETWEEN)?;
let low = parse_expression_unreserved(parser, PRIORITY_AND)?;
let and_span = parser.consume_keyword(Keyword::AND)?;
let high = parse_expression_unreserved(parser, PRIORITY_AND)?;
r.shift_expr(Expression::Between(Box::new(BetweenExpression {
lhs,
low,
high,
between_span: between_span.join_span(&and_span),
not_between: false,
})))
}
Token::Ident(_, Keyword::LIKE) if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::Like(parser.consume()))
}
Token::Ident(_, Keyword::SIMILAR)
if PRIORITY_CMP < max_priority
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
if let Err(e) = r.reduce(PRIORITY_CMP) {
parser.err_here(e)?;
}
let lhs = match r.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => parser.err_here("Expected expression before SIMILAR")?,
};
let op_span = parser.consume_keywords(&[Keyword::SIMILAR, Keyword::TO])?;
parser.postgres_only(&op_span);
r.stack.push(ReduceMember::Expression(lhs));
r.shift_binop(BinaryOperator::Like(op_span))
}
Token::Ident(_, Keyword::REGEXP)
if PRIORITY_CMP < max_priority && parser.options.dialect.is_maria() =>
{
r.shift_binop(BinaryOperator::Regexp(parser.consume()))
}
Token::Ident(_, Keyword::RLIKE)
if PRIORITY_CMP < max_priority && parser.options.dialect.is_maria() =>
{
r.shift_binop(BinaryOperator::Rlike(parser.consume()))
}
Token::RArrowJson if PRIORITY_JSON_EXTRACT < max_priority => {
r.shift_binop(BinaryOperator::JsonExtract(parser.consume()))
}
Token::RDoubleArrowJson if PRIORITY_JSON_EXTRACT < max_priority => {
r.shift_binop(BinaryOperator::JsonExtractUnquote(parser.consume()))
}
Token::Contains if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::Contains(parser.consume()))
}
Token::ContainedBy if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::ContainedBy(parser.consume()))
}
Token::AtQuestion if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::JsonPathExists(parser.consume()))
}
Token::QuestionPipe if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::JsonbAnyKeyExists(parser.consume()))
}
Token::QuestionAmpersand if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::JsonbAllKeyExists(parser.consume()))
}
Token::HashArrow if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::JsonGetPath(parser.consume()))
}
Token::HashDoubleArrow if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::JsonGetPathText(parser.consume()))
}
Token::HashMinus if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::JsonDeletePath(parser.consume()))
}
Token::TildeStar if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::RegexIMatch(parser.consume()))
}
Token::NotTilde if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::NotRegexMatch(parser.consume()))
}
Token::NotTildeStar if PRIORITY_PG_CUSTOM < max_priority => {
r.shift_binop(BinaryOperator::NotRegexIMatch(parser.consume()))
}
Token::LikeTilde if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::Like(parser.consume()))
}
Token::NotLikeTilde if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::NotLike(parser.consume()))
}
Token::AtAt
if PRIORITY_PG_CUSTOM < max_priority
&& parser.options.dialect.is_postgresql()
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
r.shift_binop(BinaryOperator::JsonPathMatch(parser.consume()))
}
Token::Tilde
if PRIORITY_PG_CUSTOM < max_priority
&& parser.options.dialect.is_postgresql()
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
r.shift_binop(BinaryOperator::RegexMatch(parser.consume()))
}
Token::QuestionMark
if PRIORITY_PG_CUSTOM < max_priority
&& parser.options.dialect.is_postgresql()
&& !matches!(parser.options.arguments, crate::SQLArguments::QuestionMark)
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
r.shift_binop(BinaryOperator::JsonbKeyExists(parser.consume()))
}
Token::PostgresOperator(op)
if PRIORITY_PG_CUSTOM < max_priority
&& parser.options.dialect.is_postgresql()
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
r.shift_binop(BinaryOperator::User(op, parser.consume()))
}
Token::Ident(_, Keyword::OPERATOR)
if PRIORITY_PG_CUSTOM < max_priority
&& parser.options.dialect.is_postgresql()
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
let operator_span = parser.consume_keyword(Keyword::OPERATOR)?;
parser.consume_token(Token::LParen)?;
let op_name = parse_operator_name(parser)?;
let rparen_span = parser.consume_token(Token::RParen)?;
let full_span = operator_span.join_span(&rparen_span);
r.shift_binop(BinaryOperator::Operator(op_name, full_span))
}
Token::Ident(_, Keyword::MEMBER)
if PRIORITY_CMP < max_priority
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
if let Err(e) = r.reduce(PRIORITY_CMP) {
parser.err_here(e)?;
}
let lhs = match r.stack.pop() {
Some(ReduceMember::Expression(e)) => e,
_ => parser.err_here("Expected expression before here")?,
};
let member_span = parser.consume_keyword(Keyword::MEMBER)?;
let of_span = parser.consume_keyword(Keyword::OF)?;
parser.consume_token(Token::LParen)?;
let rhs = parse_expression_paren(parser)?;
parser.consume_token(Token::RParen)?;
r.shift_expr(Expression::MemberOf(Box::new(MemberOfExpression {
lhs,
rhs,
member_of_span: member_span.join_span(&of_span),
})))
}
Token::Ident(_, Keyword::INTERVAL) => {
let interval_span = parser.consume();
let (time_interval, embedded_unit) = match parser.token {
Token::String(..) => {
let v = parser.consume_string()?;
let str_span = v.span();
let mut nums = Vec::new();
let mut embedded: Option<TimeUnit> = None;
for part in v.split([':', '!', ',', '.', '-', ' ']) {
if let Ok(n) = part.parse::<i64>() {
nums.push(n);
} else if !part.is_empty() {
embedded = parse_time_unit_from_str(part);
}
}
((nums, str_span), embedded)
}
Token::Integer(_) => {
let (v, s) = parser.consume_int()?;
((vec![v], s), None)
}
_ => parser.err_here("Expected integer or string")?,
};
let time_unit = if let Some(u) = parse_time_unit(&parser.token) {
(u, parser.consume())
} else if let Some(u) = embedded_unit {
(u, time_interval.1.clone())
} else {
parser.err_here("Expected time unit")?
};
let e = Expression::Interval(Box::new(IntervalExpression {
interval_span,
time_interval,
time_unit,
}));
r.shift_expr(e)
}
Token::Ident(_, Keyword::TIMESTAMPADD) => {
let timestamp_add_span = parser.consume();
parser.consume_token(Token::LParen)?;
let parts = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
let Some(u) = parse_time_unit(&parser.token) else {
parser.err_here("Expected time unit")?
};
let unit = (u, parser.consume());
parser.consume_token(Token::Comma)?;
let interval = parse_expression_outer(parser)?;
parser.consume_token(Token::Comma)?;
let datetime = parse_expression_outer(parser)?;
Ok(Some((unit, interval, datetime)))
})?;
parser.consume_token(Token::RParen)?;
if let Some((unit, interval, datetime)) = parts {
r.shift_expr(Expression::TimestampAdd(Box::new(TimestampAddExpression {
timestamp_add_span,
unit,
interval,
datetime,
})))
} else {
r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
span: timestamp_add_span,
})))
}
}
Token::Ident(_, Keyword::TIMESTAMPDIFF) => {
let timestamp_diff_span = parser.consume();
parser.consume_token(Token::LParen)?;
let parts = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
let Some(u) = parse_time_unit(&parser.token) else {
parser.err_here("Expected time unit")?
};
let unit = (u, parser.consume());
parser.consume_token(Token::Comma)?;
let e1 = parse_expression_outer(parser)?;
parser.consume_token(Token::Comma)?;
let e2 = parse_expression_outer(parser)?;
Ok(Some((unit, e1, e2)))
})?;
parser.consume_token(Token::RParen)?;
if let Some((unit, e1, e2)) = parts {
r.shift_expr(Expression::TimestampDiff(Box::new(
TimestampDiffExpression {
timestamp_diff_span,
unit,
e1,
e2,
},
)))
} else {
r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
span: timestamp_diff_span,
})))
}
}
Token::Plus if PRIORITY_ADD < max_priority => {
r.shift_binop(BinaryOperator::Add(parser.consume()))
}
Token::Div if PRIORITY_MULT < max_priority => {
r.shift_binop(BinaryOperator::Divide(parser.consume()))
}
Token::Ident(_, Keyword::DIV) if PRIORITY_MULT < max_priority => {
r.shift_binop(BinaryOperator::Div(parser.consume()))
}
Token::Minus if PRIORITY_ADD < max_priority => {
r.shift_binop(BinaryOperator::Subtract(parser.consume()))
}
Token::Ident(_, Keyword::LIKE) if PRIORITY_CMP < max_priority => {
r.shift_binop(BinaryOperator::Like(parser.consume()))
}
Token::Mul if !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) => r
.shift_expr(Expression::Identifier(Box::new(IdentifierExpression {
parts: vec![IdentifierPart::Star(parser.consume_token(Token::Mul)?)],
}))),
Token::Mul
if PRIORITY_MULT < max_priority
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
r.shift_binop(BinaryOperator::Mult(parser.consume()))
}
Token::Mod if PRIORITY_MULT < max_priority => {
r.shift_binop(BinaryOperator::Mod(parser.consume()))
}
Token::Ident(_, Keyword::MOD)
if PRIORITY_MULT < max_priority
&& matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
r.shift_binop(BinaryOperator::Mod(parser.consume()))
}
Token::Ident(_, Keyword::TRUE) => {
r.shift_expr(Expression::Bool(Box::new(BoolExpression {
value: true,
span: parser.consume_keyword(Keyword::TRUE)?,
})))
}
Token::Ident(_, Keyword::FALSE) => {
r.shift_expr(Expression::Bool(Box::new(BoolExpression {
value: false,
span: parser.consume_keyword(Keyword::FALSE)?,
})))
}
Token::Ident(_, Keyword::NULL) => {
r.shift_expr(Expression::Null(Box::new(NullExpression {
span: parser.consume_keyword(Keyword::NULL)?,
})))
}
Token::Ident(_, Keyword::_LIST_) if parser.options.list_hack => {
let arg = parser.arg;
parser.arg += 1;
r.shift_expr(Expression::ListHack(Box::new(ListHackExpression {
index: arg,
span: parser.consume_keyword(Keyword::_LIST_)?,
})))
}
Token::String(..) => {
r.shift_expr(Expression::String(Box::new(parser.consume_string()?)))
}
Token::Integer(_) => {
let (value, span) = parser.consume_int()?;
r.shift_expr(Expression::Integer(Box::new(IntegerExpression {
value,
span,
})))
}
Token::Float(_) => {
let (value, span) = parser.consume_float()?;
r.shift_expr(Expression::Float(Box::new(FloatExpression { value, span })))
}
Token::Ident(_, Keyword::CAST) => {
let cast_span = parser.consume_keyword(Keyword::CAST)?;
parser.consume_token(Token::LParen)?;
let cast = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
let expr = parse_expression_outer(parser)?;
let as_span = parser.consume_keyword(Keyword::AS)?;
let type_ = parse_data_type(parser, DataTypeContext::TypeRef)?;
Ok(Some((expr, as_span, type_)))
})?;
parser.consume_token(Token::RParen)?;
if let Some((expr, as_span, type_)) = cast {
r.shift_expr(Expression::Cast(Box::new(CastExpression {
cast_span,
expr,
as_span,
type_,
})))
} else {
r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
span: cast_span,
})))
}
}
Token::Ident(_, Keyword::CONVERT) => {
let convert_span = parser.consume_keyword(Keyword::CONVERT)?;
parser.consume_token(Token::LParen)?;
let convert =
parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
let expr = parse_expression_outer(parser)?;
if parser.skip_keyword(Keyword::USING).is_some() {
let charset = parser.consume_plain_identifier_unreserved()?;
Ok(Some((expr, None, Some(charset))))
} else {
parser.consume_token(Token::Comma)?;
let type_ = parse_data_type(parser, DataTypeContext::TypeRef)?;
Ok(Some((expr, Some(type_), None)))
}
})?;
parser.consume_token(Token::RParen)?;
if let Some((expr, type_, charset)) = convert {
r.shift_expr(Expression::Convert(Box::new(ConvertExpression {
convert_span,
expr,
type_,
using_charset: charset.map(|c| (c.span(), c)),
})))
} else {
r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
span: convert_span,
})))
}
}
Token::Ident(_, Keyword::GROUP_CONCAT) => {
let group_concat_span: core::ops::Range<usize> =
parser.consume_keyword(Keyword::GROUP_CONCAT)?;
parser.consume_token(Token::LParen)?;
let distinct_span: Option<core::ops::Range<usize>> =
parser.skip_keyword(Keyword::DISTINCT);
let expr = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
let expr = parse_expression_outer(parser)?;
Ok(Some(expr))
})?;
parser.consume_token(Token::RParen)?;
if let Some(expr) = expr {
r.shift_expr(Expression::GroupConcat(Box::new(GroupConcatExpression {
group_concat_span,
distinct_span,
expr,
})))
} else {
r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
span: group_concat_span,
})))
}
}
Token::Ident(_, Keyword::TRIM) if matches!(parser.peek(), Token::LParen) => {
let trim_span = parser.consume_keyword(Keyword::TRIM)?;
parser.consume_token(Token::LParen)?;
let parts = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
let direction = match &parser.token {
Token::Ident(_, Keyword::BOTH) => {
Some(TrimDirection::Both(parser.consume()))
}
Token::Ident(_, Keyword::LEADING) => {
Some(TrimDirection::Leading(parser.consume()))
}
Token::Ident(_, Keyword::TRAILING) => {
Some(TrimDirection::Trailing(parser.consume()))
}
_ => None,
};
let (what, from_span, value) = if direction.is_some() {
if let Some(from_s) = parser.skip_keyword(Keyword::FROM) {
let value = parse_expression_outer(parser)?;
(None, Some(from_s), value)
} else {
let what = parse_expression_outer(parser)?;
let from_s = parser.consume_keyword(Keyword::FROM)?;
let value = parse_expression_outer(parser)?;
(Some(what), Some(from_s), value)
}
} else {
let first = parse_expression_outer(parser)?;
if let Some(from_s) = parser.skip_keyword(Keyword::FROM) {
let value = parse_expression_outer(parser)?;
(Some(first), Some(from_s), value)
} else {
(None, None, first)
}
};
Ok(Some((direction, what, from_span, value)))
})?;
parser.consume_token(Token::RParen)?;
if let Some((direction, what, from_span, value)) = parts {
r.shift_expr(Expression::Trim(Box::new(TrimExpression {
trim_span,
direction,
what,
from_span,
value,
})))
} else {
r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
span: trim_span,
})))
}
}
Token::Ident(_, Keyword::EXTRACT) => {
let extract_span = parser.consume_keyword(Keyword::EXTRACT)?;
parser.consume_token(Token::LParen)?;
let parts = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
let Some(u) = parse_time_unit(&parser.token) else {
parser.err_here("Expected time unit")?
};
let time_unit = (u, parser.consume());
let from_span = parser.consume_keyword(Keyword::FROM)?;
let date = parse_expression_outer(parser)?;
Ok(Some((time_unit, from_span, date)))
})?;
parser.consume_token(Token::RParen)?;
if let Some((time_unit, from_span, date)) = parts {
r.shift_expr(Expression::Extract(Box::new(ExtractExpression {
extract_span,
time_unit,
from_span,
date,
})))
} else {
r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
span: extract_span,
})))
}
}
Token::Ident(_, Keyword::MATCH) => {
let match_span = parser.consume_keyword(Keyword::MATCH)?;
parser.consume_token(Token::LParen)?;
let mut cols = Vec::new();
loop {
parser.recovered(
"')' or ','",
&|t| matches!(t, Token::RParen | Token::Comma),
|parser| {
cols.push(parse_expression_paren(parser)?);
Ok(())
},
)?;
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
parser.consume_token(Token::RParen)?;
let against_span = parser.consume_keyword(Keyword::AGAINST)?;
parser.consume_token(Token::LParen)?;
let expr = parse_expression_unreserved(parser, PRIORITY_CMP)?;
let mut mode: Option<MatchMode> = None;
if parser.skip_keyword(Keyword::IN).is_some() {
if let Some(boolean_span) = parser.skip_keyword(Keyword::BOOLEAN) {
let mode_span = parser.consume_keyword(Keyword::MODE)?;
mode = Some(MatchMode::InBoolean(boolean_span.join_span(&mode_span)));
} else if let Some(natural_span) = parser.skip_keyword(Keyword::NATURAL) {
let _language_span = parser.skip_keyword(Keyword::LANGUAGE);
let mode_span = parser.consume_keyword(Keyword::MODE)?;
let natural_total = natural_span.join_span(&mode_span);
if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
let expansion_total = with_span.join_span(
&parser.consume_keywords(&[Keyword::QUERY, Keyword::EXPANSION])?,
);
mode = Some(MatchMode::InNaturalLanguageWithQueryExpansion(
natural_total.join_span(&expansion_total),
));
} else {
mode = Some(MatchMode::InNaturalLanguage(natural_total));
}
}
} else if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
mode = Some(MatchMode::WithQueryExpansion(with_span.join_span(
&parser.consume_keywords(&[Keyword::QUERY, Keyword::EXPANSION])?,
)));
}
parser.consume_token(Token::RParen)?;
if mode.is_none() {
if parser.skip_keyword(Keyword::IN).is_some() {
if let Some(boolean_span) = parser.skip_keyword(Keyword::BOOLEAN) {
let mode_span = parser.consume_keyword(Keyword::MODE)?;
mode = Some(MatchMode::InBoolean(boolean_span.join_span(&mode_span)));
} else if let Some(natural_span) = parser.skip_keyword(Keyword::NATURAL) {
let _language_span = parser.skip_keyword(Keyword::LANGUAGE);
let mode_span = parser.consume_keyword(Keyword::MODE)?;
let natural_total = natural_span.join_span(&mode_span);
if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
let expansion_total = with_span.join_span(
&parser
.consume_keywords(&[Keyword::QUERY, Keyword::EXPANSION])?,
);
mode = Some(MatchMode::InNaturalLanguageWithQueryExpansion(
natural_total.join_span(&expansion_total),
));
} else {
mode = Some(MatchMode::InNaturalLanguage(natural_total));
}
}
} else if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
mode = Some(MatchMode::WithQueryExpansion(with_span.join_span(
&parser.consume_keywords(&[Keyword::QUERY, Keyword::EXPANSION])?,
)));
}
}
r.shift_expr(Expression::MatchAgainst(Box::new(MatchAgainstExpression {
match_span,
columns: cols,
against_span,
expr,
mode,
})))
}
Token::Ident(_, Keyword::LEFT) if matches!(parser.peek(), Token::LParen) => {
let i = parser.token.clone();
let s = parser.span.clone();
parser.consume();
r.shift_expr(parse_function(parser, i, s)?)
}
Token::Ident(_, Keyword::CHAR) if matches!(parser.peek(), Token::LParen) => {
let s = parser.span.clone();
parser.consume();
r.shift_expr(parse_char_function(parser, s)?)
}
Token::Ident(_, keyword)
if matches!(parser.peek(), Token::LParen)
&& is_aggregate_function_ident(&keyword) =>
{
let i = parser.token.clone();
let s = parser.span.clone();
parser.consume();
r.shift_expr(parse_aggregate_function(parser, i, s)?)
}
Token::Ident(charset, _)
if charset.starts_with('_') && matches!(parser.peek(), Token::String(..)) =>
{
parser.consume();
r.shift_expr(Expression::String(Box::new(parser.consume_string()?)))
}
Token::Ident(_, Keyword::ARRAY) if matches!(parser.peek(), Token::LBracket) => {
let array_span = parser.consume_keyword(Keyword::ARRAY)?;
parser.postgres_only(&array_span);
let lbracket = parser.consume_token(Token::LBracket)?;
let mut elements = Vec::new();
if !matches!(parser.token, Token::RBracket) {
loop {
parser.recovered(
"']' or ','",
&|t| matches!(t, Token::RBracket | Token::Comma),
|parser| {
elements.push(parse_array_element(parser)?);
Ok(())
},
)?;
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
}
let rbracket = parser.consume_token(Token::RBracket)?;
let bracket_span = lbracket.join_span(&rbracket);
r.shift_expr(Expression::Array(Box::new(ArrayExpression {
array_span,
bracket_span,
elements,
})))
}
Token::Ident(_, k)
if (k.expr_ident() || !k.restricted(restrict))
&& !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
let i = parser.token.clone();
let s = parser.span.clone();
parser.consume();
if matches!(parser.token, Token::LParen) {
r.shift_expr(parse_function(parser, i, s)?)
} else {
let f = match i {
Token::Ident(_, Keyword::CURRENT_TIMESTAMP) => {
Some(Function::CurrentTimestamp)
}
Token::Ident(_, Keyword::LOCALTIME | Keyword::LOCALTIMESTAMP) => {
Some(Function::Now)
}
Token::Ident(_, Keyword::UTC_TIMESTAMP) => Some(Function::UtcTimeStamp),
Token::Ident(_, Keyword::UTC_DATE) => Some(Function::UtcDate),
Token::Ident(_, Keyword::UTC_TIME) => Some(Function::UtcTime),
Token::Ident(_, Keyword::CURRENT_DATE) => Some(Function::CurDate),
Token::Ident(_, Keyword::CURRENT_TIME) => Some(Function::CurTime),
Token::Ident(_, Keyword::CURRENT_USER) => Some(Function::CurrentUser),
Token::Ident(_, Keyword::CURRENT_ROLE) => Some(Function::CurrentRole),
Token::Ident(_, Keyword::CURRENT_CATALOG) => Some(Function::CurrentCatalog),
Token::Ident(_, Keyword::SESSION_USER) => Some(Function::SessionUser),
Token::Ident(_, Keyword::USER)
if parser.options.dialect.is_postgresql() =>
{
Some(Function::CurrentUser)
}
_ => None,
};
if let Some(f) = f {
r.shift_expr(Expression::Function(Box::new(FunctionCallExpression {
function: f,
args: Vec::new(),
function_span: s,
})))
} else {
let mut parts = vec![IdentifierPart::Name(
parser.token_to_plain_identifier(&i, s)?,
)];
let mut last_ident_tok: Option<(Token<'a>, Span)> = None;
loop {
if parser.skip_token(Token::Period).is_none() {
break;
}
match &parser.token {
Token::Mul => {
last_ident_tok = None;
parts.push(IdentifierPart::Star(
parser.consume_token(Token::Mul)?,
));
}
Token::Ident(_, _) => {
let fn_tok = parser.token.clone();
let fn_span = parser.span.clone();
last_ident_tok = Some((fn_tok, fn_span));
parts.push(IdentifierPart::Name(
parser.consume_plain_identifier_unreserved()?,
));
}
_ => parser.expected_failure("Identifier or '*'")?,
}
}
if matches!(parser.token, Token::LParen) {
if let Some((fn_tok, fn_span)) = last_ident_tok {
let mut all_idents: Vec<Identifier<'a>> = parts[..parts.len() - 1]
.iter()
.filter_map(|p| match p {
IdentifierPart::Name(id) => Some(id.clone()),
_ => None,
})
.collect();
let fn_ident = Identifier {
value: match &fn_tok {
Token::Ident(v, _) => v,
_ => "",
},
span: fn_span.clone(),
};
all_idents.push(fn_ident);
let function_span = if all_idents.len() > 1 {
all_idents[0].span.join_span(&fn_span)
} else {
fn_span
};
r.shift_expr(parse_function_call(
parser,
Function::Other(all_idents),
function_span,
)?)
} else {
r.shift_expr(Expression::Identifier(Box::new(
IdentifierExpression { parts },
)))
}
} else {
r.shift_expr(Expression::Identifier(Box::new(IdentifierExpression {
parts,
})))
}
}
}
}
Token::QuestionMark
if matches!(parser.options.arguments, crate::SQLArguments::QuestionMark) =>
{
let arg = parser.arg;
parser.arg += 1;
r.shift_expr(Expression::Arg(Box::new(ArgExpression {
index: arg,
span: parser.consume_token(Token::QuestionMark)?,
})))
}
Token::PercentS if matches!(parser.options.arguments, crate::SQLArguments::Percent) => {
let arg = parser.arg;
parser.arg += 1;
r.shift_expr(Expression::Arg(Box::new(ArgExpression {
index: arg,
span: parser.consume_token(Token::PercentS)?,
})))
}
Token::DollarArg(arg)
if matches!(parser.options.arguments, crate::SQLArguments::Dollar) =>
{
r.shift_expr(Expression::Arg(Box::new(ArgExpression {
index: arg - 1,
span: parser.consume(),
})))
}
Token::LParen => {
let paren_start = parser.consume_token(Token::LParen)?;
let first = parse_expression_paren(parser)?;
if parser.skip_token(Token::Comma).is_some() {
let mut elements = vec![first];
loop {
if matches!(parser.token, Token::RParen) {
break;
}
parser.recovered(
"')' or ','",
&|t| matches!(t, Token::RParen | Token::Comma),
|parser| {
elements.push(parse_expression_paren(parser)?);
Ok(())
},
)?;
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
let paren_end = parser.consume_token(Token::RParen)?;
r.shift_expr(Expression::Row(Box::new(RowExpression {
paren_span: paren_start.join_span(&paren_end),
elements,
})))
} else {
parser.consume_token(Token::RParen)?;
r.shift_expr(first)
}
}
Token::Ident(_, Keyword::EXISTS) => {
let exists_span = parser.consume_keyword(Keyword::EXISTS)?;
parser.consume_token(Token::LParen)?;
let subquery = parse_compound_query(parser)?;
parser.consume_token(Token::RParen)?;
let ans = Expression::Exists(Box::new(ExistsExpression {
exists_span,
subquery,
}));
r.shift_expr(ans)
}
Token::Ident(_, Keyword::ANY | Keyword::SOME | Keyword::ALL)
if parser.options.dialect.is_postgresql()
&& !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
{
let quantifier = match &parser.token {
Token::Ident(_, Keyword::ANY) => {
Quantifier::Any(parser.consume_keyword(Keyword::ANY)?)
}
Token::Ident(_, Keyword::SOME) => {
Quantifier::Some(parser.consume_keyword(Keyword::SOME)?)
}
_ => Quantifier::All(parser.consume_keyword(Keyword::ALL)?),
};
parser.consume_token(Token::LParen)?;
let operand = parse_expression_paren(parser)?;
parser.consume_token(Token::RParen)?;
r.shift_expr(Expression::Quantifier(Box::new(QuantifierExpression {
quantifier,
operand,
})))
}
Token::Ident(_, Keyword::CASE) => {
let case_span = parser.consume_keyword(Keyword::CASE)?;
let value = if !matches!(parser.token, Token::Ident(_, Keyword::WHEN)) {
Some(parse_expression_unreserved(parser, PRIORITY_MAX)?)
} else {
None
};
let mut whens = Vec::new();
let mut else_ = None;
parser.recovered(
"'END'",
&|t| matches!(t, Token::Ident(_, Keyword::END)),
|parser| {
loop {
let when_span = parser.consume_keyword(Keyword::WHEN)?;
let when = parse_expression_unreserved(parser, PRIORITY_MAX)?;
let then_span = parser.consume_keyword(Keyword::THEN)?;
let then = parse_expression_unreserved(parser, PRIORITY_MAX)?;
whens.push(When {
when_span,
when,
then_span,
then,
});
if !matches!(parser.token, Token::Ident(_, Keyword::WHEN)) {
break;
}
}
if let Some(span) = parser.skip_keyword(Keyword::ELSE) {
else_ = Some((span, parse_expression_unreserved(parser, PRIORITY_MAX)?))
};
Ok(())
},
)?;
let end_span = parser.consume_keyword(Keyword::END)?;
r.shift_expr(Expression::Case(Box::new(CaseExpression {
case_span,
value,
whens,
else_,
end_span,
})))
}
Token::AtAtGlobal | Token::AtAtSession => {
let global = parser.skip_token(Token::AtAtGlobal);
let session = if global.is_none() {
Some(parser.consume_token(Token::AtAtSession)?)
} else {
None
};
let dot = Some(parser.consume_token(Token::Period)?);
let variable = match &parser.token {
Token::Ident(_, Keyword::TIME_ZONE) => Variable::TimeZone,
Token::Ident(t, _) => Variable::Other(t),
_ => parser.expected_failure("Identifier")?,
};
let variable_span = parser.consume();
r.shift_expr(Expression::Variable(Box::new(VariableExpression {
global,
session,
dot,
variable,
variable_span,
})))
}
Token::At => {
let at_span = parser.consume_token(Token::At)?;
let name = parser.consume_plain_identifier_unreserved()?;
r.shift_expr(Expression::UserVariable(Box::new(UserVariableExpression {
name,
at_span,
})))
}
_ => break,
};
if let Err(e) = e {
parser.err_here(e.to_string())?;
}
}
if r.reduce(99999).is_err() {
parser.err_here("Expected expression")
} else if r.stack.len() != 1 {
parser.ice(file!(), line!())
} else if let Some(ReduceMember::Expression(e)) = r.stack.pop() {
Ok(e)
} else {
parser.ice(file!(), line!())
}
}
pub(crate) fn parse_expression_unreserved<'a>(
parser: &mut Parser<'a, '_>,
max_priority: usize,
) -> Result<Expression<'a>, ParseError> {
parse_expression_restricted(parser, max_priority, parser.reserved())
}
pub(crate) fn parse_expression_or_default<'a>(
parser: &mut Parser<'a, '_>,
max_priority: usize,
) -> Result<Expression<'a>, ParseError> {
if matches!(parser.token, Token::Ident(_, Keyword::DEFAULT)) {
Ok(Expression::Default(Box::new(DefaultExpression {
span: parser.consume_keyword(Keyword::DEFAULT)?,
})))
} else {
parse_expression_unreserved(parser, max_priority)
}
}
pub(crate) fn parse_expression_outer<'a>(
parser: &mut Parser<'a, '_>,
) -> Result<Expression<'a>, ParseError> {
if matches!(parser.token, Token::Ident(_, Keyword::SELECT)) {
Ok(Expression::Subquery(Box::new(SubqueryExpression {
expression: parse_compound_query(parser)?,
})))
} else {
parse_expression_unreserved(parser, PRIORITY_MAX)
}
}
pub(crate) fn parse_expression_paren<'a>(
parser: &mut Parser<'a, '_>,
) -> Result<Expression<'a>, ParseError> {
if matches!(parser.token, Token::Ident(_, Keyword::SELECT)) {
Ok(Expression::Subquery(Box::new(SubqueryExpression {
expression: parse_compound_query(parser)?,
})))
} else {
parse_expression_unreserved(parser, PRIORITY_MAX)
}
}
#[cfg(test)]
mod tests {
use core::ops::Deref;
use alloc::{
format,
string::{String, ToString},
};
use crate::{
BinaryExpression, ParseOptions, SQLDialect,
expression::{BinaryOperator, Expression, PRIORITY_MAX, Quantifier},
issue::Issues,
parser::Parser,
};
use super::{IdentifierPart, parse_expression_unreserved};
fn test_ident<'a>(e: &Expression<'a>, v: &str) -> Result<(), String> {
let v = match e {
Expression::Identifier(a) => match a.parts.as_slice() {
[IdentifierPart::Name(vv)] => vv.deref() == v,
_ => false,
},
_ => false,
};
if !v {
Err(format!("Expected identifier {} found {:?}", v, e))
} else {
Ok(())
}
}
fn test_expr(src: &'static str, f: impl FnOnce(&Expression<'_>) -> Result<(), String>) {
let mut issues = Issues::new(src);
let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
let mut parser = Parser::new(src, &mut issues, &options);
let res = parse_expression_unreserved(&mut parser, PRIORITY_MAX)
.expect("Expression in test expr");
if let Err(e) = f(&res) {
panic!("Error parsing {}: {}\nGot {:#?}", src, e, res);
}
}
#[test]
fn expressions() {
test_expr("`a` + `b` * `c` + `d`", |e| {
match e {
Expression::Binary(b) => {
let BinaryExpression {
op: BinaryOperator::Add(_),
lhs,
rhs,
..
} = b.as_ref()
else {
return Err("Expected outer +".to_string());
};
match lhs {
Expression::Binary(b) => {
let BinaryExpression {
op: BinaryOperator::Add(_),
lhs,
rhs,
..
} = b.as_ref()
else {
return Err("Expected inner +".to_string());
};
test_ident(lhs, "a")?;
match rhs {
Expression::Binary(b) => {
let BinaryExpression {
op: BinaryOperator::Mult(_),
lhs,
rhs,
..
} = b.as_ref()
else {
return Err("Expected *".to_string());
};
test_ident(lhs, "b")?;
test_ident(rhs, "c")?;
}
_ => return Err("Lhs.Rhs".to_string()),
}
}
_ => return Err("Lhs".to_string()),
}
test_ident(rhs, "d")?;
}
_ => return Err("Outer".to_string()),
}
Ok(())
});
}
fn test_expr_pg(src: &'static str, f: impl FnOnce(&Expression<'_>) -> Result<(), String>) {
let mut issues = Issues::new(src);
let options = ParseOptions::new().dialect(SQLDialect::PostgreSQL);
let mut parser = Parser::new(src, &mut issues, &options);
let res = parse_expression_unreserved(&mut parser, PRIORITY_MAX)
.expect("Expression in test_expr_pg");
if let Err(e) = f(&res) {
panic!("Error parsing {}: {}\nGot {:#?}", src, e, res);
}
}
#[test]
fn quantifier_any() {
test_expr_pg(
"salary > ANY (SELECT max_salary FROM departments)",
|e| match e {
Expression::Binary(b) => match &b.rhs {
Expression::Quantifier(q) => {
assert!(matches!(q.quantifier, Quantifier::Any(_)));
Ok(())
}
_ => Err(format!("Expected Quantifier RHS, got {:?}", b.rhs)),
},
_ => Err(format!("Expected Binary expression, got {:?}", e)),
},
);
}
#[test]
fn quantifier_some() {
test_expr_pg("x = SOME (ARRAY[1, 2, 3])", |e| match e {
Expression::Binary(b) => match &b.rhs {
Expression::Quantifier(q) => {
assert!(matches!(q.quantifier, Quantifier::Some(_)));
Ok(())
}
_ => Err(format!("Expected Quantifier RHS, got {:?}", b.rhs)),
},
_ => Err(format!("Expected Binary expression, got {:?}", e)),
});
}
#[test]
fn quantifier_all() {
test_expr_pg("price <= ALL (SELECT price FROM products)", |e| match e {
Expression::Binary(b) => match &b.rhs {
Expression::Quantifier(q) => {
assert!(matches!(q.quantifier, Quantifier::All(_)));
Ok(())
}
_ => Err(format!("Expected Quantifier RHS, got {:?}", b.rhs)),
},
_ => Err(format!("Expected Binary expression, got {:?}", e)),
});
}
}