Skip to main content

fsqlite_parser/
expr.rs

1// bd-16ov: §12.15 Expression Syntax
2//
3// Explicit-state Pratt expression and SELECT parser with SQLite-correct
4// operator precedence. Recursive implementations are retained only as test
5// oracles where noted.
6// Normative reference: §10.2 of the FrankenSQLite specification.
7//
8// Precedence table (from canonical upstream SQLite grammar, lowest to highest):
9//   OR
10//   AND
11//   NOT (prefix)
12//   = == != <> IS [NOT] MATCH LIKE GLOB BETWEEN IN ISNULL NOTNULL
13//   < <= > >=
14//   & | << >> (bitwise)
15//   + - (binary)
16//   * / %
17//   || -> ->> (left-associative; same precedence level)
18//   COLLATE (postfix)
19//   ~ - + (unary prefix)
20
21use fsqlite_ast::{
22    BinaryOp, ColumnRef, CompoundOp, Cte, CteMaterialized, Distinctness, Expr, FrameBound,
23    FrameExclude, FrameSpec, FrameType, FromClause, FunctionArgs, InSet, JoinClause,
24    JoinConstraint, JoinKind, JoinType, JsonArrow, LikeOp, LimitClause, Literal, NullsOrder,
25    OrderingTerm, PlaceholderType, QualifiedName, RaiseAction, ResultColumn, SelectBody,
26    SelectCore, SelectStatement, SortDirection, Span, TableOrSubquery, TypeName, UnaryOp,
27    ValuesClause, WindowDef, WindowReference, WindowSpec, WithClause,
28};
29#[cfg(test)]
30use std::cell::Cell;
31use std::sync::Arc;
32
33use crate::parser::{
34    HeightTracked, MAX_PARSE_DEPTH, ParseError, Parser, is_nonreserved_kw, kw_to_str,
35    starts_bare_window_name, starts_post_dot_identifier, starts_table_star_qualifier,
36    starts_window_base_name,
37};
38use crate::token::{Token, TokenKind};
39
40pub(crate) struct ParsedExpr {
41    pub(crate) expr: Expr,
42    pub(crate) height: u32,
43    is_constant: bool,
44    has_function: bool,
45    root: CachedRoot,
46}
47
48#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49enum CachedRoot {
50    Other,
51    UnaryPlus,
52    Vector,
53    ScalarSubquery,
54}
55
56fn vector_in_list_arity_error(lhs: &Expr, items: &[ParsedExpr]) -> Option<String> {
57    let expected = match lhs {
58        Expr::RowValue(lhs_terms, _) => lhs_terms.len(),
59        // A subquery-expression LHS is not an explicit row-value literal. SQLite
60        // resolves it semantically, where name/function errors, constant
61        // short-circuiting, and context-sensitive row-value diagnostics can
62        // take precedence over an IN-list arity error.
63        _ => return None,
64    };
65    for item in items {
66        let actual = match &item.expr {
67            Expr::RowValue(element_terms, _) => element_terms.len(),
68            // Exactly one bare parenthesized subquery is a set-valued RHS, for
69            // example `(a, b) IN ((SELECT 1, 2))`. In a multi-item list the
70            // same syntax is one scalar-shaped element, so normal list arity
71            // validation applies.
72            Expr::Subquery(..) if items.len() == 1 => continue,
73            _ => 1,
74        };
75        if actual == expected {
76            continue;
77        }
78        let term_suffix = if actual == 1 { "" } else { "s" };
79        return Some(format!(
80            "IN(...) element has {actual} term{term_suffix} - expected {expected}"
81        ));
82    }
83    None
84}
85
86#[cfg(test)]
87enum DeepExprFrame {
88    Unary {
89        op: UnaryOp,
90        span: Span,
91        right_bp: u8,
92    },
93    Parenthesis {
94        span: Span,
95    },
96}
97
98struct InlineStack<T, const N: usize> {
99    inline: [Option<T>; N],
100    inline_len: usize,
101    spill: Vec<T>,
102}
103
104impl<T, const N: usize> InlineStack<T, N> {
105    fn new() -> Self {
106        Self {
107            inline: [const { None }; N],
108            inline_len: 0,
109            spill: Vec::new(),
110        }
111    }
112
113    fn push(&mut self, value: T) {
114        if self.inline_len < N && self.spill.is_empty() {
115            self.inline[self.inline_len] = Some(value);
116            self.inline_len += 1;
117        } else {
118            #[cfg(test)]
119            if self.spill.is_empty() {
120                PARSE_MACHINE_STACK_SPILLS.set(PARSE_MACHINE_STACK_SPILLS.get().saturating_add(1));
121            }
122            self.spill.push(value);
123        }
124    }
125
126    fn pop(&mut self) -> Option<T> {
127        if let Some(value) = self.spill.pop() {
128            return Some(value);
129        }
130        if self.inline_len == 0 {
131            return None;
132        }
133        self.inline_len -= 1;
134        self.inline[self.inline_len].take()
135    }
136}
137
138struct FunctionBuild {
139    name: String,
140    start: Span,
141    args: FunctionArgs,
142    distinct: bool,
143    height: u32,
144    order_by: Vec<OrderingTerm>,
145    filter: Option<Box<Expr>>,
146    over: Option<WindowSpec>,
147    end: Span,
148}
149
150struct CaseBuild {
151    start: Span,
152    operand: Option<ParsedExpr>,
153    whens: Vec<(ParsedExpr, ParsedExpr)>,
154}
155
156struct SelectBuild {
157    with: Option<WithClause>,
158    first: SelectCore,
159    compounds: Vec<(CompoundOp, SelectCore)>,
160    height: u32,
161    order_by: Vec<OrderingTerm>,
162}
163
164struct CoreBuild {
165    distinct: Distinctness,
166    columns: Vec<ResultColumn>,
167    height: u32,
168    from: Option<FromClause>,
169    where_clause: Option<Box<Expr>>,
170    group_by: Vec<Expr>,
171    having: Option<Box<Expr>>,
172    windows: Vec<WindowDef>,
173}
174
175struct FromBuild {
176    source: TableOrSubquery,
177    joins: Vec<JoinClause>,
178}
179
180struct WindowBuild {
181    base_window: Option<String>,
182    partition_by: Vec<Expr>,
183    order_by: Vec<OrderingTerm>,
184}
185
186pub(crate) struct ParsedFrameBound {
187    pub(crate) value: FrameBound,
188    pub(crate) origin: Token,
189}
190
191fn frame_bound_rank(bound: &FrameBound) -> u8 {
192    match bound {
193        FrameBound::UnboundedPreceding => 0,
194        FrameBound::Preceding(_) => 1,
195        FrameBound::CurrentRow => 2,
196        FrameBound::Following(_) => 3,
197        FrameBound::UnboundedFollowing => 4,
198    }
199}
200
201pub(crate) fn validate_frame_start(
202    start: &ParsedFrameBound,
203    has_explicit_end: bool,
204) -> Result<(), ParseError> {
205    if matches!(start.value, FrameBound::UnboundedFollowing) {
206        return Err(ParseError::at(
207            "window frame starting bound must not be UNBOUNDED FOLLOWING",
208            Some(&start.origin),
209        ));
210    }
211    if !has_explicit_end && frame_bound_rank(&start.value) > 2 {
212        return Err(ParseError::at(
213            "single-bound window frame must not start after CURRENT ROW",
214            Some(&start.origin),
215        ));
216    }
217    Ok(())
218}
219
220pub(crate) fn validate_frame_end(
221    start: &ParsedFrameBound,
222    end: &ParsedFrameBound,
223) -> Result<(), ParseError> {
224    if matches!(end.value, FrameBound::UnboundedPreceding) {
225        return Err(ParseError::at(
226            "window frame ending bound must not be UNBOUNDED PRECEDING",
227            Some(&end.origin),
228        ));
229    }
230    if frame_bound_rank(&end.value) < frame_bound_rank(&start.value) {
231        return Err(ParseError::at(
232            "window frame ending bound must not precede its starting bound",
233            Some(&end.origin),
234        ));
235    }
236    Ok(())
237}
238
239// Boxing the large variants would add heap traffic to the shallow parse path
240// that this inline stack is specifically intended to keep allocation-free.
241#[allow(clippy::large_enum_variant)]
242enum MachineValue {
243    Expr(ParsedExpr),
244    Select(HeightTracked<SelectStatement>),
245    Core(HeightTracked<SelectCore>),
246    From(FromClause),
247    Table(TableOrSubquery),
248    Ordering(HeightTracked<OrderingTerm>),
249    Window(WindowSpec),
250    FrameBound(ParsedFrameBound),
251    With(WithClause),
252}
253
254// The largest continuations own partially built AST nodes. Keep them inline so
255// ordinary expressions do not allocate merely to suspend one parser phase.
256#[allow(clippy::large_enum_variant)]
257enum ParseControl {
258    ExprStart {
259        min_bp: u8,
260    },
261    ExprTail {
262        min_bp: u8,
263    },
264    UnaryDone {
265        outer_min_bp: u8,
266        op: UnaryOp,
267        span: Span,
268    },
269    CastDone {
270        outer_min_bp: u8,
271        start: Span,
272    },
273    GroupFirstDone {
274        outer_min_bp: u8,
275        start: Span,
276    },
277    RowItemDone {
278        outer_min_bp: u8,
279        start: Span,
280        values: Vec<Expr>,
281        is_constant: bool,
282        has_function: bool,
283    },
284    CaseOperandDone {
285        outer_min_bp: u8,
286        start: Span,
287    },
288    CaseWhenStart {
289        outer_min_bp: u8,
290        build: CaseBuild,
291    },
292    CaseConditionDone {
293        outer_min_bp: u8,
294        build: CaseBuild,
295    },
296    CaseResultDone {
297        outer_min_bp: u8,
298        build: CaseBuild,
299        condition: ParsedExpr,
300    },
301    CaseElseDone {
302        outer_min_bp: u8,
303        build: CaseBuild,
304    },
305    FunctionArgDone {
306        outer_min_bp: u8,
307        build: FunctionBuild,
308    },
309    FunctionOrderStart {
310        outer_min_bp: u8,
311        build: FunctionBuild,
312    },
313    FunctionOrderDone {
314        outer_min_bp: u8,
315        build: FunctionBuild,
316    },
317    FunctionClose {
318        outer_min_bp: u8,
319        build: FunctionBuild,
320    },
321    FunctionFilterDone {
322        outer_min_bp: u8,
323        build: FunctionBuild,
324        has_filter: bool,
325    },
326    FunctionOverDone {
327        outer_min_bp: u8,
328        build: FunctionBuild,
329    },
330    BinaryDone {
331        outer_min_bp: u8,
332        lhs: ParsedExpr,
333        op: BinaryOp,
334    },
335    JsonDone {
336        outer_min_bp: u8,
337        lhs: ParsedExpr,
338        arrow: JsonArrow,
339    },
340    IsDone {
341        outer_min_bp: u8,
342        lhs: ParsedExpr,
343        not: bool,
344    },
345    LikePatternDone {
346        outer_min_bp: u8,
347        lhs: ParsedExpr,
348        op: LikeOp,
349        not: bool,
350    },
351    LikeEscapeDone {
352        outer_min_bp: u8,
353        lhs: ParsedExpr,
354        pattern: ParsedExpr,
355        op: LikeOp,
356        not: bool,
357    },
358    BetweenLowDone {
359        outer_min_bp: u8,
360        lhs: ParsedExpr,
361        not: bool,
362    },
363    BetweenHighDone {
364        outer_min_bp: u8,
365        lhs: ParsedExpr,
366        low: ParsedExpr,
367        not: bool,
368    },
369    InItemDone {
370        outer_min_bp: u8,
371        lhs: ParsedExpr,
372        not: bool,
373        items: Vec<ParsedExpr>,
374        start: Span,
375    },
376    InSelectDone {
377        outer_min_bp: u8,
378        lhs: ParsedExpr,
379        not: bool,
380        start: Span,
381    },
382    ExistsDone {
383        outer_min_bp: u8,
384        not: bool,
385        start: Span,
386    },
387    ScalarSelectDone {
388        outer_min_bp: u8,
389        start: Span,
390    },
391    OrderingStart,
392    OrderingDone,
393    WindowStart,
394    WindowPartitionDone {
395        build: WindowBuild,
396    },
397    WindowOrderStart {
398        build: WindowBuild,
399    },
400    WindowOrderDone {
401        build: WindowBuild,
402    },
403    WindowFrameStart {
404        build: WindowBuild,
405    },
406    WindowFirstBoundDone {
407        build: WindowBuild,
408        frame_type: FrameType,
409        between: bool,
410    },
411    WindowSecondBoundDone {
412        build: WindowBuild,
413        frame_type: FrameType,
414        start: ParsedFrameBound,
415    },
416    FrameBoundStart,
417    FrameBoundExprDone {
418        origin: Token,
419    },
420    SubqueryStart,
421    SubqueryWithDone,
422    SelectStart {
423        with: Option<WithClause>,
424    },
425    SelectFirstCoreDone {
426        with: Option<WithClause>,
427    },
428    SelectCompoundDone {
429        build: SelectBuild,
430        op: CompoundOp,
431    },
432    SelectOrderStart {
433        build: SelectBuild,
434    },
435    SelectOrderDone {
436        build: SelectBuild,
437    },
438    SelectLimitFirstDone {
439        build: SelectBuild,
440    },
441    SelectLimitSecondDone {
442        build: SelectBuild,
443        first: ParsedExpr,
444        comma_form: bool,
445    },
446    CoreStart,
447    CoreColumnStart {
448        build: CoreBuild,
449    },
450    CoreColumnDone {
451        build: CoreBuild,
452    },
453    CoreAfterColumns {
454        build: CoreBuild,
455    },
456    CoreFromDone {
457        build: CoreBuild,
458    },
459    CoreWhereDone {
460        build: CoreBuild,
461    },
462    CoreGroupDone {
463        build: CoreBuild,
464    },
465    CoreHavingDone {
466        build: CoreBuild,
467    },
468    CoreWindowStart {
469        build: CoreBuild,
470    },
471    CoreWindowDone {
472        build: CoreBuild,
473        name: String,
474    },
475    ValuesRowStart {
476        rows: Vec<Vec<Expr>>,
477        height: u32,
478        force_union_all_from: Option<usize>,
479    },
480    ValuesItemDone {
481        rows: Vec<Vec<Expr>>,
482        row: Vec<Expr>,
483        height: u32,
484        force_union_all_from: Option<usize>,
485    },
486    FromStart,
487    FromSourceDone,
488    FromTableDone {
489        build: FromBuild,
490        join_type: JoinType,
491    },
492    FromJoinConstraintDone {
493        build: FromBuild,
494        join_type: JoinType,
495        table: TableOrSubquery,
496    },
497    TableStart,
498    TableSubqueryDone,
499    TableParenJoinDone,
500    TableFunctionArgDone {
501        name: String,
502        args: Vec<Expr>,
503    },
504    WithStart,
505    CteQueryDone {
506        recursive: bool,
507        ctes: Vec<Cte>,
508        name: String,
509        columns: Vec<String>,
510        materialized: Option<CteMaterialized>,
511    },
512}
513
514impl ParsedExpr {
515    fn leaf(expr: Expr) -> Self {
516        let (is_constant, has_function) = match &expr {
517            Expr::Literal(
518                Literal::CurrentTime | Literal::CurrentDate | Literal::CurrentTimestamp,
519                _,
520            ) => (false, true),
521            Expr::Literal(..) | Expr::BoundOuterValue { .. } => (true, false),
522            _ => (false, false),
523        };
524        Self {
525            expr,
526            height: 1,
527            is_constant,
528            has_function,
529            root: CachedRoot::Other,
530        }
531    }
532}
533
534#[cfg(test)]
535enum CachedHeightTask<'a> {
536    Expr(&'a Expr),
537    Select(&'a SelectStatement),
538    SelectCore(&'a SelectCore),
539    Limit(&'a fsqlite_ast::LimitClause),
540    Finish(CachedFinish),
541}
542
543#[cfg(test)]
544#[derive(Clone, Copy)]
545enum CachedFinish {
546    Generic(usize),
547    Unary(UnaryOp),
548    Vector(usize),
549    Like { children: usize, not: bool },
550    Between { not: bool },
551    InList { items: usize, not: bool },
552    InSubquery { not: bool },
553    InTable { not: bool },
554    Exists { not: bool },
555    Subquery,
556    Function { args: usize },
557    Select { expressions: usize },
558    Limit { expressions: usize },
559}
560
561#[cfg(test)]
562#[derive(Clone, Copy)]
563struct CachedFacts {
564    height: u32,
565    is_constant: bool,
566    has_function: bool,
567    root: CachedRoot,
568}
569
570#[cfg(test)]
571impl CachedFacts {
572    const fn leaf(is_constant: bool, has_function: bool) -> Self {
573        Self {
574            height: 1,
575            is_constant,
576            has_function,
577            root: CachedRoot::Other,
578        }
579    }
580}
581
582#[cfg(test)]
583thread_local! {
584    static HEIGHT_WALK_VISITS: Cell<usize> = const { Cell::new(0) };
585    static PARSE_MACHINE_STEPS: Cell<usize> = const { Cell::new(0) };
586    static PARSE_MACHINE_STACK_SPILLS: Cell<usize> = const { Cell::new(0) };
587}
588
589#[cfg(test)]
590fn aggregate_cached_facts(values: &mut Vec<CachedFacts>, count: usize) -> CachedFacts {
591    let start = values.len().saturating_sub(count);
592    let mut facts = CachedFacts {
593        height: 0,
594        is_constant: true,
595        has_function: false,
596        root: CachedRoot::Other,
597    };
598    for child in &values[start..] {
599        facts.height = facts.height.max(child.height);
600        facts.is_constant &= child.is_constant;
601        facts.has_function |= child.has_function;
602    }
603    if count == 1 {
604        facts.root = values[start].root;
605    }
606    values.truncate(start);
607    facts
608}
609
610#[cfg(test)]
611fn cached_facts_from_tasks(mut pending: Vec<CachedHeightTask<'_>>) -> CachedFacts {
612    let mut values = Vec::new();
613    while let Some(task) = pending.pop() {
614        #[cfg(test)]
615        HEIGHT_WALK_VISITS.set(HEIGHT_WALK_VISITS.get() + 1);
616        match task {
617            CachedHeightTask::Expr(current) => match current {
618                Expr::BinaryOp { left, right, .. } => {
619                    pending.push(CachedHeightTask::Finish(CachedFinish::Generic(2)));
620                    pending.push(CachedHeightTask::Expr(left));
621                    pending.push(CachedHeightTask::Expr(right));
622                }
623                Expr::UnaryOp { op, expr, .. } => {
624                    pending.push(CachedHeightTask::Finish(CachedFinish::Unary(*op)));
625                    pending.push(CachedHeightTask::Expr(expr));
626                }
627                Expr::Cast { expr, .. }
628                | Expr::Collate { expr, .. }
629                | Expr::IsNull { expr, .. } => {
630                    pending.push(CachedHeightTask::Finish(CachedFinish::Generic(1)));
631                    pending.push(CachedHeightTask::Expr(expr));
632                }
633                Expr::Between {
634                    expr,
635                    low,
636                    high,
637                    not,
638                    ..
639                } => {
640                    pending.push(CachedHeightTask::Finish(CachedFinish::Between {
641                        not: *not,
642                    }));
643                    pending.push(CachedHeightTask::Expr(expr));
644                    pending.push(CachedHeightTask::Expr(low));
645                    pending.push(CachedHeightTask::Expr(high));
646                }
647                Expr::In { expr, set, not, .. } => match set {
648                    InSet::List(values) => {
649                        pending.push(CachedHeightTask::Finish(CachedFinish::InList {
650                            items: values.len(),
651                            not: *not,
652                        }));
653                        pending.push(CachedHeightTask::Expr(expr));
654                        pending.extend(values.iter().map(CachedHeightTask::Expr));
655                    }
656                    InSet::Subquery(select) => {
657                        pending.push(CachedHeightTask::Finish(CachedFinish::InSubquery {
658                            not: *not,
659                        }));
660                        pending.push(CachedHeightTask::Expr(expr));
661                        pending.push(CachedHeightTask::Select(select));
662                    }
663                    InSet::Table(_) => {
664                        pending.push(CachedHeightTask::Finish(CachedFinish::InTable {
665                            not: *not,
666                        }));
667                        pending.push(CachedHeightTask::Expr(expr));
668                    }
669                },
670                Expr::Like {
671                    expr,
672                    pattern,
673                    escape,
674                    not,
675                    ..
676                } => {
677                    pending.push(CachedHeightTask::Finish(CachedFinish::Like {
678                        children: 2 + usize::from(escape.is_some()),
679                        not: *not,
680                    }));
681                    pending.push(CachedHeightTask::Expr(expr));
682                    pending.push(CachedHeightTask::Expr(pattern));
683                    if let Some(escape) = escape {
684                        pending.push(CachedHeightTask::Expr(escape));
685                    }
686                }
687                Expr::Case {
688                    operand,
689                    whens,
690                    else_expr,
691                    ..
692                } => {
693                    let children = usize::from(operand.is_some())
694                        + whens.len().saturating_mul(2)
695                        + usize::from(else_expr.is_some());
696                    pending.push(CachedHeightTask::Finish(CachedFinish::Generic(children)));
697                    if let Some(operand) = operand {
698                        pending.push(CachedHeightTask::Expr(operand));
699                    }
700                    for (condition, result) in whens {
701                        pending.push(CachedHeightTask::Expr(condition));
702                        pending.push(CachedHeightTask::Expr(result));
703                    }
704                    if let Some(else_expr) = else_expr {
705                        pending.push(CachedHeightTask::Expr(else_expr));
706                    }
707                }
708                Expr::Exists { subquery, not, .. } => {
709                    pending.push(CachedHeightTask::Finish(CachedFinish::Exists { not: *not }));
710                    pending.push(CachedHeightTask::Select(subquery));
711                }
712                Expr::Subquery(subquery, _) => {
713                    pending.push(CachedHeightTask::Finish(CachedFinish::Subquery));
714                    pending.push(CachedHeightTask::Select(subquery));
715                }
716                Expr::FunctionCall { args, .. } => {
717                    let FunctionArgs::List(args) = args else {
718                        values.push(CachedFacts {
719                            height: 1,
720                            is_constant: false,
721                            has_function: true,
722                            root: CachedRoot::Other,
723                        });
724                        continue;
725                    };
726                    pending.push(CachedHeightTask::Finish(CachedFinish::Function {
727                        args: args.len(),
728                    }));
729                    pending.extend(args.iter().map(CachedHeightTask::Expr));
730                }
731                Expr::JsonAccess { expr, path, .. } => {
732                    pending.push(CachedHeightTask::Finish(CachedFinish::Like {
733                        children: 2,
734                        not: false,
735                    }));
736                    pending.push(CachedHeightTask::Expr(expr));
737                    pending.push(CachedHeightTask::Expr(path));
738                }
739                Expr::RowValue(items, _) => {
740                    pending.push(CachedHeightTask::Finish(CachedFinish::Vector(items.len())));
741                    pending.extend(items.iter().map(CachedHeightTask::Expr));
742                }
743                Expr::Literal(
744                    Literal::CurrentTime | Literal::CurrentDate | Literal::CurrentTimestamp,
745                    _,
746                ) => values.push(CachedFacts::leaf(false, true)),
747                Expr::Literal(..) | Expr::BoundOuterValue { .. } => {
748                    values.push(CachedFacts::leaf(true, false));
749                }
750                Expr::Column(column, _) if column.table.is_some() => {
751                    values.push(CachedFacts {
752                        height: 2,
753                        is_constant: false,
754                        has_function: false,
755                        root: CachedRoot::Other,
756                    });
757                }
758                Expr::Column(..) | Expr::Raise { .. } | Expr::Placeholder(..) => {
759                    values.push(CachedFacts::leaf(false, false));
760                }
761            },
762            CachedHeightTask::Select(select) => {
763                let expressions = 1
764                    + select.body.compounds.len()
765                    + select.order_by.len()
766                    + usize::from(select.limit.is_some());
767                pending.push(CachedHeightTask::Finish(CachedFinish::Select {
768                    expressions,
769                }));
770                pending.push(CachedHeightTask::SelectCore(&select.body.select));
771                pending.extend(
772                    select
773                        .body
774                        .compounds
775                        .iter()
776                        .map(|(_, core)| CachedHeightTask::SelectCore(core)),
777                );
778                pending.extend(
779                    select
780                        .order_by
781                        .iter()
782                        .map(|term| CachedHeightTask::Expr(&term.expr)),
783                );
784                if let Some(limit) = &select.limit {
785                    pending.push(CachedHeightTask::Limit(limit));
786                }
787            }
788            CachedHeightTask::SelectCore(core) => match core {
789                SelectCore::Select {
790                    columns,
791                    where_clause,
792                    group_by,
793                    having,
794                    ..
795                } => {
796                    let expressions = columns
797                        .iter()
798                        .filter(|column| matches!(column, ResultColumn::Expr { .. }))
799                        .count()
800                        + usize::from(where_clause.is_some())
801                        + group_by.len()
802                        + usize::from(having.is_some());
803                    pending.push(CachedHeightTask::Finish(CachedFinish::Select {
804                        expressions,
805                    }));
806                    pending.extend(columns.iter().filter_map(|column| match column {
807                        ResultColumn::Expr { expr, .. } => Some(CachedHeightTask::Expr(expr)),
808                        ResultColumn::Star | ResultColumn::TableStar(_) => None,
809                    }));
810                    if let Some(where_clause) = where_clause {
811                        pending.push(CachedHeightTask::Expr(where_clause));
812                    }
813                    pending.extend(group_by.iter().map(CachedHeightTask::Expr));
814                    if let Some(having) = having {
815                        pending.push(CachedHeightTask::Expr(having));
816                    }
817                }
818                SelectCore::Values(rows) => {
819                    let expressions = rows.iter().map(Vec::len).sum();
820                    pending.push(CachedHeightTask::Finish(CachedFinish::Select {
821                        expressions,
822                    }));
823                    pending.extend(rows.iter().flatten().map(CachedHeightTask::Expr));
824                }
825            },
826            CachedHeightTask::Limit(limit) => {
827                let expressions = 1 + usize::from(limit.offset.is_some());
828                pending.push(CachedHeightTask::Finish(CachedFinish::Limit {
829                    expressions,
830                }));
831                pending.push(CachedHeightTask::Expr(&limit.limit));
832                if let Some(offset) = &limit.offset {
833                    pending.push(CachedHeightTask::Expr(offset));
834                }
835            }
836            CachedHeightTask::Finish(finish) => match finish {
837                CachedFinish::Generic(children) => {
838                    let mut facts = aggregate_cached_facts(&mut values, children);
839                    facts.height = facts.height.saturating_add(1);
840                    facts.root = CachedRoot::Other;
841                    values.push(facts);
842                }
843                CachedFinish::Unary(op) => {
844                    let mut child = values.pop().expect("unary cached-height child");
845                    if !(matches!(op, UnaryOp::Plus | UnaryOp::Negate)
846                        && child.root == CachedRoot::UnaryPlus)
847                    {
848                        child.height = child.height.saturating_add(1);
849                    }
850                    child.root = if op == UnaryOp::Plus {
851                        CachedRoot::UnaryPlus
852                    } else {
853                        CachedRoot::Other
854                    };
855                    values.push(child);
856                }
857                CachedFinish::Vector(children) => {
858                    let mut facts = aggregate_cached_facts(&mut values, children);
859                    facts.height = 1;
860                    facts.root = CachedRoot::Vector;
861                    values.push(facts);
862                }
863                CachedFinish::Like { children, not } => {
864                    let mut facts = aggregate_cached_facts(&mut values, children);
865                    facts.height = facts
866                        .height
867                        .saturating_add(1)
868                        .saturating_add(u32::from(not));
869                    facts.is_constant = false;
870                    facts.has_function = true;
871                    facts.root = CachedRoot::Other;
872                    values.push(facts);
873                }
874                CachedFinish::Between { not } => {
875                    let mut facts = aggregate_cached_facts(&mut values, 3);
876                    facts.height = facts
877                        .height
878                        .saturating_add(1)
879                        .saturating_add(u32::from(not));
880                    facts.root = CachedRoot::Other;
881                    values.push(facts);
882                }
883                CachedFinish::InList { items, not } => {
884                    let lhs = values.pop().expect("IN cached-height lhs");
885                    let item_facts = aggregate_cached_facts(&mut values, items);
886                    if items == 0 {
887                        values.push(if lhs.has_function {
888                            CachedFacts {
889                                height: lhs.height.saturating_add(1),
890                                is_constant: false,
891                                has_function: true,
892                                root: CachedRoot::Other,
893                            }
894                        } else {
895                            CachedFacts::leaf(true, false)
896                        });
897                        continue;
898                    }
899                    let cached_child_height =
900                        if items == 1 && item_facts.is_constant && lhs.root != CachedRoot::Vector {
901                            lhs.height.max(item_facts.height.saturating_add(1))
902                        } else if items == 1 && item_facts.root == CachedRoot::ScalarSubquery {
903                            lhs.height.max(item_facts.height.saturating_sub(1))
904                        } else {
905                            lhs.height.max(item_facts.height)
906                        };
907                    values.push(CachedFacts {
908                        height: cached_child_height
909                            .saturating_add(1)
910                            .saturating_add(u32::from(not)),
911                        is_constant: lhs.is_constant && item_facts.is_constant,
912                        has_function: lhs.has_function || item_facts.has_function,
913                        root: CachedRoot::Other,
914                    });
915                }
916                CachedFinish::InSubquery { not } => {
917                    let lhs = values.pop().expect("IN-subquery cached-height lhs");
918                    let select = values.pop().expect("IN-subquery cached-height SELECT");
919                    values.push(CachedFacts {
920                        height: lhs
921                            .height
922                            .max(select.height)
923                            .saturating_add(1)
924                            .saturating_add(u32::from(not)),
925                        is_constant: false,
926                        has_function: lhs.has_function,
927                        root: CachedRoot::Other,
928                    });
929                }
930                CachedFinish::InTable { not } => {
931                    let lhs = values.pop().expect("IN-table cached-height lhs");
932                    values.push(CachedFacts {
933                        height: lhs.height.saturating_add(1).saturating_add(u32::from(not)),
934                        is_constant: false,
935                        has_function: lhs.has_function,
936                        root: CachedRoot::Other,
937                    });
938                }
939                CachedFinish::Exists { not } => {
940                    let select = values.pop().expect("EXISTS cached-height SELECT");
941                    values.push(CachedFacts {
942                        height: select
943                            .height
944                            .saturating_add(1)
945                            .saturating_add(u32::from(not)),
946                        is_constant: false,
947                        has_function: false,
948                        root: CachedRoot::Other,
949                    });
950                }
951                CachedFinish::Subquery => {
952                    let select = values.pop().expect("scalar-subquery cached-height SELECT");
953                    values.push(CachedFacts {
954                        height: select.height.saturating_add(1),
955                        is_constant: false,
956                        has_function: false,
957                        root: CachedRoot::ScalarSubquery,
958                    });
959                }
960                CachedFinish::Function { args } => {
961                    let facts = aggregate_cached_facts(&mut values, args);
962                    values.push(CachedFacts {
963                        height: facts.height.saturating_add(1),
964                        is_constant: false,
965                        has_function: true,
966                        root: CachedRoot::Other,
967                    });
968                }
969                CachedFinish::Select { expressions } => {
970                    let facts = aggregate_cached_facts(&mut values, expressions);
971                    values.push(CachedFacts {
972                        height: facts.height,
973                        is_constant: false,
974                        has_function: false,
975                        root: CachedRoot::Other,
976                    });
977                }
978                CachedFinish::Limit { expressions } => {
979                    let mut facts = aggregate_cached_facts(&mut values, expressions);
980                    facts.height = facts.height.saturating_add(1);
981                    facts.root = CachedRoot::Other;
982                    values.push(facts);
983                }
984            },
985        }
986    }
987    values.pop().unwrap_or(CachedFacts {
988        height: 0,
989        is_constant: false,
990        has_function: false,
991        root: CachedRoot::Other,
992    })
993}
994
995/// Return a normalized structural height for an expression AST.
996///
997/// SQLite's signed-minimum special case is already normalized to one literal
998/// node, so the test oracle measures the same retained tree as the parser.
999#[cfg(test)]
1000#[must_use]
1001fn normalized_ast_expr_height(expr: &Expr) -> u32 {
1002    cached_facts_from_tasks(vec![CachedHeightTask::Expr(expr)]).height
1003}
1004
1005/// Return the normalized maximum expression height retained by a SELECT AST.
1006///
1007/// This is a private test oracle, not an exact reconstruction of SQLite's
1008/// syntax-sensitive cached `Expr.nHeight` values.
1009#[cfg(test)]
1010#[must_use]
1011fn normalized_ast_select_height(select: &SelectStatement) -> u32 {
1012    cached_facts_from_tasks(vec![CachedHeightTask::Select(select)]).height
1013}
1014
1015// Binding powers: higher = tighter binding.
1016// Left BP is checked against min_bp; right BP is passed to recursive call.
1017mod bp {
1018    // Infix: (left, right)
1019    pub const OR: (u8, u8) = (1, 2);
1020    pub const AND: (u8, u8) = (3, 4);
1021    // Prefix NOT right BP:
1022    pub const NOT_PREFIX: u8 = 5;
1023    // Equality / pattern / membership:
1024    pub const EQUALITY: (u8, u8) = (7, 8);
1025    // Relational comparison:
1026    pub const COMPARISON: (u8, u8) = (9, 10);
1027    // Bitwise operators (all share one level in SQLite):
1028    pub const BITWISE: (u8, u8) = (13, 14);
1029    // Addition / subtraction:
1030    pub const ADD: (u8, u8) = (15, 16);
1031    // Multiplication / division / modulo:
1032    pub const MUL: (u8, u8) = (17, 18);
1033    // String concatenation:
1034    pub const CONCAT: (u8, u8) = (19, 20);
1035    // COLLATE (postfix left BP):
1036    pub const COLLATE: u8 = 21;
1037    // Unary prefix (- + ~) right BP:
1038    pub const UNARY: u8 = 23;
1039    // JSON access (-> ->>): Same as CONCAT
1040    pub const JSON: (u8, u8) = (19, 20);
1041}
1042
1043struct ParseMachine<'a> {
1044    parser: &'a mut Parser,
1045    controls: InlineStack<ParseControl, 8>,
1046    values: InlineStack<MachineValue, 8>,
1047}
1048
1049impl<'a> ParseMachine<'a> {
1050    fn for_expr(parser: &'a mut Parser) -> Self {
1051        let mut controls = InlineStack::new();
1052        controls.push(ParseControl::ExprStart { min_bp: 0 });
1053        Self {
1054            parser,
1055            controls,
1056            values: InlineStack::new(),
1057        }
1058    }
1059
1060    fn for_select(parser: &'a mut Parser, with: Option<WithClause>) -> Self {
1061        let mut controls = InlineStack::new();
1062        controls.push(ParseControl::SelectStart { with });
1063        Self {
1064            parser,
1065            controls,
1066            values: InlineStack::new(),
1067        }
1068    }
1069
1070    fn for_with(parser: &'a mut Parser) -> Self {
1071        let mut controls = InlineStack::new();
1072        controls.push(ParseControl::WithStart);
1073        Self {
1074            parser,
1075            controls,
1076            values: InlineStack::new(),
1077        }
1078    }
1079
1080    fn for_from(parser: &'a mut Parser) -> Self {
1081        let mut controls = InlineStack::new();
1082        controls.push(ParseControl::FromStart);
1083        Self {
1084            parser,
1085            controls,
1086            values: InlineStack::new(),
1087        }
1088    }
1089
1090    fn run_expr(mut self) -> Result<ParsedExpr, ParseError> {
1091        self.run()?;
1092        self.pop_expr()
1093    }
1094
1095    fn run_select(mut self) -> Result<HeightTracked<SelectStatement>, ParseError> {
1096        self.run()?;
1097        self.pop_select()
1098    }
1099
1100    fn run_with(mut self) -> Result<WithClause, ParseError> {
1101        self.run()?;
1102        self.pop_with()
1103    }
1104
1105    fn run_from(mut self) -> Result<FromClause, ParseError> {
1106        self.run()?;
1107        self.pop_from()
1108    }
1109
1110    fn run(&mut self) -> Result<(), ParseError> {
1111        while let Some(control) = self.controls.pop() {
1112            #[cfg(test)]
1113            PARSE_MACHINE_STEPS.set(PARSE_MACHINE_STEPS.get().saturating_add(1));
1114            self.step(control)?;
1115        }
1116        Ok(())
1117    }
1118
1119    fn pop_expr(&mut self) -> Result<ParsedExpr, ParseError> {
1120        match self.values.pop() {
1121            Some(MachineValue::Expr(expr)) => Ok(expr),
1122            _ => Err(self
1123                .parser
1124                .err_here("internal expression parser state mismatch")),
1125        }
1126    }
1127
1128    fn pop_select(&mut self) -> Result<HeightTracked<SelectStatement>, ParseError> {
1129        match self.values.pop() {
1130            Some(MachineValue::Select(select)) => Ok(select),
1131            _ => Err(self
1132                .parser
1133                .err_here("internal SELECT parser state mismatch")),
1134        }
1135    }
1136
1137    fn pop_core(&mut self) -> Result<HeightTracked<SelectCore>, ParseError> {
1138        match self.values.pop() {
1139            Some(MachineValue::Core(core)) => Ok(core),
1140            _ => Err(self
1141                .parser
1142                .err_here("internal SELECT-core parser state mismatch")),
1143        }
1144    }
1145
1146    fn pop_from(&mut self) -> Result<FromClause, ParseError> {
1147        match self.values.pop() {
1148            Some(MachineValue::From(from)) => Ok(from),
1149            _ => Err(self.parser.err_here("internal FROM parser state mismatch")),
1150        }
1151    }
1152
1153    fn pop_table(&mut self) -> Result<TableOrSubquery, ParseError> {
1154        match self.values.pop() {
1155            Some(MachineValue::Table(table)) => Ok(table),
1156            _ => Err(self.parser.err_here("internal table parser state mismatch")),
1157        }
1158    }
1159
1160    fn pop_ordering(&mut self) -> Result<HeightTracked<OrderingTerm>, ParseError> {
1161        match self.values.pop() {
1162            Some(MachineValue::Ordering(term)) => Ok(term),
1163            _ => Err(self
1164                .parser
1165                .err_here("internal ORDER BY parser state mismatch")),
1166        }
1167    }
1168
1169    fn pop_window(&mut self) -> Result<WindowSpec, ParseError> {
1170        match self.values.pop() {
1171            Some(MachineValue::Window(window)) => Ok(window),
1172            _ => Err(self
1173                .parser
1174                .err_here("internal WINDOW parser state mismatch")),
1175        }
1176    }
1177
1178    fn pop_frame_bound(&mut self) -> Result<ParsedFrameBound, ParseError> {
1179        match self.values.pop() {
1180            Some(MachineValue::FrameBound(bound)) => Ok(bound),
1181            _ => Err(self
1182                .parser
1183                .err_here("internal frame-bound parser state mismatch")),
1184        }
1185    }
1186
1187    fn pop_with(&mut self) -> Result<WithClause, ParseError> {
1188        match self.values.pop() {
1189            Some(MachineValue::With(with)) => Ok(with),
1190            _ => Err(self.parser.err_here("internal WITH parser state mismatch")),
1191        }
1192    }
1193
1194    fn push_expr_tail(&mut self, expr: ParsedExpr, min_bp: u8) {
1195        self.values.push(MachineValue::Expr(expr));
1196        self.controls.push(ParseControl::ExprTail { min_bp });
1197    }
1198
1199    fn finish_function(
1200        &mut self,
1201        outer_min_bp: u8,
1202        build: FunctionBuild,
1203    ) -> Result<(), ParseError> {
1204        let span = build.start.merge(build.end);
1205        let parsed = self.parser.checked_expr(
1206            Expr::FunctionCall {
1207                name: build.name,
1208                args: build.args,
1209                distinct: build.distinct,
1210                order_by: build.order_by,
1211                filter: build.filter,
1212                over: build.over,
1213                span,
1214            },
1215            build.height,
1216            false,
1217            true,
1218        )?;
1219        self.push_expr_tail(parsed, outer_min_bp);
1220        Ok(())
1221    }
1222
1223    #[allow(clippy::too_many_lines)]
1224    fn step(&mut self, control: ParseControl) -> Result<(), ParseError> {
1225        match control {
1226            ParseControl::ExprStart { min_bp } => self.expr_start(min_bp),
1227            ParseControl::ExprTail { min_bp } => self.expr_tail(min_bp),
1228            ParseControl::UnaryDone {
1229                outer_min_bp,
1230                op,
1231                span,
1232            } => {
1233                let inner = self.pop_expr()?;
1234                let span = span.merge(inner.expr.span());
1235                let parsed = self.parser.finish_unary(op, inner, span)?;
1236                self.push_expr_tail(parsed, outer_min_bp);
1237                Ok(())
1238            }
1239            ParseControl::CastDone {
1240                outer_min_bp,
1241                start,
1242            } => {
1243                let inner = self.pop_expr()?;
1244                self.parser.expect_kind(&TokenKind::KwAs)?;
1245                let type_name = self.parser.parse_type_name()?;
1246                let end = self.parser.expect_kind(&TokenKind::RightParen)?;
1247                let height = inner.height;
1248                let is_constant = inner.is_constant;
1249                let has_function = inner.has_function;
1250                let parsed = self.parser.checked_expr(
1251                    Expr::Cast {
1252                        expr: Box::new(inner.expr),
1253                        type_name,
1254                        span: start.merge(end),
1255                    },
1256                    height,
1257                    is_constant,
1258                    has_function,
1259                )?;
1260                self.push_expr_tail(parsed, outer_min_bp);
1261                Ok(())
1262            }
1263            ParseControl::GroupFirstDone {
1264                outer_min_bp,
1265                start,
1266            } => {
1267                let first = self.pop_expr()?;
1268                if self.parser.eat_kind(&TokenKind::Comma) {
1269                    let is_constant = first.is_constant;
1270                    let has_function = first.has_function;
1271                    self.controls.push(ParseControl::RowItemDone {
1272                        outer_min_bp,
1273                        start,
1274                        values: vec![first.expr],
1275                        is_constant,
1276                        has_function,
1277                    });
1278                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
1279                } else {
1280                    self.parser.expect_kind(&TokenKind::RightParen)?;
1281                    self.push_expr_tail(first, outer_min_bp);
1282                }
1283                Ok(())
1284            }
1285            ParseControl::RowItemDone {
1286                outer_min_bp,
1287                start,
1288                mut values,
1289                mut is_constant,
1290                mut has_function,
1291            } => {
1292                let parsed = self.pop_expr()?;
1293                is_constant &= parsed.is_constant;
1294                has_function |= parsed.has_function;
1295                values.push(parsed.expr);
1296                if self.parser.eat_kind(&TokenKind::Comma) {
1297                    self.controls.push(ParseControl::RowItemDone {
1298                        outer_min_bp,
1299                        start,
1300                        values,
1301                        is_constant,
1302                        has_function,
1303                    });
1304                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
1305                } else {
1306                    let end = self.parser.expect_kind(&TokenKind::RightParen)?;
1307                    let parsed = self.parser.finish_expr(
1308                        Expr::RowValue(values, start.merge(end)),
1309                        1,
1310                        is_constant,
1311                        has_function,
1312                    )?;
1313                    self.push_expr_tail(parsed, outer_min_bp);
1314                }
1315                Ok(())
1316            }
1317            ParseControl::CaseOperandDone {
1318                outer_min_bp,
1319                start,
1320            } => {
1321                let operand = self.pop_expr()?;
1322                self.controls.push(ParseControl::CaseWhenStart {
1323                    outer_min_bp,
1324                    build: CaseBuild {
1325                        start,
1326                        operand: Some(operand),
1327                        whens: Vec::new(),
1328                    },
1329                });
1330                Ok(())
1331            }
1332            ParseControl::CaseWhenStart {
1333                outer_min_bp,
1334                build,
1335            } => {
1336                if self.parser.eat_kind(&TokenKind::KwWhen) {
1337                    self.controls.push(ParseControl::CaseConditionDone {
1338                        outer_min_bp,
1339                        build,
1340                    });
1341                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
1342                    return Ok(());
1343                }
1344                if build.whens.is_empty() {
1345                    return Err(self
1346                        .parser
1347                        .err_here("CASE requires at least one WHEN clause"));
1348                }
1349                if self.parser.eat_kind(&TokenKind::KwElse) {
1350                    self.controls.push(ParseControl::CaseElseDone {
1351                        outer_min_bp,
1352                        build,
1353                    });
1354                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
1355                    return Ok(());
1356                }
1357                self.finish_case(outer_min_bp, build, None)
1358            }
1359            ParseControl::CaseConditionDone {
1360                outer_min_bp,
1361                build,
1362            } => {
1363                let condition = self.pop_expr()?;
1364                if !self.parser.eat_kind(&TokenKind::KwThen) {
1365                    return Err(self.parser.err_here("expected THEN in CASE expression"));
1366                }
1367                self.controls.push(ParseControl::CaseResultDone {
1368                    outer_min_bp,
1369                    build,
1370                    condition,
1371                });
1372                self.controls.push(ParseControl::ExprStart { min_bp: 0 });
1373                Ok(())
1374            }
1375            ParseControl::CaseResultDone {
1376                outer_min_bp,
1377                mut build,
1378                condition,
1379            } => {
1380                let result = self.pop_expr()?;
1381                build.whens.push((condition, result));
1382                self.controls.push(ParseControl::CaseWhenStart {
1383                    outer_min_bp,
1384                    build,
1385                });
1386                Ok(())
1387            }
1388            ParseControl::CaseElseDone {
1389                outer_min_bp,
1390                build,
1391            } => {
1392                let else_expr = self.pop_expr()?;
1393                self.finish_case(outer_min_bp, build, Some(else_expr))
1394            }
1395            ParseControl::FunctionArgDone {
1396                outer_min_bp,
1397                mut build,
1398            } => {
1399                let arg = self.pop_expr()?;
1400                build.height = build.height.max(arg.height);
1401                let FunctionArgs::List(args) = &mut build.args else {
1402                    return Err(self
1403                        .parser
1404                        .err_here("internal function argument state mismatch"));
1405                };
1406                args.push(arg.expr);
1407                if self.parser.eat_kind(&TokenKind::Comma) {
1408                    self.controls.push(ParseControl::FunctionArgDone {
1409                        outer_min_bp,
1410                        build,
1411                    });
1412                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
1413                } else {
1414                    self.controls.push(ParseControl::FunctionOrderStart {
1415                        outer_min_bp,
1416                        build,
1417                    });
1418                }
1419                Ok(())
1420            }
1421            ParseControl::FunctionOrderStart {
1422                outer_min_bp,
1423                build,
1424            } => {
1425                if self.parser.eat_kind(&TokenKind::KwOrder) {
1426                    self.parser.expect_kind(&TokenKind::KwBy)?;
1427                    self.controls.push(ParseControl::FunctionOrderDone {
1428                        outer_min_bp,
1429                        build,
1430                    });
1431                    self.controls.push(ParseControl::OrderingStart);
1432                } else {
1433                    self.controls.push(ParseControl::FunctionClose {
1434                        outer_min_bp,
1435                        build,
1436                    });
1437                }
1438                Ok(())
1439            }
1440            ParseControl::FunctionOrderDone {
1441                outer_min_bp,
1442                mut build,
1443            } => {
1444                let term = self.pop_ordering()?;
1445                build.order_by.push(term.value);
1446                if self.parser.eat_kind(&TokenKind::Comma) {
1447                    self.controls.push(ParseControl::FunctionOrderDone {
1448                        outer_min_bp,
1449                        build,
1450                    });
1451                    self.controls.push(ParseControl::OrderingStart);
1452                } else {
1453                    self.controls.push(ParseControl::FunctionClose {
1454                        outer_min_bp,
1455                        build,
1456                    });
1457                }
1458                Ok(())
1459            }
1460            ParseControl::FunctionClose {
1461                outer_min_bp,
1462                mut build,
1463            } => {
1464                build.end = self.parser.expect_kind(&TokenKind::RightParen)?;
1465                if matches!(self.parser.peek_kind(), TokenKind::KwFilter)
1466                    && self
1467                        .parser
1468                        .tokens
1469                        .get(self.parser.pos + 1)
1470                        .is_some_and(|token| token.kind == TokenKind::LeftParen)
1471                {
1472                    self.parser.advance_token();
1473                    self.parser.expect_kind(&TokenKind::LeftParen)?;
1474                    self.parser.expect_kind(&TokenKind::KwWhere)?;
1475                    self.controls.push(ParseControl::FunctionFilterDone {
1476                        outer_min_bp,
1477                        build,
1478                        has_filter: true,
1479                    });
1480                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
1481                } else {
1482                    self.controls.push(ParseControl::FunctionFilterDone {
1483                        outer_min_bp,
1484                        build,
1485                        has_filter: false,
1486                    });
1487                }
1488                Ok(())
1489            }
1490            ParseControl::FunctionFilterDone {
1491                outer_min_bp,
1492                mut build,
1493                has_filter,
1494            } => {
1495                if has_filter {
1496                    let filter = self.pop_expr()?;
1497                    let end = self.parser.expect_kind(&TokenKind::RightParen)?;
1498                    build.end = build.end.merge(end);
1499                    build.filter = Some(Box::new(filter.expr));
1500                }
1501                if matches!(self.parser.peek_kind(), TokenKind::KwOver)
1502                    && self
1503                        .parser
1504                        .tokens
1505                        .get(self.parser.pos + 1)
1506                        .is_some_and(|token| {
1507                            matches!(token.kind, TokenKind::LeftParen)
1508                                || starts_bare_window_name(&token.kind)
1509                        })
1510                {
1511                    self.parser.advance_token();
1512                    if self.parser.eat_kind(&TokenKind::LeftParen) {
1513                        self.controls.push(ParseControl::FunctionOverDone {
1514                            outer_min_bp,
1515                            build,
1516                        });
1517                        self.controls.push(ParseControl::WindowStart);
1518                    } else {
1519                        let base_window = self.parser.parse_window_name()?;
1520                        let base_span = self.parser.tokens[self.parser.pos.saturating_sub(1)].span;
1521                        build.end = build.end.merge(base_span);
1522                        build.over = Some(WindowSpec {
1523                            window_ref: Some(WindowReference::Direct(base_window)),
1524                            partition_by: Vec::new(),
1525                            order_by: Vec::new(),
1526                            frame: None,
1527                        });
1528                        self.finish_function(outer_min_bp, build)?;
1529                    }
1530                } else {
1531                    self.finish_function(outer_min_bp, build)?;
1532                }
1533                Ok(())
1534            }
1535            ParseControl::FunctionOverDone {
1536                outer_min_bp,
1537                mut build,
1538            } => {
1539                build.over = Some(self.pop_window()?);
1540                let end = self.parser.expect_kind(&TokenKind::RightParen)?;
1541                build.end = build.end.merge(end);
1542                self.finish_function(outer_min_bp, build)
1543            }
1544            ParseControl::BinaryDone {
1545                outer_min_bp,
1546                lhs,
1547                op,
1548            } => {
1549                let rhs = self.pop_expr()?;
1550                let span = lhs.expr.span().merge(rhs.expr.span());
1551                let height = lhs.height.max(rhs.height);
1552                let is_constant = lhs.is_constant && rhs.is_constant;
1553                let has_function = lhs.has_function || rhs.has_function;
1554                let parsed = self.parser.checked_expr(
1555                    Expr::BinaryOp {
1556                        left: Box::new(lhs.expr),
1557                        op,
1558                        right: Box::new(rhs.expr),
1559                        span,
1560                    },
1561                    height,
1562                    is_constant,
1563                    has_function,
1564                )?;
1565                self.push_expr_tail(parsed, outer_min_bp);
1566                Ok(())
1567            }
1568            ParseControl::JsonDone {
1569                outer_min_bp,
1570                lhs,
1571                arrow,
1572            } => {
1573                let rhs = self.pop_expr()?;
1574                let span = lhs.expr.span().merge(rhs.expr.span());
1575                let height = lhs.height.max(rhs.height);
1576                let parsed = self.parser.checked_expr(
1577                    Expr::JsonAccess {
1578                        expr: Box::new(lhs.expr),
1579                        path: Box::new(rhs.expr),
1580                        arrow,
1581                        span,
1582                    },
1583                    height,
1584                    false,
1585                    true,
1586                )?;
1587                self.push_expr_tail(parsed, outer_min_bp);
1588                Ok(())
1589            }
1590            ParseControl::IsDone {
1591                outer_min_bp,
1592                lhs,
1593                not,
1594            } => {
1595                let rhs = self.pop_expr()?;
1596                let span = lhs.expr.span().merge(rhs.expr.span());
1597                let parsed = if matches!(&rhs.expr, Expr::Literal(Literal::Null, _)) {
1598                    self.parser.checked_expr(
1599                        Expr::IsNull {
1600                            expr: Box::new(lhs.expr),
1601                            not,
1602                            span,
1603                        },
1604                        lhs.height,
1605                        lhs.is_constant,
1606                        lhs.has_function,
1607                    )?
1608                } else {
1609                    let height = lhs.height.max(rhs.height);
1610                    let is_constant = lhs.is_constant && rhs.is_constant;
1611                    let has_function = lhs.has_function || rhs.has_function;
1612                    self.parser.checked_expr(
1613                        Expr::BinaryOp {
1614                            left: Box::new(lhs.expr),
1615                            op: if not { BinaryOp::IsNot } else { BinaryOp::Is },
1616                            right: Box::new(rhs.expr),
1617                            span,
1618                        },
1619                        height,
1620                        is_constant,
1621                        has_function,
1622                    )?
1623                };
1624                self.push_expr_tail(parsed, outer_min_bp);
1625                Ok(())
1626            }
1627            ParseControl::LikePatternDone {
1628                outer_min_bp,
1629                lhs,
1630                op,
1631                not,
1632            } => {
1633                let pattern = self.pop_expr()?;
1634                if self.parser.eat_kind(&TokenKind::KwEscape) {
1635                    self.controls.push(ParseControl::LikeEscapeDone {
1636                        outer_min_bp,
1637                        lhs,
1638                        pattern,
1639                        op,
1640                        not,
1641                    });
1642                    self.controls.push(ParseControl::ExprStart {
1643                        min_bp: bp::EQUALITY.1,
1644                    });
1645                    return Ok(());
1646                }
1647                self.finish_like(outer_min_bp, lhs, pattern, None, op, not)
1648            }
1649            ParseControl::LikeEscapeDone {
1650                outer_min_bp,
1651                lhs,
1652                pattern,
1653                op,
1654                not,
1655            } => {
1656                let escape = self.pop_expr()?;
1657                self.finish_like(outer_min_bp, lhs, pattern, Some(escape), op, not)
1658            }
1659            ParseControl::BetweenLowDone {
1660                outer_min_bp,
1661                lhs,
1662                not,
1663            } => {
1664                let low = self.pop_expr()?;
1665                if !self.parser.eat_kind(&TokenKind::KwAnd) {
1666                    return Err(self.parser.err_here("expected AND in BETWEEN expression"));
1667                }
1668                self.controls.push(ParseControl::BetweenHighDone {
1669                    outer_min_bp,
1670                    lhs,
1671                    low,
1672                    not,
1673                });
1674                self.controls.push(ParseControl::ExprStart {
1675                    min_bp: bp::EQUALITY.1,
1676                });
1677                Ok(())
1678            }
1679            ParseControl::BetweenHighDone {
1680                outer_min_bp,
1681                lhs,
1682                low,
1683                not,
1684            } => {
1685                let high = self.pop_expr()?;
1686                let span = lhs.expr.span().merge(high.expr.span());
1687                let height = lhs.height.max(low.height).max(high.height);
1688                let is_constant = lhs.is_constant && low.is_constant && high.is_constant;
1689                let has_function = lhs.has_function || low.has_function || high.has_function;
1690                let parsed = self.parser.checked_expr(
1691                    Expr::Between {
1692                        expr: Box::new(lhs.expr),
1693                        low: Box::new(low.expr),
1694                        high: Box::new(high.expr),
1695                        not,
1696                        span,
1697                    },
1698                    height,
1699                    is_constant,
1700                    has_function,
1701                )?;
1702                let parsed = if not {
1703                    self.parser.add_cached_parent(parsed)?
1704                } else {
1705                    parsed
1706                };
1707                self.push_expr_tail(parsed, outer_min_bp);
1708                Ok(())
1709            }
1710            ParseControl::InItemDone {
1711                outer_min_bp,
1712                lhs,
1713                not,
1714                mut items,
1715                start,
1716            } => {
1717                let item = self.pop_expr()?;
1718                items.push(item);
1719                if self.parser.eat_kind(&TokenKind::Comma) {
1720                    self.controls.push(ParseControl::InItemDone {
1721                        outer_min_bp,
1722                        lhs,
1723                        not,
1724                        items,
1725                        start,
1726                    });
1727                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
1728                } else {
1729                    let end = self.parser.expect_kind(&TokenKind::RightParen)?;
1730                    self.finish_in_list(outer_min_bp, lhs, not, items, start.merge(end))?;
1731                }
1732                Ok(())
1733            }
1734            ParseControl::InSelectDone {
1735                outer_min_bp,
1736                lhs,
1737                not,
1738                start,
1739            } => {
1740                let select = self.pop_select()?;
1741                let end = self.parser.expect_kind(&TokenKind::RightParen)?;
1742                let height = lhs.height.max(select.height);
1743                let has_function = lhs.has_function;
1744                let parsed = self.parser.checked_expr(
1745                    Expr::In {
1746                        expr: Box::new(lhs.expr),
1747                        set: InSet::Subquery(Box::new(select.value)),
1748                        not,
1749                        span: start.merge(end),
1750                    },
1751                    height,
1752                    false,
1753                    has_function,
1754                )?;
1755                let parsed = if not {
1756                    self.parser.add_cached_parent(parsed)?
1757                } else {
1758                    parsed
1759                };
1760                self.push_expr_tail(parsed, outer_min_bp);
1761                Ok(())
1762            }
1763            ParseControl::ExistsDone {
1764                outer_min_bp,
1765                not,
1766                start,
1767            } => {
1768                let select = self.pop_select()?;
1769                let end = self.parser.expect_kind(&TokenKind::RightParen)?;
1770                let parsed = self.parser.checked_expr(
1771                    Expr::Exists {
1772                        subquery: Box::new(select.value),
1773                        not,
1774                        span: start.merge(end),
1775                    },
1776                    select.height,
1777                    false,
1778                    false,
1779                )?;
1780                let parsed = if not {
1781                    self.parser.add_cached_parent(parsed)?
1782                } else {
1783                    parsed
1784                };
1785                self.push_expr_tail(parsed, outer_min_bp);
1786                Ok(())
1787            }
1788            ParseControl::ScalarSelectDone {
1789                outer_min_bp,
1790                start,
1791            } => {
1792                let select = self.pop_select()?;
1793                let end = self.parser.expect_kind(&TokenKind::RightParen)?;
1794                let parsed = self.parser.checked_expr(
1795                    Expr::Subquery(Box::new(select.value), start.merge(end)),
1796                    select.height,
1797                    false,
1798                    false,
1799                )?;
1800                self.push_expr_tail(parsed, outer_min_bp);
1801                Ok(())
1802            }
1803            ParseControl::OrderingStart => {
1804                self.controls.push(ParseControl::OrderingDone);
1805                self.controls.push(ParseControl::ExprStart { min_bp: 0 });
1806                Ok(())
1807            }
1808            ParseControl::OrderingDone => {
1809                let expr = self.pop_expr()?;
1810                let direction = if self.parser.eat_kind(&TokenKind::KwAsc) {
1811                    Some(SortDirection::Asc)
1812                } else if self.parser.eat_kind(&TokenKind::KwDesc) {
1813                    Some(SortDirection::Desc)
1814                } else {
1815                    None
1816                };
1817                let nulls = if self.parser.eat_kind(&TokenKind::KwNulls) {
1818                    if self.parser.eat_kind(&TokenKind::KwFirst) {
1819                        Some(NullsOrder::First)
1820                    } else {
1821                        self.parser.expect_kw(&TokenKind::KwLast)?;
1822                        Some(NullsOrder::Last)
1823                    }
1824                } else {
1825                    None
1826                };
1827                self.values.push(MachineValue::Ordering(HeightTracked {
1828                    height: expr.height,
1829                    value: OrderingTerm {
1830                        expr: expr.expr,
1831                        direction,
1832                        nulls,
1833                    },
1834                }));
1835                Ok(())
1836            }
1837            ParseControl::WindowStart => self.window_start(),
1838            ParseControl::WindowPartitionDone { mut build } => {
1839                build.partition_by.push(self.pop_expr()?.expr);
1840                if self.parser.eat_kind(&TokenKind::Comma) {
1841                    self.controls
1842                        .push(ParseControl::WindowPartitionDone { build });
1843                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
1844                } else {
1845                    self.controls.push(ParseControl::WindowOrderStart { build });
1846                }
1847                Ok(())
1848            }
1849            ParseControl::WindowOrderStart { build } => {
1850                if self.parser.eat_kind(&TokenKind::KwOrder) {
1851                    self.parser.expect_kw(&TokenKind::KwBy)?;
1852                    self.controls.push(ParseControl::WindowOrderDone { build });
1853                    self.controls.push(ParseControl::OrderingStart);
1854                } else {
1855                    self.controls.push(ParseControl::WindowFrameStart { build });
1856                }
1857                Ok(())
1858            }
1859            ParseControl::WindowOrderDone { mut build } => {
1860                build.order_by.push(self.pop_ordering()?.value);
1861                if self.parser.eat_kind(&TokenKind::Comma) {
1862                    self.controls.push(ParseControl::WindowOrderDone { build });
1863                    self.controls.push(ParseControl::OrderingStart);
1864                } else {
1865                    self.controls.push(ParseControl::WindowFrameStart { build });
1866                }
1867                Ok(())
1868            }
1869            ParseControl::WindowFrameStart { build } => {
1870                self.window_frame_start(build);
1871                Ok(())
1872            }
1873            ParseControl::WindowFirstBoundDone {
1874                build,
1875                frame_type,
1876                between,
1877            } => {
1878                let start = self.pop_frame_bound()?;
1879                validate_frame_start(&start, between)?;
1880                if between {
1881                    self.parser.expect_kw(&TokenKind::KwAnd)?;
1882                    self.controls.push(ParseControl::WindowSecondBoundDone {
1883                        build,
1884                        frame_type,
1885                        start,
1886                    });
1887                    self.controls.push(ParseControl::FrameBoundStart);
1888                } else {
1889                    let frame = self.finish_frame(frame_type, start.value, None)?;
1890                    self.values.push(MachineValue::Window(WindowSpec {
1891                        window_ref: build.base_window.map(WindowReference::Base),
1892                        partition_by: build.partition_by,
1893                        order_by: build.order_by,
1894                        frame: Some(frame),
1895                    }));
1896                }
1897                Ok(())
1898            }
1899            ParseControl::WindowSecondBoundDone {
1900                build,
1901                frame_type,
1902                start,
1903            } => {
1904                let end = self.pop_frame_bound()?;
1905                validate_frame_end(&start, &end)?;
1906                let frame = self.finish_frame(frame_type, start.value, Some(end.value))?;
1907                self.values.push(MachineValue::Window(WindowSpec {
1908                    window_ref: build.base_window.map(WindowReference::Base),
1909                    partition_by: build.partition_by,
1910                    order_by: build.order_by,
1911                    frame: Some(frame),
1912                }));
1913                Ok(())
1914            }
1915            ParseControl::FrameBoundStart => self.frame_bound_start(),
1916            ParseControl::FrameBoundExprDone { origin } => {
1917                let expr = self.pop_expr()?.expr;
1918                let bound = if self.parser.eat_kind(&TokenKind::KwPreceding) {
1919                    FrameBound::Preceding(Box::new(expr))
1920                } else {
1921                    self.parser.expect_kw(&TokenKind::KwFollowing)?;
1922                    FrameBound::Following(Box::new(expr))
1923                };
1924                self.values.push(MachineValue::FrameBound(ParsedFrameBound {
1925                    value: bound,
1926                    origin,
1927                }));
1928                Ok(())
1929            }
1930            ParseControl::SubqueryStart => {
1931                if self.parser.at_kind(&TokenKind::KwWith) {
1932                    self.controls.push(ParseControl::SubqueryWithDone);
1933                    self.controls.push(ParseControl::WithStart);
1934                } else {
1935                    self.controls.push(ParseControl::SelectStart { with: None });
1936                }
1937                Ok(())
1938            }
1939            ParseControl::SubqueryWithDone => {
1940                let with = self.pop_with()?;
1941                self.controls
1942                    .push(ParseControl::SelectStart { with: Some(with) });
1943                Ok(())
1944            }
1945            other => self.step_select(other),
1946        }
1947    }
1948
1949    #[allow(clippy::too_many_lines)]
1950    fn expr_start(&mut self, min_bp: u8) -> Result<(), ParseError> {
1951        let Token {
1952            kind,
1953            span: token_span,
1954            line,
1955            col,
1956        } = self.parser.advance_token();
1957        if self.parser.at_kind(&TokenKind::Dot) && starts_table_star_qualifier(&kind) {
1958            let name = match &kind {
1959                TokenKind::Id(name) | TokenKind::QuotedId(name, _) => Arc::clone(name),
1960                TokenKind::String(name) => Arc::<str>::from(name.as_str()),
1961                keyword => Arc::<str>::from(kw_to_str(keyword)),
1962            };
1963            return self.identifier_or_function(name, token_span, min_bp);
1964        }
1965        let parsed = match kind {
1966            TokenKind::Integer(value) => {
1967                ParsedExpr::leaf(Expr::Literal(Literal::Integer(value), token_span))
1968            }
1969            TokenKind::OversizedInt(value) => match value.parse::<f64>() {
1970                Ok(value) => ParsedExpr::leaf(Expr::Literal(Literal::Float(value), token_span)),
1971                Err(_) => {
1972                    return Err(ParseError {
1973                        kind: crate::parser::ParseErrorKind::Syntax,
1974                        message: "integer out of range".to_owned(),
1975                        span: token_span,
1976                        line,
1977                        col,
1978                    });
1979                }
1980            },
1981            TokenKind::Float(value) => {
1982                ParsedExpr::leaf(Expr::Literal(Literal::Float(value), token_span))
1983            }
1984            TokenKind::String(value) if self.parser.at_kind(&TokenKind::Dot) => {
1985                return self.identifier_or_function(Arc::<str>::from(value), token_span, min_bp);
1986            }
1987            TokenKind::String(value) => {
1988                ParsedExpr::leaf(Expr::Literal(Literal::String(value), token_span))
1989            }
1990            TokenKind::Blob(value) => {
1991                ParsedExpr::leaf(Expr::Literal(Literal::Blob(value), token_span))
1992            }
1993            TokenKind::KwNull => ParsedExpr::leaf(Expr::Literal(Literal::Null, token_span)),
1994            TokenKind::KwTrue => ParsedExpr::leaf(Expr::Literal(Literal::True, token_span)),
1995            TokenKind::KwFalse => ParsedExpr::leaf(Expr::Literal(Literal::False, token_span)),
1996            TokenKind::KwCurrentTime => {
1997                ParsedExpr::leaf(Expr::Literal(Literal::CurrentTime, token_span))
1998            }
1999            TokenKind::KwCurrentDate => {
2000                ParsedExpr::leaf(Expr::Literal(Literal::CurrentDate, token_span))
2001            }
2002            TokenKind::KwCurrentTimestamp => {
2003                ParsedExpr::leaf(Expr::Literal(Literal::CurrentTimestamp, token_span))
2004            }
2005            TokenKind::Question => {
2006                ParsedExpr::leaf(Expr::Placeholder(PlaceholderType::Anonymous, token_span))
2007            }
2008            TokenKind::QuestionNum(value) => ParsedExpr::leaf(Expr::Placeholder(
2009                PlaceholderType::Numbered(value),
2010                token_span,
2011            )),
2012            TokenKind::ColonParam(value) => ParsedExpr::leaf(Expr::Placeholder(
2013                PlaceholderType::ColonNamed(value),
2014                token_span,
2015            )),
2016            TokenKind::AtParam(value) => ParsedExpr::leaf(Expr::Placeholder(
2017                PlaceholderType::AtNamed(value),
2018                token_span,
2019            )),
2020            TokenKind::DollarParam(value) => ParsedExpr::leaf(Expr::Placeholder(
2021                PlaceholderType::DollarNamed(value),
2022                token_span,
2023            )),
2024            TokenKind::Minus => {
2025                if let TokenKind::OversizedInt(value) = self.parser.peek_kind()
2026                    && value == "9223372036854775808"
2027                {
2028                    let number_span = self.parser.advance_token().span;
2029                    let parsed = self.parser.finish_expr(
2030                        Expr::Literal(Literal::Integer(i64::MIN), token_span.merge(number_span)),
2031                        1,
2032                        true,
2033                        false,
2034                    )?;
2035                    self.push_expr_tail(parsed, min_bp);
2036                    return Ok(());
2037                }
2038                self.controls.push(ParseControl::UnaryDone {
2039                    outer_min_bp: min_bp,
2040                    op: UnaryOp::Negate,
2041                    span: token_span,
2042                });
2043                self.controls
2044                    .push(ParseControl::ExprStart { min_bp: bp::UNARY });
2045                return Ok(());
2046            }
2047            TokenKind::Plus => {
2048                self.controls.push(ParseControl::UnaryDone {
2049                    outer_min_bp: min_bp,
2050                    op: UnaryOp::Plus,
2051                    span: token_span,
2052                });
2053                self.controls
2054                    .push(ParseControl::ExprStart { min_bp: bp::UNARY });
2055                return Ok(());
2056            }
2057            TokenKind::Tilde => {
2058                self.controls.push(ParseControl::UnaryDone {
2059                    outer_min_bp: min_bp,
2060                    op: UnaryOp::BitNot,
2061                    span: token_span,
2062                });
2063                self.controls
2064                    .push(ParseControl::ExprStart { min_bp: bp::UNARY });
2065                return Ok(());
2066            }
2067            TokenKind::KwNot => {
2068                if self.parser.eat_kind(&TokenKind::KwExists) {
2069                    self.parser.expect_kind(&TokenKind::LeftParen)?;
2070                    self.controls.push(ParseControl::ExistsDone {
2071                        outer_min_bp: min_bp,
2072                        not: true,
2073                        start: token_span,
2074                    });
2075                    self.controls.push(ParseControl::SubqueryStart);
2076                } else {
2077                    self.controls.push(ParseControl::UnaryDone {
2078                        outer_min_bp: min_bp,
2079                        op: UnaryOp::Not,
2080                        span: token_span,
2081                    });
2082                    self.controls.push(ParseControl::ExprStart {
2083                        min_bp: bp::NOT_PREFIX,
2084                    });
2085                }
2086                return Ok(());
2087            }
2088            TokenKind::KwExists => {
2089                self.parser.expect_kind(&TokenKind::LeftParen)?;
2090                self.controls.push(ParseControl::ExistsDone {
2091                    outer_min_bp: min_bp,
2092                    not: false,
2093                    start: token_span,
2094                });
2095                self.controls.push(ParseControl::SubqueryStart);
2096                return Ok(());
2097            }
2098            TokenKind::KwCast => {
2099                self.parser.expect_kind(&TokenKind::LeftParen)?;
2100                self.controls.push(ParseControl::CastDone {
2101                    outer_min_bp: min_bp,
2102                    start: token_span,
2103                });
2104                self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2105                return Ok(());
2106            }
2107            TokenKind::KwCase => {
2108                if self.parser.at_kind(&TokenKind::KwWhen) {
2109                    self.controls.push(ParseControl::CaseWhenStart {
2110                        outer_min_bp: min_bp,
2111                        build: CaseBuild {
2112                            start: token_span,
2113                            operand: None,
2114                            whens: Vec::new(),
2115                        },
2116                    });
2117                } else {
2118                    self.controls.push(ParseControl::CaseOperandDone {
2119                        outer_min_bp: min_bp,
2120                        start: token_span,
2121                    });
2122                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2123                }
2124                return Ok(());
2125            }
2126            TokenKind::KwRaise => {
2127                self.parser.expect_kind(&TokenKind::LeftParen)?;
2128                let (action, message) = self.parser.parse_raise_args()?;
2129                let end = self.parser.expect_kind(&TokenKind::RightParen)?;
2130                ParsedExpr::leaf(Expr::Raise {
2131                    action,
2132                    message,
2133                    span: token_span.merge(end),
2134                })
2135            }
2136            TokenKind::LeftParen => {
2137                if matches!(
2138                    self.parser.peek_kind(),
2139                    TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
2140                ) {
2141                    self.controls.push(ParseControl::ScalarSelectDone {
2142                        outer_min_bp: min_bp,
2143                        start: token_span,
2144                    });
2145                    self.controls.push(ParseControl::SubqueryStart);
2146                } else {
2147                    self.controls.push(ParseControl::GroupFirstDone {
2148                        outer_min_bp: min_bp,
2149                        start: token_span,
2150                    });
2151                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2152                }
2153                return Ok(());
2154            }
2155            TokenKind::Id(name) | TokenKind::QuotedId(name, _) => {
2156                return self.identifier_or_function(name, token_span, min_bp);
2157            }
2158            TokenKind::KwReplace if self.parser.at_kind(&TokenKind::LeftParen) => {
2159                return self.start_function("replace".to_owned(), token_span, min_bp);
2160            }
2161            TokenKind::KwLike if self.parser.at_kind(&TokenKind::LeftParen) => {
2162                return self.start_function("like".to_owned(), token_span, min_bp);
2163            }
2164            TokenKind::KwGlob if self.parser.at_kind(&TokenKind::LeftParen) => {
2165                return self.start_function("glob".to_owned(), token_span, min_bp);
2166            }
2167            TokenKind::KwRegexp if self.parser.at_kind(&TokenKind::LeftParen) => {
2168                return self.start_function("regexp".to_owned(), token_span, min_bp);
2169            }
2170            TokenKind::KwMatch if self.parser.at_kind(&TokenKind::LeftParen) => {
2171                return self.start_function("match".to_owned(), token_span, min_bp);
2172            }
2173            kind if is_nonreserved_kw(&kind) => {
2174                let name = Arc::<str>::from(kw_to_str(&kind));
2175                return self.identifier_or_function(name, token_span, min_bp);
2176            }
2177            kind => {
2178                return Err(ParseError {
2179                    kind: crate::parser::ParseErrorKind::Syntax,
2180                    message: format!("unexpected token in expression: {kind:?}"),
2181                    span: token_span,
2182                    line,
2183                    col,
2184                });
2185            }
2186        };
2187        self.push_expr_tail(parsed, min_bp);
2188        Ok(())
2189    }
2190
2191    fn identifier_or_function(
2192        &mut self,
2193        name: Arc<str>,
2194        start: Span,
2195        min_bp: u8,
2196    ) -> Result<(), ParseError> {
2197        if self.parser.at_kind(&TokenKind::LeftParen) {
2198            return self.start_function(name.to_string(), start, min_bp);
2199        }
2200        let parsed = if self.parser.at_kind(&TokenKind::Dot) {
2201            let Some(column_token) = self.parser.tokens.get(self.parser.pos + 1).cloned() else {
2202                return Err(self.parser.err_here("expected column name after '.'"));
2203            };
2204            let column = match &column_token.kind {
2205                TokenKind::Id(column) | TokenKind::QuotedId(column, _) => Arc::clone(column),
2206                TokenKind::String(column) => Arc::<str>::from(column.as_str()),
2207                kind if starts_post_dot_identifier(kind) => Arc::<str>::from(kw_to_str(kind)),
2208                _ => {
2209                    return Err(ParseError::at(
2210                        format!(
2211                            "expected column name after '.', got {:?}",
2212                            column_token.kind
2213                        ),
2214                        Some(&column_token),
2215                    ));
2216                }
2217            };
2218            self.parser.pos = self.parser.pos.saturating_add(2);
2219            self.parser.finish_expr(
2220                Expr::Column(
2221                    ColumnRef::qualified(name, column),
2222                    start.merge(column_token.span),
2223                ),
2224                2,
2225                false,
2226                false,
2227            )?
2228        } else {
2229            ParsedExpr::leaf(Expr::Column(ColumnRef::bare(name), start))
2230        };
2231        self.push_expr_tail(parsed, min_bp);
2232        Ok(())
2233    }
2234
2235    fn start_function(
2236        &mut self,
2237        name: String,
2238        start: Span,
2239        outer_min_bp: u8,
2240    ) -> Result<(), ParseError> {
2241        self.parser.expect_kind(&TokenKind::LeftParen)?;
2242        let mut build = FunctionBuild {
2243            name,
2244            start,
2245            args: FunctionArgs::List(Vec::new()),
2246            distinct: false,
2247            height: 0,
2248            order_by: Vec::new(),
2249            filter: None,
2250            over: None,
2251            end: start,
2252        };
2253        if self.parser.eat_kind(&TokenKind::Star) {
2254            // bd-2fong parity: stock SQLite parses `f(*)` for ANY function.
2255            // Only count keeps star semantics; every other function treats
2256            // the star as a zero-argument call, so arity validation later
2257            // yields stock's exact behavior — `random(*)` evaluates,
2258            // `abs(*)` / `max(*)` fail with "wrong number of arguments".
2259            build.args = if build.name.eq_ignore_ascii_case("count") {
2260                FunctionArgs::Star
2261            } else {
2262                FunctionArgs::List(Vec::new())
2263            };
2264            self.controls.push(ParseControl::FunctionClose {
2265                outer_min_bp,
2266                build,
2267            });
2268        } else {
2269            build.distinct = self.parser.eat_kind(&TokenKind::KwDistinct);
2270            if self.parser.at_kind(&TokenKind::RightParen) {
2271                if build.distinct {
2272                    return Err(self
2273                        .parser
2274                        .err_here("DISTINCT requires at least one argument"));
2275                }
2276                self.controls.push(ParseControl::FunctionOrderStart {
2277                    outer_min_bp,
2278                    build,
2279                });
2280            } else {
2281                self.controls.push(ParseControl::FunctionArgDone {
2282                    outer_min_bp,
2283                    build,
2284                });
2285                self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2286            }
2287        }
2288        Ok(())
2289    }
2290
2291    #[allow(clippy::too_many_lines)]
2292    fn expr_tail(&mut self, min_bp: u8) -> Result<(), ParseError> {
2293        let lhs = self.pop_expr()?;
2294        if let Some(left_bp) = self.parser.postfix_bp()
2295            && left_bp >= min_bp
2296        {
2297            let parsed = self.parser.parse_postfix(lhs)?;
2298            self.push_expr_tail(parsed, min_bp);
2299            return Ok(());
2300        }
2301        let Some((left_bp, right_bp)) = self.parser.infix_bp() else {
2302            self.values.push(MachineValue::Expr(lhs));
2303            return Ok(());
2304        };
2305        if left_bp < min_bp {
2306            self.values.push(MachineValue::Expr(lhs));
2307            return Ok(());
2308        }
2309
2310        let token = self.parser.advance_token();
2311        let simple = match &token.kind {
2312            TokenKind::Plus => Some(BinaryOp::Add),
2313            TokenKind::Minus => Some(BinaryOp::Subtract),
2314            TokenKind::Star => Some(BinaryOp::Multiply),
2315            TokenKind::Slash => Some(BinaryOp::Divide),
2316            TokenKind::Percent => Some(BinaryOp::Modulo),
2317            TokenKind::Concat => Some(BinaryOp::Concat),
2318            TokenKind::Eq | TokenKind::EqEq => Some(BinaryOp::Eq),
2319            TokenKind::Ne | TokenKind::LtGt => Some(BinaryOp::Ne),
2320            TokenKind::Lt => Some(BinaryOp::Lt),
2321            TokenKind::Le => Some(BinaryOp::Le),
2322            TokenKind::Gt => Some(BinaryOp::Gt),
2323            TokenKind::Ge => Some(BinaryOp::Ge),
2324            TokenKind::Ampersand => Some(BinaryOp::BitAnd),
2325            TokenKind::Pipe => Some(BinaryOp::BitOr),
2326            TokenKind::ShiftLeft => Some(BinaryOp::ShiftLeft),
2327            TokenKind::ShiftRight => Some(BinaryOp::ShiftRight),
2328            TokenKind::KwOr => Some(BinaryOp::Or),
2329            TokenKind::KwAnd => Some(BinaryOp::And),
2330            _ => None,
2331        };
2332        if let Some(op) = simple {
2333            self.controls.push(ParseControl::BinaryDone {
2334                outer_min_bp: min_bp,
2335                lhs,
2336                op,
2337            });
2338            self.controls
2339                .push(ParseControl::ExprStart { min_bp: right_bp });
2340            return Ok(());
2341        }
2342        match &token.kind {
2343            TokenKind::KwIs => {
2344                let not = self.parser.eat_kind(&TokenKind::KwNot);
2345                if self.parser.eat_kind(&TokenKind::KwDistinct) {
2346                    self.parser.expect_kind(&TokenKind::KwFrom)?;
2347                    self.controls.push(ParseControl::BinaryDone {
2348                        outer_min_bp: min_bp,
2349                        lhs,
2350                        op: if not { BinaryOp::Is } else { BinaryOp::IsNot },
2351                    });
2352                } else {
2353                    self.controls.push(ParseControl::IsDone {
2354                        outer_min_bp: min_bp,
2355                        lhs,
2356                        not,
2357                    });
2358                }
2359                self.controls
2360                    .push(ParseControl::ExprStart { min_bp: right_bp });
2361            }
2362            TokenKind::KwLike | TokenKind::KwGlob | TokenKind::KwMatch | TokenKind::KwRegexp => {
2363                let op = match &token.kind {
2364                    TokenKind::KwLike => LikeOp::Like,
2365                    TokenKind::KwGlob => LikeOp::Glob,
2366                    TokenKind::KwMatch => LikeOp::Match,
2367                    TokenKind::KwRegexp => LikeOp::Regexp,
2368                    _ => unreachable!(),
2369                };
2370                self.controls.push(ParseControl::LikePatternDone {
2371                    outer_min_bp: min_bp,
2372                    lhs,
2373                    op,
2374                    not: false,
2375                });
2376                self.controls.push(ParseControl::ExprStart {
2377                    min_bp: bp::EQUALITY.1,
2378                });
2379            }
2380            TokenKind::KwBetween => {
2381                self.controls.push(ParseControl::BetweenLowDone {
2382                    outer_min_bp: min_bp,
2383                    lhs,
2384                    not: false,
2385                });
2386                self.controls.push(ParseControl::ExprStart {
2387                    min_bp: bp::NOT_PREFIX,
2388                });
2389            }
2390            TokenKind::KwIn => self.start_in(lhs, false, min_bp)?,
2391            TokenKind::Arrow => {
2392                self.controls.push(ParseControl::JsonDone {
2393                    outer_min_bp: min_bp,
2394                    lhs,
2395                    arrow: JsonArrow::Arrow,
2396                });
2397                self.controls
2398                    .push(ParseControl::ExprStart { min_bp: right_bp });
2399            }
2400            TokenKind::DoubleArrow => {
2401                self.controls.push(ParseControl::JsonDone {
2402                    outer_min_bp: min_bp,
2403                    lhs,
2404                    arrow: JsonArrow::DoubleArrow,
2405                });
2406                self.controls
2407                    .push(ParseControl::ExprStart { min_bp: right_bp });
2408            }
2409            TokenKind::KwNot => {
2410                let next = self.parser.advance_token();
2411                match &next.kind {
2412                    TokenKind::KwLike
2413                    | TokenKind::KwGlob
2414                    | TokenKind::KwMatch
2415                    | TokenKind::KwRegexp => {
2416                        let op = match &next.kind {
2417                            TokenKind::KwLike => LikeOp::Like,
2418                            TokenKind::KwGlob => LikeOp::Glob,
2419                            TokenKind::KwMatch => LikeOp::Match,
2420                            TokenKind::KwRegexp => LikeOp::Regexp,
2421                            _ => unreachable!(),
2422                        };
2423                        self.controls.push(ParseControl::LikePatternDone {
2424                            outer_min_bp: min_bp,
2425                            lhs,
2426                            op,
2427                            not: true,
2428                        });
2429                        self.controls.push(ParseControl::ExprStart {
2430                            min_bp: bp::EQUALITY.1,
2431                        });
2432                    }
2433                    TokenKind::KwBetween => {
2434                        self.controls.push(ParseControl::BetweenLowDone {
2435                            outer_min_bp: min_bp,
2436                            lhs,
2437                            not: true,
2438                        });
2439                        self.controls.push(ParseControl::ExprStart {
2440                            min_bp: bp::NOT_PREFIX,
2441                        });
2442                    }
2443                    TokenKind::KwIn => self.start_in(lhs, true, min_bp)?,
2444                    _ => {
2445                        return Err(ParseError::at(
2446                            format!(
2447                                "expected LIKE/GLOB/MATCH/REGEXP/BETWEEN/IN after NOT, got {:?}",
2448                                next.kind
2449                            ),
2450                            Some(&next),
2451                        ));
2452                    }
2453                }
2454            }
2455            other => {
2456                return Err(ParseError::at(
2457                    format!("unexpected infix token: {other:?}"),
2458                    Some(&token),
2459                ));
2460            }
2461        }
2462        Ok(())
2463    }
2464
2465    fn start_in(&mut self, lhs: ParsedExpr, not: bool, outer_min_bp: u8) -> Result<(), ParseError> {
2466        let start = lhs.expr.span();
2467        if !self.parser.eat_kind(&TokenKind::LeftParen) {
2468            let table = self.parser.parse_qualified_name()?;
2469            let end = self.parser.tokens[self.parser.pos.saturating_sub(1)].span;
2470            let height = lhs.height;
2471            let has_function = lhs.has_function;
2472            let parsed = self.parser.checked_expr(
2473                Expr::In {
2474                    expr: Box::new(lhs.expr),
2475                    set: InSet::Table(table),
2476                    not,
2477                    span: start.merge(end),
2478                },
2479                height,
2480                false,
2481                has_function,
2482            )?;
2483            let parsed = if not {
2484                self.parser.add_cached_parent(parsed)?
2485            } else {
2486                parsed
2487            };
2488            self.push_expr_tail(parsed, outer_min_bp);
2489        } else if matches!(
2490            self.parser.peek_kind(),
2491            TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
2492        ) {
2493            self.controls.push(ParseControl::InSelectDone {
2494                outer_min_bp,
2495                lhs,
2496                not,
2497                start,
2498            });
2499            self.controls.push(ParseControl::SubqueryStart);
2500        } else if self.parser.at_kind(&TokenKind::RightParen) {
2501            let end = self.parser.expect_kind(&TokenKind::RightParen)?;
2502            self.finish_in_list(outer_min_bp, lhs, not, Vec::new(), start.merge(end))?;
2503        } else {
2504            self.controls.push(ParseControl::InItemDone {
2505                outer_min_bp,
2506                lhs,
2507                not,
2508                items: Vec::new(),
2509                start,
2510            });
2511            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2512        }
2513        Ok(())
2514    }
2515
2516    fn finish_case(
2517        &mut self,
2518        outer_min_bp: u8,
2519        build: CaseBuild,
2520        else_expr: Option<ParsedExpr>,
2521    ) -> Result<(), ParseError> {
2522        if !self.parser.eat_kind(&TokenKind::KwEnd) {
2523            return Err(self.parser.err_here("expected END for CASE expression"));
2524        }
2525        let end = self.parser.tokens[self.parser.pos.saturating_sub(1)].span;
2526        let mut height = build.operand.as_ref().map_or(0, |expr| expr.height);
2527        let mut is_constant = build.operand.as_ref().is_none_or(|expr| expr.is_constant);
2528        let mut has_function = build.operand.as_ref().is_some_and(|expr| expr.has_function);
2529        for (condition, result) in &build.whens {
2530            height = height.max(condition.height).max(result.height);
2531            is_constant &= condition.is_constant && result.is_constant;
2532            has_function |= condition.has_function || result.has_function;
2533        }
2534        if let Some(expr) = &else_expr {
2535            height = height.max(expr.height);
2536            is_constant &= expr.is_constant;
2537            has_function |= expr.has_function;
2538        }
2539        let parsed = self.parser.checked_expr(
2540            Expr::Case {
2541                operand: build.operand.map(|expr| Box::new(expr.expr)),
2542                whens: build
2543                    .whens
2544                    .into_iter()
2545                    .map(|(condition, result)| (condition.expr, result.expr))
2546                    .collect(),
2547                else_expr: else_expr.map(|expr| Box::new(expr.expr)),
2548                span: build.start.merge(end),
2549            },
2550            height,
2551            is_constant,
2552            has_function,
2553        )?;
2554        self.push_expr_tail(parsed, outer_min_bp);
2555        Ok(())
2556    }
2557
2558    fn finish_like(
2559        &mut self,
2560        outer_min_bp: u8,
2561        lhs: ParsedExpr,
2562        pattern: ParsedExpr,
2563        escape: Option<ParsedExpr>,
2564        op: LikeOp,
2565        not: bool,
2566    ) -> Result<(), ParseError> {
2567        let end = escape
2568            .as_ref()
2569            .map_or_else(|| pattern.expr.span(), |expr| expr.expr.span());
2570        let height = escape.as_ref().map_or_else(
2571            || lhs.height.max(pattern.height),
2572            |expr| lhs.height.max(pattern.height).max(expr.height),
2573        );
2574        let span = lhs.expr.span().merge(end);
2575        let parsed = self.parser.checked_expr(
2576            Expr::Like {
2577                expr: Box::new(lhs.expr),
2578                pattern: Box::new(pattern.expr),
2579                escape: escape.map(|expr| Box::new(expr.expr)),
2580                op,
2581                not,
2582                span,
2583            },
2584            height,
2585            false,
2586            true,
2587        )?;
2588        let parsed = if not {
2589            self.parser.add_cached_parent(parsed)?
2590        } else {
2591            parsed
2592        };
2593        self.push_expr_tail(parsed, outer_min_bp);
2594        Ok(())
2595    }
2596
2597    fn finish_in_list(
2598        &mut self,
2599        outer_min_bp: u8,
2600        lhs: ParsedExpr,
2601        not: bool,
2602        items: Vec<ParsedExpr>,
2603        span: Span,
2604    ) -> Result<(), ParseError> {
2605        if let Some(message) = vector_in_list_arity_error(&lhs.expr, &items) {
2606            return Err(self.parser.err_here(message));
2607        }
2608        let item_height = items.iter().map(|item| item.height).max().unwrap_or(0);
2609        let items_are_constant = items.iter().all(|item| item.is_constant);
2610        let item_has_function = items.iter().any(|item| item.has_function);
2611        let singleton_constant = matches!(items.as_slice(), [item] if item.is_constant)
2612            && lhs.root != CachedRoot::Vector;
2613        let singleton_subquery =
2614            matches!(items.as_slice(), [item] if item.root == CachedRoot::ScalarSubquery);
2615        let lhs_height = lhs.height;
2616        let lhs_is_constant = lhs.is_constant;
2617        let lhs_has_function = lhs.has_function;
2618        let expr = Expr::In {
2619            expr: Box::new(lhs.expr),
2620            set: InSet::List(items.into_iter().map(|item| item.expr).collect()),
2621            not,
2622            span,
2623        };
2624        let parsed = if item_height == 0 {
2625            if lhs_has_function {
2626                self.parser
2627                    .finish_expr(expr, lhs_height.saturating_add(1), false, true)?
2628            } else {
2629                self.parser.finish_expr(expr, 1, true, false)?
2630            }
2631        } else {
2632            let cached_child_height = if singleton_constant {
2633                lhs_height.max(item_height.saturating_add(1))
2634            } else if singleton_subquery {
2635                lhs_height.max(item_height.saturating_sub(1))
2636            } else {
2637                lhs_height.max(item_height)
2638            };
2639            let parsed = self.parser.checked_expr(
2640                expr,
2641                cached_child_height,
2642                lhs_is_constant && items_are_constant,
2643                lhs_has_function || item_has_function,
2644            )?;
2645            if not {
2646                self.parser.add_cached_parent(parsed)?
2647            } else {
2648                parsed
2649            }
2650        };
2651        self.push_expr_tail(parsed, outer_min_bp);
2652        Ok(())
2653    }
2654
2655    fn window_start(&mut self) -> Result<(), ParseError> {
2656        let has_base_window = starts_window_base_name(self.parser.peek_kind());
2657        let build = WindowBuild {
2658            base_window: if has_base_window {
2659                Some(self.parser.parse_window_name()?)
2660            } else {
2661                None
2662            },
2663            partition_by: Vec::new(),
2664            order_by: Vec::new(),
2665        };
2666        if self.parser.eat_kind(&TokenKind::KwPartition) {
2667            self.parser.expect_kw(&TokenKind::KwBy)?;
2668            self.controls
2669                .push(ParseControl::WindowPartitionDone { build });
2670            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2671        } else {
2672            self.controls.push(ParseControl::WindowOrderStart { build });
2673        }
2674        Ok(())
2675    }
2676
2677    fn window_frame_start(&mut self, build: WindowBuild) {
2678        let frame_type = if self.parser.eat_kind(&TokenKind::KwRows) {
2679            Some(FrameType::Rows)
2680        } else if self.parser.eat_kind(&TokenKind::KwRange) {
2681            Some(FrameType::Range)
2682        } else if self.parser.eat_kind(&TokenKind::KwGroups) {
2683            Some(FrameType::Groups)
2684        } else {
2685            None
2686        };
2687        let Some(frame_type) = frame_type else {
2688            self.values.push(MachineValue::Window(WindowSpec {
2689                window_ref: build.base_window.map(WindowReference::Base),
2690                partition_by: build.partition_by,
2691                order_by: build.order_by,
2692                frame: None,
2693            }));
2694            return;
2695        };
2696        let between = self.parser.eat_kind(&TokenKind::KwBetween);
2697        self.controls.push(ParseControl::WindowFirstBoundDone {
2698            build,
2699            frame_type,
2700            between,
2701        });
2702        self.controls.push(ParseControl::FrameBoundStart);
2703    }
2704
2705    fn frame_bound_start(&mut self) -> Result<(), ParseError> {
2706        let origin = self
2707            .parser
2708            .peek_token()
2709            .cloned()
2710            .ok_or_else(|| self.parser.err_here("expected window frame bound"))?;
2711        if self.parser.eat_kind(&TokenKind::KwUnbounded) {
2712            let bound = if self.parser.eat_kind(&TokenKind::KwPreceding) {
2713                FrameBound::UnboundedPreceding
2714            } else {
2715                self.parser.expect_kw(&TokenKind::KwFollowing)?;
2716                FrameBound::UnboundedFollowing
2717            };
2718            self.values.push(MachineValue::FrameBound(ParsedFrameBound {
2719                value: bound,
2720                origin,
2721            }));
2722        } else if matches!(
2723            self.parser.peek_kind(),
2724            TokenKind::Id(value) if value.eq_ignore_ascii_case("CURRENT")
2725        ) {
2726            self.parser.advance_token();
2727            self.parser.expect_kw(&TokenKind::KwRow)?;
2728            self.values.push(MachineValue::FrameBound(ParsedFrameBound {
2729                value: FrameBound::CurrentRow,
2730                origin,
2731            }));
2732        } else {
2733            self.controls
2734                .push(ParseControl::FrameBoundExprDone { origin });
2735            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2736        }
2737        Ok(())
2738    }
2739
2740    fn finish_frame(
2741        &mut self,
2742        frame_type: FrameType,
2743        start: FrameBound,
2744        end: Option<FrameBound>,
2745    ) -> Result<FrameSpec, ParseError> {
2746        let exclude = if self.parser.eat_kind(&TokenKind::KwExclude) {
2747            if self.parser.eat_kind(&TokenKind::KwNo) {
2748                let others = self.parser.parse_identifier()?;
2749                if !others.eq_ignore_ascii_case("OTHERS") {
2750                    return Err(self.parser.err_here("expected OTHERS"));
2751                }
2752                Some(FrameExclude::NoOthers)
2753            } else if self.parser.eat_kind(&TokenKind::KwTies) {
2754                Some(FrameExclude::Ties)
2755            } else if self.parser.eat_kind(&TokenKind::KwGroup) {
2756                Some(FrameExclude::Group)
2757            } else if matches!(
2758                self.parser.peek_kind(),
2759                TokenKind::Id(value) if value.eq_ignore_ascii_case("CURRENT")
2760            ) {
2761                self.parser.advance_token();
2762                self.parser.expect_kw(&TokenKind::KwRow)?;
2763                Some(FrameExclude::CurrentRow)
2764            } else {
2765                return Err(self
2766                    .parser
2767                    .err_here("expected NO OTHERS, TIES, GROUP, or CURRENT ROW after EXCLUDE"));
2768            }
2769        } else {
2770            None
2771        };
2772        Ok(FrameSpec {
2773            frame_type,
2774            start,
2775            end,
2776            exclude,
2777        })
2778    }
2779
2780    fn step_select(&mut self, control: ParseControl) -> Result<(), ParseError> {
2781        match control {
2782            ParseControl::SelectStart { with } => {
2783                self.controls
2784                    .push(ParseControl::SelectFirstCoreDone { with });
2785                self.controls.push(ParseControl::CoreStart);
2786                Ok(())
2787            }
2788            ParseControl::SelectFirstCoreDone { with } => {
2789                let core = self.pop_core()?;
2790                self.continue_select_body(SelectBuild {
2791                    with,
2792                    height: core.height,
2793                    first: core.value,
2794                    compounds: Vec::new(),
2795                    order_by: Vec::new(),
2796                });
2797                Ok(())
2798            }
2799            ParseControl::SelectCompoundDone { mut build, op } => {
2800                let core = self.pop_core()?;
2801                build.height = build.height.max(core.height);
2802                build.compounds.push((op, core.value));
2803                self.continue_select_body(build);
2804                Ok(())
2805            }
2806            ParseControl::SelectOrderStart { build } => {
2807                let final_core = build
2808                    .compounds
2809                    .last()
2810                    .map_or(&build.first, |(_, core)| core);
2811                if matches!(final_core, SelectCore::Values(_))
2812                    && matches!(
2813                        self.parser.peek_kind(),
2814                        TokenKind::KwOrder | TokenKind::KwLimit
2815                    )
2816                {
2817                    return Err(self
2818                        .parser
2819                        .err_here("ORDER BY / LIMIT clause is not allowed after a VALUES term"));
2820                }
2821                if self.parser.eat_kind(&TokenKind::KwOrder) {
2822                    self.parser.expect_kw(&TokenKind::KwBy)?;
2823                    self.controls.push(ParseControl::SelectOrderDone { build });
2824                    self.controls.push(ParseControl::OrderingStart);
2825                } else {
2826                    self.start_select_limit(build)?;
2827                }
2828                Ok(())
2829            }
2830            ParseControl::SelectOrderDone { mut build } => {
2831                let term = self.pop_ordering()?;
2832                build.height = build.height.max(term.height);
2833                build.order_by.push(term.value);
2834                if self.parser.eat_kind(&TokenKind::Comma) {
2835                    self.controls.push(ParseControl::SelectOrderDone { build });
2836                    self.controls.push(ParseControl::OrderingStart);
2837                } else {
2838                    self.start_select_limit(build)?;
2839                }
2840                Ok(())
2841            }
2842            ParseControl::SelectLimitFirstDone { build } => {
2843                let first = self.pop_expr()?;
2844                if self.parser.eat_kind(&TokenKind::KwOffset) {
2845                    self.controls.push(ParseControl::SelectLimitSecondDone {
2846                        build,
2847                        first,
2848                        comma_form: false,
2849                    });
2850                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2851                } else if self.parser.eat_kind(&TokenKind::Comma) {
2852                    self.controls.push(ParseControl::SelectLimitSecondDone {
2853                        build,
2854                        first,
2855                        comma_form: true,
2856                    });
2857                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2858                } else {
2859                    let height = self.parser.checked_cached_parent_height(first.height)?;
2860                    self.finish_select(
2861                        build,
2862                        HeightTracked {
2863                            value: Some(LimitClause {
2864                                limit: first.expr,
2865                                offset: None,
2866                            }),
2867                            height,
2868                        },
2869                    )?;
2870                }
2871                Ok(())
2872            }
2873            ParseControl::SelectLimitSecondDone {
2874                build,
2875                first,
2876                comma_form,
2877            } => {
2878                let second = self.pop_expr()?;
2879                let height = self
2880                    .parser
2881                    .checked_cached_parent_height(first.height.max(second.height))?;
2882                let limit = if comma_form {
2883                    LimitClause {
2884                        limit: second.expr,
2885                        offset: Some(first.expr),
2886                    }
2887                } else {
2888                    LimitClause {
2889                        limit: first.expr,
2890                        offset: Some(second.expr),
2891                    }
2892                };
2893                self.finish_select(
2894                    build,
2895                    HeightTracked {
2896                        value: Some(limit),
2897                        height,
2898                    },
2899                )
2900            }
2901            ParseControl::CoreStart => self.core_start(),
2902            ParseControl::CoreColumnStart { build } => self.core_column_start(build),
2903            ParseControl::CoreColumnDone { mut build } => {
2904                let expr = self.pop_expr()?;
2905                build.height = build.height.max(expr.height);
2906                build.columns.push(ResultColumn::Expr {
2907                    expr: expr.expr,
2908                    alias: self.parser.try_result_alias()?,
2909                });
2910                if self.parser.eat_kind(&TokenKind::Comma) {
2911                    self.controls.push(ParseControl::CoreColumnStart { build });
2912                } else {
2913                    self.controls.push(ParseControl::CoreAfterColumns { build });
2914                }
2915                Ok(())
2916            }
2917            ParseControl::CoreAfterColumns { build } => {
2918                if self.parser.eat_kind(&TokenKind::KwFrom) {
2919                    self.controls.push(ParseControl::CoreFromDone { build });
2920                    self.controls.push(ParseControl::FromStart);
2921                } else {
2922                    self.continue_core_where(build)?;
2923                }
2924                Ok(())
2925            }
2926            ParseControl::CoreFromDone { mut build } => {
2927                build.from = Some(self.pop_from()?);
2928                self.continue_core_where(build)
2929            }
2930            ParseControl::CoreWhereDone { mut build } => {
2931                let expr = self.pop_expr()?;
2932                build.height = build.height.max(expr.height);
2933                build.where_clause = Some(Box::new(expr.expr));
2934                self.continue_core_group(build)
2935            }
2936            ParseControl::CoreGroupDone { mut build } => {
2937                let expr = self.pop_expr()?;
2938                build.height = build.height.max(expr.height);
2939                build.group_by.push(expr.expr);
2940                if self.parser.eat_kind(&TokenKind::Comma) {
2941                    self.controls.push(ParseControl::CoreGroupDone { build });
2942                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2943                } else {
2944                    self.continue_core_having(build);
2945                }
2946                Ok(())
2947            }
2948            ParseControl::CoreHavingDone { mut build } => {
2949                let expr = self.pop_expr()?;
2950                build.height = build.height.max(expr.height);
2951                build.having = Some(Box::new(expr.expr));
2952                self.continue_core_windows(build);
2953                Ok(())
2954            }
2955            ParseControl::CoreWindowStart { build } => {
2956                let name = self.parser.parse_window_name()?;
2957                self.parser.expect_kw(&TokenKind::KwAs)?;
2958                self.parser.expect_token(&TokenKind::LeftParen)?;
2959                self.controls
2960                    .push(ParseControl::CoreWindowDone { build, name });
2961                self.controls.push(ParseControl::WindowStart);
2962                Ok(())
2963            }
2964            ParseControl::CoreWindowDone { mut build, name } => {
2965                let spec = self.pop_window()?;
2966                self.parser.expect_token(&TokenKind::RightParen)?;
2967                build.windows.push(WindowDef { name, spec });
2968                if self.parser.eat_kind(&TokenKind::Comma) {
2969                    self.controls.push(ParseControl::CoreWindowStart { build });
2970                } else {
2971                    self.finish_core(build);
2972                }
2973                Ok(())
2974            }
2975            ParseControl::ValuesRowStart {
2976                rows,
2977                height,
2978                force_union_all_from,
2979            } => {
2980                self.parser.expect_token(&TokenKind::LeftParen)?;
2981                self.controls.push(ParseControl::ValuesItemDone {
2982                    rows,
2983                    row: Vec::new(),
2984                    height,
2985                    force_union_all_from,
2986                });
2987                self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2988                Ok(())
2989            }
2990            ParseControl::ValuesItemDone {
2991                mut rows,
2992                mut row,
2993                mut height,
2994                mut force_union_all_from,
2995            } => {
2996                let expr = self.pop_expr()?;
2997                height = height.max(expr.height);
2998                row.push(expr.expr);
2999                if self.parser.eat_kind(&TokenKind::Comma) {
3000                    self.controls.push(ParseControl::ValuesItemDone {
3001                        rows,
3002                        row,
3003                        height,
3004                        force_union_all_from,
3005                    });
3006                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3007                } else {
3008                    self.parser.expect_token(&TokenKind::RightParen)?;
3009                    if force_union_all_from.is_none() && self.parser.has_with {
3010                        force_union_all_from = Some(rows.len());
3011                    }
3012                    rows.push(row);
3013                    if self.parser.eat_kind(&TokenKind::Comma) {
3014                        self.controls.push(ParseControl::ValuesRowStart {
3015                            rows,
3016                            height,
3017                            force_union_all_from,
3018                        });
3019                    } else {
3020                        self.values.push(MachineValue::Core(HeightTracked {
3021                            value: SelectCore::Values(ValuesClause::parsed(
3022                                rows,
3023                                force_union_all_from,
3024                            )),
3025                            height,
3026                        }));
3027                    }
3028                }
3029                Ok(())
3030            }
3031            ParseControl::FromStart => {
3032                self.controls.push(ParseControl::FromSourceDone);
3033                self.controls.push(ParseControl::TableStart);
3034                Ok(())
3035            }
3036            ParseControl::FromSourceDone => {
3037                let source = self.pop_table()?;
3038                self.continue_from(FromBuild {
3039                    source,
3040                    joins: Vec::new(),
3041                })
3042            }
3043            ParseControl::FromTableDone { build, join_type } => {
3044                let table = self.pop_table()?;
3045                if self.parser.eat_kind(&TokenKind::KwOn) {
3046                    self.controls.push(ParseControl::FromJoinConstraintDone {
3047                        build,
3048                        join_type,
3049                        table,
3050                    });
3051                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3052                    return Ok(());
3053                }
3054                let constraint = if self.parser.eat_kind(&TokenKind::KwUsing) {
3055                    self.parser.expect_token(&TokenKind::LeftParen)?;
3056                    let mut columns = vec![self.parser.parse_identifier()?];
3057                    while self.parser.eat_kind(&TokenKind::Comma) {
3058                        columns.push(self.parser.parse_identifier()?);
3059                    }
3060                    self.parser.expect_token(&TokenKind::RightParen)?;
3061                    Some(JoinConstraint::Using(columns))
3062                } else {
3063                    None
3064                };
3065                self.append_join(build, join_type, table, constraint)
3066            }
3067            ParseControl::FromJoinConstraintDone {
3068                build,
3069                join_type,
3070                table,
3071            } => {
3072                let expr = self.pop_expr()?;
3073                self.append_join(build, join_type, table, Some(JoinConstraint::On(expr.expr)))
3074            }
3075            ParseControl::TableStart => self.table_start(),
3076            ParseControl::TableSubqueryDone => {
3077                let select = self.pop_select()?;
3078                self.parser.expect_token(&TokenKind::RightParen)?;
3079                self.values
3080                    .push(MachineValue::Table(TableOrSubquery::Subquery {
3081                        query: Box::new(select.value),
3082                        alias: self.parser.try_table_alias()?,
3083                    }));
3084                Ok(())
3085            }
3086            ParseControl::TableParenJoinDone => {
3087                let from = self.pop_from()?;
3088                self.parser.expect_token(&TokenKind::RightParen)?;
3089                self.values
3090                    .push(MachineValue::Table(TableOrSubquery::ParenJoin(Box::new(
3091                        from,
3092                    ))));
3093                Ok(())
3094            }
3095            ParseControl::TableFunctionArgDone { name, mut args } => {
3096                args.push(self.pop_expr()?.expr);
3097                if self.parser.eat_kind(&TokenKind::Comma) {
3098                    self.controls
3099                        .push(ParseControl::TableFunctionArgDone { name, args });
3100                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3101                } else {
3102                    self.parser.expect_token(&TokenKind::RightParen)?;
3103                    self.values
3104                        .push(MachineValue::Table(TableOrSubquery::TableFunction {
3105                            name,
3106                            args,
3107                            alias: self.parser.try_table_alias()?,
3108                        }));
3109                }
3110                Ok(())
3111            }
3112            ParseControl::WithStart => self.with_start(),
3113            ParseControl::CteQueryDone {
3114                recursive,
3115                mut ctes,
3116                name,
3117                columns,
3118                materialized,
3119            } => {
3120                let query = self.pop_select()?;
3121                self.parser.expect_token(&TokenKind::RightParen)?;
3122                ctes.push(Cte {
3123                    name,
3124                    columns,
3125                    materialized,
3126                    query: query.value,
3127                });
3128                if self.parser.eat_kind(&TokenKind::Comma) {
3129                    self.start_cte(recursive, ctes)?;
3130                } else {
3131                    self.values
3132                        .push(MachineValue::With(WithClause { recursive, ctes }));
3133                }
3134                Ok(())
3135            }
3136            _ => Err(self
3137                .parser
3138                .err_here("internal expression parser control reached SELECT dispatcher")),
3139        }
3140    }
3141
3142    fn continue_select_body(&mut self, build: SelectBuild) {
3143        let op = if self.parser.eat_kind(&TokenKind::KwUnion) {
3144            Some(if self.parser.eat_kind(&TokenKind::KwAll) {
3145                CompoundOp::UnionAll
3146            } else {
3147                CompoundOp::Union
3148            })
3149        } else if self.parser.eat_kind(&TokenKind::KwIntersect) {
3150            Some(CompoundOp::Intersect)
3151        } else if self.parser.eat_kind(&TokenKind::KwExcept) {
3152            Some(CompoundOp::Except)
3153        } else {
3154            None
3155        };
3156        if let Some(op) = op {
3157            self.controls
3158                .push(ParseControl::SelectCompoundDone { build, op });
3159            self.controls.push(ParseControl::CoreStart);
3160        } else {
3161            self.controls.push(ParseControl::SelectOrderStart { build });
3162        }
3163    }
3164
3165    fn start_select_limit(&mut self, build: SelectBuild) -> Result<(), ParseError> {
3166        if self.parser.eat_kind(&TokenKind::KwLimit) {
3167            self.controls
3168                .push(ParseControl::SelectLimitFirstDone { build });
3169            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3170        } else {
3171            self.finish_select(
3172                build,
3173                HeightTracked {
3174                    value: None,
3175                    height: 0,
3176                },
3177            )?;
3178        }
3179        Ok(())
3180    }
3181
3182    fn finish_select(
3183        &mut self,
3184        mut build: SelectBuild,
3185        limit: HeightTracked<Option<LimitClause>>,
3186    ) -> Result<(), ParseError> {
3187        build.height = build.height.max(limit.height);
3188        let final_core = build
3189            .compounds
3190            .last()
3191            .map_or(&build.first, |(_, core)| core);
3192        if matches!(final_core, SelectCore::Values(_))
3193            && (!build.order_by.is_empty() || limit.value.is_some())
3194        {
3195            return Err(self
3196                .parser
3197                .err_here("ORDER BY / LIMIT clause is not allowed after a VALUES term"));
3198        }
3199        self.values.push(MachineValue::Select(HeightTracked {
3200            value: SelectStatement {
3201                with: build.with,
3202                body: SelectBody {
3203                    select: build.first,
3204                    compounds: build.compounds,
3205                },
3206                order_by: build.order_by,
3207                limit: limit.value,
3208            },
3209            height: build.height,
3210        }));
3211        Ok(())
3212    }
3213
3214    fn core_start(&mut self) -> Result<(), ParseError> {
3215        if self.parser.eat_kind(&TokenKind::KwValues) {
3216            self.controls.push(ParseControl::ValuesRowStart {
3217                rows: Vec::new(),
3218                height: 0,
3219                force_union_all_from: None,
3220            });
3221            return Ok(());
3222        }
3223        self.parser.expect_kw(&TokenKind::KwSelect)?;
3224        let distinct = if self.parser.eat_kind(&TokenKind::KwDistinct) {
3225            Distinctness::Distinct
3226        } else {
3227            let _ = self.parser.eat_kind(&TokenKind::KwAll);
3228            Distinctness::All
3229        };
3230        self.controls.push(ParseControl::CoreColumnStart {
3231            build: CoreBuild {
3232                distinct,
3233                columns: Vec::new(),
3234                height: 0,
3235                from: None,
3236                where_clause: None,
3237                group_by: Vec::new(),
3238                having: None,
3239                windows: Vec::new(),
3240            },
3241        });
3242        Ok(())
3243    }
3244
3245    fn core_column_start(&mut self, mut build: CoreBuild) -> Result<(), ParseError> {
3246        if self.parser.eat_kind(&TokenKind::Star) {
3247            build.columns.push(ResultColumn::Star);
3248            if self.parser.eat_kind(&TokenKind::Comma) {
3249                self.controls.push(ParseControl::CoreColumnStart { build });
3250            } else {
3251                self.controls.push(ParseControl::CoreAfterColumns { build });
3252            }
3253            return Ok(());
3254        }
3255        if starts_table_star_qualifier(self.parser.peek_kind())
3256            && self
3257                .parser
3258                .tokens
3259                .get(self.parser.pos + 1)
3260                .is_some_and(|token| token.kind == TokenKind::Dot)
3261        {
3262            let table_star = self
3263                .parser
3264                .tokens
3265                .get(self.parser.pos + 2)
3266                .is_some_and(|token| token.kind == TokenKind::Star);
3267            let schema_table_star = self
3268                .parser
3269                .tokens
3270                .get(self.parser.pos + 2)
3271                .is_some_and(|token| starts_table_star_qualifier(&token.kind))
3272                && self
3273                    .parser
3274                    .tokens
3275                    .get(self.parser.pos + 3)
3276                    .is_some_and(|token| token.kind == TokenKind::Dot)
3277                && self
3278                    .parser
3279                    .tokens
3280                    .get(self.parser.pos + 4)
3281                    .is_some_and(|token| token.kind == TokenKind::Star);
3282            if table_star || schema_table_star {
3283                let first = self.parser.parse_table_star_qualifier()?;
3284                self.parser.expect_token(&TokenKind::Dot)?;
3285                let name = if schema_table_star {
3286                    let second = self.parser.parse_table_star_qualifier()?;
3287                    self.parser.expect_token(&TokenKind::Dot)?;
3288                    QualifiedName::qualified(first, second)
3289                } else {
3290                    QualifiedName::bare(first)
3291                };
3292                self.parser.expect_token(&TokenKind::Star)?;
3293                build.columns.push(ResultColumn::TableStar(name));
3294                if self.parser.eat_kind(&TokenKind::Comma) {
3295                    self.controls.push(ParseControl::CoreColumnStart { build });
3296                } else {
3297                    self.controls.push(ParseControl::CoreAfterColumns { build });
3298                }
3299                return Ok(());
3300            }
3301        }
3302        self.controls.push(ParseControl::CoreColumnDone { build });
3303        self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3304        Ok(())
3305    }
3306
3307    fn continue_core_where(&mut self, build: CoreBuild) -> Result<(), ParseError> {
3308        if self.parser.eat_kind(&TokenKind::KwWhere) {
3309            self.controls.push(ParseControl::CoreWhereDone { build });
3310            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3311        } else {
3312            self.continue_core_group(build)?;
3313        }
3314        Ok(())
3315    }
3316
3317    fn continue_core_group(&mut self, build: CoreBuild) -> Result<(), ParseError> {
3318        if self.parser.eat_kind(&TokenKind::KwGroup) {
3319            self.parser.expect_kw(&TokenKind::KwBy)?;
3320            self.controls.push(ParseControl::CoreGroupDone { build });
3321            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3322        } else {
3323            self.continue_core_having(build);
3324        }
3325        Ok(())
3326    }
3327
3328    fn continue_core_having(&mut self, build: CoreBuild) {
3329        if self.parser.eat_kind(&TokenKind::KwHaving) {
3330            self.controls.push(ParseControl::CoreHavingDone { build });
3331            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3332        } else {
3333            self.continue_core_windows(build);
3334        }
3335    }
3336
3337    fn continue_core_windows(&mut self, build: CoreBuild) {
3338        if self.parser.eat_kind(&TokenKind::KwWindow) {
3339            self.controls.push(ParseControl::CoreWindowStart { build });
3340        } else {
3341            self.finish_core(build);
3342        }
3343    }
3344
3345    fn finish_core(&mut self, build: CoreBuild) {
3346        self.values.push(MachineValue::Core(HeightTracked {
3347            value: SelectCore::Select {
3348                distinct: build.distinct,
3349                columns: build.columns,
3350                from: build.from,
3351                where_clause: build.where_clause,
3352                group_by: build.group_by,
3353                having: build.having,
3354                windows: build.windows,
3355            },
3356            height: build.height,
3357        }));
3358    }
3359
3360    fn continue_from(&mut self, build: FromBuild) -> Result<(), ParseError> {
3361        let join_type = if let Some(join_type) = self.parser.try_join_type()? {
3362            Some(join_type)
3363        } else if self.parser.eat_kind(&TokenKind::Comma) {
3364            Some(JoinType {
3365                natural: false,
3366                kind: JoinKind::Cross,
3367            })
3368        } else {
3369            None
3370        };
3371        if let Some(join_type) = join_type {
3372            self.controls
3373                .push(ParseControl::FromTableDone { build, join_type });
3374            self.controls.push(ParseControl::TableStart);
3375        } else {
3376            self.values.push(MachineValue::From(FromClause {
3377                source: build.source,
3378                joins: build.joins,
3379            }));
3380        }
3381        Ok(())
3382    }
3383
3384    fn append_join(
3385        &mut self,
3386        mut build: FromBuild,
3387        join_type: JoinType,
3388        table: TableOrSubquery,
3389        constraint: Option<JoinConstraint>,
3390    ) -> Result<(), ParseError> {
3391        if join_type.natural && constraint.is_some() {
3392            return Err(self
3393                .parser
3394                .err_here("a NATURAL join may not have an ON or USING clause"));
3395        }
3396        build.joins.push(JoinClause {
3397            join_type,
3398            table,
3399            constraint,
3400        });
3401        self.continue_from(build)
3402    }
3403
3404    fn table_start(&mut self) -> Result<(), ParseError> {
3405        if self.parser.eat_kind(&TokenKind::LeftParen) {
3406            if matches!(
3407                self.parser.peek_kind(),
3408                TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
3409            ) {
3410                self.controls.push(ParseControl::TableSubqueryDone);
3411                self.controls.push(ParseControl::SubqueryStart);
3412            } else {
3413                self.controls.push(ParseControl::TableParenJoinDone);
3414                self.controls.push(ParseControl::FromStart);
3415            }
3416            return Ok(());
3417        }
3418        let name = self.parser.parse_qualified_name()?;
3419        if name.schema.is_none() && self.parser.eat_kind(&TokenKind::LeftParen) {
3420            if self.parser.eat_kind(&TokenKind::RightParen) {
3421                self.values
3422                    .push(MachineValue::Table(TableOrSubquery::TableFunction {
3423                        name: name.name,
3424                        args: Vec::new(),
3425                        alias: self.parser.try_table_alias()?,
3426                    }));
3427            } else {
3428                self.controls.push(ParseControl::TableFunctionArgDone {
3429                    name: name.name,
3430                    args: Vec::new(),
3431                });
3432                self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3433            }
3434            return Ok(());
3435        }
3436        self.values
3437            .push(MachineValue::Table(TableOrSubquery::Table {
3438                name,
3439                alias: self.parser.try_table_alias()?,
3440                index_hint: self.parser.parse_index_hint()?,
3441                time_travel: self.parser.parse_time_travel_clause()?,
3442            }));
3443        Ok(())
3444    }
3445
3446    fn with_start(&mut self) -> Result<(), ParseError> {
3447        self.parser.expect_kw(&TokenKind::KwWith)?;
3448        self.parser.has_with = true;
3449        let recursive = self.parser.eat_kind(&TokenKind::KwRecursive);
3450        self.start_cte(recursive, Vec::new())
3451    }
3452
3453    fn start_cte(&mut self, recursive: bool, ctes: Vec<Cte>) -> Result<(), ParseError> {
3454        let name = self.parser.parse_identifier()?;
3455        let columns = if self.parser.eat_kind(&TokenKind::LeftParen) {
3456            let mut columns = vec![self.parser.parse_identifier()?];
3457            while self.parser.eat_kind(&TokenKind::Comma) {
3458                columns.push(self.parser.parse_identifier()?);
3459            }
3460            self.parser.expect_token(&TokenKind::RightParen)?;
3461            columns
3462        } else {
3463            Vec::new()
3464        };
3465        self.parser.expect_kw(&TokenKind::KwAs)?;
3466        let materialized = if self.parser.eat_kind(&TokenKind::KwNot) {
3467            self.parser.expect_kw(&TokenKind::KwMaterialized)?;
3468            Some(CteMaterialized::NotMaterialized)
3469        } else if self.parser.eat_kind(&TokenKind::KwMaterialized) {
3470            Some(CteMaterialized::Materialized)
3471        } else {
3472            None
3473        };
3474        self.parser.expect_token(&TokenKind::LeftParen)?;
3475        self.controls.push(ParseControl::CteQueryDone {
3476            recursive,
3477            ctes,
3478            name,
3479            columns,
3480            materialized,
3481        });
3482        self.controls.push(ParseControl::SubqueryStart);
3483        Ok(())
3484    }
3485}
3486
3487impl Parser {
3488    /// Parse a single SQL expression.
3489    pub fn parse_expr(&mut self) -> Result<Expr, ParseError> {
3490        self.parse_expr_tracked().map(|parsed| parsed.expr)
3491    }
3492
3493    pub(crate) fn parse_expr_tracked(&mut self) -> Result<ParsedExpr, ParseError> {
3494        ParseMachine::for_expr(self).run_expr()
3495    }
3496
3497    pub(crate) fn parse_select_tracked_machine(
3498        &mut self,
3499        with: Option<WithClause>,
3500    ) -> Result<HeightTracked<SelectStatement>, ParseError> {
3501        ParseMachine::for_select(self, with).run_select()
3502    }
3503
3504    pub(crate) fn parse_with_clause_machine(&mut self) -> Result<WithClause, ParseError> {
3505        ParseMachine::for_with(self).run_with()
3506    }
3507
3508    pub(crate) fn parse_from_clause_machine(&mut self) -> Result<FromClause, ParseError> {
3509        ParseMachine::for_from(self).run_from()
3510    }
3511
3512    fn finish_expr(
3513        &self,
3514        expr: Expr,
3515        height: u32,
3516        is_constant: bool,
3517        has_function: bool,
3518    ) -> Result<ParsedExpr, ParseError> {
3519        if height > MAX_PARSE_DEPTH {
3520            return Err(ParseError::expression_too_deep(
3521                MAX_PARSE_DEPTH,
3522                self.peek_token(),
3523            ));
3524        }
3525        let root = match &expr {
3526            Expr::UnaryOp {
3527                op: UnaryOp::Plus, ..
3528            } => CachedRoot::UnaryPlus,
3529            Expr::RowValue(..) => CachedRoot::Vector,
3530            Expr::Subquery(..) => CachedRoot::ScalarSubquery,
3531            _ => CachedRoot::Other,
3532        };
3533        Ok(ParsedExpr {
3534            expr,
3535            height,
3536            is_constant,
3537            has_function,
3538            root,
3539        })
3540    }
3541
3542    fn checked_expr(
3543        &self,
3544        expr: Expr,
3545        max_child_height: u32,
3546        is_constant: bool,
3547        has_function: bool,
3548    ) -> Result<ParsedExpr, ParseError> {
3549        let height = max_child_height.saturating_add(1);
3550        self.finish_expr(expr, height, is_constant, has_function)
3551    }
3552
3553    fn add_cached_parent(&self, mut parsed: ParsedExpr) -> Result<ParsedExpr, ParseError> {
3554        parsed.height = parsed.height.saturating_add(1);
3555        if parsed.height > MAX_PARSE_DEPTH {
3556            return Err(ParseError::expression_too_deep(
3557                MAX_PARSE_DEPTH,
3558                self.peek_token(),
3559            ));
3560        }
3561        parsed.root = CachedRoot::Other;
3562        Ok(parsed)
3563    }
3564
3565    fn finish_unary(
3566        &self,
3567        op: UnaryOp,
3568        mut inner: ParsedExpr,
3569        span: Span,
3570    ) -> Result<ParsedExpr, ParseError> {
3571        if matches!(op, UnaryOp::Plus | UnaryOp::Negate)
3572            && inner.root == CachedRoot::UnaryPlus
3573            && let Expr::UnaryOp {
3574                op: inner_op,
3575                span: inner_span,
3576                ..
3577            } = &mut inner.expr
3578        {
3579            *inner_op = op;
3580            *inner_span = span;
3581            inner.root = if op == UnaryOp::Plus {
3582                CachedRoot::UnaryPlus
3583            } else {
3584                CachedRoot::Other
3585            };
3586            return Ok(inner);
3587        }
3588
3589        let height = inner.height;
3590        let is_constant = inner.is_constant;
3591        let has_function = inner.has_function;
3592        self.checked_expr(
3593            Expr::UnaryOp {
3594                op,
3595                expr: Box::new(inner.expr),
3596                span,
3597            },
3598            height,
3599            is_constant,
3600            has_function,
3601        )
3602    }
3603
3604    // ── Pratt core ──────────────────────────────────────────────────────
3605
3606    #[cfg(test)]
3607    fn parse_expr_bp(&mut self, min_bp: u8) -> Result<ParsedExpr, ParseError> {
3608        self.with_recursion_guard(|p| p.parse_expr_bp_inner(min_bp))
3609    }
3610
3611    #[cfg(test)]
3612    fn parse_expr_bp_inner(&mut self, min_bp: u8) -> Result<ParsedExpr, ParseError> {
3613        let prefixes = self.collect_prefix_frames();
3614        let mut lhs = self.parse_prefix()?;
3615
3616        for prefix in prefixes.into_iter().rev() {
3617            match prefix {
3618                DeepExprFrame::Unary { op, span, right_bp } => {
3619                    lhs = self.parse_expr_tail(lhs, right_bp)?;
3620                    let span = span.merge(lhs.expr.span());
3621                    lhs = self.finish_unary(op, lhs, span)?;
3622                }
3623                DeepExprFrame::Parenthesis { span } => {
3624                    lhs = self.finish_parenthesized_frame(lhs, span)?;
3625                }
3626            }
3627        }
3628
3629        self.parse_expr_tail(lhs, min_bp)
3630    }
3631
3632    #[cfg(test)]
3633    fn parse_expr_tail(
3634        &mut self,
3635        mut lhs: ParsedExpr,
3636        min_bp: u8,
3637    ) -> Result<ParsedExpr, ParseError> {
3638        loop {
3639            // Postfix: COLLATE, ISNULL, NOTNULL
3640            if let Some(l_bp) = self.postfix_bp() {
3641                if l_bp < min_bp {
3642                    break;
3643                }
3644                lhs = self.parse_postfix(lhs)?;
3645                continue;
3646            }
3647
3648            // Infix: binary operators, IS, LIKE, BETWEEN, IN, etc.
3649            if let Some((l_bp, r_bp)) = self.infix_bp() {
3650                if l_bp < min_bp {
3651                    break;
3652                }
3653                lhs = self.parse_infix(lhs, r_bp)?;
3654                continue;
3655            }
3656
3657            break;
3658        }
3659
3660        Ok(lhs)
3661    }
3662
3663    #[cfg(test)]
3664    fn collect_prefix_frames(&mut self) -> Vec<DeepExprFrame> {
3665        let mut prefixes = Vec::new();
3666        loop {
3667            let unary = match self.peek_kind() {
3668                TokenKind::Minus => {
3669                    let folds_i64_min = matches!(
3670                        self.tokens.get(self.pos + 1).map(|token| &token.kind),
3671                        Some(TokenKind::OversizedInt(value)) if value == "9223372036854775808"
3672                    );
3673                    if folds_i64_min {
3674                        break;
3675                    }
3676                    Some((UnaryOp::Negate, bp::UNARY))
3677                }
3678                TokenKind::Plus => Some((UnaryOp::Plus, bp::UNARY)),
3679                TokenKind::Tilde => Some((UnaryOp::BitNot, bp::UNARY)),
3680                TokenKind::KwNot => {
3681                    if matches!(
3682                        self.tokens.get(self.pos + 1).map(|token| &token.kind),
3683                        Some(TokenKind::KwExists)
3684                    ) {
3685                        break;
3686                    }
3687                    Some((UnaryOp::Not, bp::NOT_PREFIX))
3688                }
3689                TokenKind::LeftParen => {
3690                    let starts_subquery = matches!(
3691                        self.tokens.get(self.pos + 1).map(|token| &token.kind),
3692                        Some(TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues)
3693                    );
3694                    if starts_subquery {
3695                        break;
3696                    }
3697                    let token = self.advance_token();
3698                    prefixes.push(DeepExprFrame::Parenthesis { span: token.span });
3699                    continue;
3700                }
3701                _ => break,
3702            };
3703            let Some((op, right_bp)) = unary else {
3704                break;
3705            };
3706            let token = self.advance_token();
3707            prefixes.push(DeepExprFrame::Unary {
3708                op,
3709                span: token.span,
3710                right_bp,
3711            });
3712        }
3713        prefixes
3714    }
3715
3716    #[cfg(test)]
3717    fn finish_parenthesized_frame(
3718        &mut self,
3719        mut first: ParsedExpr,
3720        start: Span,
3721    ) -> Result<ParsedExpr, ParseError> {
3722        first = self.parse_expr_tail(first, 0)?;
3723        if self.eat_kind(&TokenKind::Comma) {
3724            let mut is_constant = first.is_constant;
3725            let mut has_function = first.has_function;
3726            let mut exprs = vec![first.expr];
3727            loop {
3728                let parsed = self.parse_expr_bp(0)?;
3729                is_constant &= parsed.is_constant;
3730                has_function |= parsed.has_function;
3731                exprs.push(parsed.expr);
3732                if !self.eat_kind(&TokenKind::Comma) {
3733                    break;
3734                }
3735            }
3736            let end = self.expect_kind(&TokenKind::RightParen)?;
3737            return self.finish_expr(
3738                Expr::RowValue(exprs, start.merge(end)),
3739                1,
3740                is_constant,
3741                has_function,
3742            );
3743        }
3744        self.expect_kind(&TokenKind::RightParen)?;
3745        Ok(first)
3746    }
3747
3748    // ── Token helpers ───────────────────────────────────────────────────
3749
3750    fn peek_kind(&self) -> &TokenKind {
3751        self.tokens
3752            .get(self.pos)
3753            .map_or(&TokenKind::Eof, |t| &t.kind)
3754    }
3755
3756    fn peek_token(&self) -> Option<&Token> {
3757        self.tokens.get(self.pos)
3758    }
3759
3760    #[cfg(test)]
3761    fn peek_nth_token(&self, offset: usize) -> Option<&Token> {
3762        self.tokens.get(self.pos + offset)
3763    }
3764
3765    fn advance_token(&mut self) -> Token {
3766        let tok = self.tokens[self.pos].clone();
3767        if tok.kind != TokenKind::Eof {
3768            self.pos += 1;
3769        }
3770        tok
3771    }
3772
3773    fn at_kind(&self, kind: &TokenKind) -> bool {
3774        std::mem::discriminant(self.peek_kind()) == std::mem::discriminant(kind)
3775    }
3776
3777    fn eat_kind(&mut self, kind: &TokenKind) -> bool {
3778        if self.at_kind(kind) {
3779            self.advance_token();
3780            true
3781        } else {
3782            false
3783        }
3784    }
3785
3786    fn expect_kind(&mut self, expected: &TokenKind) -> Result<Span, ParseError> {
3787        if self.at_kind(expected) {
3788            Ok(self.advance_token().span)
3789        } else {
3790            Err(self.err_here(format!("expected {expected:?}, got {:?}", self.peek_kind())))
3791        }
3792    }
3793
3794    fn err_here(&self, message: impl Into<String>) -> ParseError {
3795        ParseError::at(message, self.peek_token())
3796    }
3797
3798    // ── Prefix (nud) ────────────────────────────────────────────────────
3799
3800    #[cfg(test)]
3801    #[allow(clippy::too_many_lines)]
3802    fn parse_prefix(&mut self) -> Result<ParsedExpr, ParseError> {
3803        let Token {
3804            kind,
3805            span: token_span,
3806            line,
3807            col,
3808        } = self.advance_token();
3809        if self.at_kind(&TokenKind::Dot) && starts_table_star_qualifier(&kind) {
3810            let name = match &kind {
3811                TokenKind::Id(name) | TokenKind::QuotedId(name, _) => Arc::clone(name),
3812                TokenKind::String(name) => Arc::<str>::from(name.as_str()),
3813                keyword => Arc::<str>::from(kw_to_str(keyword)),
3814            };
3815            return self.parse_ident_expr(name, token_span);
3816        }
3817        match kind {
3818            // ── Literals ────────────────────────────────────────────────
3819            TokenKind::Integer(i) => Ok(ParsedExpr::leaf(Expr::Literal(
3820                Literal::Integer(i),
3821                token_span,
3822            ))),
3823            // An integer literal too large for i64 becomes a REAL, and a
3824            // magnitude beyond f64 range becomes ±Infinity — matching C
3825            // SQLite's text-to-real conversion (no f64::MAX clamp).
3826            TokenKind::OversizedInt(s) => match s.parse::<f64>() {
3827                Ok(v) => Ok(ParsedExpr::leaf(Expr::Literal(
3828                    Literal::Float(v),
3829                    token_span,
3830                ))),
3831                Err(_) => Err(ParseError {
3832                    kind: crate::parser::ParseErrorKind::Syntax,
3833                    message: "integer out of range".to_owned(),
3834                    span: token_span,
3835                    line,
3836                    col,
3837                }),
3838            },
3839            TokenKind::Float(f) => Ok(ParsedExpr::leaf(Expr::Literal(
3840                Literal::Float(f),
3841                token_span,
3842            ))),
3843            TokenKind::String(s) if matches!(self.peek_kind(), TokenKind::Dot) => {
3844                self.parse_ident_expr(s, token_span)
3845            }
3846            TokenKind::String(s) => Ok(ParsedExpr::leaf(Expr::Literal(
3847                Literal::String(s),
3848                token_span,
3849            ))),
3850            TokenKind::Blob(b) => Ok(ParsedExpr::leaf(Expr::Literal(
3851                Literal::Blob(b),
3852                token_span,
3853            ))),
3854            TokenKind::KwNull => Ok(ParsedExpr::leaf(Expr::Literal(Literal::Null, token_span))),
3855            TokenKind::KwTrue => Ok(ParsedExpr::leaf(Expr::Literal(Literal::True, token_span))),
3856            TokenKind::KwFalse => Ok(ParsedExpr::leaf(Expr::Literal(Literal::False, token_span))),
3857            TokenKind::KwCurrentTime => Ok(ParsedExpr::leaf(Expr::Literal(
3858                Literal::CurrentTime,
3859                token_span,
3860            ))),
3861            TokenKind::KwCurrentDate => Ok(ParsedExpr::leaf(Expr::Literal(
3862                Literal::CurrentDate,
3863                token_span,
3864            ))),
3865            TokenKind::KwCurrentTimestamp => Ok(ParsedExpr::leaf(Expr::Literal(
3866                Literal::CurrentTimestamp,
3867                token_span,
3868            ))),
3869
3870            // ── Bind parameters ─────────────────────────────────────────
3871            TokenKind::Question => Ok(ParsedExpr::leaf(Expr::Placeholder(
3872                PlaceholderType::Anonymous,
3873                token_span,
3874            ))),
3875            TokenKind::QuestionNum(n) => Ok(ParsedExpr::leaf(Expr::Placeholder(
3876                PlaceholderType::Numbered(n),
3877                token_span,
3878            ))),
3879            TokenKind::ColonParam(s) => Ok(ParsedExpr::leaf(Expr::Placeholder(
3880                PlaceholderType::ColonNamed(s),
3881                token_span,
3882            ))),
3883            TokenKind::AtParam(s) => Ok(ParsedExpr::leaf(Expr::Placeholder(
3884                PlaceholderType::AtNamed(s),
3885                token_span,
3886            ))),
3887            TokenKind::DollarParam(s) => Ok(ParsedExpr::leaf(Expr::Placeholder(
3888                PlaceholderType::DollarNamed(s),
3889                token_span,
3890            ))),
3891
3892            // ── Unary prefix: - + ~ ─────────────────────────────────────
3893            TokenKind::Minus => {
3894                // SQLite accepts this one magnitude as the signed minimum.
3895                // Preserve the normalized one-node literal AST and its actual
3896                // retained height.
3897                if let TokenKind::OversizedInt(s) = self.peek_kind()
3898                    && s == "9223372036854775808"
3899                {
3900                    let num_span = self.advance_token().span;
3901                    let span = token_span.merge(num_span);
3902                    return self.finish_expr(
3903                        Expr::Literal(Literal::Integer(i64::MIN), span),
3904                        1,
3905                        true,
3906                        false,
3907                    );
3908                }
3909                let inner = self.parse_expr_bp(bp::UNARY)?;
3910                let span = token_span.merge(inner.expr.span());
3911                self.finish_unary(UnaryOp::Negate, inner, span)
3912            }
3913            TokenKind::Plus => {
3914                let inner = self.parse_expr_bp(bp::UNARY)?;
3915                let span = token_span.merge(inner.expr.span());
3916                self.finish_unary(UnaryOp::Plus, inner, span)
3917            }
3918            TokenKind::Tilde => {
3919                let inner = self.parse_expr_bp(bp::UNARY)?;
3920                let span = token_span.merge(inner.expr.span());
3921                self.finish_unary(UnaryOp::BitNot, inner, span)
3922            }
3923
3924            // ── Prefix NOT ──────────────────────────────────────────────
3925            TokenKind::KwNot => {
3926                // NOT EXISTS (subquery)
3927                if matches!(self.peek_kind(), TokenKind::KwExists) {
3928                    self.advance_token();
3929                    self.expect_kind(&TokenKind::LeftParen)?;
3930                    let subquery = self.parse_subquery_minimal()?;
3931                    let end = self.expect_kind(&TokenKind::RightParen)?;
3932                    let span = token_span.merge(end);
3933                    let height = subquery.height;
3934                    let exists = self.checked_expr(
3935                        Expr::Exists {
3936                            subquery: Box::new(subquery.value),
3937                            not: true,
3938                            span,
3939                        },
3940                        height,
3941                        false,
3942                        false,
3943                    )?;
3944                    return self.add_cached_parent(exists);
3945                }
3946                let inner = self.parse_expr_bp(bp::NOT_PREFIX)?;
3947                let span = token_span.merge(inner.expr.span());
3948                self.finish_unary(UnaryOp::Not, inner, span)
3949            }
3950
3951            // ── EXISTS (subquery) ───────────────────────────────────────
3952            TokenKind::KwExists => {
3953                self.expect_kind(&TokenKind::LeftParen)?;
3954                let subquery = self.parse_subquery_minimal()?;
3955                let end = self.expect_kind(&TokenKind::RightParen)?;
3956                let span = token_span.merge(end);
3957                let height = subquery.height;
3958                self.checked_expr(
3959                    Expr::Exists {
3960                        subquery: Box::new(subquery.value),
3961                        not: false,
3962                        span,
3963                    },
3964                    height,
3965                    false,
3966                    false,
3967                )
3968            }
3969
3970            // ── CAST(expr AS type_name) ─────────────────────────────────
3971            TokenKind::KwCast => {
3972                self.expect_kind(&TokenKind::LeftParen)?;
3973                let inner = self.parse_expr_bp(0)?;
3974                self.expect_kind(&TokenKind::KwAs)?;
3975                let type_name = self.parse_type_name()?;
3976                let end = self.expect_kind(&TokenKind::RightParen)?;
3977                let span = token_span.merge(end);
3978                let height = inner.height;
3979                let is_constant = inner.is_constant;
3980                let has_function = inner.has_function;
3981                self.checked_expr(
3982                    Expr::Cast {
3983                        expr: Box::new(inner.expr),
3984                        type_name,
3985                        span,
3986                    },
3987                    height,
3988                    is_constant,
3989                    has_function,
3990                )
3991            }
3992
3993            // ── CASE [operand] WHEN ... THEN ... [ELSE ...] END ────────
3994            TokenKind::KwCase => self.parse_case_expr(token_span),
3995
3996            // ── RAISE(action, message) ──────────────────────────────────
3997            TokenKind::KwRaise => {
3998                self.expect_kind(&TokenKind::LeftParen)?;
3999                let (action, message) = self.parse_raise_args()?;
4000                let end = self.expect_kind(&TokenKind::RightParen)?;
4001                let span = token_span.merge(end);
4002                Ok(ParsedExpr::leaf(Expr::Raise {
4003                    action,
4004                    message,
4005                    span,
4006                }))
4007            }
4008
4009            // ── Parenthesized expr / subquery / row-value ───────────────
4010            TokenKind::LeftParen => {
4011                if matches!(
4012                    self.peek_kind(),
4013                    TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
4014                ) {
4015                    let subquery = self.parse_subquery_minimal()?;
4016                    let end = self.expect_kind(&TokenKind::RightParen)?;
4017                    let span = token_span.merge(end);
4018                    return self.checked_expr(
4019                        Expr::Subquery(Box::new(subquery.value), span),
4020                        subquery.height,
4021                        false,
4022                        false,
4023                    );
4024                }
4025                let first = self.parse_expr_bp(0)?;
4026                if self.eat_kind(&TokenKind::Comma) {
4027                    let mut is_constant = first.is_constant;
4028                    let mut has_function = first.has_function;
4029                    let mut exprs = vec![first.expr];
4030                    loop {
4031                        let parsed = self.parse_expr_bp(0)?;
4032                        is_constant &= parsed.is_constant;
4033                        has_function |= parsed.has_function;
4034                        exprs.push(parsed.expr);
4035                        if !self.eat_kind(&TokenKind::Comma) {
4036                            break;
4037                        }
4038                    }
4039                    let end = self.expect_kind(&TokenKind::RightParen)?;
4040                    let span = token_span.merge(end);
4041                    self.finish_expr(Expr::RowValue(exprs, span), 1, is_constant, has_function)
4042                } else {
4043                    self.expect_kind(&TokenKind::RightParen)?;
4044                    Ok(first)
4045                }
4046            }
4047
4048            // ── Identifier: column ref or function call ─────────────────
4049            TokenKind::Id(name) | TokenKind::QuotedId(name, _) => {
4050                self.parse_ident_expr(name, token_span)
4051            }
4052
4053            // ── Keywords usable as function names ───────────────────────
4054            TokenKind::KwReplace if matches!(self.peek_kind(), TokenKind::LeftParen) => {
4055                self.parse_function_call("replace".to_owned(), token_span)
4056            }
4057            // C SQLite exposes the pattern-matching operators as scalar
4058            // functions too: `like(P, X [, E])`, `glob(P, X)`,
4059            // `regexp(P, X)`, `match(P, X)`. The token doubles as an infix
4060            // operator, so only treat it as a function name when directly
4061            // followed by `(`.
4062            TokenKind::KwLike if matches!(self.peek_kind(), TokenKind::LeftParen) => {
4063                self.parse_function_call("like".to_owned(), token_span)
4064            }
4065            TokenKind::KwGlob if matches!(self.peek_kind(), TokenKind::LeftParen) => {
4066                self.parse_function_call("glob".to_owned(), token_span)
4067            }
4068            TokenKind::KwRegexp if matches!(self.peek_kind(), TokenKind::LeftParen) => {
4069                self.parse_function_call("regexp".to_owned(), token_span)
4070            }
4071            TokenKind::KwMatch if matches!(self.peek_kind(), TokenKind::LeftParen) => {
4072                self.parse_function_call("match".to_owned(), token_span)
4073            }
4074
4075            // ── Non-reserved keywords usable as identifiers ─────────────
4076            // In SQL, non-reserved keywords (like KEY, MATCH, FIRST, etc.)
4077            // can be used as column names without quoting.
4078            k if is_nonreserved_kw(&k) => {
4079                let name = kw_to_str(&k);
4080                self.parse_ident_expr(name, token_span)
4081            }
4082
4083            kind => Err(ParseError {
4084                kind: crate::parser::ParseErrorKind::Syntax,
4085                message: format!("unexpected token in expression: {kind:?}"),
4086                span: token_span,
4087                line,
4088                col,
4089            }),
4090        }
4091    }
4092
4093    /// Parse `name`, `name.column`, or `name(args)`.
4094    #[cfg(test)]
4095    fn parse_ident_expr<S>(&mut self, name: S, start: Span) -> Result<ParsedExpr, ParseError>
4096    where
4097        S: AsRef<str> + Into<Arc<str>>,
4098    {
4099        // Function call: name(...)
4100        if matches!(self.peek_kind(), TokenKind::LeftParen) {
4101            return self.parse_function_call(name.as_ref().to_owned(), start);
4102        }
4103        let name = name.into();
4104        // Table-qualified column: name.column
4105        if matches!(self.peek_kind(), TokenKind::Dot) {
4106            let Some(col_tok) = self.peek_nth_token(1) else {
4107                return Err(self.err_here("expected column name after '.'"));
4108            };
4109            let col_name = match &col_tok.kind {
4110                TokenKind::Id(c) | TokenKind::QuotedId(c, _) => Arc::clone(c),
4111                TokenKind::String(c) => Arc::<str>::from(c.as_str()),
4112                k if starts_post_dot_identifier(k) => Arc::<str>::from(kw_to_str(k)),
4113                _ => {
4114                    return Err(ParseError::at(
4115                        format!("expected column name after '.', got {:?}", col_tok.kind),
4116                        Some(col_tok),
4117                    ));
4118                }
4119            };
4120            let span = start.merge(col_tok.span);
4121            self.pos = self.pos.saturating_add(2);
4122            return self.finish_expr(
4123                Expr::Column(ColumnRef::qualified(name, col_name), span),
4124                2,
4125                false,
4126                false,
4127            );
4128        }
4129        Ok(ParsedExpr::leaf(Expr::Column(ColumnRef::bare(name), start)))
4130    }
4131
4132    // ── Postfix ─────────────────────────────────────────────────────────
4133
4134    fn postfix_bp(&self) -> Option<u8> {
4135        match self.peek_kind() {
4136            TokenKind::KwCollate => Some(bp::COLLATE),
4137            TokenKind::KwIsnull | TokenKind::KwNotnull => Some(bp::EQUALITY.0),
4138            TokenKind::KwNot => {
4139                if let Some(next) = self.tokens.get(self.pos + 1)
4140                    && matches!(next.kind, TokenKind::KwNull)
4141                {
4142                    return Some(bp::EQUALITY.0);
4143                }
4144                None
4145            }
4146            _ => None,
4147        }
4148    }
4149
4150    fn parse_postfix(&mut self, lhs: ParsedExpr) -> Result<ParsedExpr, ParseError> {
4151        let tok = self.advance_token();
4152        match &tok.kind {
4153            TokenKind::KwCollate => {
4154                let collation = match self.parse_identifier() {
4155                    Ok(s) => s,
4156                    Err(_) => {
4157                        return Err(self.err_here("expected collation name after COLLATE"));
4158                    }
4159                };
4160                let name_span = self.tokens[self.pos.saturating_sub(1)].span;
4161                let span = lhs.expr.span().merge(name_span);
4162                let height = lhs.height;
4163                let is_constant = lhs.is_constant;
4164                let has_function = lhs.has_function;
4165                self.checked_expr(
4166                    Expr::Collate {
4167                        expr: Box::new(lhs.expr),
4168                        collation,
4169                        span,
4170                    },
4171                    height,
4172                    is_constant,
4173                    has_function,
4174                )
4175            }
4176            TokenKind::KwIsnull => {
4177                let span = lhs.expr.span().merge(tok.span);
4178                let height = lhs.height;
4179                let is_constant = lhs.is_constant;
4180                let has_function = lhs.has_function;
4181                self.checked_expr(
4182                    Expr::IsNull {
4183                        expr: Box::new(lhs.expr),
4184                        not: false,
4185                        span,
4186                    },
4187                    height,
4188                    is_constant,
4189                    has_function,
4190                )
4191            }
4192            TokenKind::KwNotnull => {
4193                let span = lhs.expr.span().merge(tok.span);
4194                let height = lhs.height;
4195                let is_constant = lhs.is_constant;
4196                let has_function = lhs.has_function;
4197                self.checked_expr(
4198                    Expr::IsNull {
4199                        expr: Box::new(lhs.expr),
4200                        not: true,
4201                        span,
4202                    },
4203                    height,
4204                    is_constant,
4205                    has_function,
4206                )
4207            }
4208            TokenKind::KwNot => {
4209                let null_tok = self.advance_token(); // we know from postfix_bp that this is KwNull
4210                let span = lhs.expr.span().merge(null_tok.span);
4211                let height = lhs.height;
4212                let is_constant = lhs.is_constant;
4213                let has_function = lhs.has_function;
4214                self.checked_expr(
4215                    Expr::IsNull {
4216                        expr: Box::new(lhs.expr),
4217                        not: true,
4218                        span,
4219                    },
4220                    height,
4221                    is_constant,
4222                    has_function,
4223                )
4224            }
4225            other => Err(ParseError::at(
4226                format!("unexpected postfix token: {other:?}"),
4227                Some(&tok),
4228            )),
4229        }
4230    }
4231
4232    // ── Infix ───────────────────────────────────────────────────────────
4233
4234    fn infix_bp(&self) -> Option<(u8, u8)> {
4235        match self.peek_kind() {
4236            TokenKind::KwOr => Some(bp::OR),
4237            TokenKind::KwAnd => Some(bp::AND),
4238
4239            TokenKind::Eq
4240            | TokenKind::EqEq
4241            | TokenKind::Ne
4242            | TokenKind::LtGt
4243            | TokenKind::KwIs
4244            | TokenKind::KwLike
4245            | TokenKind::KwGlob
4246            | TokenKind::KwMatch
4247            | TokenKind::KwRegexp
4248            | TokenKind::KwBetween
4249            | TokenKind::KwIn => Some(bp::EQUALITY),
4250
4251            // NOT LIKE / NOT IN / NOT BETWEEN / NOT GLOB / NOT MATCH / NOT REGEXP
4252            TokenKind::KwNot => {
4253                let next = self.tokens.get(self.pos + 1).map(|t| &t.kind);
4254                match next {
4255                    Some(
4256                        TokenKind::KwLike
4257                        | TokenKind::KwGlob
4258                        | TokenKind::KwMatch
4259                        | TokenKind::KwRegexp
4260                        | TokenKind::KwBetween
4261                        | TokenKind::KwIn,
4262                    ) => Some(bp::EQUALITY),
4263                    _ => None,
4264                }
4265            }
4266
4267            TokenKind::Lt | TokenKind::Le | TokenKind::Gt | TokenKind::Ge => Some(bp::COMPARISON),
4268
4269            TokenKind::Ampersand
4270            | TokenKind::Pipe
4271            | TokenKind::ShiftLeft
4272            | TokenKind::ShiftRight => Some(bp::BITWISE),
4273
4274            TokenKind::Plus | TokenKind::Minus => Some(bp::ADD),
4275            TokenKind::Star | TokenKind::Slash | TokenKind::Percent => Some(bp::MUL),
4276            TokenKind::Concat => Some(bp::CONCAT),
4277            TokenKind::Arrow | TokenKind::DoubleArrow => Some(bp::JSON),
4278
4279            _ => None,
4280        }
4281    }
4282
4283    #[cfg(test)]
4284    #[allow(clippy::too_many_lines)]
4285    fn parse_infix(&mut self, lhs: ParsedExpr, r_bp: u8) -> Result<ParsedExpr, ParseError> {
4286        let tok = self.advance_token();
4287        match &tok.kind {
4288            // ── Simple binary operators ──────────────────────────────────
4289            TokenKind::Plus => self.make_binop(lhs, BinaryOp::Add, r_bp),
4290            TokenKind::Minus => self.make_binop(lhs, BinaryOp::Subtract, r_bp),
4291            TokenKind::Star => self.make_binop(lhs, BinaryOp::Multiply, r_bp),
4292            TokenKind::Slash => self.make_binop(lhs, BinaryOp::Divide, r_bp),
4293            TokenKind::Percent => self.make_binop(lhs, BinaryOp::Modulo, r_bp),
4294            TokenKind::Concat => self.make_binop(lhs, BinaryOp::Concat, r_bp),
4295            TokenKind::Eq | TokenKind::EqEq => self.make_binop(lhs, BinaryOp::Eq, r_bp),
4296            TokenKind::Ne | TokenKind::LtGt => self.make_binop(lhs, BinaryOp::Ne, r_bp),
4297            TokenKind::Lt => self.make_binop(lhs, BinaryOp::Lt, r_bp),
4298            TokenKind::Le => self.make_binop(lhs, BinaryOp::Le, r_bp),
4299            TokenKind::Gt => self.make_binop(lhs, BinaryOp::Gt, r_bp),
4300            TokenKind::Ge => self.make_binop(lhs, BinaryOp::Ge, r_bp),
4301            TokenKind::Ampersand => self.make_binop(lhs, BinaryOp::BitAnd, r_bp),
4302            TokenKind::Pipe => self.make_binop(lhs, BinaryOp::BitOr, r_bp),
4303            TokenKind::ShiftLeft => self.make_binop(lhs, BinaryOp::ShiftLeft, r_bp),
4304            TokenKind::ShiftRight => self.make_binop(lhs, BinaryOp::ShiftRight, r_bp),
4305            TokenKind::KwOr => self.make_binop(lhs, BinaryOp::Or, r_bp),
4306            TokenKind::KwAnd => self.make_binop(lhs, BinaryOp::And, r_bp),
4307
4308            // ── IS [NOT] [DISTINCT FROM | NULL | expr] ──────────────────────────────────
4309            TokenKind::KwIs => {
4310                let not = self.eat_kind(&TokenKind::KwNot);
4311                if self.eat_kind(&TokenKind::KwDistinct) {
4312                    self.expect_kind(&TokenKind::KwFrom)?;
4313                    let rhs = self.parse_expr_bp(r_bp)?;
4314                    let span = lhs.expr.span().merge(rhs.expr.span());
4315                    let height = lhs.height.max(rhs.height);
4316                    let is_constant = lhs.is_constant && rhs.is_constant;
4317                    let has_function = lhs.has_function || rhs.has_function;
4318                    // IS DISTINCT FROM is equivalent to IS NOT
4319                    // IS NOT DISTINCT FROM is equivalent to IS
4320                    let op = if not { BinaryOp::Is } else { BinaryOp::IsNot };
4321                    return self.checked_expr(
4322                        Expr::BinaryOp {
4323                            left: Box::new(lhs.expr),
4324                            op,
4325                            right: Box::new(rhs.expr),
4326                            span,
4327                        },
4328                        height,
4329                        is_constant,
4330                        has_function,
4331                    );
4332                }
4333                let rhs = self.parse_expr_bp(r_bp)?;
4334                let span = lhs.expr.span().merge(rhs.expr.span());
4335                // SQLite folds `expr IS [NOT] expr` into a unary null-test
4336                // only when the right operand, parsed at normal precedence,
4337                // is the NULL literal. Parsing the RHS first — rather than
4338                // greedily consuming a NULL token — keeps tighter-binding operators
4339                // attached to NULL: `x IS NULL < 2` parses as
4340                // `x IS (NULL < 2)`, matching C SQLite (verified against the
4341                // sqlite3 CLI: `SELECT 1 IS NULL < 2` yields 0, not 1).
4342                if matches!(&rhs.expr, Expr::Literal(Literal::Null, _)) {
4343                    let height = lhs.height;
4344                    let is_constant = lhs.is_constant;
4345                    let has_function = lhs.has_function;
4346                    return self.checked_expr(
4347                        Expr::IsNull {
4348                            expr: Box::new(lhs.expr),
4349                            not,
4350                            span,
4351                        },
4352                        height,
4353                        is_constant,
4354                        has_function,
4355                    );
4356                }
4357                let op = if not { BinaryOp::IsNot } else { BinaryOp::Is };
4358                let height = lhs.height.max(rhs.height);
4359                let is_constant = lhs.is_constant && rhs.is_constant;
4360                let has_function = lhs.has_function || rhs.has_function;
4361                self.checked_expr(
4362                    Expr::BinaryOp {
4363                        left: Box::new(lhs.expr),
4364                        op,
4365                        right: Box::new(rhs.expr),
4366                        span,
4367                    },
4368                    height,
4369                    is_constant,
4370                    has_function,
4371                )
4372            }
4373
4374            // ── LIKE / GLOB / MATCH / REGEXP ────────────────────────────
4375            TokenKind::KwLike => self.parse_like(lhs, LikeOp::Like, false),
4376            TokenKind::KwGlob => self.parse_like(lhs, LikeOp::Glob, false),
4377            TokenKind::KwMatch => self.parse_like(lhs, LikeOp::Match, false),
4378            TokenKind::KwRegexp => self.parse_like(lhs, LikeOp::Regexp, false),
4379
4380            // ── BETWEEN ─────────────────────────────────────────────────
4381            TokenKind::KwBetween => self.parse_between(lhs, false),
4382
4383            // ── IN ──────────────────────────────────────────────────────
4384            TokenKind::KwIn => self.parse_in(lhs, false),
4385
4386            // ── JSON -> / ->> ───────────────────────────────────────────
4387            TokenKind::Arrow => {
4388                let rhs = self.parse_expr_bp(r_bp)?;
4389                let span = lhs.expr.span().merge(rhs.expr.span());
4390                let height = lhs.height.max(rhs.height);
4391                let is_constant = false;
4392                let has_function = true;
4393                self.checked_expr(
4394                    Expr::JsonAccess {
4395                        expr: Box::new(lhs.expr),
4396                        path: Box::new(rhs.expr),
4397                        arrow: JsonArrow::Arrow,
4398                        span,
4399                    },
4400                    height,
4401                    is_constant,
4402                    has_function,
4403                )
4404            }
4405            TokenKind::DoubleArrow => {
4406                let rhs = self.parse_expr_bp(r_bp)?;
4407                let span = lhs.expr.span().merge(rhs.expr.span());
4408                let height = lhs.height.max(rhs.height);
4409                let is_constant = false;
4410                let has_function = true;
4411                self.checked_expr(
4412                    Expr::JsonAccess {
4413                        expr: Box::new(lhs.expr),
4414                        path: Box::new(rhs.expr),
4415                        arrow: JsonArrow::DoubleArrow,
4416                        span,
4417                    },
4418                    height,
4419                    is_constant,
4420                    has_function,
4421                )
4422            }
4423
4424            // ── NOT LIKE / GLOB / BETWEEN / IN ──────────────────────────
4425            TokenKind::KwNot => {
4426                let next = self.advance_token();
4427                match &next.kind {
4428                    TokenKind::KwLike => self.parse_like(lhs, LikeOp::Like, true),
4429                    TokenKind::KwGlob => self.parse_like(lhs, LikeOp::Glob, true),
4430                    TokenKind::KwMatch => self.parse_like(lhs, LikeOp::Match, true),
4431                    TokenKind::KwRegexp => self.parse_like(lhs, LikeOp::Regexp, true),
4432                    TokenKind::KwBetween => self.parse_between(lhs, true),
4433                    TokenKind::KwIn => self.parse_in(lhs, true),
4434                    _ => Err(ParseError::at(
4435                        format!(
4436                            "expected LIKE/GLOB/MATCH/REGEXP/BETWEEN/IN \
4437                             after NOT, got {:?}",
4438                            next.kind
4439                        ),
4440                        Some(&next),
4441                    )),
4442                }
4443            }
4444
4445            other => Err(ParseError::at(
4446                format!("unexpected infix token: {other:?}"),
4447                Some(&tok),
4448            )),
4449        }
4450    }
4451
4452    #[cfg(test)]
4453    fn make_binop(
4454        &mut self,
4455        lhs: ParsedExpr,
4456        op: BinaryOp,
4457        r_bp: u8,
4458    ) -> Result<ParsedExpr, ParseError> {
4459        let rhs = self.parse_expr_bp(r_bp)?;
4460        let span = lhs.expr.span().merge(rhs.expr.span());
4461        let height = lhs.height.max(rhs.height);
4462        let is_constant = lhs.is_constant && rhs.is_constant;
4463        let has_function = lhs.has_function || rhs.has_function;
4464        self.checked_expr(
4465            Expr::BinaryOp {
4466                left: Box::new(lhs.expr),
4467                op,
4468                right: Box::new(rhs.expr),
4469                span,
4470            },
4471            height,
4472            is_constant,
4473            has_function,
4474        )
4475    }
4476
4477    // ── Special expression forms ────────────────────────────────────────
4478
4479    #[cfg(test)]
4480    fn parse_like(
4481        &mut self,
4482        lhs: ParsedExpr,
4483        op: LikeOp,
4484        not: bool,
4485    ) -> Result<ParsedExpr, ParseError> {
4486        let pattern = self.parse_expr_bp(bp::EQUALITY.1)?;
4487        let escape = if self.eat_kind(&TokenKind::KwEscape) {
4488            // SQLite's grammar accepts ESCAPE for all pattern-matching operators
4489            // (LIKE, GLOB, MATCH, REGEXP), not just LIKE.
4490            Some(self.parse_expr_bp(bp::EQUALITY.1)?)
4491        } else {
4492            None
4493        };
4494        let end = escape
4495            .as_ref()
4496            .map_or_else(|| pattern.expr.span(), |e| e.expr.span());
4497        let span = lhs.expr.span().merge(end);
4498        let height = escape.as_ref().map_or_else(
4499            || lhs.height.max(pattern.height),
4500            |parsed| lhs.height.max(pattern.height).max(parsed.height),
4501        );
4502        let parsed = self.checked_expr(
4503            Expr::Like {
4504                expr: Box::new(lhs.expr),
4505                pattern: Box::new(pattern.expr),
4506                escape: escape.map(|parsed| Box::new(parsed.expr)),
4507                op,
4508                not,
4509                span,
4510            },
4511            height,
4512            false,
4513            true,
4514        )?;
4515        if not {
4516            self.add_cached_parent(parsed)
4517        } else {
4518            Ok(parsed)
4519        }
4520    }
4521
4522    #[cfg(test)]
4523    fn parse_between(&mut self, lhs: ParsedExpr, not: bool) -> Result<ParsedExpr, ParseError> {
4524        // Parse low bound above AND level so AND keyword is not consumed.
4525        let low = self.parse_expr_bp(bp::NOT_PREFIX)?;
4526        if !self.eat_kind(&TokenKind::KwAnd) {
4527            return Err(self.err_here("expected AND in BETWEEN expression"));
4528        }
4529        let high = self.parse_expr_bp(bp::EQUALITY.1)?;
4530        let span = lhs.expr.span().merge(high.expr.span());
4531        let height = lhs.height.max(low.height).max(high.height);
4532        let is_constant = lhs.is_constant && low.is_constant && high.is_constant;
4533        let has_function = lhs.has_function || low.has_function || high.has_function;
4534        let parsed = self.checked_expr(
4535            Expr::Between {
4536                expr: Box::new(lhs.expr),
4537                low: Box::new(low.expr),
4538                high: Box::new(high.expr),
4539                not,
4540                span,
4541            },
4542            height,
4543            is_constant,
4544            has_function,
4545        )?;
4546        if not {
4547            self.add_cached_parent(parsed)
4548        } else {
4549            Ok(parsed)
4550        }
4551    }
4552
4553    #[cfg(test)]
4554    fn parse_in(&mut self, lhs: ParsedExpr, not: bool) -> Result<ParsedExpr, ParseError> {
4555        let start = lhs.expr.span();
4556
4557        // SQLite supports both "x IN ( ... )" and "x IN table_name".
4558        if !self.at_kind(&TokenKind::LeftParen) {
4559            let table = self.parse_qualified_name()?;
4560            let end = self.tokens[self.pos.saturating_sub(1)].span;
4561            let span = start.merge(end);
4562            let height = lhs.height;
4563            let has_function = lhs.has_function;
4564            let parsed = self.checked_expr(
4565                Expr::In {
4566                    expr: Box::new(lhs.expr),
4567                    set: InSet::Table(table),
4568                    not,
4569                    span,
4570                },
4571                height,
4572                false,
4573                has_function,
4574            )?;
4575            return if not {
4576                self.add_cached_parent(parsed)
4577            } else {
4578                Ok(parsed)
4579            };
4580        }
4581
4582        self.expect_kind(&TokenKind::LeftParen)?;
4583
4584        if matches!(
4585            self.peek_kind(),
4586            TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
4587        ) {
4588            let subquery = self.parse_subquery_minimal()?;
4589            let end = self.expect_kind(&TokenKind::RightParen)?;
4590            let span = start.merge(end);
4591            let height = lhs.height.max(subquery.height);
4592            let has_function = lhs.has_function;
4593            let parsed = self.checked_expr(
4594                Expr::In {
4595                    expr: Box::new(lhs.expr),
4596                    set: InSet::Subquery(Box::new(subquery.value)),
4597                    not,
4598                    span,
4599                },
4600                height,
4601                false,
4602                has_function,
4603            )?;
4604            return if not {
4605                self.add_cached_parent(parsed)
4606            } else {
4607                Ok(parsed)
4608            };
4609        }
4610
4611        let mut parsed_items = Vec::new();
4612        if !self.at_kind(&TokenKind::RightParen) {
4613            let item = self.parse_expr_bp(0)?;
4614            parsed_items.push(item);
4615            while self.eat_kind(&TokenKind::Comma) {
4616                let item = self.parse_expr_bp(0)?;
4617                parsed_items.push(item);
4618            }
4619        }
4620        let end = self.expect_kind(&TokenKind::RightParen)?;
4621        if let Some(message) = vector_in_list_arity_error(&lhs.expr, &parsed_items) {
4622            return Err(self.err_here(message));
4623        }
4624        let span = start.merge(end);
4625        let item_height = parsed_items
4626            .iter()
4627            .map(|item| item.height)
4628            .max()
4629            .unwrap_or(0);
4630        let items_are_constant = parsed_items.iter().all(|item| item.is_constant);
4631        let item_has_function = parsed_items.iter().any(|item| item.has_function);
4632        let singleton_constant = matches!(parsed_items.as_slice(), [item] if item.is_constant)
4633            && lhs.root != CachedRoot::Vector;
4634        let singleton_subquery =
4635            matches!(parsed_items.as_slice(), [item] if item.root == CachedRoot::ScalarSubquery);
4636        let exprs = parsed_items.into_iter().map(|parsed| parsed.expr).collect();
4637        let lhs_height = lhs.height;
4638        let lhs_is_constant = lhs.is_constant;
4639        let lhs_has_function = lhs.has_function;
4640        let expr = Expr::In {
4641            expr: Box::new(lhs.expr),
4642            set: InSet::List(exprs),
4643            not,
4644            span,
4645        };
4646
4647        if item_height == 0 {
4648            if lhs_has_function {
4649                return self.finish_expr(expr, lhs_height.saturating_add(1), false, true);
4650            }
4651            return self.finish_expr(expr, 1, true, false);
4652        }
4653
4654        let cached_child_height = if singleton_constant {
4655            lhs_height.max(item_height.saturating_add(1))
4656        } else if singleton_subquery {
4657            lhs_height.max(item_height.saturating_sub(1))
4658        } else {
4659            lhs_height.max(item_height)
4660        };
4661        let parsed = self.checked_expr(
4662            expr,
4663            cached_child_height,
4664            lhs_is_constant && items_are_constant,
4665            lhs_has_function || item_has_function,
4666        )?;
4667        if not {
4668            self.add_cached_parent(parsed)
4669        } else {
4670            Ok(parsed)
4671        }
4672    }
4673
4674    #[cfg(test)]
4675    fn parse_case_expr(&mut self, start: Span) -> Result<ParsedExpr, ParseError> {
4676        let operand = if matches!(self.peek_kind(), TokenKind::KwWhen) {
4677            None
4678        } else {
4679            Some(self.parse_expr_bp(0)?)
4680        };
4681
4682        let mut whens = Vec::new();
4683        while self.eat_kind(&TokenKind::KwWhen) {
4684            let condition = self.parse_expr_bp(0)?;
4685            if !self.eat_kind(&TokenKind::KwThen) {
4686                return Err(self.err_here("expected THEN in CASE expression"));
4687            }
4688            let result = self.parse_expr_bp(0)?;
4689            whens.push((condition, result));
4690        }
4691        if whens.is_empty() {
4692            return Err(self.err_here("CASE requires at least one WHEN clause"));
4693        }
4694
4695        let else_expr = if self.eat_kind(&TokenKind::KwElse) {
4696            Some(self.parse_expr_bp(0)?)
4697        } else {
4698            None
4699        };
4700
4701        if !self.eat_kind(&TokenKind::KwEnd) {
4702            return Err(self.err_here("expected END for CASE expression"));
4703        }
4704        let end = self.tokens[self.pos.saturating_sub(1)].span;
4705        let span = start.merge(end);
4706        let mut height = operand.as_ref().map_or(0, |parsed| parsed.height);
4707        let mut is_constant = operand.as_ref().is_none_or(|parsed| parsed.is_constant);
4708        let mut has_function = operand.as_ref().is_some_and(|parsed| parsed.has_function);
4709        for (condition, result) in &whens {
4710            height = height.max(condition.height).max(result.height);
4711            is_constant &= condition.is_constant && result.is_constant;
4712            has_function |= condition.has_function || result.has_function;
4713        }
4714        if let Some(parsed) = &else_expr {
4715            height = height.max(parsed.height);
4716            is_constant &= parsed.is_constant;
4717            has_function |= parsed.has_function;
4718        }
4719        self.checked_expr(
4720            Expr::Case {
4721                operand: operand.map(|parsed| Box::new(parsed.expr)),
4722                whens: whens
4723                    .into_iter()
4724                    .map(|(condition, result)| (condition.expr, result.expr))
4725                    .collect(),
4726                else_expr: else_expr.map(|parsed| Box::new(parsed.expr)),
4727                span,
4728            },
4729            height,
4730            is_constant,
4731            has_function,
4732        )
4733    }
4734
4735    #[cfg(test)]
4736    fn parse_function_call(&mut self, name: String, start: Span) -> Result<ParsedExpr, ParseError> {
4737        self.expect_kind(&TokenKind::LeftParen)?;
4738
4739        let (args, distinct, height) = if matches!(self.peek_kind(), TokenKind::Star) {
4740            self.advance_token();
4741            // Mirrors start_function: `f(*)` parses for any function; only
4742            // count keeps star semantics, others become zero-arg calls.
4743            if name.eq_ignore_ascii_case("count") {
4744                (FunctionArgs::Star, false, 0)
4745            } else {
4746                (FunctionArgs::List(Vec::new()), false, 0)
4747            }
4748        } else {
4749            let distinct = self.eat_kind(&TokenKind::KwDistinct);
4750            let (args, height) = if matches!(self.peek_kind(), TokenKind::RightParen) {
4751                if distinct {
4752                    return Err(self.err_here("DISTINCT requires at least one argument"));
4753                }
4754                (FunctionArgs::List(Vec::new()), 0)
4755            } else {
4756                let first = self.parse_expr_bp(0)?;
4757                let mut height = first.height;
4758                let mut list = vec![first.expr];
4759                while self.eat_kind(&TokenKind::Comma) {
4760                    let parsed = self.parse_expr_bp(0)?;
4761                    height = height.max(parsed.height);
4762                    list.push(parsed.expr);
4763                }
4764                (FunctionArgs::List(list), height)
4765            };
4766            (args, distinct, height)
4767        };
4768
4769        // In-aggregate ORDER BY (SQLite 3.44+): group_concat(x, ',' ORDER BY y DESC)
4770        let order_by =
4771            if matches!(&args, FunctionArgs::List(_)) && self.eat_kind(&TokenKind::KwOrder) {
4772                self.expect_kind(&TokenKind::KwBy)?;
4773                self.parse_comma_sep(Self::parse_ordering_term)?
4774            } else {
4775                vec![]
4776            };
4777
4778        let mut end = self.expect_kind(&TokenKind::RightParen)?;
4779        // Peek ahead: only consume FILTER if followed by '(' to avoid
4780        // swallowing FILTER when used as a column alias (it's non-reserved).
4781        let filter = if matches!(self.peek_kind(), TokenKind::KwFilter)
4782            && self
4783                .tokens
4784                .get(self.pos + 1)
4785                .is_some_and(|t| t.kind == TokenKind::LeftParen)
4786        {
4787            self.advance_token(); // consume FILTER
4788            self.expect_kind(&TokenKind::LeftParen)?;
4789            self.expect_kind(&TokenKind::KwWhere)?;
4790            let predicate = self.parse_expr()?;
4791            let filter_end = self.expect_kind(&TokenKind::RightParen)?;
4792            end = end.merge(filter_end);
4793            Some(Box::new(predicate))
4794        } else {
4795            None
4796        };
4797        // Peek: only consume OVER if followed by '(' or an identifier
4798        // (window name), to avoid swallowing OVER as a column alias.
4799        let over = if matches!(self.peek_kind(), TokenKind::KwOver)
4800            && self.tokens.get(self.pos + 1).is_some_and(|t| {
4801                matches!(t.kind, TokenKind::LeftParen) || starts_bare_window_name(&t.kind)
4802            }) {
4803            self.advance_token(); // consume OVER
4804            if self.eat_kind(&TokenKind::LeftParen) {
4805                let spec = self.parse_window_spec()?;
4806                let over_end = self.expect_kind(&TokenKind::RightParen)?;
4807                end = end.merge(over_end);
4808                Some(spec)
4809            } else {
4810                let base_window = self.parse_window_name()?;
4811                let base_span = self.tokens[self.pos.saturating_sub(1)].span;
4812                end = end.merge(base_span);
4813                Some(WindowSpec {
4814                    window_ref: Some(WindowReference::Direct(base_window)),
4815                    partition_by: Vec::new(),
4816                    order_by: Vec::new(),
4817                    frame: None,
4818                })
4819            }
4820        } else {
4821            None
4822        };
4823
4824        let span = start.merge(end);
4825        self.checked_expr(
4826            Expr::FunctionCall {
4827                name,
4828                args,
4829                distinct,
4830                order_by,
4831                filter,
4832                over,
4833                span,
4834            },
4835            height,
4836            false,
4837            true,
4838        )
4839    }
4840
4841    fn parse_raise_args(&mut self) -> Result<(RaiseAction, Option<String>), ParseError> {
4842        let action_tok = self.advance_token();
4843        let action = match &action_tok.kind {
4844            TokenKind::KwIgnore => RaiseAction::Ignore,
4845            TokenKind::KwRollback => RaiseAction::Rollback,
4846            TokenKind::KwAbort => RaiseAction::Abort,
4847            TokenKind::KwFail => RaiseAction::Fail,
4848            _ => {
4849                return Err(ParseError::at(
4850                    "expected IGNORE, ROLLBACK, ABORT, or FAIL in RAISE",
4851                    Some(&action_tok),
4852                ));
4853            }
4854        };
4855        if matches!(action, RaiseAction::Ignore) {
4856            return Ok((action, None));
4857        }
4858        self.expect_kind(&TokenKind::Comma)?;
4859        let msg_tok = self.advance_token();
4860        let message = match &msg_tok.kind {
4861            TokenKind::String(s) => s.clone(),
4862            _ => {
4863                return Err(ParseError::at(
4864                    "expected string message in RAISE",
4865                    Some(&msg_tok),
4866                ));
4867            }
4868        };
4869        Ok((action, Some(message)))
4870    }
4871
4872    fn parse_type_name(&mut self) -> Result<TypeName, ParseError> {
4873        let mut parts = Vec::new();
4874        loop {
4875            match self.peek_kind() {
4876                TokenKind::Id(_) | TokenKind::QuotedId(_, _) => {
4877                    let tok = self.advance_token();
4878                    if let TokenKind::Id(s) | TokenKind::QuotedId(s, _) = &tok.kind {
4879                        parts.push(s.to_string());
4880                    } else {
4881                        unreachable!();
4882                    }
4883                }
4884                k if is_nonreserved_kw(k) => {
4885                    let tok = self.advance_token();
4886                    parts.push(kw_to_str(&tok.kind));
4887                }
4888                _ => break,
4889            }
4890        }
4891        if parts.is_empty() {
4892            return Err(self.err_here("expected type name"));
4893        }
4894        let name = parts.join(" ");
4895
4896        let (arg1, arg2) = if self.eat_kind(&TokenKind::LeftParen) {
4897            let a1 = self.parse_type_arg()?;
4898            let a2 = if self.eat_kind(&TokenKind::Comma) {
4899                Some(self.parse_type_arg()?)
4900            } else {
4901                None
4902            };
4903            self.expect_kind(&TokenKind::RightParen)?;
4904            (Some(a1), a2)
4905        } else {
4906            (None, None)
4907        };
4908
4909        Ok(TypeName { name, arg1, arg2 })
4910    }
4911
4912    fn parse_type_arg(&mut self) -> Result<String, ParseError> {
4913        let tok = self.advance_token();
4914        match &tok.kind {
4915            TokenKind::Integer(i) => Ok(i.to_string()),
4916            TokenKind::Float(f) => Ok(f.to_string()),
4917            TokenKind::Minus => {
4918                let next = self.advance_token();
4919                match &next.kind {
4920                    TokenKind::Integer(i) => Ok(format!("-{i}")),
4921                    TokenKind::OversizedInt(s) => Ok(format!("-{s}")),
4922                    TokenKind::Float(f) => Ok(format!("-{f}")),
4923                    _ => Err(ParseError::at(
4924                        "expected number in type argument",
4925                        Some(&next),
4926                    )),
4927                }
4928            }
4929            TokenKind::Plus => {
4930                let next = self.advance_token();
4931                match &next.kind {
4932                    TokenKind::Integer(i) => Ok(format!("+{i}")),
4933                    TokenKind::OversizedInt(s) => Ok(format!("+{s}")),
4934                    TokenKind::Float(f) => Ok(format!("+{f}")),
4935                    _ => Err(ParseError::at(
4936                        "expected number in type argument",
4937                        Some(&next),
4938                    )),
4939                }
4940            }
4941            TokenKind::OversizedInt(s) => Ok(s.clone()),
4942            TokenKind::Id(s) | TokenKind::QuotedId(s, _) => Ok(s.to_string()),
4943            _ => Err(ParseError::at("expected type argument", Some(&tok))),
4944        }
4945    }
4946
4947    /// Subquery parser for EXISTS/IN expression support.
4948    #[cfg(test)]
4949    fn parse_subquery_minimal(&mut self) -> Result<HeightTracked<SelectStatement>, ParseError> {
4950        let with = if self.at_kind(&TokenKind::KwWith) {
4951            Some(ParseMachine::for_with(self).run_with()?)
4952        } else {
4953            None
4954        };
4955        ParseMachine::for_select(self, with).run_select()
4956    }
4957}
4958
4959/// Parse a single expression from raw SQL text.
4960pub fn parse_expr(sql: &str) -> Result<Expr, ParseError> {
4961    let mut parser = Parser::from_sql(sql);
4962    let expr = parser.parse_expr()?;
4963    let _ = parser.eat(&TokenKind::Semicolon);
4964    if !parser.at_eof() {
4965        return Err(parser.err_here(format!(
4966            "unexpected token after expression: {:?}",
4967            parser.peek_kind()
4968        )));
4969    }
4970    Ok(expr)
4971}
4972
4973#[cfg(test)]
4974mod tests {
4975    use super::*;
4976    use crate::parser::ParseErrorKind;
4977    use fsqlite_ast::{BoundCollation, SelectCore, TableOrSubquery};
4978    use fsqlite_types::{SqliteValue, TypeAffinity};
4979
4980    fn parse(sql: &str) -> Expr {
4981        match parse_expr(sql) {
4982            Ok(expr) => expr,
4983            Err(err) => unreachable!("parse error for `{sql}`: {err}"),
4984        }
4985    }
4986
4987    fn repeated_infix_expression(term_count: usize, operator: &str) -> String {
4988        std::iter::repeat_n("1", term_count)
4989            .collect::<Vec<_>>()
4990            .join(operator)
4991    }
4992
4993    fn assert_expression_depth_error(sql: &str) {
4994        let error = parse_expr(sql).expect_err("expression height 1001 must fail closed");
4995        assert_eq!(
4996            error.kind,
4997            ParseErrorKind::ExpressionTooDeep {
4998                max: MAX_PARSE_DEPTH
4999            }
5000        );
5001        assert_eq!(
5002            error.message,
5003            format!(
5004                "Expression tree is too large (maximum depth {})",
5005                MAX_PARSE_DEPTH
5006            )
5007        );
5008        assert!(error.is_expression_too_deep());
5009    }
5010
5011    fn right_deep_binary_expression(height: usize) -> String {
5012        format!("{}1{}", "1 + (".repeat(height - 1), ")".repeat(height - 1))
5013    }
5014
5015    fn single_arg_function_expression(height: usize) -> String {
5016        format!("{}1{}", "abs(".repeat(height - 1), ")".repeat(height - 1))
5017    }
5018
5019    fn scalar_subquery_expression(height: usize) -> String {
5020        format!(
5021            "{}1{}",
5022            "(SELECT ".repeat(height - 1),
5023            ")".repeat(height - 1)
5024        )
5025    }
5026
5027    fn on_one_mib_stack<T: Send + 'static>(task: impl FnOnce() -> T + Send + 'static) -> T {
5028        std::thread::Builder::new()
5029            .stack_size(1024 * 1024)
5030            .spawn(task)
5031            .expect("1 MiB parser thread must spawn")
5032            .join()
5033            .expect("parser task must not overflow or panic")
5034    }
5035
5036    fn parsed_select_height(sql: &str) -> u32 {
5037        let mut parser = Parser::from_sql(sql);
5038        let select = parser
5039            .parse_subquery_minimal()
5040            .expect("SELECT fixture must parse");
5041        assert_eq!(
5042            select.height,
5043            normalized_ast_select_height(&select.value),
5044            "tracked SELECT height diverged from the normalized AST test oracle"
5045        );
5046        select.height
5047    }
5048
5049    fn parsed_expr_height(sql: &str) -> u32 {
5050        let mut parser = Parser::from_sql(sql);
5051        let parsed = parser
5052            .parse_expr_tracked()
5053            .expect("expression fixture must parse");
5054        assert!(
5055            matches!(parser.peek_kind(), TokenKind::Eof | TokenKind::Semicolon),
5056            "expression fixture left an unparsed token: {sql}"
5057        );
5058        assert_eq!(
5059            parsed.height,
5060            normalized_ast_expr_height(&parsed.expr),
5061            "tracked expression height diverged from the normalized AST test oracle: {sql}"
5062        );
5063        parsed.height
5064    }
5065
5066    #[test]
5067    fn bound_outer_value_has_constant_leaf_facts() {
5068        let expr = Expr::BoundOuterValue {
5069            value: SqliteValue::Integer(42),
5070            collation: BoundCollation::Named("NOCASE".to_owned()),
5071            affinity: Some(TypeAffinity::Integer),
5072            span: Span::ZERO,
5073        };
5074
5075        let parsed = ParsedExpr::leaf(expr.clone());
5076        assert_eq!(parsed.height, 1);
5077        assert!(parsed.is_constant);
5078        assert!(!parsed.has_function);
5079
5080        let facts = cached_facts_from_tasks(vec![CachedHeightTask::Expr(&expr)]);
5081        assert_eq!(facts.height, 1);
5082        assert!(facts.is_constant);
5083        assert!(!facts.has_function);
5084    }
5085
5086    fn mixed_deep_expression(height: usize) -> String {
5087        let mut prefix = String::new();
5088        let mut closing_count = 0;
5089        for index in 1..height {
5090            if index % 7 == 0 {
5091                prefix.push('(');
5092                closing_count += 1;
5093            }
5094            match index % 4 {
5095                0 => prefix.push('~'),
5096                1 => {
5097                    prefix.push_str("abs(");
5098                    closing_count += 1;
5099                }
5100                2 => {
5101                    prefix.push_str("(SELECT ");
5102                    closing_count += 1;
5103                }
5104                _ => {
5105                    prefix.push_str("1 + (");
5106                    closing_count += 1;
5107                }
5108            }
5109        }
5110        prefix.push('1');
5111        prefix.push_str(&")".repeat(closing_count));
5112        prefix
5113    }
5114
5115    fn assert_machine_matches_recursive_oracle(sql: &str) {
5116        let mut machine = Parser::from_sql(sql);
5117        let machine_result = machine.parse_expr_tracked();
5118        let machine_pos = machine.pos;
5119        let machine_tail = machine.peek_kind().clone();
5120
5121        let mut oracle = Parser::from_sql(sql);
5122        let oracle_result = oracle.parse_expr_bp(0);
5123        let oracle_pos = oracle.pos;
5124        let oracle_tail = oracle.peek_kind().clone();
5125
5126        assert_eq!(
5127            machine_pos, oracle_pos,
5128            "parser tail position differs: {sql}"
5129        );
5130        assert_eq!(
5131            machine_tail, oracle_tail,
5132            "parser tail token differs: {sql}"
5133        );
5134        match (machine_result, oracle_result) {
5135            (Ok(machine), Ok(oracle)) => {
5136                assert_eq!(machine.expr, oracle.expr, "AST or spans differ: {sql}");
5137                assert_eq!(machine.height, oracle.height, "height differs: {sql}");
5138                assert_eq!(
5139                    machine.is_constant, oracle.is_constant,
5140                    "constant fact differs: {sql}"
5141                );
5142                assert_eq!(
5143                    machine.has_function, oracle.has_function,
5144                    "function fact differs: {sql}"
5145                );
5146                assert_eq!(machine.root, oracle.root, "root fact differs: {sql}");
5147            }
5148            (Err(machine), Err(oracle)) => {
5149                assert_eq!(machine, oracle, "diagnostic differs: {sql}");
5150            }
5151            (Ok(_), Err(_)) | (Err(_), Ok(_)) => {
5152                panic!("machine/oracle result class differs for `{sql}`");
5153            }
5154        }
5155    }
5156
5157    fn assert_select_machine_matches_recursive_oracle(sql: &str) {
5158        let mut machine = Parser::from_sql(sql);
5159        let machine_result = machine.parse_select_stmt_tracked(None);
5160        let machine_pos = machine.pos;
5161        let machine_tail = machine.peek_kind().clone();
5162
5163        let mut oracle = Parser::from_sql(sql);
5164        let oracle_result = oracle.parse_select_stmt_inner_tracked(None);
5165        let oracle_pos = oracle.pos;
5166        let oracle_tail = oracle.peek_kind().clone();
5167
5168        assert_eq!(
5169            machine_pos, oracle_pos,
5170            "SELECT parser tail position differs: {sql}"
5171        );
5172        assert_eq!(
5173            machine_tail, oracle_tail,
5174            "SELECT parser tail token differs: {sql}"
5175        );
5176        match (machine_result, oracle_result) {
5177            (Ok(machine), Ok(oracle)) => {
5178                assert_eq!(machine.value, oracle.value, "SELECT AST differs: {sql}");
5179                assert_eq!(
5180                    machine.height, oracle.height,
5181                    "SELECT height differs: {sql}"
5182                );
5183            }
5184            (Err(machine), Err(oracle)) => {
5185                assert_eq!(machine, oracle, "SELECT diagnostic differs: {sql}");
5186            }
5187            (Ok(_), Err(_)) | (Err(_), Ok(_)) => {
5188                panic!("SELECT machine/oracle result class differs for `{sql}`");
5189            }
5190        }
5191    }
5192
5193    fn bitnot_depth(mut expr: &Expr) -> usize {
5194        let mut depth = 0;
5195        while let Expr::UnaryOp {
5196            op: UnaryOp::BitNot,
5197            expr: inner,
5198            ..
5199        } = expr
5200        {
5201            depth += 1;
5202            expr = inner;
5203        }
5204        depth
5205    }
5206
5207    fn wrap_function_to_height(base: &str, target_height: usize) -> String {
5208        let base_height = parsed_expr_height(base) as usize;
5209        assert!(base_height <= target_height);
5210        let wrappers = target_height - base_height;
5211        format!("{}{base}{}", "abs(".repeat(wrappers), ")".repeat(wrappers))
5212    }
5213
5214    #[test]
5215    fn test_explicit_machine_matches_recursive_oracle_for_shallow_valid_expressions() {
5216        for sql in [
5217            "1",
5218            "-9223372036854775808",
5219            "a.b + c * 2",
5220            "'a'.b + a.'b'",
5221            "attach.x",
5222            "filter.x",
5223            "true.x",
5224            "with.x",
5225            "a.current_date",
5226            "NOT a = b",
5227            "x IS NULL < 2",
5228            "x IS NOT DISTINCT FROM y",
5229            "CAST(x + 1 AS DECIMAL(10, 2))",
5230            "CASE x WHEN 1 THEN y ELSE z END",
5231            "x NOT BETWEEN 1 AND 2",
5232            "x IN (1, 2 + 3)",
5233            "(SELECT 1, 2) IN ((1, 2))",
5234            "(a, b) IN ((SELECT 1))",
5235            "(a, b) IN ((SELECT 1, 2, 3))",
5236            "(SELECT * FROM t) IN (1)",
5237            "x IN (SELECT y FROM t WHERE z > 0 ORDER BY y LIMIT 1)",
5238            "EXISTS (SELECT 1 FROM t WHERE x = y)",
5239            "(1, 2 + 3)",
5240            "value COLLATE \"my col\"",
5241            "doc -> '$.x' || suffix",
5242            "sum(DISTINCT x ORDER BY y DESC) FILTER (WHERE z > 0) OVER (PARTITION BY p ORDER BY q ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)",
5243        ] {
5244            assert_machine_matches_recursive_oracle(sql);
5245        }
5246    }
5247
5248    #[test]
5249    fn test_explicit_machine_matches_recursive_oracle_for_shallow_malformed_expressions() {
5250        for sql in [
5251            "+",
5252            "CASE END",
5253            "CASE WHEN 1 2 END",
5254            "CASE WHEN 1 THEN 2",
5255            "CAST(1 INTEGER)",
5256            "f(DISTINCT)",
5257            "(1,)",
5258            "a.",
5259            "a.select",
5260            "a.nothing",
5261            "cast.x",
5262            "current_date.x",
5263            "raise.x",
5264            "transaction.x",
5265            "a BETWEEN 1 2",
5266            "a IN (1,)",
5267            "(a, b) IN (1)",
5268            "(a, b) IN (+(SELECT 1, 2))",
5269            "(a, b) IN ((SELECT 1), (SELECT 2))",
5270            "(a, b) IN ((1, 2), 3)",
5271            "(a, b) NOT IN ((1, 2, 3))",
5272            "(SELECT 1, 2) IN (1)",
5273            "(SELECT 1, 2) IN ((1, 2), 3)",
5274            "(SELECT 1, 2) NOT IN ((1, 2, 3))",
5275            "a LIKE",
5276            "EXISTS (SELECT)",
5277            "count(* ORDER BY x)",
5278            "t.*",
5279        ] {
5280            assert_machine_matches_recursive_oracle(sql);
5281        }
5282    }
5283
5284    #[test]
5285    fn public_parse_expr_requires_eof_after_one_optional_terminator() {
5286        assert_eq!(
5287            parse_expr("1;")
5288                .expect("one trailing expression terminator must remain valid")
5289                .to_string(),
5290            "1"
5291        );
5292
5293        for (sql, unexpected) in [("1; 2", "2"), ("1; SELECT 2", "SELECT"), ("1;;", ";")] {
5294            let error = parse_expr(sql)
5295                .expect_err("tokens after the optional expression terminator must be rejected");
5296            assert_eq!(error.kind, ParseErrorKind::Syntax);
5297            assert!(
5298                error.message.contains("unexpected token after expression"),
5299                "unexpected diagnostic for `{sql}`: {error:?}"
5300            );
5301            assert_eq!(
5302                &sql[error.span.start as usize..error.span.end as usize],
5303                unexpected,
5304                "the diagnostic for `{sql}` must point at the first forbidden token"
5305            );
5306        }
5307    }
5308
5309    #[test]
5310    fn test_explicit_select_machine_matches_recursive_shallow_oracle() {
5311        for sql in [
5312            "SELECT 1",
5313            "SELECT DISTINCT t.x AS y, count(*) FROM t INNER JOIN u ON t.id = u.id WHERE t.x > 0 GROUP BY t.x HAVING count(*) > 1 WINDOW w AS (PARTITION BY t.p ORDER BY t.q ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ORDER BY t.x DESC NULLS LAST LIMIT 5 OFFSET 1",
5314            "SELECT * FROM a CROSS JOIN b ON a.id = b.id",
5315            "SELECT * FROM a, b",
5316            "SELECT * FROM a, b ON a.id = b.id",
5317            "SELECT * FROM a, b USING(id)",
5318            "SELECT filter.* FROM t AS filter",
5319            "SELECT attach.* FROM t AS \"attach\"",
5320            "SELECT attach.x FROM (SELECT 1 AS x) AS \"attach\"",
5321            "SELECT filter.x FROM (SELECT 1 AS x) AS \"filter\"",
5322            "SELECT 't'.*, t.'select' FROM t",
5323            "SELECT 1 'single quoted'",
5324            "SELECT 1 attach",
5325            "SELECT 1 window",
5326            "SELECT * FROM (SELECT 1) 'single quoted'",
5327            "SELECT * FROM (SELECT 1) match",
5328            "SELECT sum(1) OVER attach WINDOW attach AS ()",
5329            "SELECT sum(1) OVER (attach) WINDOW attach AS ()",
5330            "SELECT sum(x) OVER (ROWS BETWEEN 1 PRECEDING AND 2 PRECEDING) FROM t",
5331            "SELECT sum(x) OVER (RANGE BETWEEN CURRENT ROW AND 1 FOLLOWING) FROM t",
5332            "VALUES (1, 2), (3, 4)",
5333            "VALUES (1) ORDER BY 1",
5334            "VALUES (1) LIMIT 1",
5335            "VALUES (1) UNION SELECT 2 ORDER BY 1 LIMIT 1",
5336            "SELECT 1 UNION ALL SELECT 2 INTERSECT SELECT 3",
5337            "SELECT 1 UNION VALUES (2), (3) ORDER BY 1",
5338            "SELECT FROM t",
5339            "SELECT nothing.* FROM t AS \"nothing\"",
5340            "SELECT sum(1) OVER filter",
5341            "SELECT sum(x) OVER (ROWS 1 FOLLOWING) FROM t",
5342            "SELECT sum(x) OVER (ROWS BETWEEN CURRENT ROW AND 1 PRECEDING) FROM t",
5343            "VALUES 1",
5344        ] {
5345            assert_select_machine_matches_recursive_oracle(sql);
5346        }
5347    }
5348
5349    #[test]
5350    fn test_threshold_unary_precedence_associativity_and_spans_are_stable() {
5351        for unary_count in [63_u32, 64] {
5352            let sql = format!("{}1 + (1)", "~".repeat(unary_count as usize));
5353            let parsed = parse_expr(&sql).expect("threshold unary-plus expression must parse");
5354            assert_eq!(
5355                parsed.span(),
5356                Span::new(0, unary_count + 6),
5357                "grouping delimiters must not change the established root span"
5358            );
5359            let Expr::BinaryOp {
5360                left,
5361                op: BinaryOp::Add,
5362                right,
5363                ..
5364            } = &parsed
5365            else {
5366                panic!("unary prefix must not capture the lower-precedence addition");
5367            };
5368            assert_eq!(bitnot_depth(left), unary_count as usize);
5369            assert!(matches!(
5370                right.as_ref(),
5371                Expr::Literal(Literal::Integer(1), _)
5372            ));
5373
5374            let subtraction = format!("{}10 - 3 - 2", "~".repeat(unary_count as usize));
5375            let subtraction =
5376                parse_expr(&subtraction).expect("threshold subtraction expression must parse");
5377            assert!(matches!(
5378                subtraction,
5379                Expr::BinaryOp {
5380                    op: BinaryOp::Subtract,
5381                    left,
5382                    ..
5383                } if matches!(
5384                    left.as_ref(),
5385                    Expr::BinaryOp {
5386                        op: BinaryOp::Subtract,
5387                        ..
5388                    }
5389                )
5390            ));
5391
5392            let precedence = format!("{}1 + 2 * 3", "~".repeat(unary_count as usize));
5393            let precedence =
5394                parse_expr(&precedence).expect("threshold precedence expression must parse");
5395            assert!(matches!(
5396                precedence,
5397                Expr::BinaryOp {
5398                    op: BinaryOp::Add,
5399                    right,
5400                    ..
5401                } if matches!(
5402                    right.as_ref(),
5403                    Expr::BinaryOp {
5404                        op: BinaryOp::Multiply,
5405                        ..
5406                    }
5407                )
5408            ));
5409
5410            let parenthesized = format!(
5411                "{}1{}",
5412                "(".repeat(unary_count as usize),
5413                ")".repeat(unary_count as usize)
5414            );
5415            let parenthesized =
5416                parse_expr(&parenthesized).expect("threshold parentheses must parse");
5417            assert_eq!(
5418                parenthesized.span(),
5419                Span::new(unary_count, unary_count + 1)
5420            );
5421        }
5422    }
5423
5424    #[test]
5425    fn test_shallow_machine_uses_only_inline_control_and_value_storage() {
5426        PARSE_MACHINE_STACK_SPILLS.set(0);
5427        let expr = parse_expr("a + b * 2").expect("shallow expression must parse");
5428        assert_eq!(expr.to_string(), "a + b * 2");
5429        assert_eq!(
5430            PARSE_MACHINE_STACK_SPILLS.get(),
5431            0,
5432            "representative shallow parsing must not allocate parser stack spill storage"
5433        );
5434    }
5435
5436    #[test]
5437    fn test_formatter_precedence_associativity_and_migration_scale_stability() {
5438        for (sql, expected) in [
5439            ("a + b * 2", "a + b * 2"),
5440            ("a * (b + c)", "a * (b + c)"),
5441            ("(a - b) - c", "a - b - c"),
5442            ("a - (b - c)", "a - (b - c)"),
5443            ("a / (b / c)", "a / (b / c)"),
5444        ] {
5445            let expr = parse_expr(sql).expect("precedence fixture must parse");
5446            let rendered = expr.to_string();
5447            assert_eq!(rendered, expected);
5448            let reparsed = parse_expr(&rendered).expect("formatted fixture must reparse");
5449            assert_eq!(reparsed.to_string(), rendered);
5450        }
5451
5452        const TERM_COUNT: usize = MAX_PARSE_DEPTH as usize;
5453        for operator in ["AND", "OR"] {
5454            let mut sql = String::new();
5455            for _ in 1..TERM_COUNT {
5456                sql.push_str("flag ");
5457                sql.push_str(operator);
5458                sql.push_str(" (");
5459            }
5460            sql.push_str("flag");
5461            sql.push_str(&")".repeat(TERM_COUNT - 1));
5462
5463            let expr = parse_expr(&sql).expect("migration-scale associative chain must parse");
5464            let rendered = expr.to_string();
5465            assert_eq!(rendered.matches(operator).count(), TERM_COUNT - 1);
5466            assert!(
5467                !rendered.contains('('),
5468                "associative {operator} chain must have a flat canonical form"
5469            );
5470            let reparsed = parse_expr(&rendered).expect("flat migration-scale chain must reparse");
5471            assert_eq!(
5472                reparsed.to_string(),
5473                rendered,
5474                "format-parse-format must be byte-stable for {operator}"
5475            );
5476        }
5477    }
5478
5479    #[test]
5480    fn test_expression_height_exact_1000_1001_flat_infix_boundary() {
5481        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5482        let at_limit = repeated_infix_expression(LIMIT, " + ");
5483        let expr = parse_expr(&at_limit).expect("1000-term left-associated tree has height 1000");
5484        let rendered = expr.to_string();
5485        parse_expr(&rendered).expect("formatted height-1000 expression must remain parseable");
5486
5487        let over_limit = repeated_infix_expression(LIMIT + 1, " + ");
5488        assert_expression_depth_error(&over_limit);
5489    }
5490
5491    #[test]
5492    fn test_expression_height_exact_1000_1001_unary_boundary() {
5493        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5494        let at_limit = format!("{}1", "~".repeat(LIMIT - 1));
5495        parse_expr(&at_limit).expect("999 unary nodes plus one leaf have height 1000");
5496
5497        let over_limit = format!("{}1", "~".repeat(LIMIT));
5498        assert_expression_depth_error(&over_limit);
5499    }
5500
5501    #[test]
5502    fn test_expression_height_exact_1000_1001_not_boundary() {
5503        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5504        let at_limit = format!("{}1", "NOT ".repeat(LIMIT - 1));
5505        parse_expr(&at_limit).expect("999 NOT nodes plus one leaf have height 1000");
5506
5507        let over_limit = format!("{}1", "NOT ".repeat(LIMIT));
5508        assert_expression_depth_error(&over_limit);
5509    }
5510
5511    #[test]
5512    fn test_expression_parenthesis_chain_is_stack_safe() {
5513        const PAREN_PAIRS: usize = MAX_PARSE_DEPTH as usize * 2;
5514        let deeply_parenthesized =
5515            format!("{}1{}", "(".repeat(PAREN_PAIRS), ")".repeat(PAREN_PAIRS));
5516        parse_expr(&deeply_parenthesized)
5517            .expect("parentheses do not add AST height or consume the native call stack");
5518    }
5519
5520    #[test]
5521    fn test_expression_prefix_frames_preserve_grouping_and_row_values() {
5522        let grouped = parse_expr("-((1 + 2)) * 3").expect("grouped unary expression must parse");
5523        assert!(matches!(
5524            grouped,
5525            Expr::BinaryOp {
5526                left,
5527                op: BinaryOp::Multiply,
5528                ..
5529            } if matches!(
5530                left.as_ref(),
5531                Expr::UnaryOp {
5532                    op: UnaryOp::Negate,
5533                    ..
5534                }
5535            )
5536        ));
5537
5538        let row = parse_expr("((1), 2)").expect("nested row value must parse");
5539        assert!(matches!(row, Expr::RowValue(values, _) if values.len() == 2));
5540    }
5541
5542    #[test]
5543    fn test_expression_iterative_prefix_frames_preserve_minimum_integer_literal() {
5544        let mut parser = Parser::from_sql("-9223372036854775808");
5545        let parsed = parser
5546            .parse_expr_tracked()
5547            .expect("minimum signed integer literal must parse");
5548        assert!(matches!(
5549            &parsed.expr,
5550            Expr::Literal(Literal::Integer(i64::MIN), _)
5551        ));
5552        assert_eq!(parsed.height, 1);
5553        assert_eq!(parsed.expr.to_string(), "-9223372036854775808");
5554    }
5555
5556    #[test]
5557    fn test_serializer_regression_double_negated_minimum_integer_round_trips() {
5558        let expr = parse_expr("- -9223372036854775808")
5559            .expect("double-negated minimum integer must parse");
5560        let rendered = expr.to_string();
5561        assert_eq!(rendered, "-(-9223372036854775808)");
5562        let reparsed = parse_expr(&rendered).expect("rendered double negation must remain SQL");
5563        assert_eq!(expr, reparsed);
5564    }
5565
5566    #[test]
5567    fn test_serializer_regression_quoted_collation_name_round_trips() {
5568        let expr =
5569            parse_expr("value COLLATE \"my col\"").expect("quoted collation name must parse");
5570        let rendered = expr.to_string();
5571        assert_eq!(rendered, "value COLLATE \"my col\"");
5572        let reparsed = parse_expr(&rendered).expect("rendered collation must remain SQL");
5573        assert_eq!(expr, reparsed);
5574    }
5575
5576    #[test]
5577    fn test_expression_height_mixed_prefix_and_flat_reductions() {
5578        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5579        const PREFIX_COUNT: usize = 499;
5580        let at_limit = format!(
5581            "{}{}",
5582            "~".repeat(PREFIX_COUNT),
5583            repeated_infix_expression(LIMIT - PREFIX_COUNT, " - ")
5584        );
5585        parse_expr(&at_limit)
5586            .expect("mixed unary and left-associated reductions have exact height 1000");
5587
5588        let over_limit = format!(
5589            "{}{}",
5590            "~".repeat(PREFIX_COUNT),
5591            repeated_infix_expression(LIMIT - PREFIX_COUNT + 1, " - ")
5592        );
5593        assert_expression_depth_error(&over_limit);
5594    }
5595
5596    #[test]
5597    fn test_expression_height_container_adds_one_level() {
5598        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5599        let at_limit = format!("abs({})", repeated_infix_expression(LIMIT - 1, " + "));
5600        parse_expr(&at_limit).expect("function node over height-999 argument has height 1000");
5601
5602        let over_limit = format!("abs({})", repeated_infix_expression(LIMIT, " + "));
5603        assert_expression_depth_error(&over_limit);
5604    }
5605
5606    #[test]
5607    fn test_right_deep_binary_exact_1000_1001_boundary_on_one_mib_stack() {
5608        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5609        let at_limit = right_deep_binary_expression(LIMIT);
5610        let expr = on_one_mib_stack(move || {
5611            parse_expr(&at_limit).expect("right-deep height-1000 expression must parse")
5612        });
5613        assert_expression_depth_error(&right_deep_binary_expression(LIMIT + 1));
5614        drop(expr);
5615    }
5616
5617    #[test]
5618    fn test_single_arg_function_exact_1000_1001_boundary_on_one_mib_stack() {
5619        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5620        let at_limit = single_arg_function_expression(LIMIT);
5621        let expr = on_one_mib_stack(move || {
5622            parse_expr(&at_limit).expect("nested function height-1000 expression must parse")
5623        });
5624        assert_expression_depth_error(&single_arg_function_expression(LIMIT + 1));
5625        drop(expr);
5626    }
5627
5628    #[test]
5629    fn test_scalar_subquery_exact_1000_1001_boundary_on_one_mib_stack() {
5630        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5631        let at_limit = scalar_subquery_expression(LIMIT);
5632        let rendered_selects = on_one_mib_stack(move || {
5633            let expr =
5634                parse_expr(&at_limit).expect("scalar subquery height-1000 expression must parse");
5635            let rendered = expr.to_string();
5636            let select_count = rendered.matches("(SELECT ").count();
5637            drop(rendered);
5638            drop(expr);
5639            select_count
5640        });
5641        assert_eq!(rendered_selects, LIMIT - 1);
5642        assert_expression_depth_error(&scalar_subquery_expression(LIMIT + 1));
5643    }
5644
5645    #[test]
5646    fn test_signed_minimum_and_qualified_bases_round_trip_on_one_mib_stack() {
5647        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5648        for (base, canonical_base) in [
5649            ("-9223372036854775808", "-9223372036854775808"),
5650            ("schema.column", "schema.\"column\""),
5651        ] {
5652            let at_limit = wrap_function_to_height(base, LIMIT);
5653            let (height, rendered) = on_one_mib_stack(move || {
5654                let expr = parse_expr(&at_limit).expect("height-1000 base expression must parse");
5655                let height = normalized_ast_expr_height(&expr);
5656                let rendered = expr.to_string();
5657                let reparsed =
5658                    parse_expr(&rendered).expect("formatted height-1000 base must reparse");
5659                assert_eq!(normalized_ast_expr_height(&reparsed), MAX_PARSE_DEPTH);
5660                assert_eq!(rendered, reparsed.to_string());
5661                drop(reparsed);
5662                drop(expr);
5663                (height, rendered)
5664            });
5665            assert_eq!(height, MAX_PARSE_DEPTH);
5666            assert!(rendered.contains(canonical_base));
5667
5668            let over_limit = wrap_function_to_height(base, LIMIT + 1);
5669            let error = on_one_mib_stack(move || {
5670                parse_expr(&over_limit).expect_err("height-1001 base must fail closed")
5671            });
5672            assert_eq!(
5673                error.kind,
5674                ParseErrorKind::ExpressionTooDeep {
5675                    max: MAX_PARSE_DEPTH
5676                }
5677            );
5678        }
5679    }
5680
5681    #[test]
5682    fn test_mixed_deep_height_1000_parse_walk_format_and_drop_on_one_mib_stack() {
5683        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5684        let at_limit = mixed_deep_expression(LIMIT);
5685        let (height, rendered_len, walk_visits) = on_one_mib_stack(move || {
5686            let expr = parse_expr(&at_limit)
5687                .expect("mixed unary/function/subquery/binary height-1000 expression must parse");
5688            HEIGHT_WALK_VISITS.set(0);
5689            let height = normalized_ast_expr_height(&expr);
5690            let walk_visits = HEIGHT_WALK_VISITS.get();
5691            let rendered = expr.to_string();
5692            let rendered_len = rendered.len();
5693            drop(rendered);
5694            drop(expr);
5695            (height, rendered_len, walk_visits)
5696        });
5697        assert_eq!(height, MAX_PARSE_DEPTH);
5698        assert!(rendered_len > LIMIT);
5699        assert!(
5700            walk_visits <= at_limit_token_bound(LIMIT),
5701            "heap-backed cached-height walk must remain linear: {walk_visits} visits"
5702        );
5703
5704        let over_limit = mixed_deep_expression(LIMIT + 1);
5705        let error = on_one_mib_stack(move || {
5706            parse_expr(&over_limit).expect_err("mixed height-1001 expression must fail closed")
5707        });
5708        assert_eq!(
5709            error.kind,
5710            ParseErrorKind::ExpressionTooDeep {
5711                max: MAX_PARSE_DEPTH
5712            }
5713        );
5714    }
5715
5716    #[test]
5717    fn test_mixed_select_shape_round_trips_and_drops_on_one_mib_stack_at_exact_limit() {
5718        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5719        let base = "CASE WHEN 5 BETWEEN 1 AND 9 THEN 3 IN (WITH c AS (SELECT x FROM t) SELECT sum(c.x) OVER (PARTITION BY u.p ORDER BY u.q ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM c INNER JOIN u ON c.x = u.x WHERE u.x > 0 GROUP BY c.x HAVING count(*) > 0 ORDER BY c.x LIMIT 1) ELSE 0 END";
5720        let at_limit = wrap_function_to_height(base, LIMIT);
5721        let (height, rendered_len) = on_one_mib_stack(move || {
5722            let expr = parse_expr(&at_limit)
5723                .expect("mixed CASE/BETWEEN/IN/CTE/JOIN/window height-1000 expression must parse");
5724            assert_eq!(normalized_ast_expr_height(&expr), MAX_PARSE_DEPTH);
5725            let rendered = expr.to_string();
5726            let reparsed =
5727                parse_expr(&rendered).expect("formatted mixed height-1000 expression must reparse");
5728            assert_eq!(normalized_ast_expr_height(&reparsed), MAX_PARSE_DEPTH);
5729            let rerendered = reparsed.to_string();
5730            assert_eq!(rendered, rerendered);
5731            let rendered_len = rendered.len();
5732            drop(rerendered);
5733            drop(reparsed);
5734            drop(rendered);
5735            drop(expr);
5736            (MAX_PARSE_DEPTH, rendered_len)
5737        });
5738        assert_eq!(height, MAX_PARSE_DEPTH);
5739        assert!(rendered_len > LIMIT);
5740
5741        let over_limit = wrap_function_to_height(base, LIMIT + 1);
5742        let error = on_one_mib_stack(move || {
5743            parse_expr(&over_limit).expect_err("mixed height-1001 expression must fail closed")
5744        });
5745        assert_eq!(
5746            error.kind,
5747            ParseErrorKind::ExpressionTooDeep {
5748                max: MAX_PARSE_DEPTH
5749            }
5750        );
5751        assert_eq!(
5752            error.message,
5753            format!(
5754                "Expression tree is too large (maximum depth {})",
5755                MAX_PARSE_DEPTH
5756            )
5757        );
5758    }
5759
5760    const fn at_limit_token_bound(height: usize) -> usize {
5761        height * 12
5762    }
5763
5764    #[test]
5765    fn test_998_wrapper_aggregate_filter_machine_steps_are_linear() {
5766        const WRAPPERS: usize = 998;
5767        let sql = format!(
5768            "{}1{} FILTER (WHERE 1)",
5769            "abs(".repeat(WRAPPERS),
5770            ")".repeat(WRAPPERS)
5771        );
5772        let token_count = Parser::from_sql(&sql).tokens.len();
5773        PARSE_MACHINE_STEPS.set(0);
5774        let expr = parse_expr(&sql).expect("near-match aggregate FILTER expression must parse");
5775        let visits = PARSE_MACHINE_STEPS.get();
5776        assert!(
5777            visits <= token_count.saturating_mul(8),
5778            "explicit parser machine revisited tokens superlinearly: {visits} steps for {token_count} tokens"
5779        );
5780        drop(expr);
5781    }
5782
5783    #[test]
5784    fn test_aggregate_order_by_auxiliary_roots_do_not_rescan_descendants() {
5785        let mut sql = "1".to_owned();
5786        for _ in 0..64 {
5787            sql = format!("f(0 ORDER BY {sql})");
5788        }
5789
5790        HEIGHT_WALK_VISITS.set(0);
5791        parse_expr(&sql).expect("nested aggregate ORDER BY expression must parse");
5792        assert_eq!(
5793            HEIGHT_WALK_VISITS.get(),
5794            0,
5795            "independent aggregate ORDER BY roots must not walk completed ASTs"
5796        );
5797    }
5798
5799    #[test]
5800    fn test_subquery_height_contract_matches_sqlite_height_of_select_fields() {
5801        for (sql, expected_height) in [
5802            ("SELECT 1 + 2 + 3", 3),
5803            ("SELECT 0 WHERE 1 + 2 + 3", 3),
5804            ("SELECT 0 GROUP BY 1 + 2 + 3 HAVING 1 + 2 + 3", 3),
5805            (
5806                "SELECT 0 ORDER BY 1 + 2 + 3 LIMIT 1 + 2 + 3 OFFSET 1 + 2 + 3",
5807                4,
5808            ),
5809            ("VALUES (1 + 2 + 3)", 3),
5810            ("SELECT 0 UNION ALL SELECT 1 + 2 + 3", 3),
5811        ] {
5812            assert_eq!(
5813                parsed_select_height(sql),
5814                expected_height,
5815                "official SELECT expression-height field was omitted: {sql}"
5816            );
5817        }
5818
5819        for sql in [
5820            "WITH c AS (SELECT 1 + 2 + 3) SELECT 0",
5821            "SELECT 0 FROM (SELECT 1 + 2 + 3)",
5822            "SELECT 0 FROM json_each(1 + 2 + 3)",
5823            "SELECT 0 FROM a JOIN b ON 1 + 2 + 3",
5824            "SELECT 0 WINDOW w AS (PARTITION BY 1 + 2 + 3 ORDER BY 1 + 2 + 3)",
5825        ] {
5826            assert_eq!(
5827                parsed_select_height(sql),
5828                1,
5829                "independent SQL root was incorrectly charged to its enclosing SELECT: {sql}"
5830            );
5831        }
5832    }
5833
5834    #[test]
5835    fn test_cached_height_matches_sqlite_grammar_rewrites() {
5836        for (sql, expected_height) in [
5837            ("NOT 1", 2),
5838            ("NOT EXISTS (SELECT 1)", 3),
5839            ("1 BETWEEN 2 AND 3", 2),
5840            ("1 NOT BETWEEN 2 AND 3", 3),
5841            ("1 LIKE 2", 2),
5842            ("1 NOT LIKE 2", 3),
5843            ("1 IN ()", 1),
5844            ("1 NOT IN ()", 1),
5845            ("abs(1) IN ()", 3),
5846            ("abs(1) NOT IN ()", 3),
5847            ("1 IN (2)", 3),
5848            ("1 IN (+2)", 4),
5849            ("1 NOT IN (2)", 4),
5850            ("1 IN (2, 3)", 2),
5851            ("1 NOT IN (2, 3)", 3),
5852            ("1 IN (SELECT 1 + 2)", 3),
5853            ("1 IN ((SELECT 1 + 2))", 3),
5854            ("(1, 2) IN ((3, 4))", 2),
5855            ("+1", 2),
5856            ("++1", 2),
5857            ("-+1", 2),
5858            ("(1 + 2 + 3, 4)", 1),
5859            ("(1 + 2 + 3, 4) + 5", 2),
5860        ] {
5861            assert_eq!(
5862                parsed_expr_height(sql),
5863                expected_height,
5864                "cached grammar height mismatch: {sql}"
5865            );
5866        }
5867    }
5868
5869    #[test]
5870    fn test_function_cached_height_excludes_auxiliary_expression_roots() {
5871        for (sql, expected_height) in [
5872            ("sum(1 + 2 + 3)", 4),
5873            ("sum(1 ORDER BY 1 + 2 + 3)", 2),
5874            ("sum(1) FILTER (WHERE 1 + 2 + 3)", 2),
5875            ("sum(1) OVER (ORDER BY 1 + 2 + 3)", 2),
5876            (
5877                "sum(1 ORDER BY 1 + 2 + 3) FILTER (WHERE 1 + 2 + 3) \
5878                 OVER (ORDER BY 1 + 2 + 3)",
5879                2,
5880            ),
5881        ] {
5882            assert_eq!(
5883                parsed_expr_height(sql),
5884                expected_height,
5885                "auxiliary root leaked into the aggregate argument height: {sql}"
5886            );
5887        }
5888    }
5889
5890    #[test]
5891    fn test_function_auxiliary_roots_have_independent_1000_1001_boundaries() {
5892        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5893        let at_limit = repeated_infix_expression(LIMIT, " + ");
5894        let over_limit = repeated_infix_expression(LIMIT + 1, " + ");
5895        for sql in [
5896            format!("sum(1) FILTER (WHERE {at_limit})"),
5897            format!("row_number() OVER (ORDER BY {at_limit})"),
5898            format!("group_concat(1 ORDER BY {at_limit})"),
5899        ] {
5900            parse_expr(&sql).expect("height-1000 auxiliary root must remain independently valid");
5901        }
5902        for sql in [
5903            format!("sum(1) FILTER (WHERE {over_limit})"),
5904            format!("row_number() OVER (ORDER BY {over_limit})"),
5905            format!("group_concat(1 ORDER BY {over_limit})"),
5906        ] {
5907            assert_expression_depth_error(&sql);
5908        }
5909    }
5910
5911    #[test]
5912    fn test_select_auxiliary_roots_have_independent_1000_1001_boundaries() {
5913        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5914        let at_limit = repeated_infix_expression(LIMIT, " + ");
5915        let over_limit = repeated_infix_expression(LIMIT + 1, " + ");
5916        for sql in [
5917            format!("(WITH c AS (SELECT {at_limit}) SELECT 0)"),
5918            format!("(SELECT 0 FROM (SELECT {at_limit}))"),
5919            format!("(SELECT 0 FROM json_each({at_limit}))"),
5920            format!("(SELECT 0 FROM a JOIN b ON {at_limit})"),
5921            format!("(SELECT 0 WINDOW w AS (ORDER BY {at_limit}))"),
5922        ] {
5923            parse_expr(&sql).expect("height-1000 independent SELECT root must remain valid");
5924        }
5925        for sql in [
5926            format!("(WITH c AS (SELECT {over_limit}) SELECT 0)"),
5927            format!("(SELECT 0 FROM (SELECT {over_limit}))"),
5928            format!("(SELECT 0 FROM json_each({over_limit}))"),
5929            format!("(SELECT 0 FROM a JOIN b ON {over_limit})"),
5930            format!("(SELECT 0 WINDOW w AS (ORDER BY {over_limit}))"),
5931        ] {
5932            assert_expression_depth_error(&sql);
5933        }
5934    }
5935
5936    #[test]
5937    fn test_nested_subquery_height_is_threaded_without_ast_rescans() {
5938        let sql = scalar_subquery_expression(63);
5939        HEIGHT_WALK_VISITS.set(0);
5940        parse_expr(&sql).expect("tracked nested scalar-subquery height must parse");
5941        assert_eq!(
5942            HEIGHT_WALK_VISITS.get(),
5943            0,
5944            "nested subqueries must return tracked SELECT height in O(1) per parent"
5945        );
5946    }
5947
5948    #[test]
5949    fn test_subquery_height_contract_exact_1000_1001_boundary() {
5950        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5951        let inner_at_limit = repeated_infix_expression(LIMIT - 1, " + ");
5952        for sql in [
5953            format!("(SELECT {inner_at_limit})"),
5954            format!("EXISTS (SELECT {inner_at_limit})"),
5955            format!("1 IN (SELECT {inner_at_limit})"),
5956        ] {
5957            parse_expr(&sql).expect("height-999 SELECT under one expression node must be accepted");
5958        }
5959
5960        let inner_over_limit = repeated_infix_expression(LIMIT, " + ");
5961        for sql in [
5962            format!("(SELECT {inner_over_limit})"),
5963            format!("EXISTS (SELECT {inner_over_limit})"),
5964            format!("1 IN (SELECT {inner_over_limit})"),
5965        ] {
5966            assert_expression_depth_error(&sql);
5967        }
5968    }
5969
5970    // ── Precedence tests (normative invariants) ─────────────────────────
5971
5972    #[test]
5973    fn test_not_lower_precedence_than_comparison() {
5974        // NOT x = y → NOT (x = y)
5975        let expr = parse("NOT x = y");
5976        match &expr {
5977            Expr::UnaryOp {
5978                op: UnaryOp::Not,
5979                expr: inner,
5980                ..
5981            } => match inner.as_ref() {
5982                Expr::BinaryOp {
5983                    op: BinaryOp::Eq, ..
5984                } => {}
5985                other => unreachable!("expected Eq inside NOT, got {other:?}"),
5986            },
5987            other => unreachable!("expected NOT(Eq), got {other:?}"),
5988        }
5989    }
5990
5991    #[test]
5992    fn test_unary_binds_tighter_than_collate() {
5993        // -x COLLATE NOCASE → (-x) COLLATE NOCASE
5994        let expr = parse("-x COLLATE NOCASE");
5995        match &expr {
5996            Expr::Collate {
5997                expr: inner,
5998                collation,
5999                ..
6000            } => {
6001                assert_eq!(collation, "NOCASE");
6002                assert!(matches!(
6003                    inner.as_ref(),
6004                    Expr::UnaryOp {
6005                        op: UnaryOp::Negate,
6006                        ..
6007                    }
6008                ));
6009            }
6010            other => unreachable!("expected COLLATE(Negate), got {other:?}"),
6011        }
6012    }
6013
6014    #[test]
6015    fn test_arithmetic_precedence() {
6016        // 1 + 2 * 3 → 1 + (2 * 3)
6017        let expr = parse("1 + 2 * 3");
6018        match &expr {
6019            Expr::BinaryOp {
6020                op: BinaryOp::Add,
6021                left,
6022                right,
6023                ..
6024            } => {
6025                assert!(matches!(
6026                    left.as_ref(),
6027                    Expr::Literal(Literal::Integer(1), _)
6028                ));
6029                assert!(matches!(
6030                    right.as_ref(),
6031                    Expr::BinaryOp {
6032                        op: BinaryOp::Multiply,
6033                        ..
6034                    }
6035                ));
6036            }
6037            other => unreachable!("expected Add(1, Mul(2,3)), got {other:?}"),
6038        }
6039    }
6040
6041    #[test]
6042    fn test_and_higher_than_or() {
6043        // a OR b AND c → a OR (b AND c)
6044        let expr = parse("a OR b AND c");
6045        match &expr {
6046            Expr::BinaryOp {
6047                op: BinaryOp::Or,
6048                right,
6049                ..
6050            } => {
6051                assert!(matches!(
6052                    right.as_ref(),
6053                    Expr::BinaryOp {
6054                        op: BinaryOp::And,
6055                        ..
6056                    }
6057                ));
6058            }
6059            other => unreachable!("expected Or(a, And(b,c)), got {other:?}"),
6060        }
6061    }
6062
6063    // ── CAST ────────────────────────────────────────────────────────────
6064
6065    #[test]
6066    fn test_cast_expression() {
6067        let expr = parse("CAST(42 AS INTEGER)");
6068        match &expr {
6069            Expr::Cast {
6070                expr: inner,
6071                type_name,
6072                ..
6073            } => {
6074                assert!(matches!(
6075                    inner.as_ref(),
6076                    Expr::Literal(Literal::Integer(42), _)
6077                ));
6078                assert_eq!(type_name.name, "INTEGER");
6079            }
6080            other => unreachable!("expected Cast, got {other:?}"),
6081        }
6082    }
6083
6084    #[test]
6085    fn test_cast_float_argument() {
6086        // CAST(x AS DECIMAL(10.5, -2.5))
6087        let expr = parse("CAST(x AS DECIMAL(10.5, -2.5))");
6088        match &expr {
6089            Expr::Cast { type_name, .. } => {
6090                assert_eq!(type_name.name, "DECIMAL");
6091                assert_eq!(type_name.arg1.as_deref(), Some("10.5"));
6092                assert_eq!(type_name.arg2.as_deref(), Some("-2.5"));
6093            }
6094            other => unreachable!("expected Cast with float args, got {other:?}"),
6095        }
6096    }
6097
6098    #[test]
6099    fn test_cast_signed_args() {
6100        // CAST(x AS NUMERIC(+5, -5))
6101        let expr = parse("CAST(x AS NUMERIC(+5, -5))");
6102        match &expr {
6103            Expr::Cast { type_name, .. } => {
6104                assert_eq!(type_name.name, "NUMERIC");
6105                assert_eq!(type_name.arg1.as_deref(), Some("+5"));
6106                assert_eq!(type_name.arg2.as_deref(), Some("-5"));
6107            }
6108            other => unreachable!("expected Cast with signed args, got {other:?}"),
6109        }
6110    }
6111
6112    // ── CASE ────────────────────────────────────────────────────────────
6113
6114    #[test]
6115    fn test_case_when_simple() {
6116        let expr = parse(
6117            "CASE x WHEN 1 THEN 'one' WHEN 2 THEN 'two' \
6118             ELSE 'other' END",
6119        );
6120        match &expr {
6121            Expr::Case {
6122                operand: Some(op),
6123                whens,
6124                else_expr: Some(_),
6125                ..
6126            } => {
6127                assert!(matches!(op.as_ref(), Expr::Column(..)));
6128                assert_eq!(whens.len(), 2);
6129            }
6130            other => unreachable!("expected simple CASE, got {other:?}"),
6131        }
6132    }
6133
6134    #[test]
6135    fn test_case_when_searched() {
6136        let expr = parse(
6137            "CASE WHEN x > 0 THEN 'pos' WHEN x < 0 THEN 'neg' \
6138             ELSE 'zero' END",
6139        );
6140        match &expr {
6141            Expr::Case {
6142                operand: None,
6143                whens,
6144                else_expr: Some(_),
6145                ..
6146            } => {
6147                assert_eq!(whens.len(), 2);
6148                assert!(matches!(
6149                    &whens[0].0,
6150                    Expr::BinaryOp {
6151                        op: BinaryOp::Gt,
6152                        ..
6153                    }
6154                ));
6155            }
6156            other => unreachable!("expected searched CASE, got {other:?}"),
6157        }
6158    }
6159
6160    // ── EXISTS ──────────────────────────────────────────────────────────
6161
6162    #[test]
6163    fn test_exists_subquery() {
6164        let expr = parse("EXISTS (SELECT 1)");
6165        assert!(matches!(expr, Expr::Exists { not: false, .. }));
6166    }
6167
6168    #[test]
6169    fn test_not_exists_subquery() {
6170        let expr = parse("NOT EXISTS (SELECT 1)");
6171        assert!(matches!(expr, Expr::Exists { not: true, .. }));
6172    }
6173
6174    #[test]
6175    fn test_exists_subquery_supports_qualified_table_with_alias() {
6176        let expr = parse("EXISTS (SELECT 1 FROM main.users AS u WHERE u.id = 1)");
6177        match expr {
6178            Expr::Exists { subquery, .. } => match subquery.body.select {
6179                SelectCore::Select {
6180                    from: Some(from), ..
6181                } => match from.source {
6182                    TableOrSubquery::Table { name, alias, .. } => {
6183                        assert_eq!(name.schema.as_deref(), Some("main"));
6184                        assert_eq!(name.name, "users");
6185                        assert_eq!(alias.as_deref(), Some("u"));
6186                    }
6187                    other => unreachable!("expected table source, got {other:?}"),
6188                },
6189                other => unreachable!("expected SELECT core with FROM, got {other:?}"),
6190            },
6191            other => unreachable!("expected EXISTS subquery, got {other:?}"),
6192        }
6193    }
6194
6195    // ── IN ──────────────────────────────────────────────────────────────
6196
6197    #[test]
6198    fn test_in_expr_list() {
6199        let expr = parse("x IN (1, 2, 3)");
6200        match &expr {
6201            Expr::In {
6202                not: false,
6203                set: InSet::List(items),
6204                ..
6205            } => assert_eq!(items.len(), 3),
6206            other => unreachable!("expected IN list, got {other:?}"),
6207        }
6208    }
6209
6210    #[test]
6211    fn test_explicit_row_value_in_list_rejects_mismatched_element_arities() {
6212        for (sql, expected_message) in [
6213            (
6214                "(a, b, c) IN ((1, 2))",
6215                "IN(...) element has 2 terms - expected 3",
6216            ),
6217            (
6218                "(a, b) IN ((1, 2, 3))",
6219                "IN(...) element has 3 terms - expected 2",
6220            ),
6221            ("(a, b) IN (1)", "IN(...) element has 1 term - expected 2"),
6222            (
6223                "(a, b) IN ((1, 2), 3)",
6224                "IN(...) element has 1 term - expected 2",
6225            ),
6226            (
6227                "(a, b) NOT IN ((1, 2), (3, 4, 5))",
6228                "IN(...) element has 3 terms - expected 2",
6229            ),
6230            (
6231                "0 AND (a, b) IN (1)",
6232                "IN(...) element has 1 term - expected 2",
6233            ),
6234            (
6235                "(a, b) IN (+(SELECT 1, 2))",
6236                "IN(...) element has 1 term - expected 2",
6237            ),
6238            (
6239                "(a, b) IN ((SELECT 1), (SELECT 2))",
6240                "IN(...) element has 1 term - expected 2",
6241            ),
6242        ] {
6243            let error = parse_expr(sql).expect_err("mismatched vector IN arity must fail parsing");
6244            assert_eq!(
6245                error.kind,
6246                ParseErrorKind::Syntax,
6247                "unexpected kind for `{sql}`"
6248            );
6249            assert_eq!(
6250                error.message, expected_message,
6251                "unexpected error for `{sql}`"
6252            );
6253        }
6254    }
6255
6256    #[test]
6257    fn test_subquery_lhs_defers_in_list_arity_to_semantic_resolution() {
6258        for sql in [
6259            "(SELECT 1, 2) IN (1)",
6260            "(SELECT 1, 2) IN ((1, 2, 3))",
6261            "(VALUES (1, 2, 3)) IN ((1, 2))",
6262            "(SELECT 1, 2 UNION ALL SELECT 3, 4) NOT IN (5)",
6263            "0 AND (SELECT 1, 2) IN (1)",
6264            "(SELECT 1, 2) IN (nosuch_vector_function())",
6265        ] {
6266            let expr = parse_expr(sql).unwrap_or_else(|error| {
6267                panic!("subquery-expression IN semantics must be deferred for `{sql}`: {error}")
6268            });
6269            assert!(
6270                matches!(expr, Expr::In { .. } | Expr::BinaryOp { .. }),
6271                "unexpected AST for `{sql}`"
6272            );
6273        }
6274    }
6275
6276    #[test]
6277    fn test_vector_in_list_accepts_matching_empty_and_singleton_subquery_forms() {
6278        for sql in [
6279            "(a, b) IN ((1, 2), (3, 4))",
6280            "(a, b) NOT IN ((1, 2), (3, 4))",
6281            "(a, b) IN ()",
6282            "(a, b) IN ((SELECT 1, 2))",
6283            "(a, b) IN ((SELECT 1))",
6284            "(a, b) IN ((SELECT 1, 2, 3))",
6285            "(SELECT 1, 2) IN ((1, 2), (3, 4))",
6286            "(VALUES (1, 2), (3, 4)) NOT IN ((1, 2))",
6287            "(SELECT 1, 2 UNION ALL SELECT 3, 4) IN ((1, 2))",
6288            // Star widths require schema lookup and must not be guessed here.
6289            "(SELECT * FROM t) IN (1)",
6290            "(SELECT t.* FROM t) IN ((1, 2, 3))",
6291            // Preserve subquery column-count diagnostics for both RHS forms.
6292            "(SELECT 1, 2) IN ((SELECT 1))",
6293            "(SELECT 1, 2) IN (SELECT 1)",
6294        ] {
6295            let expr = parse_expr(sql).unwrap_or_else(|error| {
6296                panic!("matching vector IN list must parse for `{sql}`: {error}")
6297            });
6298            assert!(
6299                matches!(expr, Expr::In { .. }),
6300                "unexpected AST for `{sql}`"
6301            );
6302        }
6303    }
6304
6305    #[test]
6306    fn test_vector_in_list_trailing_comma_syntax_error_precedes_arity() {
6307        let error = parse_expr("(a, b) IN (1,)")
6308            .expect_err("a trailing comma in an IN list must fail parsing");
6309        assert_eq!(error.kind, ParseErrorKind::Syntax);
6310        assert_eq!(error.message, "unexpected token in expression: RightParen");
6311    }
6312
6313    #[test]
6314    fn test_statement_parsers_reject_vector_in_list_arity_mismatches() {
6315        for (sql, expected_message) in [
6316            (
6317                "SELECT (a, b) IN (1) FROM t",
6318                "IN(...) element has 1 term - expected 2",
6319            ),
6320            (
6321                "UPDATE t SET flag = (a, b) IN ((1, 2, 3))",
6322                "IN(...) element has 3 terms - expected 2",
6323            ),
6324            (
6325                "DELETE FROM t WHERE (a, b) NOT IN ((1, 2), 3)",
6326                "IN(...) element has 1 term - expected 2",
6327            ),
6328        ] {
6329            let error = Parser::from_sql(sql)
6330                .parse_statement()
6331                .expect_err("statement parser must reject mismatched vector IN arity");
6332            assert_eq!(
6333                error.kind,
6334                ParseErrorKind::Syntax,
6335                "unexpected kind for `{sql}`"
6336            );
6337            assert_eq!(
6338                error.message, expected_message,
6339                "unexpected error for `{sql}`"
6340            );
6341        }
6342    }
6343
6344    #[test]
6345    fn test_statement_parsers_defer_subquery_in_list_arity() {
6346        for sql in [
6347            "SELECT (SELECT 1, 2) IN (1)",
6348            "UPDATE t SET flag = (SELECT 1, 2) IN ((1, 2, 3))",
6349            "DELETE FROM t WHERE 0 AND (SELECT 1, 2) NOT IN ((1, 2), 3)",
6350        ] {
6351            Parser::from_sql(sql)
6352                .parse_statement()
6353                .unwrap_or_else(|error| {
6354                    panic!("statement semantics must be deferred for `{sql}`: {error}")
6355                });
6356        }
6357    }
6358
6359    #[test]
6360    fn test_in_subquery() {
6361        let expr = parse("x IN (SELECT y FROM t)");
6362        assert!(matches!(
6363            expr,
6364            Expr::In {
6365                not: false,
6366                set: InSet::Subquery(_),
6367                ..
6368            }
6369        ));
6370    }
6371
6372    #[test]
6373    fn test_in_subquery_with_order_by_and_limit() {
6374        // This is the pattern used in mcp-agent-mail-db prune queries
6375        let expr =
6376            parse("id NOT IN (SELECT id FROM search_recipes ORDER BY updated_ts DESC LIMIT 5)");
6377        match &expr {
6378            Expr::In {
6379                not: true,
6380                set: InSet::Subquery(stmt),
6381                ..
6382            } => {
6383                assert_eq!(stmt.order_by.len(), 1, "ORDER BY should be parsed");
6384                assert!(stmt.limit.is_some(), "LIMIT should be parsed");
6385            }
6386            other => unreachable!("expected NOT IN subquery, got {other:?}"),
6387        }
6388    }
6389
6390    #[test]
6391    fn test_in_subquery_supports_group_by_and_having() {
6392        let expr = parse("x IN (SELECT y FROM t GROUP BY y HAVING COUNT(*) > 1)");
6393        match expr {
6394            Expr::In {
6395                set: InSet::Subquery(stmt),
6396                ..
6397            } => match stmt.body.select {
6398                SelectCore::Select {
6399                    group_by, having, ..
6400                } => {
6401                    assert_eq!(group_by.len(), 1, "GROUP BY should be parsed");
6402                    assert!(having.is_some(), "HAVING should be parsed");
6403                }
6404                SelectCore::Values(_) => unreachable!("expected SELECT core"),
6405            },
6406            other => unreachable!("expected IN subquery, got {other:?}"),
6407        }
6408    }
6409
6410    #[test]
6411    fn test_not_in() {
6412        let expr = parse("x NOT IN (1, 2)");
6413        assert!(matches!(expr, Expr::In { not: true, .. }));
6414    }
6415
6416    #[test]
6417    fn test_in_table_name() {
6418        let expr = parse("x IN t");
6419        assert!(matches!(
6420            expr,
6421            Expr::In {
6422                not: false,
6423                set: InSet::Table(_),
6424                ..
6425            }
6426        ));
6427    }
6428
6429    #[test]
6430    fn test_not_in_table_name() {
6431        let expr = parse("x NOT IN t");
6432        assert!(matches!(
6433            expr,
6434            Expr::In {
6435                not: true,
6436                set: InSet::Table(_),
6437                ..
6438            }
6439        ));
6440    }
6441
6442    #[test]
6443    fn test_in_schema_table_name() {
6444        let expr = parse("x IN main.t");
6445        match expr {
6446            Expr::In {
6447                set: InSet::Table(name),
6448                ..
6449            } => {
6450                assert_eq!(name.schema.as_deref(), Some("main"));
6451                assert_eq!(name.name, "t");
6452            }
6453            other => unreachable!("expected IN table form, got {other:?}"),
6454        }
6455    }
6456
6457    // ── BETWEEN ─────────────────────────────────────────────────────────
6458
6459    #[test]
6460    fn test_between_and() {
6461        let expr = parse("x BETWEEN 1 AND 10");
6462        assert!(matches!(expr, Expr::Between { not: false, .. }));
6463    }
6464
6465    #[test]
6466    fn test_not_between() {
6467        let expr = parse("x NOT BETWEEN 1 AND 10");
6468        assert!(matches!(expr, Expr::Between { not: true, .. }));
6469    }
6470
6471    #[test]
6472    fn test_between_does_not_consume_outer_and() {
6473        // x BETWEEN 1 AND 10 AND y = 1 → (BETWEEN) AND (y = 1)
6474        let expr = parse("x BETWEEN 1 AND 10 AND y = 1");
6475        match &expr {
6476            Expr::BinaryOp {
6477                op: BinaryOp::And,
6478                left,
6479                ..
6480            } => assert!(matches!(left.as_ref(), Expr::Between { .. })),
6481            other => unreachable!("expected AND(BETWEEN, Eq), got {other:?}"),
6482        }
6483    }
6484
6485    // ── LIKE / GLOB ─────────────────────────────────────────────────────
6486
6487    #[test]
6488    fn test_like_pattern() {
6489        let expr = parse("name LIKE '%foo%'");
6490        assert!(matches!(
6491            expr,
6492            Expr::Like {
6493                op: LikeOp::Like,
6494                not: false,
6495                escape: None,
6496                ..
6497            }
6498        ));
6499    }
6500
6501    #[test]
6502    fn test_like_escape() {
6503        let expr = parse("name LIKE '%\\%%' ESCAPE '\\'");
6504        assert!(matches!(
6505            expr,
6506            Expr::Like {
6507                op: LikeOp::Like,
6508                escape: Some(_),
6509                ..
6510            }
6511        ));
6512    }
6513
6514    #[test]
6515    fn test_glob_pattern() {
6516        let expr = parse("path GLOB '*.rs'");
6517        assert!(matches!(
6518            expr,
6519            Expr::Like {
6520                op: LikeOp::Glob,
6521                not: false,
6522                ..
6523            }
6524        ));
6525    }
6526
6527    #[test]
6528    fn test_glob_character_class() {
6529        let expr = parse("name GLOB '[a-z]*'");
6530        match &expr {
6531            Expr::Like {
6532                op: LikeOp::Glob,
6533                pattern,
6534                ..
6535            } => assert!(matches!(
6536                pattern.as_ref(),
6537                Expr::Literal(Literal::String(s), _) if s == "[a-z]*"
6538            )),
6539            other => unreachable!("expected GLOB, got {other:?}"),
6540        }
6541    }
6542
6543    // ── COLLATE ─────────────────────────────────────────────────────────
6544
6545    #[test]
6546    fn test_collate_override() {
6547        let expr = parse("name COLLATE NOCASE");
6548        match &expr {
6549            Expr::Collate { collation, .. } => {
6550                assert_eq!(collation, "NOCASE");
6551            }
6552            other => unreachable!("expected COLLATE, got {other:?}"),
6553        }
6554    }
6555
6556    // ── JSON operators ──────────────────────────────────────────────────
6557
6558    #[test]
6559    fn test_json_arrow_operator() {
6560        let expr = parse("data -> 'key'");
6561        assert!(matches!(
6562            expr,
6563            Expr::JsonAccess {
6564                arrow: JsonArrow::Arrow,
6565                ..
6566            }
6567        ));
6568    }
6569
6570    #[test]
6571    fn test_json_double_arrow_operator() {
6572        let expr = parse("data ->> 'key'");
6573        assert!(matches!(
6574            expr,
6575            Expr::JsonAccess {
6576                arrow: JsonArrow::DoubleArrow,
6577                ..
6578            }
6579        ));
6580    }
6581
6582    // ── IS NULL / IS NOT ─────────────────────────────────────────────────────
6583
6584    #[test]
6585    fn test_is_null() {
6586        assert!(matches!(
6587            parse("42"),
6588            Expr::Literal(Literal::Integer(42), _)
6589        ));
6590        assert!(matches!(parse("3.14"), Expr::Literal(Literal::Float(_), _)));
6591        assert!(matches!(
6592            parse("'hello'"),
6593            Expr::Literal(Literal::String(_), _)
6594        ));
6595        assert!(matches!(parse("NULL"), Expr::Literal(Literal::Null, _)));
6596        assert!(matches!(parse("TRUE"), Expr::Literal(Literal::True, _)));
6597        assert!(matches!(parse("FALSE"), Expr::Literal(Literal::False, _)));
6598    }
6599
6600    // ── Issue #122: postfix null-test vs `=` precedence and round-trip ──
6601
6602    /// `a IS NULL = b IS NULL` (no parentheses) groups left-associatively:
6603    /// `((a IS NULL) = b) IS NULL`. Verified against the C SQLite CLI:
6604    /// `SELECT 200 IS NULL = 'ok' IS NULL` yields 0 (not 1), because the
6605    /// null-test and `=` share one left-associative precedence level.
6606    #[test]
6607    fn test_isnull_eq_isnull_unparenthesized_left_associative() {
6608        let expr = parse("a IS NULL = b IS NULL");
6609        match &expr {
6610            Expr::IsNull {
6611                expr: inner,
6612                not: false,
6613                ..
6614            } => match inner.as_ref() {
6615                Expr::BinaryOp {
6616                    op: BinaryOp::Eq,
6617                    left,
6618                    right,
6619                    ..
6620                } => {
6621                    assert!(
6622                        matches!(left.as_ref(), Expr::IsNull { not: false, .. }),
6623                        "expected (a IS NULL) on the left, got {left:?}"
6624                    );
6625                    assert!(
6626                        matches!(right.as_ref(), Expr::Column(..)),
6627                        "expected bare column b on the right, got {right:?}"
6628                    );
6629                }
6630                other => unreachable!("expected Eq inside IsNull, got {other:?}"),
6631            },
6632            other => unreachable!("expected IsNull(Eq(IsNull(a), b)), got {other:?}"),
6633        }
6634    }
6635
6636    /// `(a IS NULL) = (b IS NULL)` must parse as Eq of two null-tests, and
6637    /// the display round-trip must preserve that grouping (issue #122: the
6638    /// serializer used to strip these parentheses, silently inverting CHECK
6639    /// constraints of the form `(a IS NULL) = (b IS NULL)`).
6640    #[test]
6641    fn test_isnull_eq_isnull_parenthesized_round_trip() {
6642        let assert_shape = |expr: &Expr| match expr {
6643            Expr::BinaryOp {
6644                op: BinaryOp::Eq,
6645                left,
6646                right,
6647                ..
6648            } => {
6649                assert!(
6650                    matches!(left.as_ref(), Expr::IsNull { not: false, .. }),
6651                    "expected IsNull on the left, got {left:?}"
6652                );
6653                assert!(
6654                    matches!(right.as_ref(), Expr::IsNull { not: false, .. }),
6655                    "expected IsNull on the right, got {right:?}"
6656                );
6657            }
6658            other => unreachable!("expected Eq(IsNull, IsNull), got {other:?}"),
6659        };
6660        let expr = parse("(a IS NULL) = (b IS NULL)");
6661        assert_shape(&expr);
6662        let rendered = expr.to_string();
6663        assert_eq!(rendered, "a IS NULL = (b IS NULL)");
6664        let reparsed = parse(&rendered);
6665        assert_shape(&reparsed);
6666        assert_eq!(reparsed.to_string(), rendered, "round-trip not idempotent");
6667    }
6668
6669    /// An operator binding tighter than IS attaches to the NULL literal, so
6670    /// no null-test fold happens: `1 IS NULL < 2` is `1 IS (NULL < 2)`.
6671    /// Verified against the C SQLite CLI: `SELECT 1 IS NULL < 2` yields 0
6672    /// (`1 IS NULL` would give 0, then `0 < 2` would give 1).
6673    #[test]
6674    fn test_is_null_followed_by_tighter_operator_binds_to_null() {
6675        let expr = parse("1 IS NULL < 2");
6676        match &expr {
6677            Expr::BinaryOp {
6678                op: BinaryOp::Is,
6679                right,
6680                ..
6681            } => assert!(
6682                matches!(
6683                    right.as_ref(),
6684                    Expr::BinaryOp {
6685                        op: BinaryOp::Lt,
6686                        ..
6687                    }
6688                ),
6689                "expected Lt(NULL, 2) on the right of IS, got {right:?}"
6690            ),
6691            other => unreachable!("expected Is(1, Lt(NULL, 2)), got {other:?}"),
6692        }
6693    }
6694
6695    /// `x IS (NULL)` folds to a null-test just like `x IS NULL`, matching
6696    /// SQLite's binaryToUnaryIfNull (the fold keys on the resolved RHS
6697    /// expression, not on the raw token).
6698    #[test]
6699    fn test_is_parenthesized_null_folds_to_isnull() {
6700        assert!(matches!(
6701            parse("x IS (NULL)"),
6702            Expr::IsNull { not: false, .. }
6703        ));
6704        assert!(matches!(
6705            parse("x IS NOT (NULL)"),
6706            Expr::IsNull { not: true, .. }
6707        ));
6708    }
6709
6710    #[test]
6711    fn test_placeholders() {
6712        assert!(matches!(
6713            parse("?"),
6714            Expr::Placeholder(PlaceholderType::Anonymous, _)
6715        ));
6716        assert!(matches!(
6717            parse("?1"),
6718            Expr::Placeholder(PlaceholderType::Numbered(1), _)
6719        ));
6720        assert!(matches!(
6721            parse(":name"),
6722            Expr::Placeholder(PlaceholderType::ColonNamed(_), _)
6723        ));
6724    }
6725
6726    // ── Column references ───────────────────────────────────────────────
6727
6728    #[test]
6729    fn test_column_bare() {
6730        match &parse("x") {
6731            Expr::Column(
6732                ColumnRef {
6733                    table: None,
6734                    column,
6735                },
6736                _,
6737            ) => assert_eq!(column.as_ref(), "x"),
6738            other => unreachable!("expected bare column, got {other:?}"),
6739        }
6740    }
6741
6742    #[test]
6743    fn test_column_qualified() {
6744        match &parse("t.x") {
6745            Expr::Column(
6746                ColumnRef {
6747                    table: Some(t),
6748                    column,
6749                },
6750                _,
6751            ) => {
6752                assert_eq!(t.as_ref(), "t");
6753                assert_eq!(column.as_ref(), "x");
6754            }
6755            other => unreachable!("expected qualified column, got {other:?}"),
6756        }
6757    }
6758
6759    #[test]
6760    fn test_qualified_column_retains_dot_height_and_exact_boundary() {
6761        assert_eq!(parsed_expr_height("x"), 1);
6762        assert_eq!(parsed_expr_height("t.x"), 2);
6763
6764        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
6765        let at_limit = format!("{}t.x", "~".repeat(LIMIT - 2));
6766        parse_expr(&at_limit).expect("998 unary nodes plus qualified column have height 1000");
6767
6768        let over_limit = format!("{}t.x", "~".repeat(LIMIT - 1));
6769        assert_expression_depth_error(&over_limit);
6770    }
6771
6772    // ── Concat / precedence ─────────────────────────────────────────────
6773
6774    #[test]
6775    fn test_concat_higher_than_add() {
6776        // a + b || c → a + (b || c) since || binds tighter
6777        let expr = parse("a + b || c");
6778        match &expr {
6779            Expr::BinaryOp {
6780                op: BinaryOp::Add,
6781                right,
6782                ..
6783            } => assert!(matches!(
6784                right.as_ref(),
6785                Expr::BinaryOp {
6786                    op: BinaryOp::Concat,
6787                    ..
6788                }
6789            )),
6790            other => unreachable!("expected Add(a, Concat(b,c)), got {other:?}"),
6791        }
6792    }
6793
6794    // ── Parenthesized ───────────────────────────────────────────────────
6795
6796    #[test]
6797    fn test_parenthesized() {
6798        // (1 + 2) * 3 → Mul(Add(1,2), 3)
6799        let expr = parse("(1 + 2) * 3");
6800        match &expr {
6801            Expr::BinaryOp {
6802                op: BinaryOp::Multiply,
6803                left,
6804                ..
6805            } => assert!(matches!(
6806                left.as_ref(),
6807                Expr::BinaryOp {
6808                    op: BinaryOp::Add,
6809                    ..
6810                }
6811            )),
6812            other => unreachable!("expected Mul(Add, 3), got {other:?}"),
6813        }
6814    }
6815
6816    // ── IS / IS NOT ─────────────────────────────────────────────────────
6817
6818    #[test]
6819    fn test_is_operator() {
6820        assert!(matches!(
6821            parse("a IS b"),
6822            Expr::BinaryOp {
6823                op: BinaryOp::Is,
6824                ..
6825            }
6826        ));
6827    }
6828
6829    #[test]
6830    fn test_is_not_operator() {
6831        assert!(matches!(
6832            parse("a IS NOT b"),
6833            Expr::BinaryOp {
6834                op: BinaryOp::IsNot,
6835                ..
6836            }
6837        ));
6838    }
6839
6840    // ── Bitwise ─────────────────────────────────────────────────────────
6841
6842    #[test]
6843    fn test_bitwise_ops() {
6844        // & and | share the same precedence (left-associative)
6845        let expr = parse("a & b | c");
6846        match &expr {
6847            Expr::BinaryOp {
6848                op: BinaryOp::BitOr,
6849                left,
6850                ..
6851            } => assert!(
6852                matches!(
6853                    left.as_ref(),
6854                    Expr::BinaryOp {
6855                        op: BinaryOp::BitAnd,
6856                        ..
6857                    }
6858                ),
6859                "bitwise operators should be left-associative"
6860            ),
6861            other => unreachable!("expected BitOr(BitAnd, c), got {other:?}"),
6862        }
6863    }
6864
6865    #[test]
6866    fn test_bitnot() {
6867        assert!(matches!(
6868            parse("~x"),
6869            Expr::UnaryOp {
6870                op: UnaryOp::BitNot,
6871                ..
6872            }
6873        ));
6874    }
6875
6876    // ── Complex expressions ─────────────────────────────────────────────
6877
6878    #[test]
6879    fn test_complex_where_clause() {
6880        let expr = parse("a > 1 AND b LIKE '%test%' OR NOT c IS NULL");
6881        assert!(matches!(
6882            expr,
6883            Expr::BinaryOp {
6884                op: BinaryOp::Or,
6885                ..
6886            }
6887        ));
6888    }
6889
6890    #[test]
6891    fn test_not_like_pattern() {
6892        assert!(matches!(
6893            parse("name NOT LIKE '%foo'"),
6894            Expr::Like {
6895                op: LikeOp::Like,
6896                not: true,
6897                ..
6898            }
6899        ));
6900    }
6901
6902    #[test]
6903    fn test_subquery_expr() {
6904        assert!(matches!(parse("(SELECT 1)"), Expr::Subquery(..)));
6905    }
6906
6907    // ── bd-kzat: §10.2 Pratt Precedence Validation ─────────────────────
6908    //
6909    // Systematic tests for ALL 11 operator precedence levels.
6910    // Each level gets a dedicated associativity test and a boundary test
6911    // against the adjacent level.
6912
6913    // Level 1: OR — left-associative
6914    #[test]
6915    fn test_pratt_level1_or_left_assoc() {
6916        // a OR b OR c → (a OR b) OR c
6917        let expr = parse("a OR b OR c");
6918        match &expr {
6919            Expr::BinaryOp {
6920                op: BinaryOp::Or,
6921                left,
6922                ..
6923            } => assert!(
6924                matches!(
6925                    left.as_ref(),
6926                    Expr::BinaryOp {
6927                        op: BinaryOp::Or,
6928                        ..
6929                    }
6930                ),
6931                "OR should be left-associative"
6932            ),
6933            other => unreachable!("expected Or(Or(a,b), c), got {other:?}"),
6934        }
6935    }
6936
6937    // Level 2: AND — left-associative, tighter than OR
6938    #[test]
6939    fn test_pratt_level2_and_left_assoc() {
6940        // a AND b AND c → (a AND b) AND c
6941        let expr = parse("a AND b AND c");
6942        match &expr {
6943            Expr::BinaryOp {
6944                op: BinaryOp::And,
6945                left,
6946                ..
6947            } => assert!(
6948                matches!(
6949                    left.as_ref(),
6950                    Expr::BinaryOp {
6951                        op: BinaryOp::And,
6952                        ..
6953                    }
6954                ),
6955                "AND should be left-associative"
6956            ),
6957            other => unreachable!("expected And(And(a,b), c), got {other:?}"),
6958        }
6959    }
6960
6961    // Level 3: NOT — prefix, higher than AND, lower than equality
6962    #[test]
6963    fn test_pratt_level3_not_higher_than_and() {
6964        // NOT a AND b → (NOT a) AND b
6965        let expr = parse("NOT a AND b");
6966        match &expr {
6967            Expr::BinaryOp {
6968                op: BinaryOp::And,
6969                left,
6970                ..
6971            } => assert!(
6972                matches!(
6973                    left.as_ref(),
6974                    Expr::UnaryOp {
6975                        op: UnaryOp::Not,
6976                        ..
6977                    }
6978                ),
6979                "NOT should bind tighter than AND"
6980            ),
6981            other => unreachable!("expected And(Not(a), b), got {other:?}"),
6982        }
6983    }
6984
6985    // Level 4: Equality/membership — left-associative
6986    #[test]
6987    fn test_pratt_level4_equality_left_assoc() {
6988        // a = b != c → (a = b) != c
6989        let expr = parse("a = b != c");
6990        match &expr {
6991            Expr::BinaryOp {
6992                op: BinaryOp::Ne,
6993                left,
6994                ..
6995            } => assert!(
6996                matches!(
6997                    left.as_ref(),
6998                    Expr::BinaryOp {
6999                        op: BinaryOp::Eq,
7000                        ..
7001                    }
7002                ),
7003                "equality operators should be left-associative at same level"
7004            ),
7005            other => unreachable!("expected Ne(Eq(a,b), c), got {other:?}"),
7006        }
7007    }
7008
7009    // Level 4 vs Level 5: THE CRITICAL BOUNDARY
7010    // Equality (level 4) and relational (level 5) are SEPARATE levels
7011    // per canonical upstream SQLite grammar.
7012    #[test]
7013    fn test_pratt_level4_vs_level5_eq_lt_boundary() {
7014        // a = b < c MUST parse as a = (b < c), NOT (a = b) < c
7015        // This is the normative invariant from §10.2.
7016        let expr = parse("a = b < c");
7017        match &expr {
7018            Expr::BinaryOp {
7019                op: BinaryOp::Eq,
7020                right,
7021                ..
7022            } => assert!(
7023                matches!(
7024                    right.as_ref(),
7025                    Expr::BinaryOp {
7026                        op: BinaryOp::Lt,
7027                        ..
7028                    }
7029                ),
7030                "a = b < c MUST parse as a = (b < c): relational binds tighter"
7031            ),
7032            other => unreachable!("expected Eq(a, Lt(b,c)), got {other:?}"),
7033        }
7034    }
7035
7036    // Reverse direction of the same boundary
7037    #[test]
7038    fn test_pratt_level4_vs_level5_ne_ge_boundary() {
7039        // a != b >= c → a != (b >= c)
7040        let expr = parse("a != b >= c");
7041        match &expr {
7042            Expr::BinaryOp {
7043                op: BinaryOp::Ne,
7044                right,
7045                ..
7046            } => assert!(
7047                matches!(
7048                    right.as_ref(),
7049                    Expr::BinaryOp {
7050                        op: BinaryOp::Ge,
7051                        ..
7052                    }
7053                ),
7054                "a != b >= c must parse as a != (b >= c)"
7055            ),
7056            other => unreachable!("expected Ne(Ge(b,c)), got {other:?}"),
7057        }
7058    }
7059
7060    // Level 5: Relational — left-associative
7061    #[test]
7062    fn test_pratt_level5_relational_left_assoc() {
7063        // a < b >= c → (a < b) >= c
7064        let expr = parse("a < b >= c");
7065        match &expr {
7066            Expr::BinaryOp {
7067                op: BinaryOp::Ge,
7068                left,
7069                ..
7070            } => assert!(
7071                matches!(
7072                    left.as_ref(),
7073                    Expr::BinaryOp {
7074                        op: BinaryOp::Lt,
7075                        ..
7076                    }
7077                ),
7078                "relational operators should be left-associative"
7079            ),
7080            other => unreachable!("expected Ge(Lt(a,b), c), got {other:?}"),
7081        }
7082    }
7083
7084    // Level 6: Bitwise — tighter than relational
7085    #[test]
7086    fn test_pratt_level6_bitwise_tighter_than_comparison() {
7087        // a < b & c → a < (b & c)
7088        let expr = parse("a < b & c");
7089        match &expr {
7090            Expr::BinaryOp {
7091                op: BinaryOp::Lt,
7092                right,
7093                ..
7094            } => assert!(
7095                matches!(
7096                    right.as_ref(),
7097                    Expr::BinaryOp {
7098                        op: BinaryOp::BitAnd,
7099                        ..
7100                    }
7101                ),
7102                "bitwise should bind tighter than relational"
7103            ),
7104            other => unreachable!("expected Lt(a, BitAnd(b,c)), got {other:?}"),
7105        }
7106    }
7107
7108    // Level 6: Shift operators left-associative
7109    #[test]
7110    fn test_pratt_level6_shifts_left_assoc() {
7111        // a << b >> c → (a << b) >> c
7112        let expr = parse("a << b >> c");
7113        match &expr {
7114            Expr::BinaryOp {
7115                op: BinaryOp::ShiftRight,
7116                left,
7117                ..
7118            } => assert!(
7119                matches!(
7120                    left.as_ref(),
7121                    Expr::BinaryOp {
7122                        op: BinaryOp::ShiftLeft,
7123                        ..
7124                    }
7125                ),
7126                "shift operators should be left-associative"
7127            ),
7128            other => unreachable!("expected ShiftRight(ShiftLeft(a,b), c), got {other:?}"),
7129        }
7130    }
7131
7132    // Level 7: Addition/subtraction — left-associative, tighter than bitwise
7133    #[test]
7134    fn test_pratt_level7_add_sub_left_assoc() {
7135        // a + b - c → (a + b) - c
7136        let expr = parse("a + b - c");
7137        match &expr {
7138            Expr::BinaryOp {
7139                op: BinaryOp::Subtract,
7140                left,
7141                ..
7142            } => assert!(
7143                matches!(
7144                    left.as_ref(),
7145                    Expr::BinaryOp {
7146                        op: BinaryOp::Add,
7147                        ..
7148                    }
7149                ),
7150                "add/sub should be left-associative"
7151            ),
7152            other => unreachable!("expected Sub(Add(a,b), c), got {other:?}"),
7153        }
7154    }
7155
7156    #[test]
7157    fn test_pratt_level7_add_sub_left_assoc_reverse() {
7158        // a - b + c → (a - b) + c
7159        let expr = parse("a - b + c");
7160        match &expr {
7161            Expr::BinaryOp {
7162                op: BinaryOp::Add,
7163                left,
7164                ..
7165            } => assert!(
7166                matches!(
7167                    left.as_ref(),
7168                    Expr::BinaryOp {
7169                        op: BinaryOp::Subtract,
7170                        ..
7171                    }
7172                ),
7173                "add/sub should be left-associative"
7174            ),
7175            other => unreachable!("expected Add(Sub(a,b), c), got {other:?}"),
7176        }
7177    }
7178
7179    #[test]
7180    fn test_pratt_level9_concat_tighter_than_mul() {
7181        // a * b || c → a * (b || c)
7182        let expr = parse("a * b || c");
7183        match &expr {
7184            Expr::BinaryOp {
7185                op: BinaryOp::Multiply,
7186                right,
7187                ..
7188            } => assert!(
7189                matches!(
7190                    right.as_ref(),
7191                    Expr::BinaryOp {
7192                        op: BinaryOp::Concat,
7193                        ..
7194                    }
7195                ),
7196                "concat should bind tighter than multiply"
7197            ),
7198            other => unreachable!("expected Mul(a, Concat(b,c)), got {other:?}"),
7199        }
7200    }
7201
7202    // Level 8: Multiplication/division/modulo — left-associative
7203    #[test]
7204    fn test_pratt_level8_mul_div_left_assoc() {
7205        // a * b / c → (a * b) / c
7206        let expr = parse("a * b / c");
7207        match &expr {
7208            Expr::BinaryOp {
7209                op: BinaryOp::Divide,
7210                left,
7211                ..
7212            } => assert!(
7213                matches!(
7214                    left.as_ref(),
7215                    Expr::BinaryOp {
7216                        op: BinaryOp::Multiply,
7217                        ..
7218                    }
7219                ),
7220                "mul/div should be left-associative"
7221            ),
7222            other => unreachable!("expected Div(Mul(a,b), c), got {other:?}"),
7223        }
7224    }
7225
7226    #[test]
7227    fn test_pratt_level8_modulo() {
7228        // a * b % c → (a * b) % c
7229        let expr = parse("a * b % c");
7230        match &expr {
7231            Expr::BinaryOp {
7232                op: BinaryOp::Modulo,
7233                left,
7234                ..
7235            } => assert!(
7236                matches!(
7237                    left.as_ref(),
7238                    Expr::BinaryOp {
7239                        op: BinaryOp::Multiply,
7240                        ..
7241                    }
7242                ),
7243                "modulo and multiply at same level, left-associative"
7244            ),
7245            other => unreachable!("expected Mod(Mul(a,b), c), got {other:?}"),
7246        }
7247    }
7248
7249    // Level 9: Concatenation (||) — left-associative, tighter than mul
7250    #[test]
7251    fn test_pratt_level9_concat_left_assoc() {
7252        // a || b || c → (a || b) || c
7253        let expr = parse("a || b || c");
7254        match &expr {
7255            Expr::BinaryOp {
7256                op: BinaryOp::Concat,
7257                left,
7258                ..
7259            } => assert!(
7260                matches!(
7261                    left.as_ref(),
7262                    Expr::BinaryOp {
7263                        op: BinaryOp::Concat,
7264                        ..
7265                    }
7266                ),
7267                "concatenation should be left-associative"
7268            ),
7269            other => unreachable!("expected Concat(Concat(a,b), c), got {other:?}"),
7270        }
7271    }
7272
7273    #[test]
7274    fn test_pratt_level9_concat_left_assoc_reverse() {
7275        // a || b || c → (a || b) || c
7276        let expr = parse("a || b || c");
7277        match &expr {
7278            Expr::BinaryOp {
7279                op: BinaryOp::Concat,
7280                left,
7281                ..
7282            } => assert!(
7283                matches!(
7284                    left.as_ref(),
7285                    Expr::BinaryOp {
7286                        op: BinaryOp::Concat,
7287                        ..
7288                    }
7289                ),
7290                "concatenation should be left-associative"
7291            ),
7292            other => unreachable!("expected Concat(Concat(a,b), c), got {other:?}"),
7293        }
7294    }
7295
7296    // Level 10: COLLATE — postfix, tighter than concat
7297    #[test]
7298    fn test_pratt_level10_collate_tighter_than_concat() {
7299        // a || b COLLATE NOCASE → a || (b COLLATE NOCASE)
7300        let expr = parse("a || b COLLATE NOCASE");
7301        match &expr {
7302            Expr::BinaryOp {
7303                op: BinaryOp::Concat,
7304                right,
7305                ..
7306            } => assert!(
7307                matches!(right.as_ref(), Expr::Collate { .. }),
7308                "COLLATE should bind tighter than concat"
7309            ),
7310            other => unreachable!("expected Concat(a, Collate(b)), got {other:?}"),
7311        }
7312    }
7313
7314    // Level 11: Unary prefix (- + ~) — tightest of all
7315    #[test]
7316    fn test_pratt_level11_unary_negate_tightest() {
7317        // -a * b → (-a) * b
7318        let expr = parse("-a * b");
7319        match &expr {
7320            Expr::BinaryOp {
7321                op: BinaryOp::Multiply,
7322                left,
7323                ..
7324            } => assert!(
7325                matches!(
7326                    left.as_ref(),
7327                    Expr::UnaryOp {
7328                        op: UnaryOp::Negate,
7329                        ..
7330                    }
7331                ),
7332                "unary minus should bind tighter than multiply"
7333            ),
7334            other => unreachable!("expected Mul(Negate(a), b), got {other:?}"),
7335        }
7336    }
7337
7338    #[test]
7339    fn test_pratt_level11_bitnot_tightest() {
7340        // ~a + b → (~a) + b
7341        let expr = parse("~a + b");
7342        match &expr {
7343            Expr::BinaryOp {
7344                op: BinaryOp::Add,
7345                left,
7346                ..
7347            } => assert!(
7348                matches!(
7349                    left.as_ref(),
7350                    Expr::UnaryOp {
7351                        op: UnaryOp::BitNot,
7352                        ..
7353                    }
7354                ),
7355                "bitwise NOT should bind tighter than addition"
7356            ),
7357            other => unreachable!("expected Add(BitNot(a), b), got {other:?}"),
7358        }
7359    }
7360
7361    // ESCAPE is NOT a standalone infix operator — it's suffix of LIKE/GLOB
7362    #[test]
7363    fn test_pratt_escape_not_infix_operator() {
7364        // a LIKE b ESCAPE c → Like(a, b, escape=c)
7365        let expr = parse("a LIKE b ESCAPE c");
7366        match &expr {
7367            Expr::Like {
7368                escape: Some(esc), ..
7369            } => assert!(
7370                matches!(esc.as_ref(), Expr::Column(_, _)),
7371                "ESCAPE should be parsed as suffix of LIKE, not standalone infix"
7372            ),
7373            other => unreachable!("expected Like with escape, got {other:?}"),
7374        }
7375    }
7376
7377    #[test]
7378    fn test_pratt_escape_glob_not_infix() {
7379        // a GLOB b ESCAPE c → Like(a, b, op=Glob, escape=c)
7380        let expr = parse("a GLOB b ESCAPE c");
7381        match &expr {
7382            Expr::Like {
7383                op: LikeOp::Glob,
7384                escape: Some(_),
7385                ..
7386            } => {}
7387            other => unreachable!("expected Glob with escape, got {other:?}"),
7388        }
7389    }
7390
7391    // Error recovery: multiple errors collected in one pass
7392    #[test]
7393    fn test_pratt_error_recovery_multiple_errors() {
7394        use crate::parser::Parser;
7395        let mut p = Parser::from_sql("SELECT +; SELECT *; SELECT 1");
7396        let (stmts, errs) = p.parse_all();
7397        // SELECT + fails (missing operand), SELECT * fails (no FROM for bare *),
7398        // SELECT 1 should succeed.
7399        assert!(
7400            !stmts.is_empty(),
7401            "should recover and parse at least one valid statement"
7402        );
7403        assert!(
7404            !errs.is_empty(),
7405            "should collect at least one error from malformed statements"
7406        );
7407    }
7408
7409    // Complex mixed expression: full 11-level test
7410    #[test]
7411    fn test_pratt_complex_mixed_all_levels() {
7412        // NOT a = b + c * -d OR e < f AND g LIKE h
7413        // → (NOT (a = (b + (c * (-d))))) OR ((e < f) AND (g LIKE h))
7414        let expr = parse("NOT a = b + c * -d OR e < f AND g LIKE h");
7415        // Top level: OR
7416        match &expr {
7417            Expr::BinaryOp {
7418                op: BinaryOp::Or,
7419                left,
7420                right,
7421                ..
7422            } => {
7423                // left = NOT (a = (b + (c * (-d))))
7424                assert!(
7425                    matches!(
7426                        left.as_ref(),
7427                        Expr::UnaryOp {
7428                            op: UnaryOp::Not,
7429                            ..
7430                        }
7431                    ),
7432                    "left of OR should be NOT(...)"
7433                );
7434                // right = (e < f) AND (g LIKE h)
7435                match right.as_ref() {
7436                    Expr::BinaryOp {
7437                        op: BinaryOp::And,
7438                        left: and_left,
7439                        right: and_right,
7440                        ..
7441                    } => {
7442                        assert!(
7443                            matches!(
7444                                and_left.as_ref(),
7445                                Expr::BinaryOp {
7446                                    op: BinaryOp::Lt,
7447                                    ..
7448                                }
7449                            ),
7450                            "left of AND should be Lt(e,f)"
7451                        );
7452                        assert!(
7453                            matches!(and_right.as_ref(), Expr::Like { .. }),
7454                            "right of AND should be Like(g,h)"
7455                        );
7456                    }
7457                    other => unreachable!("expected And(Lt, Like), got {other:?}"),
7458                }
7459
7460                // Drill into the NOT to verify deeper structure:
7461                // NOT → Eq → right = Add → right = Mul → right = Negate
7462                if let Expr::UnaryOp {
7463                    expr: not_inner, ..
7464                } = left.as_ref()
7465                {
7466                    if let Expr::BinaryOp {
7467                        op: BinaryOp::Eq,
7468                        right: eq_right,
7469                        ..
7470                    } = not_inner.as_ref()
7471                    {
7472                        if let Expr::BinaryOp {
7473                            op: BinaryOp::Add,
7474                            right: add_right,
7475                            ..
7476                        } = eq_right.as_ref()
7477                        {
7478                            if let Expr::BinaryOp {
7479                                op: BinaryOp::Multiply,
7480                                right: mul_right,
7481                                ..
7482                            } = add_right.as_ref()
7483                            {
7484                                assert!(
7485                                    matches!(
7486                                        mul_right.as_ref(),
7487                                        Expr::UnaryOp {
7488                                            op: UnaryOp::Negate,
7489                                            ..
7490                                        }
7491                                    ),
7492                                    "deepest: negate"
7493                                );
7494                            } else {
7495                                unreachable!("expected Mul in add_right");
7496                            }
7497                        } else {
7498                            unreachable!("expected Add in eq_right");
7499                        }
7500                    } else {
7501                        unreachable!("expected Eq inside NOT");
7502                    }
7503                }
7504            }
7505            other => unreachable!("expected Or(Not(...), And(...)), got {other:?}"),
7506        }
7507    }
7508
7509    // JSON operators share precedence with concat and associate left-to-right.
7510    #[test]
7511    fn test_pratt_json_same_precedence_as_concat() {
7512        // a || b -> c parses as (a || b) -> c.
7513        let expr = parse("a || b -> c");
7514        match &expr {
7515            Expr::JsonAccess {
7516                expr: left,
7517                path: right,
7518                arrow: JsonArrow::Arrow,
7519                ..
7520            } => {
7521                assert!(
7522                    matches!(
7523                        left.as_ref(),
7524                        Expr::BinaryOp {
7525                            op: BinaryOp::Concat,
7526                            ..
7527                        }
7528                    ),
7529                    "left side should be concat expression"
7530                );
7531                assert!(
7532                    matches!(right.as_ref(), Expr::Column(_, _)),
7533                    "path should remain the right-hand expression"
7534                );
7535            }
7536            other => unreachable!("expected JsonAccess(Concat(a,b), c), got {other:?}"),
7537        }
7538    }
7539
7540    #[test]
7541    fn test_pratt_double_arrow_same_precedence_as_concat() {
7542        let expr = parse("a || b ->> c");
7543        assert!(
7544            matches!(
7545                expr,
7546                Expr::JsonAccess {
7547                    arrow: JsonArrow::DoubleArrow,
7548                    ..
7549                }
7550            ),
7551            "double-arrow should parse as JsonAccess at the same precedence level as concat"
7552        );
7553    }
7554}