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