Skip to main content

qusql_parse/
expression.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13use crate::{
14    DataType, Identifier, QualifiedName, SString, Span, Spanned, Statement,
15    data_type::{DataTypeContext, parse_data_type},
16    function_expression::{
17        AggregateFunctionCallExpression, CharFunctionExpression, Function, FunctionCallExpression,
18        WindowFunctionCallExpression, is_aggregate_function_ident, parse_aggregate_function,
19        parse_char_function, parse_function, parse_function_call,
20    },
21    keywords::{Keyword, Restrict},
22    lexer::Token,
23    operator::parse_operator_name,
24    parser::{ParseError, Parser},
25    span::OptSpanned,
26    statement::parse_compound_query,
27};
28use alloc::string::ToString;
29use alloc::vec;
30use alloc::{boxed::Box, vec::Vec};
31
32/// Function to execute
33#[derive(Debug, Clone)]
34pub enum Variable<'a> {
35    TimeZone,
36    Other(&'a str),
37}
38
39/// Binary operator to apply
40#[derive(Debug, Clone)]
41pub enum BinaryOperator<'a> {
42    Or(Span),
43    Xor(Span),
44    And(Span),
45    Eq(Span),
46    NullSafeEq(Span),
47    GtEq(Span),
48    Gt(Span),
49    LtEq(Span),
50    Lt(Span),
51    Neq(Span),
52    ShiftLeft(Span),
53    ShiftRight(Span),
54    BitAnd(Span),
55    BitOr(Span),
56    BitXor(Span),
57    Add(Span),
58    Subtract(Span),
59    Divide(Span),
60    Div(Span),
61    Mod(Span),
62    Mult(Span),
63    Like(Span),
64    NotLike(Span),
65    Regexp(Span),
66    NotRegexp(Span),
67    Rlike(Span),
68    NotRlike(Span),
69    Collate(Span),
70    JsonExtract(Span),
71    JsonExtractUnquote(Span),
72    Assignment(Span),
73    /// String/array concatenation `||`
74    Concat(Span),
75    // PostgreSQL-specific binary operators
76    /// && (array/range overlap)
77    Overlap(Span),
78    /// @>
79    Contains(Span),
80    /// <@
81    ContainedBy(Span),
82    /// @@ (full-text / jsonpath match)
83    JsonPathMatch(Span),
84    /// @?
85    JsonPathExists(Span),
86    /// ? (jsonb key exists)
87    JsonbKeyExists(Span),
88    /// ?|
89    JsonbAnyKeyExists(Span),
90    /// ?&
91    JsonbAllKeyExists(Span),
92    /// #>
93    JsonGetPath(Span),
94    /// #>>
95    JsonGetPathText(Span),
96    /// #-
97    JsonDeletePath(Span),
98    /// ~ (regex match)
99    RegexMatch(Span),
100    /// ~* (case-insensitive regex match)
101    RegexIMatch(Span),
102    /// !~ (regex not match)
103    NotRegexMatch(Span),
104    /// !~* (case-insensitive regex not match)
105    NotRegexIMatch(Span),
106    /// User-defined / unrecognised PostgreSQL operator
107    User(&'a str, Span),
108    /// PostgreSQL OPERATOR(schema.op) expression
109    Operator(QualifiedName<'a>, Span),
110}
111
112impl<'a> Spanned for BinaryOperator<'a> {
113    fn span(&self) -> Span {
114        match self {
115            BinaryOperator::Or(s)
116            | BinaryOperator::Xor(s)
117            | BinaryOperator::And(s)
118            | BinaryOperator::Eq(s)
119            | BinaryOperator::NullSafeEq(s)
120            | BinaryOperator::GtEq(s)
121            | BinaryOperator::Gt(s)
122            | BinaryOperator::LtEq(s)
123            | BinaryOperator::Lt(s)
124            | BinaryOperator::Neq(s)
125            | BinaryOperator::ShiftLeft(s)
126            | BinaryOperator::ShiftRight(s)
127            | BinaryOperator::BitAnd(s)
128            | BinaryOperator::BitOr(s)
129            | BinaryOperator::BitXor(s)
130            | BinaryOperator::Add(s)
131            | BinaryOperator::Subtract(s)
132            | BinaryOperator::Divide(s)
133            | BinaryOperator::Div(s)
134            | BinaryOperator::Mod(s)
135            | BinaryOperator::Mult(s)
136            | BinaryOperator::Like(s)
137            | BinaryOperator::NotLike(s)
138            | BinaryOperator::Regexp(s)
139            | BinaryOperator::NotRegexp(s)
140            | BinaryOperator::Rlike(s)
141            | BinaryOperator::NotRlike(s)
142            | BinaryOperator::Collate(s)
143            | BinaryOperator::JsonExtract(s)
144            | BinaryOperator::JsonExtractUnquote(s)
145            | BinaryOperator::Assignment(s)
146            | BinaryOperator::Concat(s)
147            | BinaryOperator::Overlap(s)
148            | BinaryOperator::Contains(s)
149            | BinaryOperator::ContainedBy(s)
150            | BinaryOperator::JsonPathMatch(s)
151            | BinaryOperator::JsonPathExists(s)
152            | BinaryOperator::JsonbKeyExists(s)
153            | BinaryOperator::JsonbAnyKeyExists(s)
154            | BinaryOperator::JsonbAllKeyExists(s)
155            | BinaryOperator::JsonGetPath(s)
156            | BinaryOperator::JsonGetPathText(s)
157            | BinaryOperator::JsonDeletePath(s)
158            | BinaryOperator::RegexMatch(s)
159            | BinaryOperator::RegexIMatch(s)
160            | BinaryOperator::NotRegexMatch(s)
161            | BinaryOperator::NotRegexIMatch(s) => s.clone(),
162            BinaryOperator::User(_, s) => s.clone(),
163            BinaryOperator::Operator(_, s) => s.clone(),
164        }
165    }
166}
167
168/// Mode for MATCH ... AGAINST
169#[derive(Debug, Clone)]
170pub enum MatchMode {
171    InBoolean(Span),
172    InNaturalLanguage(Span),
173    InNaturalLanguageWithQueryExpansion(Span),
174    WithQueryExpansion(Span),
175}
176
177impl Spanned for MatchMode {
178    fn span(&self) -> Span {
179        match self {
180            MatchMode::InBoolean(s) => s.clone(),
181            MatchMode::InNaturalLanguage(s) => s.clone(),
182            MatchMode::InNaturalLanguageWithQueryExpansion(s) => s.clone(),
183            MatchMode::WithQueryExpansion(s) => s.clone(),
184        }
185    }
186}
187
188/// Type of is expression
189#[derive(Debug, Clone)]
190pub enum Is<'a> {
191    Null,
192    NotNull,
193    True,
194    NotTrue,
195    False,
196    NotFalse,
197    Unknown,
198    NotUnknown,
199    DistinctFrom(Expression<'a>),
200    NotDistinctFrom(Expression<'a>),
201}
202
203/// Unary operator to apply
204#[derive(Debug, Clone)]
205pub enum UnaryOperator {
206    Binary(Span),
207    LogicalNot(Span),
208    Minus(Span),
209    Not(Span),
210}
211
212impl Spanned for UnaryOperator {
213    fn span(&self) -> Span {
214        match self {
215            UnaryOperator::Binary(s)
216            | UnaryOperator::LogicalNot(s)
217            | UnaryOperator::Minus(s)
218            | UnaryOperator::Not(s) => s.clone(),
219        }
220    }
221}
222
223/// Part of a full identifier
224#[derive(Debug, Clone)]
225pub enum IdentifierPart<'a> {
226    Name(Identifier<'a>),
227    Star(Span),
228}
229
230impl<'a> Spanned for IdentifierPart<'a> {
231    fn span(&self) -> Span {
232        match &self {
233            IdentifierPart::Name(v) => v.span(),
234            IdentifierPart::Star(v) => v.span(),
235        }
236    }
237}
238
239/// When part of CASE
240#[derive(Debug, Clone)]
241pub struct When<'a> {
242    /// Span of WHEN
243    pub when_span: Span,
244    /// When to return then
245    pub when: Expression<'a>,
246    /// Span of THEN
247    pub then_span: Span,
248    /// What to return when when applyes
249    pub then: Expression<'a>,
250}
251
252impl<'a> Spanned for When<'a> {
253    fn span(&self) -> Span {
254        self.when_span
255            .join_span(&self.when)
256            .join_span(&self.then_span)
257            .join_span(&self.then)
258    }
259}
260
261/// Units of time
262#[derive(Debug, Clone, PartialEq, Eq)]
263pub enum TimeUnit {
264    /// Microseconds
265    Microsecond,
266    /// Seconds
267    Second,
268    /// Minutes
269    Minute,
270    /// Hours
271    Hour,
272    /// Days
273    Day,
274    /// Weeks
275    Week,
276    /// Months
277    Month,
278    /// Quarters
279    Quarter,
280    /// Years
281    Year,
282    /// Seconds.Microseconds
283    SecondMicrosecond,
284    /// Minutes.Seconds.Microseconds
285    MinuteMicrosecond,
286    /// Minutes.Seconds
287    MinuteSecond,
288    /// Hours.Minutes.Seconds.Microseconds
289    HourMicrosecond,
290    /// Hours.Minutes.Seconds
291    HourSecond,
292    /// Hours.Minutes
293    HourMinute,
294    /// Days Hours.Minutes.Seconds.Microseconds
295    DayMicrosecond,
296    /// Days Hours.Minutes.Seconds
297    DaySecond,
298    /// Days Hours.Minutes
299    DayMinute,
300    /// Days Hours
301    DayHour,
302    /// Years-Months
303    YearMonth,
304    /// PostgreSQL: epoch (seconds since 1970-01-01)
305    Epoch,
306    /// PostgreSQL: day of week (0=Sunday)
307    Dow,
308    /// PostgreSQL: day of year
309    Doy,
310    /// PostgreSQL: century
311    Century,
312    /// PostgreSQL: decade
313    Decade,
314    /// PostgreSQL: ISO day of week (1=Monday)
315    IsoDow,
316    /// PostgreSQL: ISO year
317    IsoYear,
318    /// PostgreSQL: Julian day
319    Julian,
320    /// PostgreSQL: millennium
321    Millennium,
322    /// PostgreSQL: timezone offset in seconds
323    Timezone,
324    /// PostgreSQL: timezone hours component
325    TimezoneHour,
326    /// PostgreSQL: timezone minutes component
327    TimezoneMinute,
328}
329
330fn parse_time_unit_from_str(s: &str) -> Option<TimeUnit> {
331    match s.to_ascii_lowercase().trim_end_matches('s') {
332        "microsecond" => Some(TimeUnit::Microsecond),
333        "second" => Some(TimeUnit::Second),
334        "minute" => Some(TimeUnit::Minute),
335        "hour" => Some(TimeUnit::Hour),
336        "day" => Some(TimeUnit::Day),
337        "week" => Some(TimeUnit::Week),
338        "month" => Some(TimeUnit::Month),
339        "quarter" => Some(TimeUnit::Quarter),
340        "year" => Some(TimeUnit::Year),
341        "epoch" => Some(TimeUnit::Epoch),
342        "dow" => Some(TimeUnit::Dow),
343        "doy" => Some(TimeUnit::Doy),
344        "century" | "centurie" => Some(TimeUnit::Century),
345        "decade" => Some(TimeUnit::Decade),
346        "isodow" => Some(TimeUnit::IsoDow),
347        "isoyear" => Some(TimeUnit::IsoYear),
348        "julian" => Some(TimeUnit::Julian),
349        "millennium" | "millennia" | "millenniu" => Some(TimeUnit::Millennium),
350        "timezone" => Some(TimeUnit::Timezone),
351        "timezone_hour" => Some(TimeUnit::TimezoneHour),
352        "timezone_minute" => Some(TimeUnit::TimezoneMinute),
353        _ => None,
354    }
355}
356
357fn parse_time_unit(t: &Token<'_>) -> Option<TimeUnit> {
358    match t {
359        Token::Ident(_, Keyword::MICROSECOND) => Some(TimeUnit::Microsecond),
360        Token::Ident(_, Keyword::SECOND) => Some(TimeUnit::Second),
361        Token::Ident(_, Keyword::MINUTE) => Some(TimeUnit::Minute),
362        Token::Ident(_, Keyword::HOUR) => Some(TimeUnit::Hour),
363        Token::Ident(_, Keyword::DAY) => Some(TimeUnit::Day),
364        Token::Ident(_, Keyword::WEEK) => Some(TimeUnit::Week),
365        Token::Ident(_, Keyword::MONTH) => Some(TimeUnit::Month),
366        Token::Ident(_, Keyword::QUARTER) => Some(TimeUnit::Quarter),
367        Token::Ident(_, Keyword::YEAR) => Some(TimeUnit::Year),
368        Token::Ident(_, Keyword::SECOND_MICROSECOND) => Some(TimeUnit::SecondMicrosecond),
369        Token::Ident(_, Keyword::MINUTE_MICROSECOND) => Some(TimeUnit::MinuteMicrosecond),
370        Token::Ident(_, Keyword::MINUTE_SECOND) => Some(TimeUnit::MinuteSecond),
371        Token::Ident(_, Keyword::HOUR_MICROSECOND) => Some(TimeUnit::HourMicrosecond),
372        Token::Ident(_, Keyword::HOUR_SECOND) => Some(TimeUnit::HourSecond),
373        Token::Ident(_, Keyword::HOUR_MINUTE) => Some(TimeUnit::HourMinute),
374        Token::Ident(_, Keyword::DAY_MICROSECOND) => Some(TimeUnit::DayMicrosecond),
375        Token::Ident(_, Keyword::DAY_SECOND) => Some(TimeUnit::DaySecond),
376        Token::Ident(_, Keyword::DAY_MINUTE) => Some(TimeUnit::DayMinute),
377        Token::Ident(_, Keyword::DAY_HOUR) => Some(TimeUnit::DayHour),
378        Token::Ident(_, Keyword::YEAR_MONTH) => Some(TimeUnit::YearMonth),
379        Token::Ident(s, Keyword::NOT_A_KEYWORD) => parse_time_unit_from_str(s),
380        _ => None,
381    }
382}
383
384/// Expression with binary operator
385#[derive(Debug, Clone)]
386pub struct BinaryExpression<'a> {
387    pub op: BinaryOperator<'a>,
388    pub lhs: Expression<'a>,
389    pub rhs: Expression<'a>,
390}
391
392impl Spanned for BinaryExpression<'_> {
393    fn span(&self) -> Span {
394        self.op.span().join_span(&self.lhs).join_span(&self.rhs)
395    }
396}
397
398/// Expression with a unary (prefix) operator
399#[derive(Debug, Clone)]
400pub struct UnaryExpression<'a> {
401    pub op: UnaryOperator,
402    pub operand: Expression<'a>,
403}
404
405impl Spanned for UnaryExpression<'_> {
406    fn span(&self) -> Span {
407        self.op.span().join_span(&self.operand)
408    }
409}
410/// Time Interval
411#[derive(Debug, Clone)]
412pub struct IntervalExpression {
413    /// Span of "INTERVAL"
414    pub interval_span: Span,
415    /// Time internal
416    pub time_interval: (Vec<i64>, Span),
417    /// Unit of the time interval
418    pub time_unit: (TimeUnit, Span),
419}
420
421impl Spanned for IntervalExpression {
422    fn span(&self) -> Span {
423        self.interval_span
424            .join_span(&self.time_interval.1)
425            .join_span(&self.time_unit.1)
426    }
427}
428
429/// Extract expression
430#[derive(Debug, Clone)]
431pub struct ExtractExpression<'a> {
432    /// Span of "EXTRACT"
433    pub extract_span: Span,
434    /// Unit of the time interval
435    pub time_unit: (TimeUnit, Span),
436    /// Span of "FROM"
437    pub from_span: Span,
438    /// Date expression
439    pub date: Expression<'a>,
440    /// Span of the closing ')'
441    pub r_paren_span: Span,
442}
443
444impl Spanned for ExtractExpression<'_> {
445    fn span(&self) -> Span {
446        self.extract_span
447            .join_span(&self.time_unit.1)
448            .join_span(&self.from_span)
449            .join_span(&self.date)
450            .join_span(&self.r_paren_span)
451    }
452}
453
454/// Direction for TRIM()
455#[derive(Debug, Clone)]
456pub enum TrimDirection {
457    Both(Span),
458    Leading(Span),
459    Trailing(Span),
460}
461
462impl Spanned for TrimDirection {
463    fn span(&self) -> Span {
464        match self {
465            TrimDirection::Both(s) | TrimDirection::Leading(s) | TrimDirection::Trailing(s) => {
466                s.clone()
467            }
468        }
469    }
470}
471
472/// TRIM([{BOTH|LEADING|TRAILING} [remstr] FROM] str) expression
473#[derive(Debug, Clone)]
474pub struct TrimExpression<'a> {
475    /// Span of "TRIM"
476    pub trim_span: Span,
477    /// Optional BOTH / LEADING / TRAILING direction
478    pub direction: Option<TrimDirection>,
479    /// Optional removal string (remstr)
480    pub what: Option<Expression<'a>>,
481    /// Span of "FROM" when present
482    pub from_span: Option<Span>,
483    /// The string to trim
484    pub value: Expression<'a>,
485    /// Span of the closing ')'
486    pub r_paren_span: Span,
487}
488
489impl Spanned for TrimExpression<'_> {
490    fn span(&self) -> Span {
491        self.trim_span
492            .join_span(&self.direction)
493            .join_span(&self.what)
494            .join_span(&self.from_span)
495            .join_span(&self.value)
496            .join_span(&self.r_paren_span)
497    }
498}
499
500/// In expression
501#[derive(Debug, Clone)]
502pub struct InExpression<'a> {
503    /// Left hand side expression
504    pub lhs: Expression<'a>,
505    /// Right hand side expression
506    pub rhs: Vec<Expression<'a>>,
507    /// Span of "IN" or "NOT IN"
508    pub in_span: Span,
509    /// True if not in
510    pub not_in: bool,
511    /// Span of the closing ')'
512    pub r_paren_span: Span,
513}
514
515impl Spanned for InExpression<'_> {
516    fn span(&self) -> Span {
517        self.in_span
518            .join_span(&self.lhs)
519            .join_span(&self.rhs)
520            .join_span(&self.r_paren_span)
521    }
522}
523
524/// Between expression (expr BETWEEN low AND high)
525#[derive(Debug, Clone)]
526pub struct BetweenExpression<'a> {
527    /// The value being tested
528    pub lhs: Expression<'a>,
529    /// Lower bound
530    pub low: Expression<'a>,
531    /// Upper bound
532    pub high: Expression<'a>,
533    /// Span covering "BETWEEN ... AND"
534    pub between_span: Span,
535    /// True if NOT BETWEEN
536    pub not_between: bool,
537}
538
539impl Spanned for BetweenExpression<'_> {
540    fn span(&self) -> Span {
541        self.between_span
542            .join_span(&self.lhs)
543            .join_span(&self.low)
544            .join_span(&self.high)
545    }
546}
547
548/// The operator used in a [`LikeExpression`]
549#[derive(Debug, Clone)]
550pub enum LikeOperator {
551    /// LIKE
552    Like(Span),
553    /// NOT LIKE
554    NotLike(Span),
555    /// SIMILAR TO (PostgreSQL)
556    SimilarTo(Span),
557}
558
559impl Spanned for LikeOperator {
560    fn span(&self) -> Span {
561        match self {
562            LikeOperator::Like(s) | LikeOperator::NotLike(s) | LikeOperator::SimilarTo(s) => {
563                s.clone()
564            }
565        }
566    }
567}
568
569/// Like expression (expr [NOT] LIKE pattern [ESCAPE escape_char]), also used for
570/// PostgreSQL's `SIMILAR TO` which shares the same grammar shape.
571#[derive(Debug, Clone)]
572pub struct LikeExpression<'a> {
573    /// The value being tested
574    pub lhs: Expression<'a>,
575    /// The pattern to match against
576    pub rhs: Expression<'a>,
577    /// The operator: LIKE, NOT LIKE or SIMILAR TO
578    pub op: LikeOperator,
579    /// Optional ESCAPE clause specifying the escape character
580    pub escape: Option<Expression<'a>>,
581}
582
583impl Spanned for LikeExpression<'_> {
584    fn span(&self) -> Span {
585        self.op
586            .span()
587            .join_span(&self.lhs)
588            .join_span(&self.rhs)
589            .join_span(&self.escape)
590    }
591}
592
593/// Member of expression
594#[derive(Debug, Clone)]
595pub struct MemberOfExpression<'a> {
596    /// Left hand side expression
597    pub lhs: Expression<'a>,
598    /// Right hand side expression
599    pub rhs: Expression<'a>,
600    /// Span of "MEMBER OF"
601    pub member_of_span: Span,
602    /// Span of the closing ')'
603    pub r_paren_span: Span,
604}
605
606impl Spanned for MemberOfExpression<'_> {
607    fn span(&self) -> Span {
608        self.member_of_span
609            .join_span(&self.lhs)
610            .join_span(&self.rhs)
611            .join_span(&self.r_paren_span)
612    }
613}
614
615/// Case expression
616#[derive(Debug, Clone)]
617pub struct CaseExpression<'a> {
618    /// Span of "CASE"
619    pub case_span: Span,
620    /// Optional value to switch over
621    pub value: Option<Expression<'a>>,
622    /// When parts
623    pub whens: Vec<When<'a>>,
624    /// Span of "ELSE" and else value if specified
625    pub else_: Option<(Span, Expression<'a>)>,
626    /// Span of "END"
627    pub end_span: Span,
628}
629
630impl Spanned for CaseExpression<'_> {
631    fn span(&self) -> Span {
632        self.case_span
633            .join_span(&self.value)
634            .join_span(&self.whens)
635            .join_span(&self.else_)
636            .join_span(&self.end_span)
637    }
638}
639
640/// Cast expression
641#[derive(Debug, Clone)]
642pub struct CastExpression<'a> {
643    /// Span of "CAST"
644    pub cast_span: Span,
645    /// Value to cast
646    pub expr: Expression<'a>,
647    /// Span of "AS"
648    pub as_span: Span,
649    /// Type to cast to
650    pub type_: DataType<'a>,
651    /// Span of the closing ')'
652    pub r_paren_span: Span,
653}
654
655impl Spanned for CastExpression<'_> {
656    fn span(&self) -> Span {
657        self.cast_span
658            .join_span(&self.expr)
659            .join_span(&self.as_span)
660            .join_span(&self.type_)
661            .join_span(&self.r_paren_span)
662    }
663}
664
665/// Convert expression (CONVERT(expr, type) or CONVERT(expr USING charset))
666#[derive(Debug, Clone)]
667pub struct ConvertExpression<'a> {
668    /// Span of "CONVERT"
669    pub convert_span: Span,
670    /// Value to convert
671    pub expr: Expression<'a>,
672    /// Type to convert to (for CONVERT(expr, type))
673    pub type_: Option<DataType<'a>>,
674    /// Charset (for CONVERT(expr USING charset))
675    pub using_charset: Option<(Span, Identifier<'a>)>,
676    /// Span of the closing ')'
677    pub r_paren_span: Span,
678}
679
680impl Spanned for ConvertExpression<'_> {
681    fn span(&self) -> Span {
682        self.convert_span
683            .join_span(&self.expr)
684            .join_span(&self.type_)
685            .join_span(&self.using_charset)
686            .join_span(&self.r_paren_span)
687    }
688}
689
690/// PostgreSQL-style typecast expression (expr::type)
691#[derive(Debug, Clone)]
692pub struct TypeCastExpression<'a> {
693    /// The expression being cast
694    pub expr: Expression<'a>,
695    /// Span of "::"
696    pub doublecolon_span: Span,
697    /// Type to cast to
698    pub type_: DataType<'a>,
699}
700
701impl Spanned for TypeCastExpression<'_> {
702    fn span(&self) -> Span {
703        self.expr
704            .span()
705            .join_span(&self.doublecolon_span)
706            .join_span(&self.type_)
707    }
708}
709
710/// PostgreSQL ARRAY[...] literal expression
711#[derive(Debug, Clone)]
712pub struct ArrayExpression<'a> {
713    /// Span of the "ARRAY" keyword
714    pub array_span: Span,
715    /// Span of the bracket region "[...]"
716    pub bracket_span: Span,
717    /// The element expressions
718    pub elements: Vec<Expression<'a>>,
719}
720
721impl Spanned for ArrayExpression<'_> {
722    fn span(&self) -> Span {
723        self.array_span.join_span(&self.bracket_span)
724    }
725}
726
727/// Array subscript or slice expression: expr[idx] or expr[lower:upper]
728#[derive(Debug, Clone)]
729pub struct ArraySubscriptExpression<'a> {
730    /// The array expression being subscripted
731    pub expr: Expression<'a>,
732    /// Span of the "[...]" bracket region
733    pub bracket_span: Span,
734    /// The lower bound / index
735    pub lower: Expression<'a>,
736    /// The upper bound for slice notation (expr[lower:upper])
737    pub upper: Option<Expression<'a>>,
738}
739
740impl Spanned for ArraySubscriptExpression<'_> {
741    fn span(&self) -> Span {
742        self.expr.span().join_span(&self.bracket_span)
743    }
744}
745
746/// PostgreSQL composite type field access: (expr).field
747#[derive(Debug, Clone)]
748pub struct FieldAccessExpression<'a> {
749    /// The expression whose field is accessed
750    pub expr: Expression<'a>,
751    /// Span of the "."
752    pub dot_span: Span,
753    /// The field name
754    pub field: Identifier<'a>,
755}
756
757impl Spanned for FieldAccessExpression<'_> {
758    fn span(&self) -> Span {
759        self.expr
760            .span()
761            .join_span(&self.dot_span)
762            .join_span(&self.field)
763    }
764}
765
766/// Group contat expression
767#[derive(Debug, Clone)]
768pub struct GroupConcatExpression<'a> {
769    /// Span of "GROUP_CONCAT"
770    pub group_concat_span: Span,
771    /// Span of "DISTINCT" if specified
772    pub distinct_span: Option<Span>,
773    /// Expression to count
774    pub expr: Expression<'a>,
775    /// Span of the closing ')'
776    pub r_paren_span: Span,
777}
778
779impl Spanned for GroupConcatExpression<'_> {
780    fn span(&self) -> Span {
781        self.group_concat_span
782            .join_span(&self.distinct_span)
783            .join_span(&self.expr)
784            .join_span(&self.r_paren_span)
785    }
786}
787
788/// Variable expression
789#[derive(Debug, Clone)]
790pub struct VariableExpression<'a> {
791    /// Span of "@@GLOBAL"
792    pub global: Option<Span>,
793    /// Span of "@@SESSION"
794    pub session: Option<Span>,
795    /// Span of '.'
796    pub dot: Option<Span>,
797    /// variable
798    pub variable: Variable<'a>,
799    // Span of variable
800    pub variable_span: Span,
801}
802
803impl Spanned for VariableExpression<'_> {
804    fn span(&self) -> Span {
805        self.variable_span
806            .join_span(&self.global)
807            .join_span(&self.session)
808            .join_span(&self.dot)
809    }
810}
811
812/// User variable expression (@variable_name)
813#[derive(Debug, Clone)]
814pub struct UserVariableExpression<'a> {
815    /// The variable name
816    pub name: Identifier<'a>,
817    /// Span of '@'
818    pub at_span: Span,
819}
820
821impl Spanned for UserVariableExpression<'_> {
822    fn span(&self) -> Span {
823        self.at_span.join_span(&self.name)
824    }
825}
826
827/// Timestampadd call
828#[derive(Debug, Clone)]
829pub struct TimestampAddExpression<'a> {
830    /// Span of "TIMESTAMPADD"
831    pub timestamp_add_span: Span,
832    /// Unit of the interval
833    pub unit: (TimeUnit, Span),
834    /// Interval expression
835    pub interval: Expression<'a>,
836    /// Datetime expression
837    pub datetime: Expression<'a>,
838    /// Span of the closing ')'
839    pub r_paren_span: Span,
840}
841
842impl Spanned for TimestampAddExpression<'_> {
843    fn span(&self) -> Span {
844        self.timestamp_add_span
845            .join_span(&self.unit.1)
846            .join_span(&self.interval)
847            .join_span(&self.datetime)
848            .join_span(&self.r_paren_span)
849    }
850}
851
852/// Timestampdiff call
853#[derive(Debug, Clone)]
854pub struct TimestampDiffExpression<'a> {
855    /// Span of "TIMESTAMPDIFF"
856    pub timestamp_diff_span: Span,
857    /// Unit of the interval
858    pub unit: (TimeUnit, Span),
859    /// First expression
860    pub e1: Expression<'a>,
861    /// Second expression
862    pub e2: Expression<'a>,
863    /// Span of the closing ')'
864    pub r_paren_span: Span,
865}
866
867impl Spanned for TimestampDiffExpression<'_> {
868    fn span(&self) -> Span {
869        self.timestamp_diff_span
870            .join_span(&self.unit.1)
871            .join_span(&self.e1)
872            .join_span(&self.e2)
873            .join_span(&self.r_paren_span)
874    }
875}
876
877/// Full-text MATCH ... AGAINST expression
878#[derive(Debug, Clone)]
879pub struct MatchAgainstExpression<'a> {
880    /// Span of "MATCH"
881    pub match_span: Span,
882    /// Columns to match against
883    pub columns: Vec<Expression<'a>>,
884    /// Span of "AGAINST"
885    pub against_span: Span,
886    /// Expression to match against
887    pub expr: Expression<'a>,
888    /// Match mode
889    pub mode: Option<MatchMode>,
890    /// Span of the closing ')' of AGAINST(...)
891    pub r_paren_span: Span,
892}
893
894impl Spanned for MatchAgainstExpression<'_> {
895    fn span(&self) -> Span {
896        self.match_span
897            .join_span(&self.columns)
898            .join_span(&self.against_span)
899            .join_span(&self.expr)
900            .join_span(&self.mode)
901            .join_span(&self.r_paren_span)
902    }
903}
904
905/// Is expression
906#[derive(Debug, Clone)]
907pub struct IsExpression<'a> {
908    /// Left hand side expression
909    pub lhs: Expression<'a>,
910    /// Type of is expression
911    pub is: Is<'a>,
912    /// Span of "IS" and "NOT"
913    pub is_span: Span,
914}
915
916impl<'a> Spanned for IsExpression<'a> {
917    fn span(&self) -> Span {
918        match &self.is {
919            Is::DistinctFrom(rhs) | Is::NotDistinctFrom(rhs) => {
920                self.lhs.span().join_span(&self.is_span).join_span(rhs)
921            }
922            _ => self.lhs.span().join_span(&self.is_span),
923        }
924    }
925}
926
927/// Literal NULL expression
928#[derive(Debug, Clone)]
929pub struct NullExpression {
930    /// Span of "NULL"
931    pub span: Span,
932}
933
934impl Spanned for NullExpression {
935    fn span(&self) -> Span {
936        self.span.clone()
937    }
938}
939
940/// Literal DEFAULT expression
941#[derive(Debug, Clone)]
942pub struct DefaultExpression {
943    /// Span of "DEFAULT"
944    pub span: Span,
945}
946
947impl Spanned for DefaultExpression {
948    fn span(&self) -> Span {
949        self.span.clone()
950    }
951}
952
953/// Literal bool expression "TRUE" or "FALSE"
954#[derive(Debug, Clone)]
955pub struct BoolExpression {
956    /// The boolean value
957    pub value: bool,
958    /// Span of "TRUE" or "FALSE"
959    pub span: Span,
960}
961
962impl Spanned for BoolExpression {
963    fn span(&self) -> Span {
964        self.span.clone()
965    }
966}
967
968/// Literal integer expression
969#[derive(Debug, Clone)]
970pub struct IntegerExpression {
971    /// The integer value
972    pub value: u64,
973    /// Span of the integer
974    pub span: Span,
975}
976
977impl Spanned for IntegerExpression {
978    fn span(&self) -> Span {
979        self.span.clone()
980    }
981}
982
983/// Literal _LIST_ expression
984#[derive(Debug, Clone)]
985pub struct ListHackExpression {
986    /// The index of the list
987    pub index: usize,
988    /// Span of the list
989    pub span: Span,
990}
991
992impl Spanned for ListHackExpression {
993    fn span(&self) -> Span {
994        self.span.clone()
995    }
996}
997
998/// Literal floating point expression
999#[derive(Debug, Clone)]
1000pub struct FloatExpression {
1001    /// The floating point value
1002    pub value: f64,
1003    /// Span of the floating point
1004    pub span: Span,
1005}
1006
1007impl Spanned for FloatExpression {
1008    fn span(&self) -> Span {
1009        self.span.clone()
1010    }
1011}
1012
1013/// Input argument to query, the first argument is the occurrence number of the argument
1014#[derive(Debug, Clone)]
1015pub struct ArgExpression {
1016    /// The occurrence number of the argument
1017    pub index: usize,
1018    /// Span of the argument
1019    pub span: Span,
1020}
1021
1022impl Spanned for ArgExpression {
1023    fn span(&self) -> Span {
1024        self.span.clone()
1025    }
1026}
1027
1028/// Invalid expression, returned on recovery of a parse error
1029#[derive(Debug, Clone)]
1030pub struct InvalidExpression {
1031    /// Span of the invalid expression
1032    pub span: Span,
1033}
1034
1035impl Spanned for InvalidExpression {
1036    fn span(&self) -> Span {
1037        self.span.clone()
1038    }
1039}
1040
1041/// Identifier pointing to column
1042#[derive(Debug, Clone)]
1043pub struct IdentifierExpression<'a> {
1044    /// The identifier  parts, for example in "a.b.*" the parts are "a", "b" and "*"
1045    pub parts: Vec<IdentifierPart<'a>>,
1046}
1047
1048impl<'a> Spanned for IdentifierExpression<'a> {
1049    fn span(&self) -> Span {
1050        self.parts.opt_span().expect("Span of identifier parts")
1051    }
1052}
1053
1054/// Subquery expression
1055#[derive(Debug, Clone)]
1056pub struct SubqueryExpression<'a> {
1057    /// The subquery
1058    pub expression: Statement<'a>,
1059}
1060
1061impl Spanned for SubqueryExpression<'_> {
1062    fn span(&self) -> Span {
1063        self.expression.span()
1064    }
1065}
1066
1067/// Exists expression
1068#[derive(Debug, Clone)]
1069pub struct ExistsExpression<'a> {
1070    /// Span of "EXISTS"
1071    pub exists_span: Span,
1072    /// The subquery
1073    pub subquery: Statement<'a>,
1074    /// Span of the closing ')'
1075    pub r_paren_span: Span,
1076}
1077
1078impl Spanned for ExistsExpression<'_> {
1079    fn span(&self) -> Span {
1080        self.exists_span
1081            .join_span(&self.subquery)
1082            .join_span(&self.r_paren_span)
1083    }
1084}
1085
1086/// Which row quantifier keyword was used
1087#[derive(Debug, Clone, PartialEq, Eq)]
1088pub enum Quantifier {
1089    /// ANY(subquery_or_array)
1090    Any(Span),
1091    /// SOME(subquery_or_array) — synonym for ANY in PostgreSQL
1092    Some(Span),
1093    /// ALL(subquery_or_array)
1094    All(Span),
1095}
1096
1097impl Spanned for Quantifier {
1098    fn span(&self) -> Span {
1099        match self {
1100            Quantifier::Any(s) | Quantifier::Some(s) | Quantifier::All(s) => s.clone(),
1101        }
1102    }
1103}
1104
1105/// PostgreSQL ANY / SOME / ALL quantifier expression.
1106///
1107/// Appears as the right-hand operand of a comparison:
1108/// `expr op ANY (subquery_or_array)`
1109#[derive(Debug, Clone)]
1110pub struct QuantifierExpression<'a> {
1111    /// Which quantifier keyword was written
1112    pub quantifier: Quantifier,
1113    /// The operand - either a subquery expression or an array expression
1114    pub operand: Expression<'a>,
1115    /// Span of the closing ')'
1116    pub r_paren_span: Span,
1117}
1118
1119impl Spanned for QuantifierExpression<'_> {
1120    fn span(&self) -> Span {
1121        self.quantifier
1122            .join_span(&self.operand)
1123            .join_span(&self.r_paren_span)
1124    }
1125}
1126
1127/// A row/tuple constructor expression: `(expr1, expr2, ...)`
1128#[derive(Debug, Clone)]
1129pub struct RowExpression<'a> {
1130    /// Span of the surrounding parentheses
1131    pub paren_span: Span,
1132    /// Elements of the tuple
1133    pub elements: Vec<Expression<'a>>,
1134}
1135
1136impl Spanned for RowExpression<'_> {
1137    fn span(&self) -> Span {
1138        self.paren_span.clone()
1139    }
1140}
1141
1142/// Representation of an expression
1143#[derive(Debug, Clone)]
1144pub enum Expression<'a> {
1145    /// Expression with binary operator
1146    Binary(Box<BinaryExpression<'a>>),
1147    /// Expression with a unary (prefix) operator
1148    Unary(Box<UnaryExpression<'a>>),
1149    /// Subquery expression
1150    Subquery(Box<SubqueryExpression<'a>>),
1151    /// Literal NULL expression
1152    Null(Box<NullExpression>),
1153    /// Literal DEFAULT expression
1154    Default(Box<DefaultExpression>),
1155    /// Literal bool expression "TRUE" or "FALSE"
1156    Bool(Box<BoolExpression>),
1157    /// Literal string expression, the SString contains the represented string
1158    /// with escaping removed
1159    String(Box<SString<'a>>),
1160    /// Literal integer expression
1161    Integer(Box<IntegerExpression>),
1162    /// Literal _LIST_
1163    ListHack(Box<ListHackExpression>),
1164    /// Literal floating point expression
1165    Float(Box<FloatExpression>),
1166    /// Function call expression,
1167    Function(Box<FunctionCallExpression<'a>>),
1168    /// A window function call expression
1169    WindowFunction(Box<WindowFunctionCallExpression<'a>>),
1170    /// Aggregate function call expression with optional DISTINCT/FILTER/OVER
1171    AggregateFunction(Box<AggregateFunctionCallExpression<'a>>),
1172    /// Identifier pointing to column
1173    Identifier(Box<IdentifierExpression<'a>>),
1174    /// Time Interval
1175    Interval(Box<IntervalExpression>),
1176    /// Input argument to query, the first argument is the occurrence number of the argument
1177    Arg(Box<ArgExpression>),
1178    /// Exists expression
1179    Exists(Box<ExistsExpression<'a>>),
1180    /// Extract expression
1181    Extract(Box<ExtractExpression<'a>>),
1182    /// Trim expression
1183    Trim(Box<TrimExpression<'a>>),
1184    /// In expression
1185    In(Box<InExpression<'a>>),
1186    /// Between expression
1187    Between(Box<BetweenExpression<'a>>),
1188    /// Like / Not like / Similar to expression
1189    Like(Box<LikeExpression<'a>>),
1190    /// Member of expression
1191    MemberOf(Box<MemberOfExpression<'a>>),
1192    /// Is expression
1193    Is(Box<IsExpression<'a>>),
1194    /// Invalid expression, returned on recovery of a parse error
1195    Invalid(Box<InvalidExpression>),
1196    /// Case expression
1197    Case(Box<CaseExpression<'a>>),
1198    /// Cast expression
1199    Cast(Box<CastExpression<'a>>),
1200    /// Convert expression (CONVERT(expr, type) or CONVERT(expr USING charset))
1201    Convert(Box<ConvertExpression<'a>>),
1202    /// Group contat expression
1203    GroupConcat(Box<GroupConcatExpression<'a>>),
1204    /// Variable expression
1205    Variable(Box<VariableExpression<'a>>),
1206    /// User variable expression (@variable_name)
1207    UserVariable(Box<UserVariableExpression<'a>>),
1208    /// Timestampadd call
1209    TimestampAdd(Box<TimestampAddExpression<'a>>),
1210    /// Timestampdiff call
1211    TimestampDiff(Box<TimestampDiffExpression<'a>>),
1212    /// PostgreSQL-style typecast expression (expr::type)
1213    TypeCast(Box<TypeCastExpression<'a>>),
1214    /// Full-text MATCH ... AGAINST expression
1215    MatchAgainst(Box<MatchAgainstExpression<'a>>),
1216    /// PostgreSQL ARRAY[...] literal expression
1217    Array(Box<ArrayExpression<'a>>),
1218    /// Array subscript / slice expression: expr[idx] or expr[lower:upper]
1219    ArraySubscript(Box<ArraySubscriptExpression<'a>>),
1220    /// PostgreSQL ANY / SOME / ALL quantifier: ANY(subquery_or_array)
1221    Quantifier(Box<QuantifierExpression<'a>>),
1222    /// PostgreSQL composite type field access: (expr).field
1223    FieldAccess(Box<FieldAccessExpression<'a>>),
1224    /// CHAR(N,... [USING charset_name]) expression
1225    Char(Box<CharFunctionExpression<'a>>),
1226    /// Row / tuple constructor: `(expr1, expr2, ...)`
1227    Row(Box<RowExpression<'a>>),
1228}
1229
1230impl<'a> Spanned for Expression<'a> {
1231    fn span(&self) -> Span {
1232        match &self {
1233            Expression::Binary(e) => e.span(),
1234            Expression::Unary(e) => e.span(),
1235            Expression::Subquery(v) => v.span(),
1236            Expression::Null(v) => v.span(),
1237            Expression::Default(v) => v.span(),
1238            Expression::Bool(v) => v.span(),
1239            Expression::String(v) => v.span(),
1240            Expression::Integer(v) => v.span(),
1241            Expression::Float(v) => v.span(),
1242            Expression::ListHack(v) => v.span(),
1243            Expression::Function(e) => e.span(),
1244            Expression::AggregateFunction(e) => e.span(),
1245            Expression::Identifier(e) => e.span(),
1246            Expression::Arg(v) => v.span(),
1247            Expression::Exists(v) => v.span(),
1248            Expression::In(e) => e.span(),
1249            Expression::Between(e) => e.span(),
1250            Expression::Like(e) => e.span(),
1251            Expression::MemberOf(e) => e.span(),
1252            Expression::Is(e) => e.span(),
1253            Expression::Invalid(s) => s.span(),
1254            Expression::Case(e) => e.span(),
1255            Expression::Cast(e) => e.span(),
1256            Expression::Convert(e) => e.span(),
1257            Expression::TypeCast(e) => e.span(),
1258            Expression::GroupConcat(e) => e.span(),
1259            Expression::Variable(e) => e.span(),
1260            Expression::UserVariable(e) => e.span(),
1261            Expression::WindowFunction(e) => e.span(),
1262            Expression::Interval(e) => e.span(),
1263            Expression::Extract(e) => e.span(),
1264            Expression::Trim(e) => e.span(),
1265            Expression::TimestampAdd(e) => e.span(),
1266            Expression::TimestampDiff(e) => e.span(),
1267            Expression::MatchAgainst(e) => e.span(),
1268            Expression::Array(e) => e.span(),
1269            Expression::ArraySubscript(e) => e.span(),
1270            Expression::Quantifier(e) => e.span(),
1271            Expression::FieldAccess(e) => e.span(),
1272            Expression::Char(e) => e.span(),
1273            Expression::Row(e) => e.span(),
1274        }
1275    }
1276}
1277
1278// Operator parsing priority table.
1279// Lower number = tighter binding (reduced later, applied first).
1280// An operator is only parsed when its priority < max_priority passed to the parser.
1281pub(crate) const PRIORITY_TYPECAST: usize = 10; // ::
1282pub(crate) const PRIORITY_JSON_EXTRACT: usize = 30; // -> ->>
1283pub(crate) const PRIORITY_BITXOR: usize = 50; // ^ (XOR bitwise)
1284pub(crate) const PRIORITY_MULT: usize = 60; // * / % DIV MOD
1285pub(crate) const PRIORITY_ADD: usize = 70; // + -
1286pub(crate) const PRIORITY_PG_CUSTOM: usize = 75; // PostgreSQL custom / user-defined operators
1287pub(crate) const PRIORITY_SHIFT: usize = 80; // << >>
1288pub(crate) const PRIORITY_BITAND: usize = 90; // &
1289pub(crate) const PRIORITY_BITOR: usize = 100; // |
1290pub(crate) const PRIORITY_CMP: usize = 110; // = != < > <= >= IS IN LIKE BETWEEN SIMILAR TO ...
1291pub(crate) const PRIORITY_AND: usize = 140; // AND  (tighter than OR)
1292pub(crate) const PRIORITY_XOR: usize = 150; // XOR (keyword logical XOR)
1293pub(crate) const PRIORITY_OR: usize = 160; // OR   (loosest binary operator)
1294pub(crate) const PRIORITY_ASSIGN: usize = 200; // :=
1295
1296/// Parse all operators (no restriction).
1297pub(crate) const PRIORITY_MAX: usize = usize::MAX;
1298
1299trait Priority {
1300    fn priority(&self) -> usize;
1301}
1302
1303impl<'a> Priority for BinaryOperator<'a> {
1304    fn priority(&self) -> usize {
1305        match self {
1306            BinaryOperator::Assignment(_) => PRIORITY_ASSIGN,
1307            BinaryOperator::Or(_) => PRIORITY_OR,
1308            BinaryOperator::Concat(_) => PRIORITY_ADD,
1309            BinaryOperator::Xor(_) => PRIORITY_XOR,
1310            BinaryOperator::And(_) => PRIORITY_AND,
1311            BinaryOperator::Eq(_) => PRIORITY_CMP,
1312            BinaryOperator::NullSafeEq(_) => PRIORITY_CMP,
1313            BinaryOperator::GtEq(_) => PRIORITY_CMP,
1314            BinaryOperator::Gt(_) => PRIORITY_CMP,
1315            BinaryOperator::LtEq(_) => PRIORITY_CMP,
1316            BinaryOperator::Lt(_) => PRIORITY_CMP,
1317            BinaryOperator::Neq(_) => PRIORITY_CMP,
1318            BinaryOperator::Like(_) => PRIORITY_CMP,
1319            BinaryOperator::NotLike(_) => PRIORITY_CMP,
1320            BinaryOperator::Regexp(_) => PRIORITY_CMP,
1321            BinaryOperator::NotRegexp(_) => PRIORITY_CMP,
1322            BinaryOperator::Rlike(_) => PRIORITY_CMP,
1323            BinaryOperator::NotRlike(_) => PRIORITY_CMP,
1324            BinaryOperator::ShiftLeft(_) => PRIORITY_SHIFT,
1325            BinaryOperator::ShiftRight(_) => PRIORITY_SHIFT,
1326            BinaryOperator::BitAnd(_) => PRIORITY_BITAND,
1327            BinaryOperator::BitOr(_) => PRIORITY_BITOR,
1328            BinaryOperator::BitXor(_) => PRIORITY_BITXOR,
1329            BinaryOperator::Add(_) => PRIORITY_ADD,
1330            BinaryOperator::Subtract(_) => PRIORITY_ADD,
1331            BinaryOperator::Divide(_) => PRIORITY_MULT,
1332            BinaryOperator::Div(_) => PRIORITY_MULT,
1333            BinaryOperator::Mod(_) => PRIORITY_MULT,
1334            BinaryOperator::Mult(_) => PRIORITY_MULT,
1335            BinaryOperator::Collate(_) => 20,
1336            BinaryOperator::JsonExtract(_) => PRIORITY_JSON_EXTRACT,
1337            BinaryOperator::JsonExtractUnquote(_) => PRIORITY_JSON_EXTRACT,
1338            BinaryOperator::Overlap(_)
1339            | BinaryOperator::Contains(_)
1340            | BinaryOperator::ContainedBy(_)
1341            | BinaryOperator::JsonPathMatch(_)
1342            | BinaryOperator::JsonPathExists(_)
1343            | BinaryOperator::JsonbKeyExists(_)
1344            | BinaryOperator::JsonbAnyKeyExists(_)
1345            | BinaryOperator::JsonbAllKeyExists(_)
1346            | BinaryOperator::JsonGetPath(_)
1347            | BinaryOperator::JsonGetPathText(_)
1348            | BinaryOperator::JsonDeletePath(_)
1349            | BinaryOperator::RegexMatch(_)
1350            | BinaryOperator::RegexIMatch(_)
1351            | BinaryOperator::NotRegexMatch(_)
1352            | BinaryOperator::NotRegexIMatch(_)
1353            | BinaryOperator::User(_, _)
1354            | BinaryOperator::Operator(_, _) => PRIORITY_PG_CUSTOM,
1355        }
1356    }
1357}
1358
1359impl Priority for UnaryOperator {
1360    fn priority(&self) -> usize {
1361        match self {
1362            UnaryOperator::Binary(_) => 20,
1363            UnaryOperator::LogicalNot(_) => 30,
1364            UnaryOperator::Minus(_) => 40,
1365            UnaryOperator::Not(_) => 130,
1366        }
1367    }
1368}
1369
1370#[derive(Debug)]
1371enum ReduceMember<'a> {
1372    Expression(Expression<'a>),
1373    Binary(BinaryOperator<'a>),
1374    Unary(UnaryOperator),
1375}
1376
1377struct Reducer<'a> {
1378    stack: Vec<ReduceMember<'a>>,
1379}
1380
1381impl<'a> Reducer<'a> {
1382    fn reduce(&mut self, priority: usize) -> Result<(), &'static str> {
1383        let mut e = match self.stack.pop() {
1384            Some(ReduceMember::Expression(e)) => e,
1385            _ => {
1386                return Err("Expected expression before here");
1387            }
1388        };
1389        loop {
1390            let v = self.stack.pop();
1391            match v {
1392                None => break,
1393                Some(ReduceMember::Expression(_)) => return Err("ICE Reduce stack error 1"),
1394                Some(ReduceMember::Unary(op)) if op.priority() > priority => {
1395                    self.stack.push(ReduceMember::Unary(op));
1396                    break;
1397                }
1398                Some(ReduceMember::Binary(op)) if op.priority() > priority => {
1399                    self.stack.push(ReduceMember::Binary(op));
1400                    break;
1401                }
1402                Some(ReduceMember::Unary(op)) => {
1403                    e = Expression::Unary(Box::new(UnaryExpression { op, operand: e }));
1404                }
1405                Some(ReduceMember::Binary(op)) => {
1406                    let lhs = match self.stack.pop() {
1407                        Some(ReduceMember::Expression(e)) => e,
1408                        _ => return Err("ICE Reduce stack error 2"),
1409                    };
1410                    e = Expression::Binary(Box::new(BinaryExpression { op, lhs, rhs: e }));
1411                }
1412            }
1413        }
1414        self.stack.push(ReduceMember::Expression(e));
1415        Ok(())
1416    }
1417
1418    fn shift_binop(&mut self, op: BinaryOperator<'a>) -> Result<(), &'static str> {
1419        self.reduce(op.priority())?;
1420        self.stack.push(ReduceMember::Binary(op));
1421        Ok(())
1422    }
1423
1424    fn shift_unary(&mut self, op: UnaryOperator) -> Result<(), &'static str> {
1425        if matches!(self.stack.last(), Some(ReduceMember::Expression(_))) {
1426            return Err("Unary operator cannot come before expression");
1427        }
1428        self.stack.push(ReduceMember::Unary(op));
1429        Ok(())
1430    }
1431
1432    fn shift_expr(&mut self, e: Expression<'a>) -> Result<(), &'static str> {
1433        if matches!(self.stack.last(), Some(ReduceMember::Expression(_))) {
1434            //panic!();
1435            return Err("Expression should not follow expression");
1436        }
1437        self.stack.push(ReduceMember::Expression(e));
1438        Ok(())
1439    }
1440}
1441
1442/// Parse a single element inside an ARRAY[...] literal.
1443/// If the current token is `[`, parses a nested sub-array `[elem, ...]` recursively.
1444/// Otherwise delegates to `parse_expression_unreserved`.
1445fn parse_array_element<'a>(parser: &mut Parser<'a, '_>) -> Result<Expression<'a>, ParseError> {
1446    if matches!(parser.token, Token::LBracket) {
1447        let lbracket = parser.consume_token(Token::LBracket)?;
1448        let mut elements = Vec::new();
1449        if !matches!(parser.token, Token::RBracket) {
1450            loop {
1451                parser.recovered(
1452                    "']' or ','",
1453                    &|t| matches!(t, Token::RBracket | Token::Comma),
1454                    |parser| {
1455                        elements.push(parse_array_element(parser)?);
1456                        Ok(())
1457                    },
1458                )?;
1459                if parser.skip_token(Token::Comma).is_none() {
1460                    break;
1461                }
1462            }
1463        }
1464        let rbracket = parser.consume_token(Token::RBracket)?;
1465        let bracket_span = lbracket.join_span(&rbracket);
1466        Ok(Expression::Array(Box::new(ArrayExpression {
1467            array_span: bracket_span.clone(),
1468            bracket_span,
1469            elements,
1470        })))
1471    } else {
1472        parse_expression_unreserved(parser, PRIORITY_MAX)
1473    }
1474}
1475
1476pub(crate) fn parse_expression_restricted<'a>(
1477    parser: &mut Parser<'a, '_>,
1478    max_priority: usize,
1479    restrict: Restrict,
1480) -> Result<Expression<'a>, ParseError> {
1481    let mut r = Reducer { stack: Vec::new() };
1482    loop {
1483        if matches!(r.stack.last(), Some(ReduceMember::Expression(_)))
1484            && matches!(
1485                parser.token,
1486                Token::Ident(
1487                    _,
1488                    Keyword::NULLS
1489                        | Keyword::FIRST
1490                        | Keyword::LAST
1491                        | Keyword::ROW
1492                        | Keyword::ROWS
1493                        | Keyword::RANGE
1494                        | Keyword::ONLY
1495                        | Keyword::FILTER
1496                        | Keyword::PRECEDING
1497                        | Keyword::FOLLOWING
1498                )
1499            )
1500        {
1501            break;
1502        }
1503        // `NOT NULL` is a column constraint, not an expression operator
1504        // (e.g. `DEFAULT (foo())::TEXT NOT NULL`).  Break before consuming
1505        // the NOT so the caller can handle NOT NULL as a constraint.
1506        if matches!(r.stack.last(), Some(ReduceMember::Expression(_)))
1507            && matches!(parser.token, Token::Ident(_, Keyword::NOT))
1508            && matches!(parser.peek(), Token::Ident(_, Keyword::NULL))
1509        {
1510            break;
1511        }
1512        let e = match parser.token.clone() {
1513            Token::ColonEq if PRIORITY_ASSIGN < max_priority => {
1514                r.shift_binop(BinaryOperator::Assignment(parser.consume()))
1515            }
1516            Token::Ident(_, Keyword::OR) if PRIORITY_OR < max_priority => {
1517                r.shift_binop(BinaryOperator::Or(parser.consume()))
1518            }
1519            Token::DoublePipe if PRIORITY_ADD < max_priority => {
1520                r.shift_binop(BinaryOperator::Concat(parser.consume()))
1521            }
1522            Token::Ident(_, Keyword::XOR) if PRIORITY_XOR < max_priority => {
1523                r.shift_binop(BinaryOperator::Xor(parser.consume()))
1524            }
1525            Token::Ident(_, Keyword::AND) if PRIORITY_AND < max_priority => {
1526                r.shift_binop(BinaryOperator::And(parser.consume()))
1527            }
1528            Token::DoubleAmpersand
1529                if PRIORITY_AND < max_priority && !parser.options.dialect.is_postgresql() =>
1530            {
1531                r.shift_binop(BinaryOperator::And(parser.consume()))
1532            }
1533            Token::DoubleAmpersand
1534                if PRIORITY_PG_CUSTOM < max_priority && parser.options.dialect.is_postgresql() =>
1535            {
1536                r.shift_binop(BinaryOperator::Overlap(parser.consume()))
1537            }
1538            Token::Eq if PRIORITY_CMP < max_priority => {
1539                r.shift_binop(BinaryOperator::Eq(parser.consume()))
1540            }
1541            Token::Spaceship if PRIORITY_CMP < max_priority => {
1542                r.shift_binop(BinaryOperator::NullSafeEq(parser.consume()))
1543            }
1544            Token::GtEq if PRIORITY_CMP < max_priority => {
1545                r.shift_binop(BinaryOperator::GtEq(parser.consume()))
1546            }
1547            Token::Gt if PRIORITY_CMP < max_priority => {
1548                r.shift_binop(BinaryOperator::Gt(parser.consume()))
1549            }
1550            Token::LtEq if PRIORITY_CMP < max_priority => {
1551                r.shift_binop(BinaryOperator::LtEq(parser.consume()))
1552            }
1553            Token::Lt if PRIORITY_CMP < max_priority => {
1554                r.shift_binop(BinaryOperator::Lt(parser.consume()))
1555            }
1556            Token::Neq if PRIORITY_CMP < max_priority => {
1557                r.shift_binop(BinaryOperator::Neq(parser.consume()))
1558            }
1559            Token::ShiftLeft if PRIORITY_SHIFT < max_priority => {
1560                r.shift_binop(BinaryOperator::ShiftLeft(parser.consume()))
1561            }
1562            Token::ShiftRight if PRIORITY_SHIFT < max_priority => {
1563                r.shift_binop(BinaryOperator::ShiftRight(parser.consume()))
1564            }
1565            Token::Ampersand => r.shift_binop(BinaryOperator::BitAnd(parser.consume())),
1566            Token::Pipe if PRIORITY_BITOR < max_priority => {
1567                r.shift_binop(BinaryOperator::BitOr(parser.consume()))
1568            }
1569            Token::Ident(_, Keyword::BINARY) if PRIORITY_JSON_EXTRACT < max_priority => {
1570                r.shift_unary(UnaryOperator::Binary(parser.consume()))
1571            }
1572            Token::Ident(_, Keyword::COLLATE)
1573                if 20 < max_priority
1574                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1575            {
1576                // COLLATE is a binary operator: expr COLLATE collation_name
1577                let collate_span = parser.consume_keyword(Keyword::COLLATE)?;
1578                let collation = parser.consume_plain_identifier_unreserved()?;
1579                if let Err(e) = r.shift_binop(BinaryOperator::Collate(collate_span)) {
1580                    parser.err_here(e)?;
1581                }
1582                r.shift_expr(Expression::Identifier(Box::new(IdentifierExpression {
1583                    parts: vec![IdentifierPart::Name(collation)],
1584                })))
1585            }
1586            Token::ExclamationMark if PRIORITY_JSON_EXTRACT < max_priority => {
1587                r.shift_unary(UnaryOperator::LogicalNot(parser.consume()))
1588            }
1589            Token::Minus if !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) => {
1590                r.shift_unary(UnaryOperator::Minus(parser.consume()))
1591            }
1592            Token::Minus
1593                if PRIORITY_ADD < max_priority
1594                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1595            {
1596                r.shift_binop(BinaryOperator::Subtract(parser.consume()))
1597            }
1598            Token::Ident(_, Keyword::IN) if PRIORITY_CMP < max_priority => {
1599                if let Err(e) = r.reduce(PRIORITY_CMP) {
1600                    parser.err_here(e)?;
1601                }
1602                let lhs = match r.stack.pop() {
1603                    Some(ReduceMember::Expression(e)) => e,
1604                    _ => parser.err_here("Expected expression before here 3")?,
1605                };
1606                let op = parser.consume_keyword(Keyword::IN)?;
1607                parser.consume_token(Token::LParen)?;
1608                let mut rhs = Vec::new();
1609                loop {
1610                    parser.recovered(
1611                        "')' or ','",
1612                        &|t| matches!(t, Token::RParen | Token::Comma),
1613                        |parser| {
1614                            rhs.push(parse_expression_paren(parser)?);
1615                            Ok(())
1616                        },
1617                    )?;
1618                    if parser.skip_token(Token::Comma).is_none() {
1619                        break;
1620                    }
1621                }
1622                let r_paren_span = parser.consume_token(Token::RParen)?;
1623                r.shift_expr(Expression::In(Box::new(InExpression {
1624                    lhs,
1625                    rhs,
1626                    in_span: op,
1627                    not_in: false,
1628                    r_paren_span,
1629                })))
1630            }
1631            Token::Ident(_, Keyword::IS) if PRIORITY_CMP < max_priority => {
1632                if let Err(e) = r.reduce(PRIORITY_CMP) {
1633                    parser.err_here(e)?;
1634                }
1635                let lhs = match r.stack.pop() {
1636                    Some(ReduceMember::Expression(e)) => e,
1637                    _ => parser.err_here("Expected expression before here 4")?,
1638                };
1639                let op = parser.consume_keyword(Keyword::IS)?;
1640                let (is, op) = match &parser.token {
1641                    Token::Ident(_, Keyword::NOT) => {
1642                        parser.consume();
1643                        match &parser.token {
1644                            Token::Ident(_, Keyword::TRUE) => {
1645                                (Is::NotTrue, parser.consume().join_span(&op))
1646                            }
1647                            Token::Ident(_, Keyword::FALSE) => {
1648                                (Is::NotFalse, parser.consume().join_span(&op))
1649                            }
1650                            Token::Ident(_, Keyword::NULL) => {
1651                                (Is::NotNull, parser.consume().join_span(&op))
1652                            }
1653                            Token::Ident(_, Keyword::UNKNOWN) => {
1654                                (Is::NotUnknown, parser.consume().join_span(&op))
1655                            }
1656                            Token::Ident(_, Keyword::DISTINCT) => {
1657                                let op_span = parser
1658                                    .consume_keywords(&[Keyword::DISTINCT, Keyword::FROM])?
1659                                    .join_span(&op);
1660                                parser.postgres_only(&op_span);
1661                                let rhs = parse_expression_unreserved(parser, PRIORITY_AND)?;
1662                                (Is::NotDistinctFrom(rhs), op_span)
1663                            }
1664                            _ => parser.expected_failure(
1665                                "'TRUE', 'FALSE', 'UNKNOWN', 'NULL' or 'DISTINCT'",
1666                            )?,
1667                        }
1668                    }
1669                    Token::Ident(_, Keyword::TRUE) => (Is::True, parser.consume().join_span(&op)),
1670                    Token::Ident(_, Keyword::FALSE) => (Is::False, parser.consume().join_span(&op)),
1671                    Token::Ident(_, Keyword::NULL) => (Is::Null, parser.consume().join_span(&op)),
1672                    Token::Ident(_, Keyword::DISTINCT) => {
1673                        let op_span = parser
1674                            .consume_keywords(&[Keyword::DISTINCT, Keyword::FROM])?
1675                            .join_span(&op);
1676                        parser.postgres_only(&op_span);
1677                        let rhs = parse_expression_unreserved(parser, PRIORITY_AND)?;
1678                        (Is::DistinctFrom(rhs), op_span)
1679                    }
1680                    Token::Ident(_, Keyword::UNKNOWN) => {
1681                        (Is::Unknown, parser.consume().join_span(&op))
1682                    }
1683                    _ => parser.expected_failure(
1684                        "'NOT', 'TRUE', 'FALSE', 'UNKNOWN', 'NULL' or 'DISTINCT'",
1685                    )?,
1686                };
1687                r.shift_expr(Expression::Is(Box::new(IsExpression {
1688                    lhs,
1689                    is,
1690                    is_span: op,
1691                })))
1692            }
1693            Token::DoubleColon
1694                if parser.options.dialect.is_postgresql()
1695                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1696            {
1697                // PostgreSQL typecast operator: expr::type
1698                // Reduce with very high priority (binds tighter than most operators)
1699                if let Err(e) = r.reduce(PRIORITY_TYPECAST) {
1700                    parser.err_here(e)?;
1701                }
1702                let expr = match r.stack.pop() {
1703                    Some(ReduceMember::Expression(e)) => e,
1704                    _ => parser.err_here("Expected expression before '::'")?,
1705                };
1706                let doublecolon_span = parser.consume_token(Token::DoubleColon)?;
1707                let type_ = parse_data_type(parser, DataTypeContext::TypeRef)?;
1708                r.shift_expr(Expression::TypeCast(Box::new(TypeCastExpression {
1709                    expr,
1710                    doublecolon_span,
1711                    type_,
1712                })))
1713            }
1714            Token::LBracket
1715                if parser.options.dialect.is_postgresql()
1716                    && PRIORITY_TYPECAST < max_priority
1717                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1718            {
1719                // Array subscript / slice: expr[idx] or expr[lower:upper]
1720                if let Err(e) = r.reduce(PRIORITY_TYPECAST) {
1721                    parser.err_here(e)?;
1722                }
1723                let expr = match r.stack.pop() {
1724                    Some(ReduceMember::Expression(e)) => e,
1725                    _ => parser.err_here("Expected expression before '['")?,
1726                };
1727                let lbracket = parser.consume_token(Token::LBracket)?;
1728                let lower = parse_expression_unreserved(parser, PRIORITY_MAX)?;
1729                let upper = if parser.skip_token(Token::Colon).is_some() {
1730                    Some(parse_expression_unreserved(parser, PRIORITY_MAX)?)
1731                } else {
1732                    None
1733                };
1734                let rbracket = parser.consume_token(Token::RBracket)?;
1735                let bracket_span = lbracket.join_span(&rbracket);
1736                r.shift_expr(Expression::ArraySubscript(Box::new(
1737                    ArraySubscriptExpression {
1738                        expr,
1739                        bracket_span,
1740                        lower,
1741                        upper,
1742                    },
1743                )))
1744            }
1745            Token::Period
1746                if parser.options.dialect.is_postgresql()
1747                    && PRIORITY_TYPECAST < max_priority
1748                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1749            {
1750                // PostgreSQL composite type field access: (expr).field
1751                if let Err(e) = r.reduce(PRIORITY_TYPECAST) {
1752                    parser.err_here(e)?;
1753                }
1754                let expr = match r.stack.pop() {
1755                    Some(ReduceMember::Expression(e)) => e,
1756                    _ => parser.err_here("Expected expression before '.'")?,
1757                };
1758                let dot_span = parser.consume_token(Token::Period)?;
1759                let field = parser.consume_plain_identifier_unreserved()?;
1760                r.shift_expr(Expression::FieldAccess(Box::new(FieldAccessExpression {
1761                    expr,
1762                    dot_span,
1763                    field,
1764                })))
1765            }
1766            Token::Ident(_, Keyword::NOT)
1767                if !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1768            {
1769                r.shift_unary(UnaryOperator::Not(parser.consume()))
1770            }
1771            Token::Ident(_, Keyword::NOT)
1772                if PRIORITY_CMP < max_priority
1773                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1774            {
1775                if let Err(e) = r.reduce(PRIORITY_CMP) {
1776                    parser.err_here(e)?;
1777                }
1778                let lhs = match r.stack.pop() {
1779                    Some(ReduceMember::Expression(e)) => e,
1780                    _ => parser.err_here("Expected expression before here 2")?,
1781                };
1782                let op = parser.consume_keyword(Keyword::NOT)?;
1783                match &parser.token {
1784                    Token::Ident(_, Keyword::IN) => {
1785                        let op = parser.consume_keyword(Keyword::IN)?.join_span(&op);
1786                        parser.consume_token(Token::LParen)?;
1787                        let mut rhs = Vec::new();
1788                        loop {
1789                            parser.recovered(
1790                                "')' or ','",
1791                                &|t| matches!(t, Token::RParen | Token::Comma),
1792                                |parser| {
1793                                    rhs.push(parse_expression_paren(parser)?);
1794                                    Ok(())
1795                                },
1796                            )?;
1797                            if parser.skip_token(Token::Comma).is_none() {
1798                                break;
1799                            }
1800                        }
1801                        let r_paren_span = parser.consume_token(Token::RParen)?;
1802                        r.shift_expr(Expression::In(Box::new(InExpression {
1803                            lhs,
1804                            rhs,
1805                            in_span: op,
1806                            not_in: true,
1807                            r_paren_span,
1808                        })))
1809                    }
1810                    Token::Ident(_, Keyword::LIKE) => {
1811                        let like_span = parser.consume().join_span(&op);
1812                        let rhs = parse_expression_unreserved(parser, PRIORITY_CMP)?;
1813                        let escape = if matches!(parser.token, Token::Ident(_, Keyword::ESCAPE)) {
1814                            parser.consume();
1815                            Some(parse_expression_unreserved(parser, PRIORITY_CMP)?)
1816                        } else {
1817                            None
1818                        };
1819                        r.shift_expr(Expression::Like(Box::new(LikeExpression {
1820                            lhs,
1821                            rhs,
1822                            op: LikeOperator::NotLike(like_span),
1823                            escape,
1824                        })))
1825                    }
1826                    Token::Ident(_, Keyword::REGEXP) if parser.options.dialect.is_maria() => {
1827                        r.stack.push(ReduceMember::Expression(lhs));
1828                        r.shift_binop(BinaryOperator::NotRegexp(parser.consume().join_span(&op)))
1829                    }
1830                    Token::Ident(_, Keyword::RLIKE) if parser.options.dialect.is_maria() => {
1831                        r.stack.push(ReduceMember::Expression(lhs));
1832                        r.shift_binop(BinaryOperator::NotRlike(parser.consume().join_span(&op)))
1833                    }
1834                    Token::Ident(_, Keyword::BETWEEN) => {
1835                        let between_span = parser.consume_keyword(Keyword::BETWEEN)?.join_span(&op);
1836                        let low = parse_expression_unreserved(parser, PRIORITY_AND)?;
1837                        let and_span = parser.consume_keyword(Keyword::AND)?;
1838                        let high = parse_expression_unreserved(parser, PRIORITY_AND)?;
1839                        r.shift_expr(Expression::Between(Box::new(BetweenExpression {
1840                            lhs,
1841                            low,
1842                            high,
1843                            between_span: between_span.join_span(&and_span),
1844                            not_between: true,
1845                        })))
1846                    }
1847                    _ => parser.expected_failure("'IN', 'LIKE', 'REGEXP', 'RLIKE' or 'BETWEEN'")?,
1848                }
1849            }
1850            Token::Ident(_, Keyword::BETWEEN)
1851                if PRIORITY_CMP < max_priority
1852                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1853            {
1854                if let Err(e) = r.reduce(PRIORITY_CMP) {
1855                    parser.err_here(e)?;
1856                }
1857                let lhs = match r.stack.pop() {
1858                    Some(ReduceMember::Expression(e)) => e,
1859                    _ => parser.err_here("Expected expression before BETWEEN")?,
1860                };
1861                let between_span = parser.consume_keyword(Keyword::BETWEEN)?;
1862                let low = parse_expression_unreserved(parser, PRIORITY_AND)?;
1863                let and_span = parser.consume_keyword(Keyword::AND)?;
1864                let high = parse_expression_unreserved(parser, PRIORITY_AND)?;
1865                r.shift_expr(Expression::Between(Box::new(BetweenExpression {
1866                    lhs,
1867                    low,
1868                    high,
1869                    between_span: between_span.join_span(&and_span),
1870                    not_between: false,
1871                })))
1872            }
1873            Token::Ident(_, Keyword::LIKE)
1874                if PRIORITY_CMP < max_priority
1875                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1876            {
1877                if let Err(e) = r.reduce(PRIORITY_CMP) {
1878                    parser.err_here(e)?;
1879                }
1880                let lhs = match r.stack.pop() {
1881                    Some(ReduceMember::Expression(e)) => e,
1882                    _ => parser.err_here("Expected expression before LIKE")?,
1883                };
1884                let like_span = parser.consume();
1885                let rhs = parse_expression_unreserved(parser, PRIORITY_CMP)?;
1886                let escape = if matches!(parser.token, Token::Ident(_, Keyword::ESCAPE)) {
1887                    parser.consume();
1888                    Some(parse_expression_unreserved(parser, PRIORITY_CMP)?)
1889                } else {
1890                    None
1891                };
1892                r.shift_expr(Expression::Like(Box::new(LikeExpression {
1893                    lhs,
1894                    rhs,
1895                    op: LikeOperator::Like(like_span),
1896                    escape,
1897                })))
1898            }
1899            Token::Ident(_, Keyword::SIMILAR)
1900                if PRIORITY_CMP < max_priority
1901                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1902            {
1903                // SIMILAR TO expr (PostgreSQL)
1904                if let Err(e) = r.reduce(PRIORITY_CMP) {
1905                    parser.err_here(e)?;
1906                }
1907                let lhs = match r.stack.pop() {
1908                    Some(ReduceMember::Expression(e)) => e,
1909                    _ => parser.err_here("Expected expression before SIMILAR")?,
1910                };
1911                let like_span = parser.consume_keywords(&[Keyword::SIMILAR, Keyword::TO])?;
1912                parser.postgres_only(&like_span);
1913                let rhs = parse_expression_unreserved(parser, PRIORITY_CMP)?;
1914                let escape = if matches!(parser.token, Token::Ident(_, Keyword::ESCAPE)) {
1915                    parser.consume();
1916                    Some(parse_expression_unreserved(parser, PRIORITY_CMP)?)
1917                } else {
1918                    None
1919                };
1920                r.shift_expr(Expression::Like(Box::new(LikeExpression {
1921                    lhs,
1922                    rhs,
1923                    op: LikeOperator::SimilarTo(like_span),
1924                    escape,
1925                })))
1926            }
1927            Token::Ident(_, Keyword::REGEXP)
1928                if PRIORITY_CMP < max_priority && parser.options.dialect.is_maria() =>
1929            {
1930                r.shift_binop(BinaryOperator::Regexp(parser.consume()))
1931            }
1932            Token::Ident(_, Keyword::RLIKE)
1933                if PRIORITY_CMP < max_priority && parser.options.dialect.is_maria() =>
1934            {
1935                r.shift_binop(BinaryOperator::Rlike(parser.consume()))
1936            }
1937            Token::RArrowJson if PRIORITY_JSON_EXTRACT < max_priority => {
1938                r.shift_binop(BinaryOperator::JsonExtract(parser.consume()))
1939            }
1940            Token::RDoubleArrowJson if PRIORITY_JSON_EXTRACT < max_priority => {
1941                r.shift_binop(BinaryOperator::JsonExtractUnquote(parser.consume()))
1942            }
1943            // PostgreSQL built-in operator tokens
1944            Token::Contains if PRIORITY_PG_CUSTOM < max_priority => {
1945                r.shift_binop(BinaryOperator::Contains(parser.consume()))
1946            }
1947            Token::ContainedBy if PRIORITY_PG_CUSTOM < max_priority => {
1948                r.shift_binop(BinaryOperator::ContainedBy(parser.consume()))
1949            }
1950            Token::AtQuestion if PRIORITY_PG_CUSTOM < max_priority => {
1951                r.shift_binop(BinaryOperator::JsonPathExists(parser.consume()))
1952            }
1953            Token::QuestionPipe if PRIORITY_PG_CUSTOM < max_priority => {
1954                r.shift_binop(BinaryOperator::JsonbAnyKeyExists(parser.consume()))
1955            }
1956            Token::QuestionAmpersand if PRIORITY_PG_CUSTOM < max_priority => {
1957                r.shift_binop(BinaryOperator::JsonbAllKeyExists(parser.consume()))
1958            }
1959            Token::HashArrow if PRIORITY_PG_CUSTOM < max_priority => {
1960                r.shift_binop(BinaryOperator::JsonGetPath(parser.consume()))
1961            }
1962            Token::HashDoubleArrow if PRIORITY_PG_CUSTOM < max_priority => {
1963                r.shift_binop(BinaryOperator::JsonGetPathText(parser.consume()))
1964            }
1965            Token::HashMinus if PRIORITY_PG_CUSTOM < max_priority => {
1966                r.shift_binop(BinaryOperator::JsonDeletePath(parser.consume()))
1967            }
1968            Token::TildeStar if PRIORITY_PG_CUSTOM < max_priority => {
1969                r.shift_binop(BinaryOperator::RegexIMatch(parser.consume()))
1970            }
1971            Token::NotTilde if PRIORITY_PG_CUSTOM < max_priority => {
1972                r.shift_binop(BinaryOperator::NotRegexMatch(parser.consume()))
1973            }
1974            Token::NotTildeStar if PRIORITY_PG_CUSTOM < max_priority => {
1975                r.shift_binop(BinaryOperator::NotRegexIMatch(parser.consume()))
1976            }
1977            Token::LikeTilde if PRIORITY_CMP < max_priority => {
1978                r.shift_binop(BinaryOperator::Like(parser.consume()))
1979            }
1980            Token::NotLikeTilde if PRIORITY_CMP < max_priority => {
1981                r.shift_binop(BinaryOperator::NotLike(parser.consume()))
1982            }
1983            // @@ as binary (full-text / jsonpath match)
1984            Token::AtAt
1985                if PRIORITY_PG_CUSTOM < max_priority
1986                    && parser.options.dialect.is_postgresql()
1987                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1988            {
1989                r.shift_binop(BinaryOperator::JsonPathMatch(parser.consume()))
1990            }
1991            // ~ as binary regex match (only when expression already on left)
1992            Token::Tilde
1993                if PRIORITY_PG_CUSTOM < max_priority
1994                    && parser.options.dialect.is_postgresql()
1995                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
1996            {
1997                r.shift_binop(BinaryOperator::RegexMatch(parser.consume()))
1998            }
1999            // ? as jsonb key-exists binary operator (PG, non-argument mode)
2000            Token::QuestionMark
2001                if PRIORITY_PG_CUSTOM < max_priority
2002                    && parser.options.dialect.is_postgresql()
2003                    && !matches!(parser.options.arguments, crate::SQLArguments::QuestionMark)
2004                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
2005            {
2006                r.shift_binop(BinaryOperator::JsonbKeyExists(parser.consume()))
2007            }
2008            // Remaining user-defined PostgreSQL operators
2009            Token::PostgresOperator(op)
2010                if PRIORITY_PG_CUSTOM < max_priority
2011                    && parser.options.dialect.is_postgresql()
2012                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
2013            {
2014                r.shift_binop(BinaryOperator::User(op, parser.consume()))
2015            }
2016            Token::Ident(_, Keyword::OPERATOR)
2017                if PRIORITY_PG_CUSTOM < max_priority
2018                    && parser.options.dialect.is_postgresql()
2019                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
2020            {
2021                let operator_span = parser.consume_keyword(Keyword::OPERATOR)?;
2022                parser.consume_token(Token::LParen)?;
2023                let op_name = parse_operator_name(parser)?;
2024                let rparen_span = parser.consume_token(Token::RParen)?;
2025                let full_span = operator_span.join_span(&rparen_span);
2026                r.shift_binop(BinaryOperator::Operator(op_name, full_span))
2027            }
2028            Token::Ident(_, Keyword::MEMBER)
2029                if PRIORITY_CMP < max_priority
2030                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
2031            {
2032                if let Err(e) = r.reduce(PRIORITY_CMP) {
2033                    parser.err_here(e)?;
2034                }
2035                let lhs = match r.stack.pop() {
2036                    Some(ReduceMember::Expression(e)) => e,
2037                    _ => parser.err_here("Expected expression before here")?,
2038                };
2039                let member_span = parser.consume_keyword(Keyword::MEMBER)?;
2040                let of_span = parser.consume_keyword(Keyword::OF)?;
2041                parser.consume_token(Token::LParen)?;
2042                let rhs = parse_expression_paren(parser)?;
2043                let r_paren_span = parser.consume_token(Token::RParen)?;
2044                r.shift_expr(Expression::MemberOf(Box::new(MemberOfExpression {
2045                    lhs,
2046                    rhs,
2047                    member_of_span: member_span.join_span(&of_span),
2048                    r_paren_span,
2049                })))
2050            }
2051            Token::Ident(_, Keyword::INTERVAL) => {
2052                let interval_span = parser.consume();
2053                let (time_interval, embedded_unit) = match parser.token {
2054                    Token::String(..) => {
2055                        let v = parser.consume_string()?;
2056                        let str_span = v.span();
2057                        let mut nums = Vec::new();
2058                        let mut embedded: Option<TimeUnit> = None;
2059                        for part in v.split([':', '!', ',', '.', '-', ' ']) {
2060                            if let Ok(n) = part.parse::<i64>() {
2061                                nums.push(n);
2062                            } else if !part.is_empty() {
2063                                embedded = parse_time_unit_from_str(part);
2064                            }
2065                        }
2066                        ((nums, str_span), embedded)
2067                    }
2068                    Token::Integer(_) => {
2069                        let (v, s) = parser.consume_int()?;
2070                        ((vec![v], s), None)
2071                    }
2072                    _ => parser.err_here("Expected integer or string")?,
2073                };
2074                let time_unit = if let Some(u) = parse_time_unit(&parser.token) {
2075                    (u, parser.consume())
2076                } else if let Some(u) = embedded_unit {
2077                    (u, time_interval.1.clone())
2078                } else {
2079                    parser.err_here("Expected time unit")?
2080                };
2081                let e = Expression::Interval(Box::new(IntervalExpression {
2082                    interval_span,
2083                    time_interval,
2084                    time_unit,
2085                }));
2086                r.shift_expr(e)
2087            }
2088            Token::Ident(_, Keyword::TIMESTAMPADD) => {
2089                let timestamp_add_span = parser.consume();
2090                parser.consume_token(Token::LParen)?;
2091                let parts = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
2092                    let Some(u) = parse_time_unit(&parser.token) else {
2093                        parser.err_here("Expected time unit")?
2094                    };
2095                    let unit = (u, parser.consume());
2096                    parser.consume_token(Token::Comma)?;
2097                    let interval = parse_expression_outer(parser)?;
2098                    parser.consume_token(Token::Comma)?;
2099                    let datetime = parse_expression_outer(parser)?;
2100                    Ok(Some((unit, interval, datetime)))
2101                })?;
2102                let r_paren_span = parser.consume_token(Token::RParen)?;
2103                if let Some((unit, interval, datetime)) = parts {
2104                    r.shift_expr(Expression::TimestampAdd(Box::new(TimestampAddExpression {
2105                        timestamp_add_span,
2106                        unit,
2107                        interval,
2108                        datetime,
2109                        r_paren_span,
2110                    })))
2111                } else {
2112                    r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
2113                        span: timestamp_add_span,
2114                    })))
2115                }
2116            }
2117            Token::Ident(_, Keyword::TIMESTAMPDIFF) => {
2118                let timestamp_diff_span = parser.consume();
2119                parser.consume_token(Token::LParen)?;
2120                let parts = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
2121                    let Some(u) = parse_time_unit(&parser.token) else {
2122                        parser.err_here("Expected time unit")?
2123                    };
2124                    let unit = (u, parser.consume());
2125                    parser.consume_token(Token::Comma)?;
2126                    let e1 = parse_expression_outer(parser)?;
2127                    parser.consume_token(Token::Comma)?;
2128                    let e2 = parse_expression_outer(parser)?;
2129                    Ok(Some((unit, e1, e2)))
2130                })?;
2131                let r_paren_span = parser.consume_token(Token::RParen)?;
2132                if let Some((unit, e1, e2)) = parts {
2133                    r.shift_expr(Expression::TimestampDiff(Box::new(
2134                        TimestampDiffExpression {
2135                            timestamp_diff_span,
2136                            unit,
2137                            e1,
2138                            e2,
2139                            r_paren_span,
2140                        },
2141                    )))
2142                } else {
2143                    r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
2144                        span: timestamp_diff_span,
2145                    })))
2146                }
2147            }
2148            Token::Plus if PRIORITY_ADD < max_priority => {
2149                r.shift_binop(BinaryOperator::Add(parser.consume()))
2150            }
2151            Token::Div if PRIORITY_MULT < max_priority => {
2152                r.shift_binop(BinaryOperator::Divide(parser.consume()))
2153            }
2154            Token::Ident(_, Keyword::DIV) if PRIORITY_MULT < max_priority => {
2155                r.shift_binop(BinaryOperator::Div(parser.consume()))
2156            }
2157            Token::Minus if PRIORITY_ADD < max_priority => {
2158                r.shift_binop(BinaryOperator::Subtract(parser.consume()))
2159            }
2160            Token::Mul if !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) => r
2161                .shift_expr(Expression::Identifier(Box::new(IdentifierExpression {
2162                    parts: vec![IdentifierPart::Star(parser.consume_token(Token::Mul)?)],
2163                }))),
2164            Token::Mul
2165                if PRIORITY_MULT < max_priority
2166                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
2167            {
2168                r.shift_binop(BinaryOperator::Mult(parser.consume()))
2169            }
2170            Token::Mod if PRIORITY_MULT < max_priority => {
2171                r.shift_binop(BinaryOperator::Mod(parser.consume()))
2172            }
2173            Token::Ident(_, Keyword::MOD)
2174                if PRIORITY_MULT < max_priority
2175                    && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
2176            {
2177                r.shift_binop(BinaryOperator::Mod(parser.consume()))
2178            }
2179            Token::Ident(_, Keyword::TRUE) => {
2180                r.shift_expr(Expression::Bool(Box::new(BoolExpression {
2181                    value: true,
2182                    span: parser.consume_keyword(Keyword::TRUE)?,
2183                })))
2184            }
2185            Token::Ident(_, Keyword::FALSE) => {
2186                r.shift_expr(Expression::Bool(Box::new(BoolExpression {
2187                    value: false,
2188                    span: parser.consume_keyword(Keyword::FALSE)?,
2189                })))
2190            }
2191            Token::Ident(_, Keyword::NULL) => {
2192                r.shift_expr(Expression::Null(Box::new(NullExpression {
2193                    span: parser.consume_keyword(Keyword::NULL)?,
2194                })))
2195            }
2196            Token::Ident(_, Keyword::_LIST_) if parser.options.list_hack => {
2197                let arg = parser.arg;
2198                parser.arg += 1;
2199                r.shift_expr(Expression::ListHack(Box::new(ListHackExpression {
2200                    index: arg,
2201                    span: parser.consume_keyword(Keyword::_LIST_)?,
2202                })))
2203            }
2204            Token::String(..) => {
2205                r.shift_expr(Expression::String(Box::new(parser.consume_string()?)))
2206            }
2207            Token::Integer(_) => {
2208                let (value, span) = parser.consume_int()?;
2209                r.shift_expr(Expression::Integer(Box::new(IntegerExpression {
2210                    value,
2211                    span,
2212                })))
2213            }
2214            Token::Float(_) => {
2215                let (value, span) = parser.consume_float()?;
2216                r.shift_expr(Expression::Float(Box::new(FloatExpression { value, span })))
2217            }
2218            Token::Ident(_, Keyword::CAST) => {
2219                let cast_span = parser.consume_keyword(Keyword::CAST)?;
2220                parser.consume_token(Token::LParen)?;
2221                let cast = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
2222                    let expr = parse_expression_outer(parser)?;
2223                    let as_span = parser.consume_keyword(Keyword::AS)?;
2224                    let type_ = parse_data_type(parser, DataTypeContext::TypeRef)?;
2225                    Ok(Some((expr, as_span, type_)))
2226                })?;
2227                let r_paren_span = parser.consume_token(Token::RParen)?;
2228                if let Some((expr, as_span, type_)) = cast {
2229                    r.shift_expr(Expression::Cast(Box::new(CastExpression {
2230                        cast_span,
2231                        expr,
2232                        as_span,
2233                        type_,
2234                        r_paren_span,
2235                    })))
2236                } else {
2237                    r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
2238                        span: cast_span,
2239                    })))
2240                }
2241            }
2242            Token::Ident(_, Keyword::CONVERT) => {
2243                let convert_span = parser.consume_keyword(Keyword::CONVERT)?;
2244                parser.consume_token(Token::LParen)?;
2245                let convert =
2246                    parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
2247                        let expr = parse_expression_outer(parser)?;
2248                        // Check if it's CONVERT(expr, type) or CONVERT(expr USING charset)
2249                        if parser.skip_keyword(Keyword::USING).is_some() {
2250                            // CONVERT(expr USING charset)
2251                            let charset = parser.consume_plain_identifier_unreserved()?;
2252                            Ok(Some((expr, None, Some(charset))))
2253                        } else {
2254                            // CONVERT(expr, type)
2255                            parser.consume_token(Token::Comma)?;
2256                            let type_ = parse_data_type(parser, DataTypeContext::TypeRef)?;
2257                            Ok(Some((expr, Some(type_), None)))
2258                        }
2259                    })?;
2260                let r_paren_span = parser.consume_token(Token::RParen)?;
2261                if let Some((expr, type_, charset)) = convert {
2262                    r.shift_expr(Expression::Convert(Box::new(ConvertExpression {
2263                        convert_span,
2264                        expr,
2265                        type_,
2266                        using_charset: charset.map(|c| (c.span(), c)),
2267                        r_paren_span,
2268                    })))
2269                } else {
2270                    r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
2271                        span: convert_span,
2272                    })))
2273                }
2274            }
2275            Token::Ident(_, Keyword::GROUP_CONCAT) => {
2276                let group_concat_span: core::ops::Range<usize> =
2277                    parser.consume_keyword(Keyword::GROUP_CONCAT)?;
2278                parser.consume_token(Token::LParen)?;
2279                let distinct_span: Option<core::ops::Range<usize>> =
2280                    parser.skip_keyword(Keyword::DISTINCT);
2281                let expr = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
2282                    let expr = parse_expression_outer(parser)?;
2283                    Ok(Some(expr))
2284                })?;
2285                // TODO
2286                // [ORDER BY {unsigned_integer | col_name | expr}
2287                //     [ASC | DESC] [,col_name ...]]
2288                // [SEPARATOR str_val]
2289                // [LIMIT {[offset,] row_count | row_count OFFSET offset}])
2290                let r_paren_span = parser.consume_token(Token::RParen)?;
2291                if let Some(expr) = expr {
2292                    r.shift_expr(Expression::GroupConcat(Box::new(GroupConcatExpression {
2293                        group_concat_span,
2294                        distinct_span,
2295                        expr,
2296                        r_paren_span,
2297                    })))
2298                } else {
2299                    r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
2300                        span: group_concat_span,
2301                    })))
2302                }
2303            }
2304            Token::Ident(_, Keyword::TRIM) if matches!(parser.peek(), Token::LParen) => {
2305                let trim_span = parser.consume_keyword(Keyword::TRIM)?;
2306                parser.consume_token(Token::LParen)?;
2307                let parts = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
2308                    // Optional BOTH / LEADING / TRAILING
2309                    let direction = match &parser.token {
2310                        Token::Ident(_, Keyword::BOTH) => {
2311                            Some(TrimDirection::Both(parser.consume()))
2312                        }
2313                        Token::Ident(_, Keyword::LEADING) => {
2314                            Some(TrimDirection::Leading(parser.consume()))
2315                        }
2316                        Token::Ident(_, Keyword::TRAILING) => {
2317                            Some(TrimDirection::Trailing(parser.consume()))
2318                        }
2319                        _ => None,
2320                    };
2321
2322                    let (what, from_span, value) = if direction.is_some() {
2323                        // After direction: optionally [remstr] FROM value
2324                        if let Some(from_s) = parser.skip_keyword(Keyword::FROM) {
2325                            // No remstr: TRIM(BOTH FROM str)
2326                            let value = parse_expression_outer(parser)?;
2327                            (None, Some(from_s), value)
2328                        } else {
2329                            // Has remstr: TRIM(BOTH remstr FROM str)
2330                            let what = parse_expression_outer(parser)?;
2331                            let from_s = parser.consume_keyword(Keyword::FROM)?;
2332                            let value = parse_expression_outer(parser)?;
2333                            (Some(what), Some(from_s), value)
2334                        }
2335                    } else {
2336                        // No direction: TRIM(str) or TRIM(remstr FROM str)
2337                        let first = parse_expression_outer(parser)?;
2338                        if let Some(from_s) = parser.skip_keyword(Keyword::FROM) {
2339                            // first is remstr
2340                            let value = parse_expression_outer(parser)?;
2341                            (Some(first), Some(from_s), value)
2342                        } else {
2343                            // first is the value itself
2344                            (None, None, first)
2345                        }
2346                    };
2347                    Ok(Some((direction, what, from_span, value)))
2348                })?;
2349                let r_paren_span = parser.consume_token(Token::RParen)?;
2350                if let Some((direction, what, from_span, value)) = parts {
2351                    r.shift_expr(Expression::Trim(Box::new(TrimExpression {
2352                        trim_span,
2353                        direction,
2354                        what,
2355                        from_span,
2356                        value,
2357                        r_paren_span,
2358                    })))
2359                } else {
2360                    r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
2361                        span: trim_span,
2362                    })))
2363                }
2364            }
2365            Token::Ident(_, Keyword::EXTRACT) => {
2366                let extract_span = parser.consume_keyword(Keyword::EXTRACT)?;
2367                parser.consume_token(Token::LParen)?;
2368                let parts = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
2369                    let Some(u) = parse_time_unit(&parser.token) else {
2370                        parser.err_here("Expected time unit")?
2371                    };
2372                    let time_unit = (u, parser.consume());
2373                    let from_span = parser.consume_keyword(Keyword::FROM)?;
2374                    let date = parse_expression_outer(parser)?;
2375                    Ok(Some((time_unit, from_span, date)))
2376                })?;
2377                let r_paren_span = parser.consume_token(Token::RParen)?;
2378                if let Some((time_unit, from_span, date)) = parts {
2379                    r.shift_expr(Expression::Extract(Box::new(ExtractExpression {
2380                        extract_span,
2381                        time_unit,
2382                        from_span,
2383                        date,
2384                        r_paren_span,
2385                    })))
2386                } else {
2387                    r.shift_expr(Expression::Invalid(Box::new(InvalidExpression {
2388                        span: extract_span,
2389                    })))
2390                }
2391            }
2392            Token::Ident(_, Keyword::MATCH) => {
2393                let match_span = parser.consume_keyword(Keyword::MATCH)?;
2394                parser.consume_token(Token::LParen)?;
2395                let mut cols = Vec::new();
2396                loop {
2397                    parser.recovered(
2398                        "')' or ','",
2399                        &|t| matches!(t, Token::RParen | Token::Comma),
2400                        |parser| {
2401                            cols.push(parse_expression_paren(parser)?);
2402                            Ok(())
2403                        },
2404                    )?;
2405                    if parser.skip_token(Token::Comma).is_none() {
2406                        break;
2407                    }
2408                }
2409                parser.consume_token(Token::RParen)?;
2410                let against_span = parser.consume_keyword(Keyword::AGAINST)?;
2411                parser.consume_token(Token::LParen)?;
2412
2413                // Parse the search expression but don't treat `IN`/`WITH` as binary
2414                // operators here — they are MATCH modes and may appear inside the
2415                // AGAINST(...) parentheses (MySQL allows both inside and outside).
2416                // PRIORITY_CMP stops before IN/WITH so they remain available as MATCH mode keywords
2417                let expr = parse_expression_unreserved(parser, PRIORITY_CMP)?;
2418
2419                // optional mode that may appear inside the AGAINST(...) parentheses
2420                let mut mode: Option<MatchMode> = None;
2421                if parser.skip_keyword(Keyword::IN).is_some() {
2422                    if let Some(boolean_span) = parser.skip_keyword(Keyword::BOOLEAN) {
2423                        let mode_span = parser.consume_keyword(Keyword::MODE)?;
2424                        mode = Some(MatchMode::InBoolean(boolean_span.join_span(&mode_span)));
2425                    } else if let Some(natural_span) = parser.skip_keyword(Keyword::NATURAL) {
2426                        // optional LANGUAGE after NATURAL
2427                        let _language_span = parser.skip_keyword(Keyword::LANGUAGE);
2428                        let mode_span = parser.consume_keyword(Keyword::MODE)?;
2429                        let natural_total = natural_span.join_span(&mode_span);
2430                        // optional WITH QUERY EXPANSION following NATURAL MODE inside parens
2431                        if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
2432                            let expansion_total = with_span.join_span(
2433                                &parser.consume_keywords(&[Keyword::QUERY, Keyword::EXPANSION])?,
2434                            );
2435                            mode = Some(MatchMode::InNaturalLanguageWithQueryExpansion(
2436                                natural_total.join_span(&expansion_total),
2437                            ));
2438                        } else {
2439                            mode = Some(MatchMode::InNaturalLanguage(natural_total));
2440                        }
2441                    }
2442                } else if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
2443                    mode = Some(MatchMode::WithQueryExpansion(with_span.join_span(
2444                        &parser.consume_keywords(&[Keyword::QUERY, Keyword::EXPANSION])?,
2445                    )));
2446                }
2447
2448                let r_paren_span = parser.consume_token(Token::RParen)?;
2449
2450                // If no mode was found inside the parens, allow it after the closing
2451                // parenthesis as well (some dialects/placeholders may put it there).
2452                if mode.is_none() {
2453                    if parser.skip_keyword(Keyword::IN).is_some() {
2454                        if let Some(boolean_span) = parser.skip_keyword(Keyword::BOOLEAN) {
2455                            let mode_span = parser.consume_keyword(Keyword::MODE)?;
2456                            mode = Some(MatchMode::InBoolean(boolean_span.join_span(&mode_span)));
2457                        } else if let Some(natural_span) = parser.skip_keyword(Keyword::NATURAL) {
2458                            let _language_span = parser.skip_keyword(Keyword::LANGUAGE);
2459                            let mode_span = parser.consume_keyword(Keyword::MODE)?;
2460                            let natural_total = natural_span.join_span(&mode_span);
2461                            // optional WITH QUERY EXPANSION following NATURAL MODE after paren
2462                            if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
2463                                let expansion_total = with_span.join_span(
2464                                    &parser
2465                                        .consume_keywords(&[Keyword::QUERY, Keyword::EXPANSION])?,
2466                                );
2467                                mode = Some(MatchMode::InNaturalLanguageWithQueryExpansion(
2468                                    natural_total.join_span(&expansion_total),
2469                                ));
2470                            } else {
2471                                mode = Some(MatchMode::InNaturalLanguage(natural_total));
2472                            }
2473                        }
2474                    } else if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
2475                        mode = Some(MatchMode::WithQueryExpansion(with_span.join_span(
2476                            &parser.consume_keywords(&[Keyword::QUERY, Keyword::EXPANSION])?,
2477                        )));
2478                    }
2479                }
2480
2481                r.shift_expr(Expression::MatchAgainst(Box::new(MatchAgainstExpression {
2482                    match_span,
2483                    columns: cols,
2484                    against_span,
2485                    expr,
2486                    mode,
2487                    r_paren_span,
2488                })))
2489            }
2490            Token::Ident(_, Keyword::LEFT) if matches!(parser.peek(), Token::LParen) => {
2491                let i = parser.token.clone();
2492                let s = parser.span.clone();
2493                parser.consume();
2494                r.shift_expr(parse_function(parser, i, s)?)
2495            }
2496            Token::Ident(_, Keyword::CHAR) if matches!(parser.peek(), Token::LParen) => {
2497                let s = parser.span.clone();
2498                parser.consume();
2499                r.shift_expr(parse_char_function(parser, s)?)
2500            }
2501            Token::Ident(_, keyword)
2502                if matches!(parser.peek(), Token::LParen)
2503                    && is_aggregate_function_ident(&keyword) =>
2504            {
2505                let i = parser.token.clone();
2506                let s = parser.span.clone();
2507                parser.consume();
2508                r.shift_expr(parse_aggregate_function(parser, i, s)?)
2509            }
2510            // Handle charset-prefixed strings like _utf8mb4 'abc' or _binary 'data'
2511            Token::Ident(charset, _)
2512                if charset.starts_with('_') && matches!(parser.peek(), Token::String(..)) =>
2513            {
2514                // Consume the charset prefix
2515                parser.consume();
2516                // Parse the string literal
2517                r.shift_expr(Expression::String(Box::new(parser.consume_string()?)))
2518            }
2519            // PostgreSQL ARRAY[...] literal
2520            Token::Ident(_, Keyword::ARRAY) if matches!(parser.peek(), Token::LBracket) => {
2521                let array_span = parser.consume_keyword(Keyword::ARRAY)?;
2522                parser.postgres_only(&array_span);
2523                let lbracket = parser.consume_token(Token::LBracket)?;
2524                let mut elements = Vec::new();
2525                if !matches!(parser.token, Token::RBracket) {
2526                    loop {
2527                        parser.recovered(
2528                            "']' or ','",
2529                            &|t| matches!(t, Token::RBracket | Token::Comma),
2530                            |parser| {
2531                                elements.push(parse_array_element(parser)?);
2532                                Ok(())
2533                            },
2534                        )?;
2535                        if parser.skip_token(Token::Comma).is_none() {
2536                            break;
2537                        }
2538                    }
2539                }
2540                let rbracket = parser.consume_token(Token::RBracket)?;
2541                let bracket_span = lbracket.join_span(&rbracket);
2542                r.shift_expr(Expression::Array(Box::new(ArrayExpression {
2543                    array_span,
2544                    bracket_span,
2545                    elements,
2546                })))
2547            }
2548            Token::Ident(_, k)
2549                if (k.expr_ident() || !k.restricted(restrict))
2550                    && !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
2551            {
2552                let i = parser.token.clone();
2553                let s = parser.span.clone();
2554                parser.consume();
2555                if matches!(parser.token, Token::LParen) {
2556                    r.shift_expr(parse_function(parser, i, s)?)
2557                } else {
2558                    let f = match i {
2559                        Token::Ident(_, Keyword::CURRENT_TIMESTAMP) => {
2560                            Some(Function::CurrentTimestamp)
2561                        }
2562                        Token::Ident(_, Keyword::LOCALTIME | Keyword::LOCALTIMESTAMP) => {
2563                            Some(Function::Now)
2564                        }
2565                        Token::Ident(_, Keyword::UTC_TIMESTAMP) => Some(Function::UtcTimeStamp),
2566                        Token::Ident(_, Keyword::UTC_DATE) => Some(Function::UtcDate),
2567                        Token::Ident(_, Keyword::UTC_TIME) => Some(Function::UtcTime),
2568                        Token::Ident(_, Keyword::CURRENT_DATE) => Some(Function::CurDate),
2569                        Token::Ident(_, Keyword::CURRENT_TIME) => Some(Function::CurTime),
2570                        Token::Ident(_, Keyword::CURRENT_USER) => Some(Function::CurrentUser),
2571                        Token::Ident(_, Keyword::CURRENT_ROLE) => Some(Function::CurrentRole),
2572                        Token::Ident(_, Keyword::CURRENT_CATALOG) => Some(Function::CurrentCatalog),
2573                        Token::Ident(_, Keyword::SESSION_USER) => Some(Function::SessionUser),
2574                        Token::Ident(_, Keyword::USER)
2575                            if parser.options.dialect.is_postgresql() =>
2576                        {
2577                            Some(Function::CurrentUser)
2578                        }
2579                        _ => None,
2580                    };
2581                    if let Some(f) = f {
2582                        r.shift_expr(Expression::Function(Box::new(FunctionCallExpression {
2583                            function: f,
2584                            args: Vec::new(),
2585                            function_span: s.clone(),
2586                            r_paren_span: s,
2587                        })))
2588                    } else {
2589                        let mut parts = vec![IdentifierPart::Name(
2590                            parser.token_to_plain_identifier(&i, s)?,
2591                        )];
2592                        // Save the last identifier token so we can call parse_function if
2593                        // the chain is followed by `(` (schema-qualified function call).
2594                        let mut last_ident_tok: Option<(Token<'a>, Span)> = None;
2595                        loop {
2596                            if parser.skip_token(Token::Period).is_none() {
2597                                break;
2598                            }
2599                            match &parser.token {
2600                                Token::Mul => {
2601                                    last_ident_tok = None;
2602                                    parts.push(IdentifierPart::Star(
2603                                        parser.consume_token(Token::Mul)?,
2604                                    ));
2605                                }
2606                                Token::Ident(_, _) => {
2607                                    let fn_tok = parser.token.clone();
2608                                    let fn_span = parser.span.clone();
2609                                    last_ident_tok = Some((fn_tok, fn_span));
2610                                    parts.push(IdentifierPart::Name(
2611                                        parser.consume_plain_identifier_unreserved()?,
2612                                    ));
2613                                }
2614                                _ => parser.expected_failure("Identifier or '*'")?,
2615                            }
2616                        }
2617                        // Schema-qualified function call: schema.func(args)
2618                        if matches!(parser.token, Token::LParen) {
2619                            if let Some((fn_tok, fn_span)) = last_ident_tok {
2620                                // All parts form the qualified name; last part is function name
2621                                let mut all_idents: Vec<Identifier<'a>> = parts[..parts.len() - 1]
2622                                    .iter()
2623                                    .filter_map(|p| match p {
2624                                        IdentifierPart::Name(id) => Some(id.clone()),
2625                                        _ => None,
2626                                    })
2627                                    .collect();
2628                                let fn_ident = Identifier {
2629                                    value: match &fn_tok {
2630                                        Token::Ident(v, _) => v,
2631                                        _ => "",
2632                                    },
2633                                    span: fn_span.clone(),
2634                                    case_sensitive: false,
2635                                };
2636                                all_idents.push(fn_ident);
2637                                // function_span covers from first qualifier to function name
2638                                let function_span = if all_idents.len() > 1 {
2639                                    all_idents[0].span.join_span(&fn_span)
2640                                } else {
2641                                    fn_span
2642                                };
2643                                r.shift_expr(parse_function_call(
2644                                    parser,
2645                                    Function::Other(all_idents),
2646                                    function_span,
2647                                )?)
2648                            } else {
2649                                r.shift_expr(Expression::Identifier(Box::new(
2650                                    IdentifierExpression { parts },
2651                                )))
2652                            }
2653                        } else {
2654                            r.shift_expr(Expression::Identifier(Box::new(IdentifierExpression {
2655                                parts,
2656                            })))
2657                        }
2658                    }
2659                }
2660            }
2661            Token::QuestionMark
2662                if matches!(parser.options.arguments, crate::SQLArguments::QuestionMark) =>
2663            {
2664                let arg = parser.arg;
2665                parser.arg += 1;
2666                r.shift_expr(Expression::Arg(Box::new(ArgExpression {
2667                    index: arg,
2668                    span: parser.consume_token(Token::QuestionMark)?,
2669                })))
2670            }
2671            Token::PercentS if matches!(parser.options.arguments, crate::SQLArguments::Percent) => {
2672                let arg = parser.arg;
2673                parser.arg += 1;
2674                r.shift_expr(Expression::Arg(Box::new(ArgExpression {
2675                    index: arg,
2676                    span: parser.consume_token(Token::PercentS)?,
2677                })))
2678            }
2679            Token::DollarArg(arg)
2680                if matches!(parser.options.arguments, crate::SQLArguments::Dollar) =>
2681            {
2682                r.shift_expr(Expression::Arg(Box::new(ArgExpression {
2683                    index: arg - 1,
2684                    span: parser.consume(),
2685                })))
2686            }
2687            Token::LParen => {
2688                let paren_start = parser.consume_token(Token::LParen)?;
2689                let first = parse_expression_paren(parser)?;
2690                if parser.skip_token(Token::Comma).is_some() {
2691                    // Row / tuple constructor: (expr1, expr2, ...)
2692                    let mut elements = vec![first];
2693                    loop {
2694                        if matches!(parser.token, Token::RParen) {
2695                            break;
2696                        }
2697                        parser.recovered(
2698                            "')' or ','",
2699                            &|t| matches!(t, Token::RParen | Token::Comma),
2700                            |parser| {
2701                                elements.push(parse_expression_paren(parser)?);
2702                                Ok(())
2703                            },
2704                        )?;
2705                        if parser.skip_token(Token::Comma).is_none() {
2706                            break;
2707                        }
2708                    }
2709                    let paren_end = parser.consume_token(Token::RParen)?;
2710                    r.shift_expr(Expression::Row(Box::new(RowExpression {
2711                        paren_span: paren_start.join_span(&paren_end),
2712                        elements,
2713                    })))
2714                } else {
2715                    parser.consume_token(Token::RParen)?;
2716                    r.shift_expr(first)
2717                }
2718            }
2719            Token::Ident(_, Keyword::EXISTS) => {
2720                let exists_span = parser.consume_keyword(Keyword::EXISTS)?;
2721                parser.consume_token(Token::LParen)?;
2722                let subquery = parse_compound_query(parser)?;
2723                let r_paren_span = parser.consume_token(Token::RParen)?;
2724                let ans = Expression::Exists(Box::new(ExistsExpression {
2725                    exists_span,
2726                    subquery,
2727                    r_paren_span,
2728                }));
2729                r.shift_expr(ans)
2730            }
2731            Token::Ident(_, Keyword::ANY | Keyword::SOME | Keyword::ALL)
2732                if parser.options.dialect.is_postgresql()
2733                    && !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
2734            {
2735                let quantifier = match &parser.token {
2736                    Token::Ident(_, Keyword::ANY) => {
2737                        Quantifier::Any(parser.consume_keyword(Keyword::ANY)?)
2738                    }
2739                    Token::Ident(_, Keyword::SOME) => {
2740                        Quantifier::Some(parser.consume_keyword(Keyword::SOME)?)
2741                    }
2742                    _ => Quantifier::All(parser.consume_keyword(Keyword::ALL)?),
2743                };
2744                parser.consume_token(Token::LParen)?;
2745                let operand = parse_expression_paren(parser)?;
2746                let r_paren_span = parser.consume_token(Token::RParen)?;
2747                r.shift_expr(Expression::Quantifier(Box::new(QuantifierExpression {
2748                    quantifier,
2749                    operand,
2750                    r_paren_span,
2751                })))
2752            }
2753            Token::Ident(_, Keyword::CASE) => {
2754                let case_span = parser.consume_keyword(Keyword::CASE)?;
2755                let value = if !matches!(parser.token, Token::Ident(_, Keyword::WHEN)) {
2756                    Some(parse_expression_unreserved(parser, PRIORITY_MAX)?)
2757                } else {
2758                    None
2759                };
2760                let mut whens = Vec::new();
2761                let mut else_ = None;
2762                parser.recovered(
2763                    "'END'",
2764                    &|t| matches!(t, Token::Ident(_, Keyword::END)),
2765                    |parser| {
2766                        loop {
2767                            let when_span = parser.consume_keyword(Keyword::WHEN)?;
2768                            let when = parse_expression_unreserved(parser, PRIORITY_MAX)?;
2769                            let then_span = parser.consume_keyword(Keyword::THEN)?;
2770                            let then = parse_expression_unreserved(parser, PRIORITY_MAX)?;
2771                            whens.push(When {
2772                                when_span,
2773                                when,
2774                                then_span,
2775                                then,
2776                            });
2777                            if !matches!(parser.token, Token::Ident(_, Keyword::WHEN)) {
2778                                break;
2779                            }
2780                        }
2781                        if let Some(span) = parser.skip_keyword(Keyword::ELSE) {
2782                            else_ = Some((span, parse_expression_unreserved(parser, PRIORITY_MAX)?))
2783                        };
2784                        Ok(())
2785                    },
2786                )?;
2787                let end_span = parser.consume_keyword(Keyword::END)?;
2788                r.shift_expr(Expression::Case(Box::new(CaseExpression {
2789                    case_span,
2790                    value,
2791                    whens,
2792                    else_,
2793                    end_span,
2794                })))
2795            }
2796            Token::AtAtGlobal | Token::AtAtSession => {
2797                let global = parser.skip_token(Token::AtAtGlobal);
2798                let session = if global.is_none() {
2799                    Some(parser.consume_token(Token::AtAtSession)?)
2800                } else {
2801                    None
2802                };
2803                let dot = Some(parser.consume_token(Token::Period)?);
2804                let variable = match &parser.token {
2805                    Token::Ident(_, Keyword::TIME_ZONE) => Variable::TimeZone,
2806                    Token::Ident(t, _) => Variable::Other(t),
2807                    _ => parser.expected_failure("Identifier")?,
2808                };
2809                let variable_span = parser.consume();
2810                r.shift_expr(Expression::Variable(Box::new(VariableExpression {
2811                    global,
2812                    session,
2813                    dot,
2814                    variable,
2815                    variable_span,
2816                })))
2817            }
2818            Token::At => {
2819                // User variable: @variable_name
2820                let at_span = parser.consume_token(Token::At)?;
2821                let name = parser.consume_plain_identifier_unreserved()?;
2822                r.shift_expr(Expression::UserVariable(Box::new(UserVariableExpression {
2823                    name,
2824                    at_span,
2825                })))
2826            }
2827            _ => break,
2828        };
2829        if let Err(e) = e {
2830            parser.err_here(e.to_string())?;
2831        }
2832    }
2833    if r.reduce(99999).is_err() {
2834        parser.err_here("Expected expression")
2835    } else if r.stack.len() != 1 {
2836        parser.ice(file!(), line!())
2837    } else if let Some(ReduceMember::Expression(e)) = r.stack.pop() {
2838        Ok(e)
2839    } else {
2840        parser.ice(file!(), line!())
2841    }
2842}
2843
2844pub(crate) fn parse_expression_unreserved<'a>(
2845    parser: &mut Parser<'a, '_>,
2846    max_priority: usize,
2847) -> Result<Expression<'a>, ParseError> {
2848    parse_expression_restricted(parser, max_priority, parser.reserved())
2849}
2850
2851/// Parse an expression that may be DEFAULT (for INSERT VALUES, INSERT SET, UPDATE SET contexts)
2852pub(crate) fn parse_expression_or_default<'a>(
2853    parser: &mut Parser<'a, '_>,
2854    max_priority: usize,
2855) -> Result<Expression<'a>, ParseError> {
2856    if matches!(parser.token, Token::Ident(_, Keyword::DEFAULT)) {
2857        Ok(Expression::Default(Box::new(DefaultExpression {
2858            span: parser.consume_keyword(Keyword::DEFAULT)?,
2859        })))
2860    } else {
2861        parse_expression_unreserved(parser, max_priority)
2862    }
2863}
2864
2865pub(crate) fn parse_expression_outer<'a>(
2866    parser: &mut Parser<'a, '_>,
2867) -> Result<Expression<'a>, ParseError> {
2868    if matches!(parser.token, Token::Ident(_, Keyword::SELECT)) {
2869        Ok(Expression::Subquery(Box::new(SubqueryExpression {
2870            expression: parse_compound_query(parser)?,
2871        })))
2872    } else {
2873        parse_expression_unreserved(parser, PRIORITY_MAX)
2874    }
2875}
2876
2877pub(crate) fn parse_expression_paren<'a>(
2878    parser: &mut Parser<'a, '_>,
2879) -> Result<Expression<'a>, ParseError> {
2880    if matches!(parser.token, Token::Ident(_, Keyword::SELECT)) {
2881        Ok(Expression::Subquery(Box::new(SubqueryExpression {
2882            expression: parse_compound_query(parser)?,
2883        })))
2884    } else {
2885        parse_expression_unreserved(parser, PRIORITY_MAX)
2886    }
2887}
2888
2889#[cfg(test)]
2890mod tests {
2891    use core::ops::Deref;
2892
2893    use alloc::{
2894        format,
2895        string::{String, ToString},
2896    };
2897
2898    use crate::{
2899        BinaryExpression, ParseOptions, SQLDialect,
2900        expression::{BinaryOperator, Expression, LikeOperator, PRIORITY_MAX, Quantifier},
2901        issue::Issues,
2902        parser::Parser,
2903    };
2904
2905    use super::{IdentifierPart, parse_expression_unreserved};
2906
2907    fn test_ident<'a>(e: &Expression<'a>, v: &str) -> Result<(), String> {
2908        let v = match e {
2909            Expression::Identifier(a) => match a.parts.as_slice() {
2910                [IdentifierPart::Name(vv)] => vv.deref() == v,
2911                _ => false,
2912            },
2913            _ => false,
2914        };
2915        if !v {
2916            Err(format!("Expected identifier {} found {:?}", v, e))
2917        } else {
2918            Ok(())
2919        }
2920    }
2921
2922    fn test_expr(src: &'static str, f: impl FnOnce(&Expression<'_>) -> Result<(), String>) {
2923        let mut issues = Issues::new(src);
2924        let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
2925        let mut parser = Parser::new(src, &mut issues, &options);
2926        let res = parse_expression_unreserved(&mut parser, PRIORITY_MAX)
2927            .expect("Expression in test expr");
2928        if let Err(e) = f(&res) {
2929            panic!("Error parsing {}: {}\nGot {:#?}", src, e, res);
2930        }
2931    }
2932
2933    #[test]
2934    fn expressions() {
2935        test_expr("`a` + `b` * `c` + `d`", |e| {
2936            match e {
2937                Expression::Binary(b) => {
2938                    let BinaryExpression {
2939                        op: BinaryOperator::Add(_),
2940                        lhs,
2941                        rhs,
2942                        ..
2943                    } = b.as_ref()
2944                    else {
2945                        return Err("Expected outer +".to_string());
2946                    };
2947                    match lhs {
2948                        Expression::Binary(b) => {
2949                            let BinaryExpression {
2950                                op: BinaryOperator::Add(_),
2951                                lhs,
2952                                rhs,
2953                                ..
2954                            } = b.as_ref()
2955                            else {
2956                                return Err("Expected inner +".to_string());
2957                            };
2958                            test_ident(lhs, "a")?;
2959                            match rhs {
2960                                Expression::Binary(b) => {
2961                                    let BinaryExpression {
2962                                        op: BinaryOperator::Mult(_),
2963                                        lhs,
2964                                        rhs,
2965                                        ..
2966                                    } = b.as_ref()
2967                                    else {
2968                                        return Err("Expected *".to_string());
2969                                    };
2970                                    test_ident(lhs, "b")?;
2971                                    test_ident(rhs, "c")?;
2972                                }
2973                                _ => return Err("Lhs.Rhs".to_string()),
2974                            }
2975                        }
2976                        _ => return Err("Lhs".to_string()),
2977                    }
2978                    test_ident(rhs, "d")?;
2979                }
2980                _ => return Err("Outer".to_string()),
2981            }
2982            Ok(())
2983        });
2984    }
2985
2986    fn test_expr_pg(src: &'static str, f: impl FnOnce(&Expression<'_>) -> Result<(), String>) {
2987        let mut issues = Issues::new(src);
2988        let options = ParseOptions::new().dialect(SQLDialect::PostgreSQL);
2989        let mut parser = Parser::new(src, &mut issues, &options);
2990        let res = parse_expression_unreserved(&mut parser, PRIORITY_MAX)
2991            .expect("Expression in test_expr_pg");
2992        if let Err(e) = f(&res) {
2993            panic!("Error parsing {}: {}\nGot {:#?}", src, e, res);
2994        }
2995    }
2996
2997    fn test_expr_sqlite(src: &'static str, f: impl FnOnce(&Expression<'_>) -> Result<(), String>) {
2998        let mut issues = Issues::new(src);
2999        let options = ParseOptions::new().dialect(SQLDialect::Sqlite);
3000        let mut parser = Parser::new(src, &mut issues, &options);
3001        let res = parse_expression_unreserved(&mut parser, PRIORITY_MAX)
3002            .expect("Expression in test_expr_sqlite");
3003        if let Err(e) = f(&res) {
3004            panic!("Error parsing {}: {}\nGot {:#?}", src, e, res);
3005        }
3006    }
3007
3008    fn test_like<'a>(
3009        e: &Expression<'a>,
3010        not_like: bool,
3011        similar_to: bool,
3012        has_escape: bool,
3013    ) -> Result<(), String> {
3014        match e {
3015            Expression::Like(l) => {
3016                let (nl, st) = match &l.op {
3017                    LikeOperator::Like(_) => (false, false),
3018                    LikeOperator::NotLike(_) => (true, false),
3019                    LikeOperator::SimilarTo(_) => (false, true),
3020                };
3021                if nl != not_like {
3022                    return Err(format!("Expected not_like={} got {}", not_like, nl));
3023                }
3024                if st != similar_to {
3025                    return Err(format!("Expected similar_to={} got {}", similar_to, st));
3026                }
3027                if l.escape.is_some() != has_escape {
3028                    return Err(format!(
3029                        "Expected escape.is_some()={} got {}",
3030                        has_escape,
3031                        l.escape.is_some()
3032                    ));
3033                }
3034                Ok(())
3035            }
3036            _ => Err(format!("Expected Like expression, got {:?}", e)),
3037        }
3038    }
3039
3040    #[test]
3041    fn like_no_escape() {
3042        test_expr("a LIKE '%x%'", |e| test_like(e, false, false, false));
3043        test_expr_pg("a LIKE '%x%'", |e| test_like(e, false, false, false));
3044        test_expr_sqlite("a LIKE '%x%'", |e| test_like(e, false, false, false));
3045    }
3046
3047    #[test]
3048    fn like_with_escape() {
3049        test_expr("a LIKE 'a$_c' ESCAPE '$'", |e| {
3050            test_like(e, false, false, true)
3051        });
3052        test_expr_pg("a LIKE 'a$_c' ESCAPE '$'", |e| {
3053            test_like(e, false, false, true)
3054        });
3055        test_expr_sqlite("a LIKE 'a$_c' ESCAPE '$'", |e| {
3056            test_like(e, false, false, true)
3057        });
3058    }
3059
3060    #[test]
3061    fn not_like_with_escape() {
3062        test_expr("a NOT LIKE 'a$_c' ESCAPE '$'", |e| {
3063            test_like(e, true, false, true)
3064        });
3065        test_expr_sqlite("a NOT LIKE 'a$_c' ESCAPE '$'", |e| {
3066            test_like(e, true, false, true)
3067        });
3068    }
3069
3070    #[test]
3071    fn similar_to_with_escape() {
3072        test_expr_pg("a SIMILAR TO 'a%' ESCAPE '$'", |e| {
3073            test_like(e, false, true, true)
3074        });
3075    }
3076
3077    #[test]
3078    fn like_reported_sqlite_backslash_escape() {
3079        // https://github.com/... reported bug: backslash-escaped LIKE pattern with
3080        // an explicit ESCAPE clause failed to parse under SQLite.
3081        test_expr_sqlite(r"X LIKE 'tmp\_ci\_%' ESCAPE '\'", |e| match e {
3082            Expression::Like(l) => {
3083                test_like(e, false, false, true)?;
3084                match &l.rhs {
3085                    Expression::String(s) if s.value.as_ref() == r"tmp\_ci\_%" => Ok(()),
3086                    other => Err(format!("Unexpected pattern {:?}", other)),
3087                }?;
3088                match &l.escape {
3089                    Some(Expression::String(s)) if s.value.as_ref() == r"\" => Ok(()),
3090                    other => Err(format!("Unexpected escape {:?}", other)),
3091                }
3092            }
3093            _ => Err(format!("Expected Like expression, got {:?}", e)),
3094        });
3095    }
3096
3097    #[test]
3098    fn like_and_precedence() {
3099        // ESCAPE should only bind to the LIKE it directly follows.
3100        test_expr("a LIKE 'x' AND b LIKE 'y' ESCAPE '$'", |e| match e {
3101            Expression::Binary(b) => {
3102                let BinaryExpression {
3103                    op: BinaryOperator::And(_),
3104                    lhs,
3105                    rhs,
3106                    ..
3107                } = b.as_ref()
3108                else {
3109                    return Err("Expected outer AND".to_string());
3110                };
3111                test_like(lhs, false, false, false)?;
3112                test_like(rhs, false, false, true)
3113            }
3114            _ => Err(format!("Expected Binary expression, got {:?}", e)),
3115        });
3116    }
3117
3118    #[test]
3119    fn quantifier_any() {
3120        test_expr_pg(
3121            "salary > ANY (SELECT max_salary FROM departments)",
3122            |e| match e {
3123                Expression::Binary(b) => match &b.rhs {
3124                    Expression::Quantifier(q) => {
3125                        assert!(matches!(q.quantifier, Quantifier::Any(_)));
3126                        Ok(())
3127                    }
3128                    _ => Err(format!("Expected Quantifier RHS, got {:?}", b.rhs)),
3129                },
3130                _ => Err(format!("Expected Binary expression, got {:?}", e)),
3131            },
3132        );
3133    }
3134
3135    #[test]
3136    fn quantifier_some() {
3137        test_expr_pg("x = SOME (ARRAY[1, 2, 3])", |e| match e {
3138            Expression::Binary(b) => match &b.rhs {
3139                Expression::Quantifier(q) => {
3140                    assert!(matches!(q.quantifier, Quantifier::Some(_)));
3141                    Ok(())
3142                }
3143                _ => Err(format!("Expected Quantifier RHS, got {:?}", b.rhs)),
3144            },
3145            _ => Err(format!("Expected Binary expression, got {:?}", e)),
3146        });
3147    }
3148
3149    #[test]
3150    fn quantifier_all() {
3151        test_expr_pg("price <= ALL (SELECT price FROM products)", |e| match e {
3152            Expression::Binary(b) => match &b.rhs {
3153                Expression::Quantifier(q) => {
3154                    assert!(matches!(q.quantifier, Quantifier::All(_)));
3155                    Ok(())
3156                }
3157                _ => Err(format!("Expected Quantifier RHS, got {:?}", b.rhs)),
3158            },
3159            _ => Err(format!("Expected Binary expression, got {:?}", e)),
3160        });
3161    }
3162}