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                    // A lexer error token (unterminated / unrecognized / malformed)
2183                    // already carries its stock-matching message; propagate it
2184                    // verbatim instead of Debug-wrapping it as
2185                    // `unexpected token in expression: Error("...")`.
2186                    message: match &kind {
2187                        TokenKind::Error(msg) => msg.clone(),
2188                        _ => format!("unexpected token in expression: {kind:?}"),
2189                    },
2190                    span: token_span,
2191                    line,
2192                    col,
2193                });
2194            }
2195        };
2196        self.push_expr_tail(parsed, min_bp);
2197        Ok(())
2198    }
2199
2200    fn identifier_or_function(
2201        &mut self,
2202        name: Arc<str>,
2203        start: Span,
2204        min_bp: u8,
2205    ) -> Result<(), ParseError> {
2206        if self.parser.at_kind(&TokenKind::LeftParen) {
2207            return self.start_function(name.to_string(), start, min_bp);
2208        }
2209        let parsed = if self.parser.at_kind(&TokenKind::Dot) {
2210            let Some(column_token) = self.parser.tokens.get(self.parser.pos + 1).cloned() else {
2211                return Err(self.parser.err_here("expected column name after '.'"));
2212            };
2213            let column = match &column_token.kind {
2214                TokenKind::Id(column) | TokenKind::QuotedId(column, _) => Arc::clone(column),
2215                TokenKind::String(column) => Arc::<str>::from(column.as_str()),
2216                kind if starts_post_dot_identifier(kind) => Arc::<str>::from(kw_to_str(kind)),
2217                _ => {
2218                    return Err(ParseError::at(
2219                        format!(
2220                            "expected column name after '.', got {:?}",
2221                            column_token.kind
2222                        ),
2223                        Some(&column_token),
2224                    ));
2225                }
2226            };
2227            self.parser.pos = self.parser.pos.saturating_add(2);
2228            self.parser.finish_expr(
2229                Expr::Column(
2230                    ColumnRef::qualified(name, column),
2231                    start.merge(column_token.span),
2232                ),
2233                2,
2234                false,
2235                false,
2236            )?
2237        } else {
2238            ParsedExpr::leaf(Expr::Column(ColumnRef::bare(name), start))
2239        };
2240        self.push_expr_tail(parsed, min_bp);
2241        Ok(())
2242    }
2243
2244    fn start_function(
2245        &mut self,
2246        name: String,
2247        start: Span,
2248        outer_min_bp: u8,
2249    ) -> Result<(), ParseError> {
2250        self.parser.expect_kind(&TokenKind::LeftParen)?;
2251        let mut build = FunctionBuild {
2252            name,
2253            start,
2254            args: FunctionArgs::List(Vec::new()),
2255            distinct: false,
2256            height: 0,
2257            order_by: Vec::new(),
2258            filter: None,
2259            over: None,
2260            end: start,
2261        };
2262        if self.parser.eat_kind(&TokenKind::Star) {
2263            // bd-2fong parity: stock SQLite parses `f(*)` for ANY function.
2264            // Only count keeps star semantics; every other function treats
2265            // the star as a zero-argument call, so arity validation later
2266            // yields stock's exact behavior — `random(*)` evaluates,
2267            // `abs(*)` / `max(*)` fail with "wrong number of arguments".
2268            build.args = if build.name.eq_ignore_ascii_case("count") {
2269                FunctionArgs::Star
2270            } else {
2271                FunctionArgs::List(Vec::new())
2272            };
2273            self.controls.push(ParseControl::FunctionClose {
2274                outer_min_bp,
2275                build,
2276            });
2277        } else {
2278            build.distinct = self.parser.eat_kind(&TokenKind::KwDistinct);
2279            if self.parser.at_kind(&TokenKind::RightParen) {
2280                if build.distinct {
2281                    return Err(self
2282                        .parser
2283                        .err_here("DISTINCT requires at least one argument"));
2284                }
2285                self.controls.push(ParseControl::FunctionOrderStart {
2286                    outer_min_bp,
2287                    build,
2288                });
2289            } else {
2290                self.controls.push(ParseControl::FunctionArgDone {
2291                    outer_min_bp,
2292                    build,
2293                });
2294                self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2295            }
2296        }
2297        Ok(())
2298    }
2299
2300    #[allow(clippy::too_many_lines)]
2301    fn expr_tail(&mut self, min_bp: u8) -> Result<(), ParseError> {
2302        let lhs = self.pop_expr()?;
2303        if let Some(left_bp) = self.parser.postfix_bp()
2304            && left_bp >= min_bp
2305        {
2306            let parsed = self.parser.parse_postfix(lhs)?;
2307            self.push_expr_tail(parsed, min_bp);
2308            return Ok(());
2309        }
2310        let Some((left_bp, right_bp)) = self.parser.infix_bp() else {
2311            self.values.push(MachineValue::Expr(lhs));
2312            return Ok(());
2313        };
2314        if left_bp < min_bp {
2315            self.values.push(MachineValue::Expr(lhs));
2316            return Ok(());
2317        }
2318
2319        let token = self.parser.advance_token();
2320        let simple = match &token.kind {
2321            TokenKind::Plus => Some(BinaryOp::Add),
2322            TokenKind::Minus => Some(BinaryOp::Subtract),
2323            TokenKind::Star => Some(BinaryOp::Multiply),
2324            TokenKind::Slash => Some(BinaryOp::Divide),
2325            TokenKind::Percent => Some(BinaryOp::Modulo),
2326            TokenKind::Concat => Some(BinaryOp::Concat),
2327            TokenKind::Eq | TokenKind::EqEq => Some(BinaryOp::Eq),
2328            TokenKind::Ne | TokenKind::LtGt => Some(BinaryOp::Ne),
2329            TokenKind::Lt => Some(BinaryOp::Lt),
2330            TokenKind::Le => Some(BinaryOp::Le),
2331            TokenKind::Gt => Some(BinaryOp::Gt),
2332            TokenKind::Ge => Some(BinaryOp::Ge),
2333            TokenKind::Ampersand => Some(BinaryOp::BitAnd),
2334            TokenKind::Pipe => Some(BinaryOp::BitOr),
2335            TokenKind::ShiftLeft => Some(BinaryOp::ShiftLeft),
2336            TokenKind::ShiftRight => Some(BinaryOp::ShiftRight),
2337            TokenKind::KwOr => Some(BinaryOp::Or),
2338            TokenKind::KwAnd => Some(BinaryOp::And),
2339            _ => None,
2340        };
2341        if let Some(op) = simple {
2342            self.controls.push(ParseControl::BinaryDone {
2343                outer_min_bp: min_bp,
2344                lhs,
2345                op,
2346            });
2347            self.controls
2348                .push(ParseControl::ExprStart { min_bp: right_bp });
2349            return Ok(());
2350        }
2351        match &token.kind {
2352            TokenKind::KwIs => {
2353                let not = self.parser.eat_kind(&TokenKind::KwNot);
2354                if self.parser.eat_kind(&TokenKind::KwDistinct) {
2355                    self.parser.expect_kind(&TokenKind::KwFrom)?;
2356                    self.controls.push(ParseControl::BinaryDone {
2357                        outer_min_bp: min_bp,
2358                        lhs,
2359                        op: if not { BinaryOp::Is } else { BinaryOp::IsNot },
2360                    });
2361                } else {
2362                    self.controls.push(ParseControl::IsDone {
2363                        outer_min_bp: min_bp,
2364                        lhs,
2365                        not,
2366                    });
2367                }
2368                self.controls
2369                    .push(ParseControl::ExprStart { min_bp: right_bp });
2370            }
2371            TokenKind::KwLike | TokenKind::KwGlob | TokenKind::KwMatch | TokenKind::KwRegexp => {
2372                let op = match &token.kind {
2373                    TokenKind::KwLike => LikeOp::Like,
2374                    TokenKind::KwGlob => LikeOp::Glob,
2375                    TokenKind::KwMatch => LikeOp::Match,
2376                    TokenKind::KwRegexp => LikeOp::Regexp,
2377                    _ => unreachable!(),
2378                };
2379                self.controls.push(ParseControl::LikePatternDone {
2380                    outer_min_bp: min_bp,
2381                    lhs,
2382                    op,
2383                    not: false,
2384                });
2385                self.controls.push(ParseControl::ExprStart {
2386                    min_bp: bp::EQUALITY.1,
2387                });
2388            }
2389            TokenKind::KwBetween => {
2390                self.controls.push(ParseControl::BetweenLowDone {
2391                    outer_min_bp: min_bp,
2392                    lhs,
2393                    not: false,
2394                });
2395                self.controls.push(ParseControl::ExprStart {
2396                    min_bp: bp::NOT_PREFIX,
2397                });
2398            }
2399            TokenKind::KwIn => self.start_in(lhs, false, min_bp)?,
2400            TokenKind::Arrow => {
2401                self.controls.push(ParseControl::JsonDone {
2402                    outer_min_bp: min_bp,
2403                    lhs,
2404                    arrow: JsonArrow::Arrow,
2405                });
2406                self.controls
2407                    .push(ParseControl::ExprStart { min_bp: right_bp });
2408            }
2409            TokenKind::DoubleArrow => {
2410                self.controls.push(ParseControl::JsonDone {
2411                    outer_min_bp: min_bp,
2412                    lhs,
2413                    arrow: JsonArrow::DoubleArrow,
2414                });
2415                self.controls
2416                    .push(ParseControl::ExprStart { min_bp: right_bp });
2417            }
2418            TokenKind::KwNot => {
2419                let next = self.parser.advance_token();
2420                match &next.kind {
2421                    TokenKind::KwLike
2422                    | TokenKind::KwGlob
2423                    | TokenKind::KwMatch
2424                    | TokenKind::KwRegexp => {
2425                        let op = match &next.kind {
2426                            TokenKind::KwLike => LikeOp::Like,
2427                            TokenKind::KwGlob => LikeOp::Glob,
2428                            TokenKind::KwMatch => LikeOp::Match,
2429                            TokenKind::KwRegexp => LikeOp::Regexp,
2430                            _ => unreachable!(),
2431                        };
2432                        self.controls.push(ParseControl::LikePatternDone {
2433                            outer_min_bp: min_bp,
2434                            lhs,
2435                            op,
2436                            not: true,
2437                        });
2438                        self.controls.push(ParseControl::ExprStart {
2439                            min_bp: bp::EQUALITY.1,
2440                        });
2441                    }
2442                    TokenKind::KwBetween => {
2443                        self.controls.push(ParseControl::BetweenLowDone {
2444                            outer_min_bp: min_bp,
2445                            lhs,
2446                            not: true,
2447                        });
2448                        self.controls.push(ParseControl::ExprStart {
2449                            min_bp: bp::NOT_PREFIX,
2450                        });
2451                    }
2452                    TokenKind::KwIn => self.start_in(lhs, true, min_bp)?,
2453                    _ => {
2454                        return Err(ParseError::at(
2455                            format!(
2456                                "expected LIKE/GLOB/MATCH/REGEXP/BETWEEN/IN after NOT, got {:?}",
2457                                next.kind
2458                            ),
2459                            Some(&next),
2460                        ));
2461                    }
2462                }
2463            }
2464            other => {
2465                return Err(ParseError::at(
2466                    format!("unexpected infix token: {other:?}"),
2467                    Some(&token),
2468                ));
2469            }
2470        }
2471        Ok(())
2472    }
2473
2474    fn start_in(&mut self, lhs: ParsedExpr, not: bool, outer_min_bp: u8) -> Result<(), ParseError> {
2475        let start = lhs.expr.span();
2476        if !self.parser.eat_kind(&TokenKind::LeftParen) {
2477            let table = self.parser.parse_qualified_name()?;
2478            let end = self.parser.tokens[self.parser.pos.saturating_sub(1)].span;
2479            let height = lhs.height;
2480            let has_function = lhs.has_function;
2481            let parsed = self.parser.checked_expr(
2482                Expr::In {
2483                    expr: Box::new(lhs.expr),
2484                    set: InSet::Table(table),
2485                    not,
2486                    span: start.merge(end),
2487                },
2488                height,
2489                false,
2490                has_function,
2491            )?;
2492            let parsed = if not {
2493                self.parser.add_cached_parent(parsed)?
2494            } else {
2495                parsed
2496            };
2497            self.push_expr_tail(parsed, outer_min_bp);
2498        } else if matches!(
2499            self.parser.peek_kind(),
2500            TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
2501        ) {
2502            self.controls.push(ParseControl::InSelectDone {
2503                outer_min_bp,
2504                lhs,
2505                not,
2506                start,
2507            });
2508            self.controls.push(ParseControl::SubqueryStart);
2509        } else if self.parser.at_kind(&TokenKind::RightParen) {
2510            let end = self.parser.expect_kind(&TokenKind::RightParen)?;
2511            self.finish_in_list(outer_min_bp, lhs, not, Vec::new(), start.merge(end))?;
2512        } else {
2513            self.controls.push(ParseControl::InItemDone {
2514                outer_min_bp,
2515                lhs,
2516                not,
2517                items: Vec::new(),
2518                start,
2519            });
2520            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2521        }
2522        Ok(())
2523    }
2524
2525    fn finish_case(
2526        &mut self,
2527        outer_min_bp: u8,
2528        build: CaseBuild,
2529        else_expr: Option<ParsedExpr>,
2530    ) -> Result<(), ParseError> {
2531        if !self.parser.eat_kind(&TokenKind::KwEnd) {
2532            return Err(self.parser.err_here("expected END for CASE expression"));
2533        }
2534        let end = self.parser.tokens[self.parser.pos.saturating_sub(1)].span;
2535        let mut height = build.operand.as_ref().map_or(0, |expr| expr.height);
2536        let mut is_constant = build.operand.as_ref().is_none_or(|expr| expr.is_constant);
2537        let mut has_function = build.operand.as_ref().is_some_and(|expr| expr.has_function);
2538        for (condition, result) in &build.whens {
2539            height = height.max(condition.height).max(result.height);
2540            is_constant &= condition.is_constant && result.is_constant;
2541            has_function |= condition.has_function || result.has_function;
2542        }
2543        if let Some(expr) = &else_expr {
2544            height = height.max(expr.height);
2545            is_constant &= expr.is_constant;
2546            has_function |= expr.has_function;
2547        }
2548        let parsed = self.parser.checked_expr(
2549            Expr::Case {
2550                operand: build.operand.map(|expr| Box::new(expr.expr)),
2551                whens: build
2552                    .whens
2553                    .into_iter()
2554                    .map(|(condition, result)| (condition.expr, result.expr))
2555                    .collect(),
2556                else_expr: else_expr.map(|expr| Box::new(expr.expr)),
2557                span: build.start.merge(end),
2558            },
2559            height,
2560            is_constant,
2561            has_function,
2562        )?;
2563        self.push_expr_tail(parsed, outer_min_bp);
2564        Ok(())
2565    }
2566
2567    fn finish_like(
2568        &mut self,
2569        outer_min_bp: u8,
2570        lhs: ParsedExpr,
2571        pattern: ParsedExpr,
2572        escape: Option<ParsedExpr>,
2573        op: LikeOp,
2574        not: bool,
2575    ) -> Result<(), ParseError> {
2576        let end = escape
2577            .as_ref()
2578            .map_or_else(|| pattern.expr.span(), |expr| expr.expr.span());
2579        let height = escape.as_ref().map_or_else(
2580            || lhs.height.max(pattern.height),
2581            |expr| lhs.height.max(pattern.height).max(expr.height),
2582        );
2583        let span = lhs.expr.span().merge(end);
2584        let parsed = self.parser.checked_expr(
2585            Expr::Like {
2586                expr: Box::new(lhs.expr),
2587                pattern: Box::new(pattern.expr),
2588                escape: escape.map(|expr| Box::new(expr.expr)),
2589                op,
2590                not,
2591                span,
2592            },
2593            height,
2594            false,
2595            true,
2596        )?;
2597        let parsed = if not {
2598            self.parser.add_cached_parent(parsed)?
2599        } else {
2600            parsed
2601        };
2602        self.push_expr_tail(parsed, outer_min_bp);
2603        Ok(())
2604    }
2605
2606    fn finish_in_list(
2607        &mut self,
2608        outer_min_bp: u8,
2609        lhs: ParsedExpr,
2610        not: bool,
2611        items: Vec<ParsedExpr>,
2612        span: Span,
2613    ) -> Result<(), ParseError> {
2614        if let Some(message) = vector_in_list_arity_error(&lhs.expr, &items) {
2615            return Err(self.parser.err_here(message));
2616        }
2617        let item_height = items.iter().map(|item| item.height).max().unwrap_or(0);
2618        let items_are_constant = items.iter().all(|item| item.is_constant);
2619        let item_has_function = items.iter().any(|item| item.has_function);
2620        let singleton_constant = matches!(items.as_slice(), [item] if item.is_constant)
2621            && lhs.root != CachedRoot::Vector;
2622        let singleton_subquery =
2623            matches!(items.as_slice(), [item] if item.root == CachedRoot::ScalarSubquery);
2624        let lhs_height = lhs.height;
2625        let lhs_is_constant = lhs.is_constant;
2626        let lhs_has_function = lhs.has_function;
2627        let expr = Expr::In {
2628            expr: Box::new(lhs.expr),
2629            set: InSet::List(items.into_iter().map(|item| item.expr).collect()),
2630            not,
2631            span,
2632        };
2633        let parsed = if item_height == 0 {
2634            if lhs_has_function {
2635                self.parser
2636                    .finish_expr(expr, lhs_height.saturating_add(1), false, true)?
2637            } else {
2638                self.parser.finish_expr(expr, 1, true, false)?
2639            }
2640        } else {
2641            let cached_child_height = if singleton_constant {
2642                lhs_height.max(item_height.saturating_add(1))
2643            } else if singleton_subquery {
2644                lhs_height.max(item_height.saturating_sub(1))
2645            } else {
2646                lhs_height.max(item_height)
2647            };
2648            let parsed = self.parser.checked_expr(
2649                expr,
2650                cached_child_height,
2651                lhs_is_constant && items_are_constant,
2652                lhs_has_function || item_has_function,
2653            )?;
2654            if not {
2655                self.parser.add_cached_parent(parsed)?
2656            } else {
2657                parsed
2658            }
2659        };
2660        self.push_expr_tail(parsed, outer_min_bp);
2661        Ok(())
2662    }
2663
2664    fn window_start(&mut self) -> Result<(), ParseError> {
2665        let has_base_window = starts_window_base_name(self.parser.peek_kind());
2666        let build = WindowBuild {
2667            base_window: if has_base_window {
2668                Some(self.parser.parse_window_name()?)
2669            } else {
2670                None
2671            },
2672            partition_by: Vec::new(),
2673            order_by: Vec::new(),
2674        };
2675        if self.parser.eat_kind(&TokenKind::KwPartition) {
2676            self.parser.expect_kw(&TokenKind::KwBy)?;
2677            self.controls
2678                .push(ParseControl::WindowPartitionDone { build });
2679            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2680        } else {
2681            self.controls.push(ParseControl::WindowOrderStart { build });
2682        }
2683        Ok(())
2684    }
2685
2686    fn window_frame_start(&mut self, build: WindowBuild) {
2687        let frame_type = if self.parser.eat_kind(&TokenKind::KwRows) {
2688            Some(FrameType::Rows)
2689        } else if self.parser.eat_kind(&TokenKind::KwRange) {
2690            Some(FrameType::Range)
2691        } else if self.parser.eat_kind(&TokenKind::KwGroups) {
2692            Some(FrameType::Groups)
2693        } else {
2694            None
2695        };
2696        let Some(frame_type) = frame_type else {
2697            self.values.push(MachineValue::Window(WindowSpec {
2698                window_ref: build.base_window.map(WindowReference::Base),
2699                partition_by: build.partition_by,
2700                order_by: build.order_by,
2701                frame: None,
2702            }));
2703            return;
2704        };
2705        let between = self.parser.eat_kind(&TokenKind::KwBetween);
2706        self.controls.push(ParseControl::WindowFirstBoundDone {
2707            build,
2708            frame_type,
2709            between,
2710        });
2711        self.controls.push(ParseControl::FrameBoundStart);
2712    }
2713
2714    fn frame_bound_start(&mut self) -> Result<(), ParseError> {
2715        let origin = self
2716            .parser
2717            .peek_token()
2718            .cloned()
2719            .ok_or_else(|| self.parser.err_here("expected window frame bound"))?;
2720        if self.parser.eat_kind(&TokenKind::KwUnbounded) {
2721            let bound = if self.parser.eat_kind(&TokenKind::KwPreceding) {
2722                FrameBound::UnboundedPreceding
2723            } else {
2724                self.parser.expect_kw(&TokenKind::KwFollowing)?;
2725                FrameBound::UnboundedFollowing
2726            };
2727            self.values.push(MachineValue::FrameBound(ParsedFrameBound {
2728                value: bound,
2729                origin,
2730            }));
2731        } else if matches!(
2732            self.parser.peek_kind(),
2733            TokenKind::Id(value) if value.eq_ignore_ascii_case("CURRENT")
2734        ) {
2735            self.parser.advance_token();
2736            self.parser.expect_kw(&TokenKind::KwRow)?;
2737            self.values.push(MachineValue::FrameBound(ParsedFrameBound {
2738                value: FrameBound::CurrentRow,
2739                origin,
2740            }));
2741        } else {
2742            self.controls
2743                .push(ParseControl::FrameBoundExprDone { origin });
2744            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2745        }
2746        Ok(())
2747    }
2748
2749    fn finish_frame(
2750        &mut self,
2751        frame_type: FrameType,
2752        start: FrameBound,
2753        end: Option<FrameBound>,
2754    ) -> Result<FrameSpec, ParseError> {
2755        let exclude = if self.parser.eat_kind(&TokenKind::KwExclude) {
2756            if self.parser.eat_kind(&TokenKind::KwNo) {
2757                let others = self.parser.parse_identifier()?;
2758                if !others.eq_ignore_ascii_case("OTHERS") {
2759                    return Err(self.parser.err_here("expected OTHERS"));
2760                }
2761                Some(FrameExclude::NoOthers)
2762            } else if self.parser.eat_kind(&TokenKind::KwTies) {
2763                Some(FrameExclude::Ties)
2764            } else if self.parser.eat_kind(&TokenKind::KwGroup) {
2765                Some(FrameExclude::Group)
2766            } else if matches!(
2767                self.parser.peek_kind(),
2768                TokenKind::Id(value) if value.eq_ignore_ascii_case("CURRENT")
2769            ) {
2770                self.parser.advance_token();
2771                self.parser.expect_kw(&TokenKind::KwRow)?;
2772                Some(FrameExclude::CurrentRow)
2773            } else {
2774                return Err(self
2775                    .parser
2776                    .err_here("expected NO OTHERS, TIES, GROUP, or CURRENT ROW after EXCLUDE"));
2777            }
2778        } else {
2779            None
2780        };
2781        Ok(FrameSpec {
2782            frame_type,
2783            start,
2784            end,
2785            exclude,
2786        })
2787    }
2788
2789    fn step_select(&mut self, control: ParseControl) -> Result<(), ParseError> {
2790        match control {
2791            ParseControl::SelectStart { with } => {
2792                self.controls
2793                    .push(ParseControl::SelectFirstCoreDone { with });
2794                self.controls.push(ParseControl::CoreStart);
2795                Ok(())
2796            }
2797            ParseControl::SelectFirstCoreDone { with } => {
2798                let core = self.pop_core()?;
2799                self.continue_select_body(SelectBuild {
2800                    with,
2801                    height: core.height,
2802                    first: core.value,
2803                    compounds: Vec::new(),
2804                    order_by: Vec::new(),
2805                });
2806                Ok(())
2807            }
2808            ParseControl::SelectCompoundDone { mut build, op } => {
2809                let core = self.pop_core()?;
2810                build.height = build.height.max(core.height);
2811                build.compounds.push((op, core.value));
2812                self.continue_select_body(build);
2813                Ok(())
2814            }
2815            ParseControl::SelectOrderStart { build } => {
2816                let final_core = build
2817                    .compounds
2818                    .last()
2819                    .map_or(&build.first, |(_, core)| core);
2820                if matches!(final_core, SelectCore::Values(_))
2821                    && matches!(
2822                        self.parser.peek_kind(),
2823                        TokenKind::KwOrder | TokenKind::KwLimit
2824                    )
2825                {
2826                    return Err(self
2827                        .parser
2828                        .err_here("ORDER BY / LIMIT clause is not allowed after a VALUES term"));
2829                }
2830                if self.parser.eat_kind(&TokenKind::KwOrder) {
2831                    self.parser.expect_kw(&TokenKind::KwBy)?;
2832                    self.controls.push(ParseControl::SelectOrderDone { build });
2833                    self.controls.push(ParseControl::OrderingStart);
2834                } else {
2835                    self.start_select_limit(build)?;
2836                }
2837                Ok(())
2838            }
2839            ParseControl::SelectOrderDone { mut build } => {
2840                let term = self.pop_ordering()?;
2841                build.height = build.height.max(term.height);
2842                build.order_by.push(term.value);
2843                if self.parser.eat_kind(&TokenKind::Comma) {
2844                    self.controls.push(ParseControl::SelectOrderDone { build });
2845                    self.controls.push(ParseControl::OrderingStart);
2846                } else {
2847                    self.start_select_limit(build)?;
2848                }
2849                Ok(())
2850            }
2851            ParseControl::SelectLimitFirstDone { build } => {
2852                let first = self.pop_expr()?;
2853                if self.parser.eat_kind(&TokenKind::KwOffset) {
2854                    self.controls.push(ParseControl::SelectLimitSecondDone {
2855                        build,
2856                        first,
2857                        comma_form: false,
2858                    });
2859                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2860                } else if self.parser.eat_kind(&TokenKind::Comma) {
2861                    self.controls.push(ParseControl::SelectLimitSecondDone {
2862                        build,
2863                        first,
2864                        comma_form: true,
2865                    });
2866                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2867                } else {
2868                    let height = self.parser.checked_cached_parent_height(first.height)?;
2869                    self.finish_select(
2870                        build,
2871                        HeightTracked {
2872                            value: Some(LimitClause {
2873                                limit: first.expr,
2874                                offset: None,
2875                            }),
2876                            height,
2877                        },
2878                    )?;
2879                }
2880                Ok(())
2881            }
2882            ParseControl::SelectLimitSecondDone {
2883                build,
2884                first,
2885                comma_form,
2886            } => {
2887                let second = self.pop_expr()?;
2888                let height = self
2889                    .parser
2890                    .checked_cached_parent_height(first.height.max(second.height))?;
2891                let limit = if comma_form {
2892                    LimitClause {
2893                        limit: second.expr,
2894                        offset: Some(first.expr),
2895                    }
2896                } else {
2897                    LimitClause {
2898                        limit: first.expr,
2899                        offset: Some(second.expr),
2900                    }
2901                };
2902                self.finish_select(
2903                    build,
2904                    HeightTracked {
2905                        value: Some(limit),
2906                        height,
2907                    },
2908                )
2909            }
2910            ParseControl::CoreStart => self.core_start(),
2911            ParseControl::CoreColumnStart { build } => self.core_column_start(build),
2912            ParseControl::CoreColumnDone { mut build } => {
2913                let expr = self.pop_expr()?;
2914                build.height = build.height.max(expr.height);
2915                build.columns.push(ResultColumn::Expr {
2916                    expr: expr.expr,
2917                    alias: self.parser.try_result_alias()?,
2918                });
2919                if self.parser.eat_kind(&TokenKind::Comma) {
2920                    self.controls.push(ParseControl::CoreColumnStart { build });
2921                } else {
2922                    self.controls.push(ParseControl::CoreAfterColumns { build });
2923                }
2924                Ok(())
2925            }
2926            ParseControl::CoreAfterColumns { build } => {
2927                if self.parser.eat_kind(&TokenKind::KwFrom) {
2928                    self.controls.push(ParseControl::CoreFromDone { build });
2929                    self.controls.push(ParseControl::FromStart);
2930                } else {
2931                    self.continue_core_where(build)?;
2932                }
2933                Ok(())
2934            }
2935            ParseControl::CoreFromDone { mut build } => {
2936                build.from = Some(self.pop_from()?);
2937                self.continue_core_where(build)
2938            }
2939            ParseControl::CoreWhereDone { mut build } => {
2940                let expr = self.pop_expr()?;
2941                build.height = build.height.max(expr.height);
2942                build.where_clause = Some(Box::new(expr.expr));
2943                self.continue_core_group(build)
2944            }
2945            ParseControl::CoreGroupDone { mut build } => {
2946                let expr = self.pop_expr()?;
2947                build.height = build.height.max(expr.height);
2948                build.group_by.push(expr.expr);
2949                if self.parser.eat_kind(&TokenKind::Comma) {
2950                    self.controls.push(ParseControl::CoreGroupDone { build });
2951                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2952                } else {
2953                    self.continue_core_having(build);
2954                }
2955                Ok(())
2956            }
2957            ParseControl::CoreHavingDone { mut build } => {
2958                let expr = self.pop_expr()?;
2959                build.height = build.height.max(expr.height);
2960                build.having = Some(Box::new(expr.expr));
2961                self.continue_core_windows(build);
2962                Ok(())
2963            }
2964            ParseControl::CoreWindowStart { build } => {
2965                let name = self.parser.parse_window_name()?;
2966                self.parser.expect_kw(&TokenKind::KwAs)?;
2967                self.parser.expect_token(&TokenKind::LeftParen)?;
2968                self.controls
2969                    .push(ParseControl::CoreWindowDone { build, name });
2970                self.controls.push(ParseControl::WindowStart);
2971                Ok(())
2972            }
2973            ParseControl::CoreWindowDone { mut build, name } => {
2974                let spec = self.pop_window()?;
2975                self.parser.expect_token(&TokenKind::RightParen)?;
2976                build.windows.push(WindowDef { name, spec });
2977                if self.parser.eat_kind(&TokenKind::Comma) {
2978                    self.controls.push(ParseControl::CoreWindowStart { build });
2979                } else {
2980                    self.finish_core(build);
2981                }
2982                Ok(())
2983            }
2984            ParseControl::ValuesRowStart {
2985                rows,
2986                height,
2987                force_union_all_from,
2988            } => {
2989                self.parser.expect_token(&TokenKind::LeftParen)?;
2990                self.controls.push(ParseControl::ValuesItemDone {
2991                    rows,
2992                    row: Vec::new(),
2993                    height,
2994                    force_union_all_from,
2995                });
2996                self.controls.push(ParseControl::ExprStart { min_bp: 0 });
2997                Ok(())
2998            }
2999            ParseControl::ValuesItemDone {
3000                mut rows,
3001                mut row,
3002                mut height,
3003                mut force_union_all_from,
3004            } => {
3005                let expr = self.pop_expr()?;
3006                height = height.max(expr.height);
3007                row.push(expr.expr);
3008                if self.parser.eat_kind(&TokenKind::Comma) {
3009                    self.controls.push(ParseControl::ValuesItemDone {
3010                        rows,
3011                        row,
3012                        height,
3013                        force_union_all_from,
3014                    });
3015                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3016                } else {
3017                    self.parser.expect_token(&TokenKind::RightParen)?;
3018                    if force_union_all_from.is_none() && self.parser.has_with {
3019                        force_union_all_from = Some(rows.len());
3020                    }
3021                    rows.push(row);
3022                    if self.parser.eat_kind(&TokenKind::Comma) {
3023                        self.controls.push(ParseControl::ValuesRowStart {
3024                            rows,
3025                            height,
3026                            force_union_all_from,
3027                        });
3028                    } else {
3029                        self.values.push(MachineValue::Core(HeightTracked {
3030                            value: SelectCore::Values(ValuesClause::parsed(
3031                                rows,
3032                                force_union_all_from,
3033                            )),
3034                            height,
3035                        }));
3036                    }
3037                }
3038                Ok(())
3039            }
3040            ParseControl::FromStart => {
3041                self.controls.push(ParseControl::FromSourceDone);
3042                self.controls.push(ParseControl::TableStart);
3043                Ok(())
3044            }
3045            ParseControl::FromSourceDone => {
3046                let source = self.pop_table()?;
3047                self.continue_from(FromBuild {
3048                    source,
3049                    joins: Vec::new(),
3050                })
3051            }
3052            ParseControl::FromTableDone { build, join_type } => {
3053                let table = self.pop_table()?;
3054                if self.parser.eat_kind(&TokenKind::KwOn) {
3055                    self.controls.push(ParseControl::FromJoinConstraintDone {
3056                        build,
3057                        join_type,
3058                        table,
3059                    });
3060                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3061                    return Ok(());
3062                }
3063                let constraint = if self.parser.eat_kind(&TokenKind::KwUsing) {
3064                    self.parser.expect_token(&TokenKind::LeftParen)?;
3065                    let mut columns = vec![self.parser.parse_identifier()?];
3066                    while self.parser.eat_kind(&TokenKind::Comma) {
3067                        columns.push(self.parser.parse_identifier()?);
3068                    }
3069                    self.parser.expect_token(&TokenKind::RightParen)?;
3070                    Some(JoinConstraint::Using(columns))
3071                } else {
3072                    None
3073                };
3074                self.append_join(build, join_type, table, constraint)
3075            }
3076            ParseControl::FromJoinConstraintDone {
3077                build,
3078                join_type,
3079                table,
3080            } => {
3081                let expr = self.pop_expr()?;
3082                self.append_join(build, join_type, table, Some(JoinConstraint::On(expr.expr)))
3083            }
3084            ParseControl::TableStart => self.table_start(),
3085            ParseControl::TableSubqueryDone => {
3086                let select = self.pop_select()?;
3087                self.parser.expect_token(&TokenKind::RightParen)?;
3088                self.values
3089                    .push(MachineValue::Table(TableOrSubquery::Subquery {
3090                        query: Box::new(select.value),
3091                        alias: self.parser.try_table_alias()?,
3092                    }));
3093                Ok(())
3094            }
3095            ParseControl::TableParenJoinDone => {
3096                let from = self.pop_from()?;
3097                self.parser.expect_token(&TokenKind::RightParen)?;
3098                self.values
3099                    .push(MachineValue::Table(TableOrSubquery::ParenJoin(Box::new(
3100                        from,
3101                    ))));
3102                Ok(())
3103            }
3104            ParseControl::TableFunctionArgDone { name, mut args } => {
3105                args.push(self.pop_expr()?.expr);
3106                if self.parser.eat_kind(&TokenKind::Comma) {
3107                    self.controls
3108                        .push(ParseControl::TableFunctionArgDone { name, args });
3109                    self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3110                } else {
3111                    self.parser.expect_token(&TokenKind::RightParen)?;
3112                    self.values
3113                        .push(MachineValue::Table(TableOrSubquery::TableFunction {
3114                            name,
3115                            args,
3116                            alias: self.parser.try_table_alias()?,
3117                        }));
3118                }
3119                Ok(())
3120            }
3121            ParseControl::WithStart => self.with_start(),
3122            ParseControl::CteQueryDone {
3123                recursive,
3124                mut ctes,
3125                name,
3126                columns,
3127                materialized,
3128            } => {
3129                let query = self.pop_select()?;
3130                self.parser.expect_token(&TokenKind::RightParen)?;
3131                ctes.push(Cte {
3132                    name,
3133                    columns,
3134                    materialized,
3135                    query: query.value,
3136                });
3137                if self.parser.eat_kind(&TokenKind::Comma) {
3138                    self.start_cte(recursive, ctes)?;
3139                } else {
3140                    // SQLite treats `RECURSIVE` as optional; a self-referencing
3141                    // CTE is recursive regardless of the keyword.
3142                    let mut with = WithClause { recursive, ctes };
3143                    with.normalize_recursive();
3144                    self.values.push(MachineValue::With(with));
3145                }
3146                Ok(())
3147            }
3148            _ => Err(self
3149                .parser
3150                .err_here("internal expression parser control reached SELECT dispatcher")),
3151        }
3152    }
3153
3154    fn continue_select_body(&mut self, build: SelectBuild) {
3155        let op = if self.parser.eat_kind(&TokenKind::KwUnion) {
3156            Some(if self.parser.eat_kind(&TokenKind::KwAll) {
3157                CompoundOp::UnionAll
3158            } else {
3159                CompoundOp::Union
3160            })
3161        } else if self.parser.eat_kind(&TokenKind::KwIntersect) {
3162            Some(CompoundOp::Intersect)
3163        } else if self.parser.eat_kind(&TokenKind::KwExcept) {
3164            Some(CompoundOp::Except)
3165        } else {
3166            None
3167        };
3168        if let Some(op) = op {
3169            self.controls
3170                .push(ParseControl::SelectCompoundDone { build, op });
3171            self.controls.push(ParseControl::CoreStart);
3172        } else {
3173            self.controls.push(ParseControl::SelectOrderStart { build });
3174        }
3175    }
3176
3177    fn start_select_limit(&mut self, build: SelectBuild) -> Result<(), ParseError> {
3178        if self.parser.eat_kind(&TokenKind::KwLimit) {
3179            self.controls
3180                .push(ParseControl::SelectLimitFirstDone { build });
3181            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3182        } else {
3183            self.finish_select(
3184                build,
3185                HeightTracked {
3186                    value: None,
3187                    height: 0,
3188                },
3189            )?;
3190        }
3191        Ok(())
3192    }
3193
3194    fn finish_select(
3195        &mut self,
3196        mut build: SelectBuild,
3197        limit: HeightTracked<Option<LimitClause>>,
3198    ) -> Result<(), ParseError> {
3199        build.height = build.height.max(limit.height);
3200        let final_core = build
3201            .compounds
3202            .last()
3203            .map_or(&build.first, |(_, core)| core);
3204        if matches!(final_core, SelectCore::Values(_))
3205            && (!build.order_by.is_empty() || limit.value.is_some())
3206        {
3207            return Err(self
3208                .parser
3209                .err_here("ORDER BY / LIMIT clause is not allowed after a VALUES term"));
3210        }
3211        self.values.push(MachineValue::Select(HeightTracked {
3212            value: SelectStatement {
3213                with: build.with,
3214                body: SelectBody {
3215                    select: build.first,
3216                    compounds: build.compounds,
3217                },
3218                order_by: build.order_by,
3219                limit: limit.value,
3220            },
3221            height: build.height,
3222        }));
3223        Ok(())
3224    }
3225
3226    fn core_start(&mut self) -> Result<(), ParseError> {
3227        if self.parser.eat_kind(&TokenKind::KwValues) {
3228            self.controls.push(ParseControl::ValuesRowStart {
3229                rows: Vec::new(),
3230                height: 0,
3231                force_union_all_from: None,
3232            });
3233            return Ok(());
3234        }
3235        self.parser.expect_kw(&TokenKind::KwSelect)?;
3236        let distinct = if self.parser.eat_kind(&TokenKind::KwDistinct) {
3237            Distinctness::Distinct
3238        } else {
3239            let _ = self.parser.eat_kind(&TokenKind::KwAll);
3240            Distinctness::All
3241        };
3242        self.controls.push(ParseControl::CoreColumnStart {
3243            build: CoreBuild {
3244                distinct,
3245                columns: Vec::new(),
3246                height: 0,
3247                from: None,
3248                where_clause: None,
3249                group_by: Vec::new(),
3250                having: None,
3251                windows: Vec::new(),
3252            },
3253        });
3254        Ok(())
3255    }
3256
3257    fn core_column_start(&mut self, mut build: CoreBuild) -> Result<(), ParseError> {
3258        if self.parser.eat_kind(&TokenKind::Star) {
3259            build.columns.push(ResultColumn::Star);
3260            if self.parser.eat_kind(&TokenKind::Comma) {
3261                self.controls.push(ParseControl::CoreColumnStart { build });
3262            } else {
3263                self.controls.push(ParseControl::CoreAfterColumns { build });
3264            }
3265            return Ok(());
3266        }
3267        if starts_table_star_qualifier(self.parser.peek_kind())
3268            && self
3269                .parser
3270                .tokens
3271                .get(self.parser.pos + 1)
3272                .is_some_and(|token| token.kind == TokenKind::Dot)
3273        {
3274            let table_star = self
3275                .parser
3276                .tokens
3277                .get(self.parser.pos + 2)
3278                .is_some_and(|token| token.kind == TokenKind::Star);
3279            let schema_table_star = self
3280                .parser
3281                .tokens
3282                .get(self.parser.pos + 2)
3283                .is_some_and(|token| starts_table_star_qualifier(&token.kind))
3284                && self
3285                    .parser
3286                    .tokens
3287                    .get(self.parser.pos + 3)
3288                    .is_some_and(|token| token.kind == TokenKind::Dot)
3289                && self
3290                    .parser
3291                    .tokens
3292                    .get(self.parser.pos + 4)
3293                    .is_some_and(|token| token.kind == TokenKind::Star);
3294            if table_star || schema_table_star {
3295                let first = self.parser.parse_table_star_qualifier()?;
3296                self.parser.expect_token(&TokenKind::Dot)?;
3297                let name = if schema_table_star {
3298                    let second = self.parser.parse_table_star_qualifier()?;
3299                    self.parser.expect_token(&TokenKind::Dot)?;
3300                    QualifiedName::qualified(first, second)
3301                } else {
3302                    QualifiedName::bare(first)
3303                };
3304                self.parser.expect_token(&TokenKind::Star)?;
3305                build.columns.push(ResultColumn::TableStar(name));
3306                if self.parser.eat_kind(&TokenKind::Comma) {
3307                    self.controls.push(ParseControl::CoreColumnStart { build });
3308                } else {
3309                    self.controls.push(ParseControl::CoreAfterColumns { build });
3310                }
3311                return Ok(());
3312            }
3313        }
3314        self.controls.push(ParseControl::CoreColumnDone { build });
3315        self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3316        Ok(())
3317    }
3318
3319    fn continue_core_where(&mut self, build: CoreBuild) -> Result<(), ParseError> {
3320        if self.parser.eat_kind(&TokenKind::KwWhere) {
3321            self.controls.push(ParseControl::CoreWhereDone { build });
3322            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3323        } else {
3324            self.continue_core_group(build)?;
3325        }
3326        Ok(())
3327    }
3328
3329    fn continue_core_group(&mut self, build: CoreBuild) -> Result<(), ParseError> {
3330        if self.parser.eat_kind(&TokenKind::KwGroup) {
3331            self.parser.expect_kw(&TokenKind::KwBy)?;
3332            self.controls.push(ParseControl::CoreGroupDone { build });
3333            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3334        } else {
3335            self.continue_core_having(build);
3336        }
3337        Ok(())
3338    }
3339
3340    fn continue_core_having(&mut self, build: CoreBuild) {
3341        if self.parser.eat_kind(&TokenKind::KwHaving) {
3342            self.controls.push(ParseControl::CoreHavingDone { build });
3343            self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3344        } else {
3345            self.continue_core_windows(build);
3346        }
3347    }
3348
3349    fn continue_core_windows(&mut self, build: CoreBuild) {
3350        if self.parser.eat_kind(&TokenKind::KwWindow) {
3351            self.controls.push(ParseControl::CoreWindowStart { build });
3352        } else {
3353            self.finish_core(build);
3354        }
3355    }
3356
3357    fn finish_core(&mut self, build: CoreBuild) {
3358        self.values.push(MachineValue::Core(HeightTracked {
3359            value: SelectCore::Select {
3360                distinct: build.distinct,
3361                columns: build.columns,
3362                from: build.from,
3363                where_clause: build.where_clause,
3364                group_by: build.group_by,
3365                having: build.having,
3366                windows: build.windows,
3367            },
3368            height: build.height,
3369        }));
3370    }
3371
3372    fn continue_from(&mut self, build: FromBuild) -> Result<(), ParseError> {
3373        let join_type = if let Some(join_type) = self.parser.try_join_type()? {
3374            Some(join_type)
3375        } else if self.parser.eat_kind(&TokenKind::Comma) {
3376            Some(JoinType {
3377                natural: false,
3378                kind: JoinKind::Cross,
3379            })
3380        } else {
3381            None
3382        };
3383        if let Some(join_type) = join_type {
3384            self.controls
3385                .push(ParseControl::FromTableDone { build, join_type });
3386            self.controls.push(ParseControl::TableStart);
3387        } else {
3388            self.values.push(MachineValue::From(FromClause {
3389                source: build.source,
3390                joins: build.joins,
3391            }));
3392        }
3393        Ok(())
3394    }
3395
3396    fn append_join(
3397        &mut self,
3398        mut build: FromBuild,
3399        join_type: JoinType,
3400        table: TableOrSubquery,
3401        constraint: Option<JoinConstraint>,
3402    ) -> Result<(), ParseError> {
3403        if join_type.natural && constraint.is_some() {
3404            return Err(self
3405                .parser
3406                .err_here("a NATURAL join may not have an ON or USING clause"));
3407        }
3408        build.joins.push(JoinClause {
3409            join_type,
3410            table,
3411            constraint,
3412        });
3413        self.continue_from(build)
3414    }
3415
3416    fn table_start(&mut self) -> Result<(), ParseError> {
3417        if self.parser.eat_kind(&TokenKind::LeftParen) {
3418            if matches!(
3419                self.parser.peek_kind(),
3420                TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
3421            ) {
3422                self.controls.push(ParseControl::TableSubqueryDone);
3423                self.controls.push(ParseControl::SubqueryStart);
3424            } else {
3425                self.controls.push(ParseControl::TableParenJoinDone);
3426                self.controls.push(ParseControl::FromStart);
3427            }
3428            return Ok(());
3429        }
3430        let name = self.parser.parse_qualified_name()?;
3431        if name.schema.is_none() && self.parser.eat_kind(&TokenKind::LeftParen) {
3432            if self.parser.eat_kind(&TokenKind::RightParen) {
3433                self.values
3434                    .push(MachineValue::Table(TableOrSubquery::TableFunction {
3435                        name: name.name,
3436                        args: Vec::new(),
3437                        alias: self.parser.try_table_alias()?,
3438                    }));
3439            } else {
3440                self.controls.push(ParseControl::TableFunctionArgDone {
3441                    name: name.name,
3442                    args: Vec::new(),
3443                });
3444                self.controls.push(ParseControl::ExprStart { min_bp: 0 });
3445            }
3446            return Ok(());
3447        }
3448        self.values
3449            .push(MachineValue::Table(TableOrSubquery::Table {
3450                name,
3451                alias: self.parser.try_table_alias()?,
3452                index_hint: self.parser.parse_index_hint()?,
3453                time_travel: self.parser.parse_time_travel_clause()?,
3454            }));
3455        Ok(())
3456    }
3457
3458    fn with_start(&mut self) -> Result<(), ParseError> {
3459        self.parser.expect_kw(&TokenKind::KwWith)?;
3460        self.parser.has_with = true;
3461        let recursive = self.parser.eat_kind(&TokenKind::KwRecursive);
3462        self.start_cte(recursive, Vec::new())
3463    }
3464
3465    fn start_cte(&mut self, recursive: bool, ctes: Vec<Cte>) -> Result<(), ParseError> {
3466        let name = self.parser.parse_identifier()?;
3467        let columns = if self.parser.eat_kind(&TokenKind::LeftParen) {
3468            let mut columns = vec![self.parser.parse_identifier()?];
3469            while self.parser.eat_kind(&TokenKind::Comma) {
3470                columns.push(self.parser.parse_identifier()?);
3471            }
3472            self.parser.expect_token(&TokenKind::RightParen)?;
3473            columns
3474        } else {
3475            Vec::new()
3476        };
3477        self.parser.expect_kw(&TokenKind::KwAs)?;
3478        let materialized = if self.parser.eat_kind(&TokenKind::KwNot) {
3479            self.parser.expect_kw(&TokenKind::KwMaterialized)?;
3480            Some(CteMaterialized::NotMaterialized)
3481        } else if self.parser.eat_kind(&TokenKind::KwMaterialized) {
3482            Some(CteMaterialized::Materialized)
3483        } else {
3484            None
3485        };
3486        self.parser.expect_token(&TokenKind::LeftParen)?;
3487        self.controls.push(ParseControl::CteQueryDone {
3488            recursive,
3489            ctes,
3490            name,
3491            columns,
3492            materialized,
3493        });
3494        self.controls.push(ParseControl::SubqueryStart);
3495        Ok(())
3496    }
3497}
3498
3499impl Parser {
3500    /// Parse a single SQL expression.
3501    pub fn parse_expr(&mut self) -> Result<Expr, ParseError> {
3502        self.parse_expr_tracked().map(|parsed| parsed.expr)
3503    }
3504
3505    pub(crate) fn parse_expr_tracked(&mut self) -> Result<ParsedExpr, ParseError> {
3506        ParseMachine::for_expr(self).run_expr()
3507    }
3508
3509    pub(crate) fn parse_select_tracked_machine(
3510        &mut self,
3511        with: Option<WithClause>,
3512    ) -> Result<HeightTracked<SelectStatement>, ParseError> {
3513        ParseMachine::for_select(self, with).run_select()
3514    }
3515
3516    pub(crate) fn parse_with_clause_machine(&mut self) -> Result<WithClause, ParseError> {
3517        ParseMachine::for_with(self).run_with()
3518    }
3519
3520    pub(crate) fn parse_from_clause_machine(&mut self) -> Result<FromClause, ParseError> {
3521        ParseMachine::for_from(self).run_from()
3522    }
3523
3524    fn finish_expr(
3525        &self,
3526        expr: Expr,
3527        height: u32,
3528        is_constant: bool,
3529        has_function: bool,
3530    ) -> Result<ParsedExpr, ParseError> {
3531        if height > MAX_PARSE_DEPTH {
3532            return Err(ParseError::expression_too_deep(
3533                MAX_PARSE_DEPTH,
3534                self.peek_token(),
3535            ));
3536        }
3537        let root = match &expr {
3538            Expr::UnaryOp {
3539                op: UnaryOp::Plus, ..
3540            } => CachedRoot::UnaryPlus,
3541            Expr::RowValue(..) => CachedRoot::Vector,
3542            Expr::Subquery(..) => CachedRoot::ScalarSubquery,
3543            _ => CachedRoot::Other,
3544        };
3545        Ok(ParsedExpr {
3546            expr,
3547            height,
3548            is_constant,
3549            has_function,
3550            root,
3551        })
3552    }
3553
3554    fn checked_expr(
3555        &self,
3556        expr: Expr,
3557        max_child_height: u32,
3558        is_constant: bool,
3559        has_function: bool,
3560    ) -> Result<ParsedExpr, ParseError> {
3561        let height = max_child_height.saturating_add(1);
3562        self.finish_expr(expr, height, is_constant, has_function)
3563    }
3564
3565    fn add_cached_parent(&self, mut parsed: ParsedExpr) -> Result<ParsedExpr, ParseError> {
3566        parsed.height = parsed.height.saturating_add(1);
3567        if parsed.height > MAX_PARSE_DEPTH {
3568            return Err(ParseError::expression_too_deep(
3569                MAX_PARSE_DEPTH,
3570                self.peek_token(),
3571            ));
3572        }
3573        parsed.root = CachedRoot::Other;
3574        Ok(parsed)
3575    }
3576
3577    fn finish_unary(
3578        &self,
3579        op: UnaryOp,
3580        mut inner: ParsedExpr,
3581        span: Span,
3582    ) -> Result<ParsedExpr, ParseError> {
3583        if matches!(op, UnaryOp::Plus | UnaryOp::Negate)
3584            && inner.root == CachedRoot::UnaryPlus
3585            && let Expr::UnaryOp {
3586                op: inner_op,
3587                span: inner_span,
3588                ..
3589            } = &mut inner.expr
3590        {
3591            *inner_op = op;
3592            *inner_span = span;
3593            inner.root = if op == UnaryOp::Plus {
3594                CachedRoot::UnaryPlus
3595            } else {
3596                CachedRoot::Other
3597            };
3598            return Ok(inner);
3599        }
3600
3601        let height = inner.height;
3602        let is_constant = inner.is_constant;
3603        let has_function = inner.has_function;
3604        self.checked_expr(
3605            Expr::UnaryOp {
3606                op,
3607                expr: Box::new(inner.expr),
3608                span,
3609            },
3610            height,
3611            is_constant,
3612            has_function,
3613        )
3614    }
3615
3616    // ── Pratt core ──────────────────────────────────────────────────────
3617
3618    #[cfg(test)]
3619    fn parse_expr_bp(&mut self, min_bp: u8) -> Result<ParsedExpr, ParseError> {
3620        self.with_recursion_guard(|p| p.parse_expr_bp_inner(min_bp))
3621    }
3622
3623    #[cfg(test)]
3624    fn parse_expr_bp_inner(&mut self, min_bp: u8) -> Result<ParsedExpr, ParseError> {
3625        let prefixes = self.collect_prefix_frames();
3626        let mut lhs = self.parse_prefix()?;
3627
3628        for prefix in prefixes.into_iter().rev() {
3629            match prefix {
3630                DeepExprFrame::Unary { op, span, right_bp } => {
3631                    lhs = self.parse_expr_tail(lhs, right_bp)?;
3632                    let span = span.merge(lhs.expr.span());
3633                    lhs = self.finish_unary(op, lhs, span)?;
3634                }
3635                DeepExprFrame::Parenthesis { span } => {
3636                    lhs = self.finish_parenthesized_frame(lhs, span)?;
3637                }
3638            }
3639        }
3640
3641        self.parse_expr_tail(lhs, min_bp)
3642    }
3643
3644    #[cfg(test)]
3645    fn parse_expr_tail(
3646        &mut self,
3647        mut lhs: ParsedExpr,
3648        min_bp: u8,
3649    ) -> Result<ParsedExpr, ParseError> {
3650        loop {
3651            // Postfix: COLLATE, ISNULL, NOTNULL
3652            if let Some(l_bp) = self.postfix_bp() {
3653                if l_bp < min_bp {
3654                    break;
3655                }
3656                lhs = self.parse_postfix(lhs)?;
3657                continue;
3658            }
3659
3660            // Infix: binary operators, IS, LIKE, BETWEEN, IN, etc.
3661            if let Some((l_bp, r_bp)) = self.infix_bp() {
3662                if l_bp < min_bp {
3663                    break;
3664                }
3665                lhs = self.parse_infix(lhs, r_bp)?;
3666                continue;
3667            }
3668
3669            break;
3670        }
3671
3672        Ok(lhs)
3673    }
3674
3675    #[cfg(test)]
3676    fn collect_prefix_frames(&mut self) -> Vec<DeepExprFrame> {
3677        let mut prefixes = Vec::new();
3678        loop {
3679            let unary = match self.peek_kind() {
3680                TokenKind::Minus => {
3681                    let folds_i64_min = matches!(
3682                        self.tokens.get(self.pos + 1).map(|token| &token.kind),
3683                        Some(TokenKind::OversizedInt(value)) if value == "9223372036854775808"
3684                    );
3685                    if folds_i64_min {
3686                        break;
3687                    }
3688                    Some((UnaryOp::Negate, bp::UNARY))
3689                }
3690                TokenKind::Plus => Some((UnaryOp::Plus, bp::UNARY)),
3691                TokenKind::Tilde => Some((UnaryOp::BitNot, bp::UNARY)),
3692                TokenKind::KwNot => {
3693                    if matches!(
3694                        self.tokens.get(self.pos + 1).map(|token| &token.kind),
3695                        Some(TokenKind::KwExists)
3696                    ) {
3697                        break;
3698                    }
3699                    Some((UnaryOp::Not, bp::NOT_PREFIX))
3700                }
3701                TokenKind::LeftParen => {
3702                    let starts_subquery = matches!(
3703                        self.tokens.get(self.pos + 1).map(|token| &token.kind),
3704                        Some(TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues)
3705                    );
3706                    if starts_subquery {
3707                        break;
3708                    }
3709                    let token = self.advance_token();
3710                    prefixes.push(DeepExprFrame::Parenthesis { span: token.span });
3711                    continue;
3712                }
3713                _ => break,
3714            };
3715            let Some((op, right_bp)) = unary else {
3716                break;
3717            };
3718            let token = self.advance_token();
3719            prefixes.push(DeepExprFrame::Unary {
3720                op,
3721                span: token.span,
3722                right_bp,
3723            });
3724        }
3725        prefixes
3726    }
3727
3728    #[cfg(test)]
3729    fn finish_parenthesized_frame(
3730        &mut self,
3731        mut first: ParsedExpr,
3732        start: Span,
3733    ) -> Result<ParsedExpr, ParseError> {
3734        first = self.parse_expr_tail(first, 0)?;
3735        if self.eat_kind(&TokenKind::Comma) {
3736            let mut is_constant = first.is_constant;
3737            let mut has_function = first.has_function;
3738            let mut exprs = vec![first.expr];
3739            loop {
3740                let parsed = self.parse_expr_bp(0)?;
3741                is_constant &= parsed.is_constant;
3742                has_function |= parsed.has_function;
3743                exprs.push(parsed.expr);
3744                if !self.eat_kind(&TokenKind::Comma) {
3745                    break;
3746                }
3747            }
3748            let end = self.expect_kind(&TokenKind::RightParen)?;
3749            return self.finish_expr(
3750                Expr::RowValue(exprs, start.merge(end)),
3751                1,
3752                is_constant,
3753                has_function,
3754            );
3755        }
3756        self.expect_kind(&TokenKind::RightParen)?;
3757        Ok(first)
3758    }
3759
3760    // ── Token helpers ───────────────────────────────────────────────────
3761
3762    fn peek_kind(&self) -> &TokenKind {
3763        self.tokens
3764            .get(self.pos)
3765            .map_or(&TokenKind::Eof, |t| &t.kind)
3766    }
3767
3768    fn peek_token(&self) -> Option<&Token> {
3769        self.tokens.get(self.pos)
3770    }
3771
3772    #[cfg(test)]
3773    fn peek_nth_token(&self, offset: usize) -> Option<&Token> {
3774        self.tokens.get(self.pos + offset)
3775    }
3776
3777    fn advance_token(&mut self) -> Token {
3778        let tok = self.tokens[self.pos].clone();
3779        if tok.kind != TokenKind::Eof {
3780            self.pos += 1;
3781        }
3782        tok
3783    }
3784
3785    fn at_kind(&self, kind: &TokenKind) -> bool {
3786        std::mem::discriminant(self.peek_kind()) == std::mem::discriminant(kind)
3787    }
3788
3789    fn eat_kind(&mut self, kind: &TokenKind) -> bool {
3790        if self.at_kind(kind) {
3791            self.advance_token();
3792            true
3793        } else {
3794            false
3795        }
3796    }
3797
3798    fn expect_kind(&mut self, expected: &TokenKind) -> Result<Span, ParseError> {
3799        if self.at_kind(expected) {
3800            Ok(self.advance_token().span)
3801        } else {
3802            Err(self.err_here(format!("expected {expected:?}, got {:?}", self.peek_kind())))
3803        }
3804    }
3805
3806    fn err_here(&self, message: impl Into<String>) -> ParseError {
3807        ParseError::at(message, self.peek_token())
3808    }
3809
3810    // ── Prefix (nud) ────────────────────────────────────────────────────
3811
3812    #[cfg(test)]
3813    #[allow(clippy::too_many_lines)]
3814    fn parse_prefix(&mut self) -> Result<ParsedExpr, ParseError> {
3815        let Token {
3816            kind,
3817            span: token_span,
3818            line,
3819            col,
3820        } = self.advance_token();
3821        if self.at_kind(&TokenKind::Dot) && starts_table_star_qualifier(&kind) {
3822            let name = match &kind {
3823                TokenKind::Id(name) | TokenKind::QuotedId(name, _) => Arc::clone(name),
3824                TokenKind::String(name) => Arc::<str>::from(name.as_str()),
3825                keyword => Arc::<str>::from(kw_to_str(keyword)),
3826            };
3827            return self.parse_ident_expr(name, token_span);
3828        }
3829        match kind {
3830            // ── Literals ────────────────────────────────────────────────
3831            TokenKind::Integer(i) => Ok(ParsedExpr::leaf(Expr::Literal(
3832                Literal::Integer(i),
3833                token_span,
3834            ))),
3835            // An integer literal too large for i64 becomes a REAL, and a
3836            // magnitude beyond f64 range becomes ±Infinity — matching C
3837            // SQLite's text-to-real conversion (no f64::MAX clamp).
3838            TokenKind::OversizedInt(s) => match s.parse::<f64>() {
3839                Ok(v) => Ok(ParsedExpr::leaf(Expr::Literal(
3840                    Literal::Float(v),
3841                    token_span,
3842                ))),
3843                Err(_) => Err(ParseError {
3844                    kind: crate::parser::ParseErrorKind::Syntax,
3845                    message: "integer out of range".to_owned(),
3846                    span: token_span,
3847                    line,
3848                    col,
3849                }),
3850            },
3851            TokenKind::Float(f) => Ok(ParsedExpr::leaf(Expr::Literal(
3852                Literal::Float(f),
3853                token_span,
3854            ))),
3855            TokenKind::String(s) if matches!(self.peek_kind(), TokenKind::Dot) => {
3856                self.parse_ident_expr(s, token_span)
3857            }
3858            TokenKind::String(s) => Ok(ParsedExpr::leaf(Expr::Literal(
3859                Literal::String(s),
3860                token_span,
3861            ))),
3862            TokenKind::Blob(b) => Ok(ParsedExpr::leaf(Expr::Literal(
3863                Literal::Blob(b),
3864                token_span,
3865            ))),
3866            TokenKind::KwNull => Ok(ParsedExpr::leaf(Expr::Literal(Literal::Null, token_span))),
3867            TokenKind::KwTrue => Ok(ParsedExpr::leaf(Expr::Literal(Literal::True, token_span))),
3868            TokenKind::KwFalse => Ok(ParsedExpr::leaf(Expr::Literal(Literal::False, token_span))),
3869            TokenKind::KwCurrentTime => Ok(ParsedExpr::leaf(Expr::Literal(
3870                Literal::CurrentTime,
3871                token_span,
3872            ))),
3873            TokenKind::KwCurrentDate => Ok(ParsedExpr::leaf(Expr::Literal(
3874                Literal::CurrentDate,
3875                token_span,
3876            ))),
3877            TokenKind::KwCurrentTimestamp => Ok(ParsedExpr::leaf(Expr::Literal(
3878                Literal::CurrentTimestamp,
3879                token_span,
3880            ))),
3881
3882            // ── Bind parameters ─────────────────────────────────────────
3883            TokenKind::Question => Ok(ParsedExpr::leaf(Expr::Placeholder(
3884                PlaceholderType::Anonymous,
3885                token_span,
3886            ))),
3887            TokenKind::QuestionNum(n) => Ok(ParsedExpr::leaf(Expr::Placeholder(
3888                PlaceholderType::Numbered(n),
3889                token_span,
3890            ))),
3891            TokenKind::ColonParam(s) => Ok(ParsedExpr::leaf(Expr::Placeholder(
3892                PlaceholderType::ColonNamed(s),
3893                token_span,
3894            ))),
3895            TokenKind::AtParam(s) => Ok(ParsedExpr::leaf(Expr::Placeholder(
3896                PlaceholderType::AtNamed(s),
3897                token_span,
3898            ))),
3899            TokenKind::DollarParam(s) => Ok(ParsedExpr::leaf(Expr::Placeholder(
3900                PlaceholderType::DollarNamed(s),
3901                token_span,
3902            ))),
3903
3904            // ── Unary prefix: - + ~ ─────────────────────────────────────
3905            TokenKind::Minus => {
3906                // SQLite accepts this one magnitude as the signed minimum.
3907                // Preserve the normalized one-node literal AST and its actual
3908                // retained height.
3909                if let TokenKind::OversizedInt(s) = self.peek_kind()
3910                    && s == "9223372036854775808"
3911                {
3912                    let num_span = self.advance_token().span;
3913                    let span = token_span.merge(num_span);
3914                    return self.finish_expr(
3915                        Expr::Literal(Literal::Integer(i64::MIN), span),
3916                        1,
3917                        true,
3918                        false,
3919                    );
3920                }
3921                let inner = self.parse_expr_bp(bp::UNARY)?;
3922                let span = token_span.merge(inner.expr.span());
3923                self.finish_unary(UnaryOp::Negate, inner, span)
3924            }
3925            TokenKind::Plus => {
3926                let inner = self.parse_expr_bp(bp::UNARY)?;
3927                let span = token_span.merge(inner.expr.span());
3928                self.finish_unary(UnaryOp::Plus, inner, span)
3929            }
3930            TokenKind::Tilde => {
3931                let inner = self.parse_expr_bp(bp::UNARY)?;
3932                let span = token_span.merge(inner.expr.span());
3933                self.finish_unary(UnaryOp::BitNot, inner, span)
3934            }
3935
3936            // ── Prefix NOT ──────────────────────────────────────────────
3937            TokenKind::KwNot => {
3938                // NOT EXISTS (subquery)
3939                if matches!(self.peek_kind(), TokenKind::KwExists) {
3940                    self.advance_token();
3941                    self.expect_kind(&TokenKind::LeftParen)?;
3942                    let subquery = self.parse_subquery_minimal()?;
3943                    let end = self.expect_kind(&TokenKind::RightParen)?;
3944                    let span = token_span.merge(end);
3945                    let height = subquery.height;
3946                    let exists = self.checked_expr(
3947                        Expr::Exists {
3948                            subquery: Box::new(subquery.value),
3949                            not: true,
3950                            span,
3951                        },
3952                        height,
3953                        false,
3954                        false,
3955                    )?;
3956                    return self.add_cached_parent(exists);
3957                }
3958                let inner = self.parse_expr_bp(bp::NOT_PREFIX)?;
3959                let span = token_span.merge(inner.expr.span());
3960                self.finish_unary(UnaryOp::Not, inner, span)
3961            }
3962
3963            // ── EXISTS (subquery) ───────────────────────────────────────
3964            TokenKind::KwExists => {
3965                self.expect_kind(&TokenKind::LeftParen)?;
3966                let subquery = self.parse_subquery_minimal()?;
3967                let end = self.expect_kind(&TokenKind::RightParen)?;
3968                let span = token_span.merge(end);
3969                let height = subquery.height;
3970                self.checked_expr(
3971                    Expr::Exists {
3972                        subquery: Box::new(subquery.value),
3973                        not: false,
3974                        span,
3975                    },
3976                    height,
3977                    false,
3978                    false,
3979                )
3980            }
3981
3982            // ── CAST(expr AS type_name) ─────────────────────────────────
3983            TokenKind::KwCast => {
3984                self.expect_kind(&TokenKind::LeftParen)?;
3985                let inner = self.parse_expr_bp(0)?;
3986                self.expect_kind(&TokenKind::KwAs)?;
3987                let type_name = self.parse_type_name()?;
3988                let end = self.expect_kind(&TokenKind::RightParen)?;
3989                let span = token_span.merge(end);
3990                let height = inner.height;
3991                let is_constant = inner.is_constant;
3992                let has_function = inner.has_function;
3993                self.checked_expr(
3994                    Expr::Cast {
3995                        expr: Box::new(inner.expr),
3996                        type_name,
3997                        span,
3998                    },
3999                    height,
4000                    is_constant,
4001                    has_function,
4002                )
4003            }
4004
4005            // ── CASE [operand] WHEN ... THEN ... [ELSE ...] END ────────
4006            TokenKind::KwCase => self.parse_case_expr(token_span),
4007
4008            // ── RAISE(action, message) ──────────────────────────────────
4009            TokenKind::KwRaise => {
4010                self.expect_kind(&TokenKind::LeftParen)?;
4011                let (action, message) = self.parse_raise_args()?;
4012                let end = self.expect_kind(&TokenKind::RightParen)?;
4013                let span = token_span.merge(end);
4014                Ok(ParsedExpr::leaf(Expr::Raise {
4015                    action,
4016                    message,
4017                    span,
4018                }))
4019            }
4020
4021            // ── Parenthesized expr / subquery / row-value ───────────────
4022            TokenKind::LeftParen => {
4023                if matches!(
4024                    self.peek_kind(),
4025                    TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
4026                ) {
4027                    let subquery = self.parse_subquery_minimal()?;
4028                    let end = self.expect_kind(&TokenKind::RightParen)?;
4029                    let span = token_span.merge(end);
4030                    return self.checked_expr(
4031                        Expr::Subquery(Box::new(subquery.value), span),
4032                        subquery.height,
4033                        false,
4034                        false,
4035                    );
4036                }
4037                let first = self.parse_expr_bp(0)?;
4038                if self.eat_kind(&TokenKind::Comma) {
4039                    let mut is_constant = first.is_constant;
4040                    let mut has_function = first.has_function;
4041                    let mut exprs = vec![first.expr];
4042                    loop {
4043                        let parsed = self.parse_expr_bp(0)?;
4044                        is_constant &= parsed.is_constant;
4045                        has_function |= parsed.has_function;
4046                        exprs.push(parsed.expr);
4047                        if !self.eat_kind(&TokenKind::Comma) {
4048                            break;
4049                        }
4050                    }
4051                    let end = self.expect_kind(&TokenKind::RightParen)?;
4052                    let span = token_span.merge(end);
4053                    self.finish_expr(Expr::RowValue(exprs, span), 1, is_constant, has_function)
4054                } else {
4055                    self.expect_kind(&TokenKind::RightParen)?;
4056                    Ok(first)
4057                }
4058            }
4059
4060            // ── Identifier: column ref or function call ─────────────────
4061            TokenKind::Id(name) | TokenKind::QuotedId(name, _) => {
4062                self.parse_ident_expr(name, token_span)
4063            }
4064
4065            // ── Keywords usable as function names ───────────────────────
4066            TokenKind::KwReplace if matches!(self.peek_kind(), TokenKind::LeftParen) => {
4067                self.parse_function_call("replace".to_owned(), token_span)
4068            }
4069            // C SQLite exposes the pattern-matching operators as scalar
4070            // functions too: `like(P, X [, E])`, `glob(P, X)`,
4071            // `regexp(P, X)`, `match(P, X)`. The token doubles as an infix
4072            // operator, so only treat it as a function name when directly
4073            // followed by `(`.
4074            TokenKind::KwLike if matches!(self.peek_kind(), TokenKind::LeftParen) => {
4075                self.parse_function_call("like".to_owned(), token_span)
4076            }
4077            TokenKind::KwGlob if matches!(self.peek_kind(), TokenKind::LeftParen) => {
4078                self.parse_function_call("glob".to_owned(), token_span)
4079            }
4080            TokenKind::KwRegexp if matches!(self.peek_kind(), TokenKind::LeftParen) => {
4081                self.parse_function_call("regexp".to_owned(), token_span)
4082            }
4083            TokenKind::KwMatch if matches!(self.peek_kind(), TokenKind::LeftParen) => {
4084                self.parse_function_call("match".to_owned(), token_span)
4085            }
4086
4087            // ── Non-reserved keywords usable as identifiers ─────────────
4088            // In SQL, non-reserved keywords (like KEY, MATCH, FIRST, etc.)
4089            // can be used as column names without quoting.
4090            k if is_nonreserved_kw(&k) => {
4091                let name = kw_to_str(&k);
4092                self.parse_ident_expr(name, token_span)
4093            }
4094
4095            kind => Err(ParseError {
4096                kind: crate::parser::ParseErrorKind::Syntax,
4097                // Propagate a lexer error token's message verbatim (see the other
4098                // arm) rather than Debug-wrapping it.
4099                message: match &kind {
4100                    TokenKind::Error(msg) => msg.clone(),
4101                    _ => format!("unexpected token in expression: {kind:?}"),
4102                },
4103                span: token_span,
4104                line,
4105                col,
4106            }),
4107        }
4108    }
4109
4110    /// Parse `name`, `name.column`, or `name(args)`.
4111    #[cfg(test)]
4112    fn parse_ident_expr<S>(&mut self, name: S, start: Span) -> Result<ParsedExpr, ParseError>
4113    where
4114        S: AsRef<str> + Into<Arc<str>>,
4115    {
4116        // Function call: name(...)
4117        if matches!(self.peek_kind(), TokenKind::LeftParen) {
4118            return self.parse_function_call(name.as_ref().to_owned(), start);
4119        }
4120        let name = name.into();
4121        // Table-qualified column: name.column
4122        if matches!(self.peek_kind(), TokenKind::Dot) {
4123            let Some(col_tok) = self.peek_nth_token(1) else {
4124                return Err(self.err_here("expected column name after '.'"));
4125            };
4126            let col_name = match &col_tok.kind {
4127                TokenKind::Id(c) | TokenKind::QuotedId(c, _) => Arc::clone(c),
4128                TokenKind::String(c) => Arc::<str>::from(c.as_str()),
4129                k if starts_post_dot_identifier(k) => Arc::<str>::from(kw_to_str(k)),
4130                _ => {
4131                    return Err(ParseError::at(
4132                        format!("expected column name after '.', got {:?}", col_tok.kind),
4133                        Some(col_tok),
4134                    ));
4135                }
4136            };
4137            let span = start.merge(col_tok.span);
4138            self.pos = self.pos.saturating_add(2);
4139            return self.finish_expr(
4140                Expr::Column(ColumnRef::qualified(name, col_name), span),
4141                2,
4142                false,
4143                false,
4144            );
4145        }
4146        Ok(ParsedExpr::leaf(Expr::Column(ColumnRef::bare(name), start)))
4147    }
4148
4149    // ── Postfix ─────────────────────────────────────────────────────────
4150
4151    fn postfix_bp(&self) -> Option<u8> {
4152        match self.peek_kind() {
4153            TokenKind::KwCollate => Some(bp::COLLATE),
4154            TokenKind::KwIsnull | TokenKind::KwNotnull => Some(bp::EQUALITY.0),
4155            TokenKind::KwNot => {
4156                if let Some(next) = self.tokens.get(self.pos + 1)
4157                    && matches!(next.kind, TokenKind::KwNull)
4158                {
4159                    return Some(bp::EQUALITY.0);
4160                }
4161                None
4162            }
4163            _ => None,
4164        }
4165    }
4166
4167    fn parse_postfix(&mut self, lhs: ParsedExpr) -> Result<ParsedExpr, ParseError> {
4168        let tok = self.advance_token();
4169        match &tok.kind {
4170            TokenKind::KwCollate => {
4171                let collation = match self.parse_identifier() {
4172                    Ok(s) => s,
4173                    Err(_) => {
4174                        return Err(self.err_here("expected collation name after COLLATE"));
4175                    }
4176                };
4177                let name_span = self.tokens[self.pos.saturating_sub(1)].span;
4178                let span = lhs.expr.span().merge(name_span);
4179                let height = lhs.height;
4180                let is_constant = lhs.is_constant;
4181                let has_function = lhs.has_function;
4182                self.checked_expr(
4183                    Expr::Collate {
4184                        expr: Box::new(lhs.expr),
4185                        collation,
4186                        span,
4187                    },
4188                    height,
4189                    is_constant,
4190                    has_function,
4191                )
4192            }
4193            TokenKind::KwIsnull => {
4194                let span = lhs.expr.span().merge(tok.span);
4195                let height = lhs.height;
4196                let is_constant = lhs.is_constant;
4197                let has_function = lhs.has_function;
4198                self.checked_expr(
4199                    Expr::IsNull {
4200                        expr: Box::new(lhs.expr),
4201                        not: false,
4202                        span,
4203                    },
4204                    height,
4205                    is_constant,
4206                    has_function,
4207                )
4208            }
4209            TokenKind::KwNotnull => {
4210                let span = lhs.expr.span().merge(tok.span);
4211                let height = lhs.height;
4212                let is_constant = lhs.is_constant;
4213                let has_function = lhs.has_function;
4214                self.checked_expr(
4215                    Expr::IsNull {
4216                        expr: Box::new(lhs.expr),
4217                        not: true,
4218                        span,
4219                    },
4220                    height,
4221                    is_constant,
4222                    has_function,
4223                )
4224            }
4225            TokenKind::KwNot => {
4226                let null_tok = self.advance_token(); // we know from postfix_bp that this is KwNull
4227                let span = lhs.expr.span().merge(null_tok.span);
4228                let height = lhs.height;
4229                let is_constant = lhs.is_constant;
4230                let has_function = lhs.has_function;
4231                self.checked_expr(
4232                    Expr::IsNull {
4233                        expr: Box::new(lhs.expr),
4234                        not: true,
4235                        span,
4236                    },
4237                    height,
4238                    is_constant,
4239                    has_function,
4240                )
4241            }
4242            other => Err(ParseError::at(
4243                format!("unexpected postfix token: {other:?}"),
4244                Some(&tok),
4245            )),
4246        }
4247    }
4248
4249    // ── Infix ───────────────────────────────────────────────────────────
4250
4251    fn infix_bp(&self) -> Option<(u8, u8)> {
4252        match self.peek_kind() {
4253            TokenKind::KwOr => Some(bp::OR),
4254            TokenKind::KwAnd => Some(bp::AND),
4255
4256            TokenKind::Eq
4257            | TokenKind::EqEq
4258            | TokenKind::Ne
4259            | TokenKind::LtGt
4260            | TokenKind::KwIs
4261            | TokenKind::KwLike
4262            | TokenKind::KwGlob
4263            | TokenKind::KwMatch
4264            | TokenKind::KwRegexp
4265            | TokenKind::KwBetween
4266            | TokenKind::KwIn => Some(bp::EQUALITY),
4267
4268            // NOT LIKE / NOT IN / NOT BETWEEN / NOT GLOB / NOT MATCH / NOT REGEXP
4269            TokenKind::KwNot => {
4270                let next = self.tokens.get(self.pos + 1).map(|t| &t.kind);
4271                match next {
4272                    Some(
4273                        TokenKind::KwLike
4274                        | TokenKind::KwGlob
4275                        | TokenKind::KwMatch
4276                        | TokenKind::KwRegexp
4277                        | TokenKind::KwBetween
4278                        | TokenKind::KwIn,
4279                    ) => Some(bp::EQUALITY),
4280                    _ => None,
4281                }
4282            }
4283
4284            TokenKind::Lt | TokenKind::Le | TokenKind::Gt | TokenKind::Ge => Some(bp::COMPARISON),
4285
4286            TokenKind::Ampersand
4287            | TokenKind::Pipe
4288            | TokenKind::ShiftLeft
4289            | TokenKind::ShiftRight => Some(bp::BITWISE),
4290
4291            TokenKind::Plus | TokenKind::Minus => Some(bp::ADD),
4292            TokenKind::Star | TokenKind::Slash | TokenKind::Percent => Some(bp::MUL),
4293            TokenKind::Concat => Some(bp::CONCAT),
4294            TokenKind::Arrow | TokenKind::DoubleArrow => Some(bp::JSON),
4295
4296            _ => None,
4297        }
4298    }
4299
4300    #[cfg(test)]
4301    #[allow(clippy::too_many_lines)]
4302    fn parse_infix(&mut self, lhs: ParsedExpr, r_bp: u8) -> Result<ParsedExpr, ParseError> {
4303        let tok = self.advance_token();
4304        match &tok.kind {
4305            // ── Simple binary operators ──────────────────────────────────
4306            TokenKind::Plus => self.make_binop(lhs, BinaryOp::Add, r_bp),
4307            TokenKind::Minus => self.make_binop(lhs, BinaryOp::Subtract, r_bp),
4308            TokenKind::Star => self.make_binop(lhs, BinaryOp::Multiply, r_bp),
4309            TokenKind::Slash => self.make_binop(lhs, BinaryOp::Divide, r_bp),
4310            TokenKind::Percent => self.make_binop(lhs, BinaryOp::Modulo, r_bp),
4311            TokenKind::Concat => self.make_binop(lhs, BinaryOp::Concat, r_bp),
4312            TokenKind::Eq | TokenKind::EqEq => self.make_binop(lhs, BinaryOp::Eq, r_bp),
4313            TokenKind::Ne | TokenKind::LtGt => self.make_binop(lhs, BinaryOp::Ne, r_bp),
4314            TokenKind::Lt => self.make_binop(lhs, BinaryOp::Lt, r_bp),
4315            TokenKind::Le => self.make_binop(lhs, BinaryOp::Le, r_bp),
4316            TokenKind::Gt => self.make_binop(lhs, BinaryOp::Gt, r_bp),
4317            TokenKind::Ge => self.make_binop(lhs, BinaryOp::Ge, r_bp),
4318            TokenKind::Ampersand => self.make_binop(lhs, BinaryOp::BitAnd, r_bp),
4319            TokenKind::Pipe => self.make_binop(lhs, BinaryOp::BitOr, r_bp),
4320            TokenKind::ShiftLeft => self.make_binop(lhs, BinaryOp::ShiftLeft, r_bp),
4321            TokenKind::ShiftRight => self.make_binop(lhs, BinaryOp::ShiftRight, r_bp),
4322            TokenKind::KwOr => self.make_binop(lhs, BinaryOp::Or, r_bp),
4323            TokenKind::KwAnd => self.make_binop(lhs, BinaryOp::And, r_bp),
4324
4325            // ── IS [NOT] [DISTINCT FROM | NULL | expr] ──────────────────────────────────
4326            TokenKind::KwIs => {
4327                let not = self.eat_kind(&TokenKind::KwNot);
4328                if self.eat_kind(&TokenKind::KwDistinct) {
4329                    self.expect_kind(&TokenKind::KwFrom)?;
4330                    let rhs = self.parse_expr_bp(r_bp)?;
4331                    let span = lhs.expr.span().merge(rhs.expr.span());
4332                    let height = lhs.height.max(rhs.height);
4333                    let is_constant = lhs.is_constant && rhs.is_constant;
4334                    let has_function = lhs.has_function || rhs.has_function;
4335                    // IS DISTINCT FROM is equivalent to IS NOT
4336                    // IS NOT DISTINCT FROM is equivalent to IS
4337                    let op = if not { BinaryOp::Is } else { BinaryOp::IsNot };
4338                    return self.checked_expr(
4339                        Expr::BinaryOp {
4340                            left: Box::new(lhs.expr),
4341                            op,
4342                            right: Box::new(rhs.expr),
4343                            span,
4344                        },
4345                        height,
4346                        is_constant,
4347                        has_function,
4348                    );
4349                }
4350                let rhs = self.parse_expr_bp(r_bp)?;
4351                let span = lhs.expr.span().merge(rhs.expr.span());
4352                // SQLite folds `expr IS [NOT] expr` into a unary null-test
4353                // only when the right operand, parsed at normal precedence,
4354                // is the NULL literal. Parsing the RHS first — rather than
4355                // greedily consuming a NULL token — keeps tighter-binding operators
4356                // attached to NULL: `x IS NULL < 2` parses as
4357                // `x IS (NULL < 2)`, matching C SQLite (verified against the
4358                // sqlite3 CLI: `SELECT 1 IS NULL < 2` yields 0, not 1).
4359                if matches!(&rhs.expr, Expr::Literal(Literal::Null, _)) {
4360                    let height = lhs.height;
4361                    let is_constant = lhs.is_constant;
4362                    let has_function = lhs.has_function;
4363                    return self.checked_expr(
4364                        Expr::IsNull {
4365                            expr: Box::new(lhs.expr),
4366                            not,
4367                            span,
4368                        },
4369                        height,
4370                        is_constant,
4371                        has_function,
4372                    );
4373                }
4374                let op = if not { BinaryOp::IsNot } else { BinaryOp::Is };
4375                let height = lhs.height.max(rhs.height);
4376                let is_constant = lhs.is_constant && rhs.is_constant;
4377                let has_function = lhs.has_function || rhs.has_function;
4378                self.checked_expr(
4379                    Expr::BinaryOp {
4380                        left: Box::new(lhs.expr),
4381                        op,
4382                        right: Box::new(rhs.expr),
4383                        span,
4384                    },
4385                    height,
4386                    is_constant,
4387                    has_function,
4388                )
4389            }
4390
4391            // ── LIKE / GLOB / MATCH / REGEXP ────────────────────────────
4392            TokenKind::KwLike => self.parse_like(lhs, LikeOp::Like, false),
4393            TokenKind::KwGlob => self.parse_like(lhs, LikeOp::Glob, false),
4394            TokenKind::KwMatch => self.parse_like(lhs, LikeOp::Match, false),
4395            TokenKind::KwRegexp => self.parse_like(lhs, LikeOp::Regexp, false),
4396
4397            // ── BETWEEN ─────────────────────────────────────────────────
4398            TokenKind::KwBetween => self.parse_between(lhs, false),
4399
4400            // ── IN ──────────────────────────────────────────────────────
4401            TokenKind::KwIn => self.parse_in(lhs, false),
4402
4403            // ── JSON -> / ->> ───────────────────────────────────────────
4404            TokenKind::Arrow => {
4405                let rhs = self.parse_expr_bp(r_bp)?;
4406                let span = lhs.expr.span().merge(rhs.expr.span());
4407                let height = lhs.height.max(rhs.height);
4408                let is_constant = false;
4409                let has_function = true;
4410                self.checked_expr(
4411                    Expr::JsonAccess {
4412                        expr: Box::new(lhs.expr),
4413                        path: Box::new(rhs.expr),
4414                        arrow: JsonArrow::Arrow,
4415                        span,
4416                    },
4417                    height,
4418                    is_constant,
4419                    has_function,
4420                )
4421            }
4422            TokenKind::DoubleArrow => {
4423                let rhs = self.parse_expr_bp(r_bp)?;
4424                let span = lhs.expr.span().merge(rhs.expr.span());
4425                let height = lhs.height.max(rhs.height);
4426                let is_constant = false;
4427                let has_function = true;
4428                self.checked_expr(
4429                    Expr::JsonAccess {
4430                        expr: Box::new(lhs.expr),
4431                        path: Box::new(rhs.expr),
4432                        arrow: JsonArrow::DoubleArrow,
4433                        span,
4434                    },
4435                    height,
4436                    is_constant,
4437                    has_function,
4438                )
4439            }
4440
4441            // ── NOT LIKE / GLOB / BETWEEN / IN ──────────────────────────
4442            TokenKind::KwNot => {
4443                let next = self.advance_token();
4444                match &next.kind {
4445                    TokenKind::KwLike => self.parse_like(lhs, LikeOp::Like, true),
4446                    TokenKind::KwGlob => self.parse_like(lhs, LikeOp::Glob, true),
4447                    TokenKind::KwMatch => self.parse_like(lhs, LikeOp::Match, true),
4448                    TokenKind::KwRegexp => self.parse_like(lhs, LikeOp::Regexp, true),
4449                    TokenKind::KwBetween => self.parse_between(lhs, true),
4450                    TokenKind::KwIn => self.parse_in(lhs, true),
4451                    _ => Err(ParseError::at(
4452                        format!(
4453                            "expected LIKE/GLOB/MATCH/REGEXP/BETWEEN/IN \
4454                             after NOT, got {:?}",
4455                            next.kind
4456                        ),
4457                        Some(&next),
4458                    )),
4459                }
4460            }
4461
4462            other => Err(ParseError::at(
4463                format!("unexpected infix token: {other:?}"),
4464                Some(&tok),
4465            )),
4466        }
4467    }
4468
4469    #[cfg(test)]
4470    fn make_binop(
4471        &mut self,
4472        lhs: ParsedExpr,
4473        op: BinaryOp,
4474        r_bp: u8,
4475    ) -> Result<ParsedExpr, ParseError> {
4476        let rhs = self.parse_expr_bp(r_bp)?;
4477        let span = lhs.expr.span().merge(rhs.expr.span());
4478        let height = lhs.height.max(rhs.height);
4479        let is_constant = lhs.is_constant && rhs.is_constant;
4480        let has_function = lhs.has_function || rhs.has_function;
4481        self.checked_expr(
4482            Expr::BinaryOp {
4483                left: Box::new(lhs.expr),
4484                op,
4485                right: Box::new(rhs.expr),
4486                span,
4487            },
4488            height,
4489            is_constant,
4490            has_function,
4491        )
4492    }
4493
4494    // ── Special expression forms ────────────────────────────────────────
4495
4496    #[cfg(test)]
4497    fn parse_like(
4498        &mut self,
4499        lhs: ParsedExpr,
4500        op: LikeOp,
4501        not: bool,
4502    ) -> Result<ParsedExpr, ParseError> {
4503        let pattern = self.parse_expr_bp(bp::EQUALITY.1)?;
4504        let escape = if self.eat_kind(&TokenKind::KwEscape) {
4505            // SQLite's grammar accepts ESCAPE for all pattern-matching operators
4506            // (LIKE, GLOB, MATCH, REGEXP), not just LIKE.
4507            Some(self.parse_expr_bp(bp::EQUALITY.1)?)
4508        } else {
4509            None
4510        };
4511        let end = escape
4512            .as_ref()
4513            .map_or_else(|| pattern.expr.span(), |e| e.expr.span());
4514        let span = lhs.expr.span().merge(end);
4515        let height = escape.as_ref().map_or_else(
4516            || lhs.height.max(pattern.height),
4517            |parsed| lhs.height.max(pattern.height).max(parsed.height),
4518        );
4519        let parsed = self.checked_expr(
4520            Expr::Like {
4521                expr: Box::new(lhs.expr),
4522                pattern: Box::new(pattern.expr),
4523                escape: escape.map(|parsed| Box::new(parsed.expr)),
4524                op,
4525                not,
4526                span,
4527            },
4528            height,
4529            false,
4530            true,
4531        )?;
4532        if not {
4533            self.add_cached_parent(parsed)
4534        } else {
4535            Ok(parsed)
4536        }
4537    }
4538
4539    #[cfg(test)]
4540    fn parse_between(&mut self, lhs: ParsedExpr, not: bool) -> Result<ParsedExpr, ParseError> {
4541        // Parse low bound above AND level so AND keyword is not consumed.
4542        let low = self.parse_expr_bp(bp::NOT_PREFIX)?;
4543        if !self.eat_kind(&TokenKind::KwAnd) {
4544            return Err(self.err_here("expected AND in BETWEEN expression"));
4545        }
4546        let high = self.parse_expr_bp(bp::EQUALITY.1)?;
4547        let span = lhs.expr.span().merge(high.expr.span());
4548        let height = lhs.height.max(low.height).max(high.height);
4549        let is_constant = lhs.is_constant && low.is_constant && high.is_constant;
4550        let has_function = lhs.has_function || low.has_function || high.has_function;
4551        let parsed = self.checked_expr(
4552            Expr::Between {
4553                expr: Box::new(lhs.expr),
4554                low: Box::new(low.expr),
4555                high: Box::new(high.expr),
4556                not,
4557                span,
4558            },
4559            height,
4560            is_constant,
4561            has_function,
4562        )?;
4563        if not {
4564            self.add_cached_parent(parsed)
4565        } else {
4566            Ok(parsed)
4567        }
4568    }
4569
4570    #[cfg(test)]
4571    fn parse_in(&mut self, lhs: ParsedExpr, not: bool) -> Result<ParsedExpr, ParseError> {
4572        let start = lhs.expr.span();
4573
4574        // SQLite supports both "x IN ( ... )" and "x IN table_name".
4575        if !self.at_kind(&TokenKind::LeftParen) {
4576            let table = self.parse_qualified_name()?;
4577            let end = self.tokens[self.pos.saturating_sub(1)].span;
4578            let span = start.merge(end);
4579            let height = lhs.height;
4580            let has_function = lhs.has_function;
4581            let parsed = self.checked_expr(
4582                Expr::In {
4583                    expr: Box::new(lhs.expr),
4584                    set: InSet::Table(table),
4585                    not,
4586                    span,
4587                },
4588                height,
4589                false,
4590                has_function,
4591            )?;
4592            return if not {
4593                self.add_cached_parent(parsed)
4594            } else {
4595                Ok(parsed)
4596            };
4597        }
4598
4599        self.expect_kind(&TokenKind::LeftParen)?;
4600
4601        if matches!(
4602            self.peek_kind(),
4603            TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
4604        ) {
4605            let subquery = self.parse_subquery_minimal()?;
4606            let end = self.expect_kind(&TokenKind::RightParen)?;
4607            let span = start.merge(end);
4608            let height = lhs.height.max(subquery.height);
4609            let has_function = lhs.has_function;
4610            let parsed = self.checked_expr(
4611                Expr::In {
4612                    expr: Box::new(lhs.expr),
4613                    set: InSet::Subquery(Box::new(subquery.value)),
4614                    not,
4615                    span,
4616                },
4617                height,
4618                false,
4619                has_function,
4620            )?;
4621            return if not {
4622                self.add_cached_parent(parsed)
4623            } else {
4624                Ok(parsed)
4625            };
4626        }
4627
4628        let mut parsed_items = Vec::new();
4629        if !self.at_kind(&TokenKind::RightParen) {
4630            let item = self.parse_expr_bp(0)?;
4631            parsed_items.push(item);
4632            while self.eat_kind(&TokenKind::Comma) {
4633                let item = self.parse_expr_bp(0)?;
4634                parsed_items.push(item);
4635            }
4636        }
4637        let end = self.expect_kind(&TokenKind::RightParen)?;
4638        if let Some(message) = vector_in_list_arity_error(&lhs.expr, &parsed_items) {
4639            return Err(self.err_here(message));
4640        }
4641        let span = start.merge(end);
4642        let item_height = parsed_items
4643            .iter()
4644            .map(|item| item.height)
4645            .max()
4646            .unwrap_or(0);
4647        let items_are_constant = parsed_items.iter().all(|item| item.is_constant);
4648        let item_has_function = parsed_items.iter().any(|item| item.has_function);
4649        let singleton_constant = matches!(parsed_items.as_slice(), [item] if item.is_constant)
4650            && lhs.root != CachedRoot::Vector;
4651        let singleton_subquery =
4652            matches!(parsed_items.as_slice(), [item] if item.root == CachedRoot::ScalarSubquery);
4653        let exprs = parsed_items.into_iter().map(|parsed| parsed.expr).collect();
4654        let lhs_height = lhs.height;
4655        let lhs_is_constant = lhs.is_constant;
4656        let lhs_has_function = lhs.has_function;
4657        let expr = Expr::In {
4658            expr: Box::new(lhs.expr),
4659            set: InSet::List(exprs),
4660            not,
4661            span,
4662        };
4663
4664        if item_height == 0 {
4665            if lhs_has_function {
4666                return self.finish_expr(expr, lhs_height.saturating_add(1), false, true);
4667            }
4668            return self.finish_expr(expr, 1, true, false);
4669        }
4670
4671        let cached_child_height = if singleton_constant {
4672            lhs_height.max(item_height.saturating_add(1))
4673        } else if singleton_subquery {
4674            lhs_height.max(item_height.saturating_sub(1))
4675        } else {
4676            lhs_height.max(item_height)
4677        };
4678        let parsed = self.checked_expr(
4679            expr,
4680            cached_child_height,
4681            lhs_is_constant && items_are_constant,
4682            lhs_has_function || item_has_function,
4683        )?;
4684        if not {
4685            self.add_cached_parent(parsed)
4686        } else {
4687            Ok(parsed)
4688        }
4689    }
4690
4691    #[cfg(test)]
4692    fn parse_case_expr(&mut self, start: Span) -> Result<ParsedExpr, ParseError> {
4693        let operand = if matches!(self.peek_kind(), TokenKind::KwWhen) {
4694            None
4695        } else {
4696            Some(self.parse_expr_bp(0)?)
4697        };
4698
4699        let mut whens = Vec::new();
4700        while self.eat_kind(&TokenKind::KwWhen) {
4701            let condition = self.parse_expr_bp(0)?;
4702            if !self.eat_kind(&TokenKind::KwThen) {
4703                return Err(self.err_here("expected THEN in CASE expression"));
4704            }
4705            let result = self.parse_expr_bp(0)?;
4706            whens.push((condition, result));
4707        }
4708        if whens.is_empty() {
4709            return Err(self.err_here("CASE requires at least one WHEN clause"));
4710        }
4711
4712        let else_expr = if self.eat_kind(&TokenKind::KwElse) {
4713            Some(self.parse_expr_bp(0)?)
4714        } else {
4715            None
4716        };
4717
4718        if !self.eat_kind(&TokenKind::KwEnd) {
4719            return Err(self.err_here("expected END for CASE expression"));
4720        }
4721        let end = self.tokens[self.pos.saturating_sub(1)].span;
4722        let span = start.merge(end);
4723        let mut height = operand.as_ref().map_or(0, |parsed| parsed.height);
4724        let mut is_constant = operand.as_ref().is_none_or(|parsed| parsed.is_constant);
4725        let mut has_function = operand.as_ref().is_some_and(|parsed| parsed.has_function);
4726        for (condition, result) in &whens {
4727            height = height.max(condition.height).max(result.height);
4728            is_constant &= condition.is_constant && result.is_constant;
4729            has_function |= condition.has_function || result.has_function;
4730        }
4731        if let Some(parsed) = &else_expr {
4732            height = height.max(parsed.height);
4733            is_constant &= parsed.is_constant;
4734            has_function |= parsed.has_function;
4735        }
4736        self.checked_expr(
4737            Expr::Case {
4738                operand: operand.map(|parsed| Box::new(parsed.expr)),
4739                whens: whens
4740                    .into_iter()
4741                    .map(|(condition, result)| (condition.expr, result.expr))
4742                    .collect(),
4743                else_expr: else_expr.map(|parsed| Box::new(parsed.expr)),
4744                span,
4745            },
4746            height,
4747            is_constant,
4748            has_function,
4749        )
4750    }
4751
4752    #[cfg(test)]
4753    fn parse_function_call(&mut self, name: String, start: Span) -> Result<ParsedExpr, ParseError> {
4754        self.expect_kind(&TokenKind::LeftParen)?;
4755
4756        let (args, distinct, height) = if matches!(self.peek_kind(), TokenKind::Star) {
4757            self.advance_token();
4758            // Mirrors start_function: `f(*)` parses for any function; only
4759            // count keeps star semantics, others become zero-arg calls.
4760            if name.eq_ignore_ascii_case("count") {
4761                (FunctionArgs::Star, false, 0)
4762            } else {
4763                (FunctionArgs::List(Vec::new()), false, 0)
4764            }
4765        } else {
4766            let distinct = self.eat_kind(&TokenKind::KwDistinct);
4767            let (args, height) = if matches!(self.peek_kind(), TokenKind::RightParen) {
4768                if distinct {
4769                    return Err(self.err_here("DISTINCT requires at least one argument"));
4770                }
4771                (FunctionArgs::List(Vec::new()), 0)
4772            } else {
4773                let first = self.parse_expr_bp(0)?;
4774                let mut height = first.height;
4775                let mut list = vec![first.expr];
4776                while self.eat_kind(&TokenKind::Comma) {
4777                    let parsed = self.parse_expr_bp(0)?;
4778                    height = height.max(parsed.height);
4779                    list.push(parsed.expr);
4780                }
4781                (FunctionArgs::List(list), height)
4782            };
4783            (args, distinct, height)
4784        };
4785
4786        // In-aggregate ORDER BY (SQLite 3.44+): group_concat(x, ',' ORDER BY y DESC)
4787        let order_by =
4788            if matches!(&args, FunctionArgs::List(_)) && self.eat_kind(&TokenKind::KwOrder) {
4789                self.expect_kind(&TokenKind::KwBy)?;
4790                self.parse_comma_sep(Self::parse_ordering_term)?
4791            } else {
4792                vec![]
4793            };
4794
4795        let mut end = self.expect_kind(&TokenKind::RightParen)?;
4796        // Peek ahead: only consume FILTER if followed by '(' to avoid
4797        // swallowing FILTER when used as a column alias (it's non-reserved).
4798        let filter = if matches!(self.peek_kind(), TokenKind::KwFilter)
4799            && self
4800                .tokens
4801                .get(self.pos + 1)
4802                .is_some_and(|t| t.kind == TokenKind::LeftParen)
4803        {
4804            self.advance_token(); // consume FILTER
4805            self.expect_kind(&TokenKind::LeftParen)?;
4806            self.expect_kind(&TokenKind::KwWhere)?;
4807            let predicate = self.parse_expr()?;
4808            let filter_end = self.expect_kind(&TokenKind::RightParen)?;
4809            end = end.merge(filter_end);
4810            Some(Box::new(predicate))
4811        } else {
4812            None
4813        };
4814        // Peek: only consume OVER if followed by '(' or an identifier
4815        // (window name), to avoid swallowing OVER as a column alias.
4816        let over = if matches!(self.peek_kind(), TokenKind::KwOver)
4817            && self.tokens.get(self.pos + 1).is_some_and(|t| {
4818                matches!(t.kind, TokenKind::LeftParen) || starts_bare_window_name(&t.kind)
4819            }) {
4820            self.advance_token(); // consume OVER
4821            if self.eat_kind(&TokenKind::LeftParen) {
4822                let spec = self.parse_window_spec()?;
4823                let over_end = self.expect_kind(&TokenKind::RightParen)?;
4824                end = end.merge(over_end);
4825                Some(spec)
4826            } else {
4827                let base_window = self.parse_window_name()?;
4828                let base_span = self.tokens[self.pos.saturating_sub(1)].span;
4829                end = end.merge(base_span);
4830                Some(WindowSpec {
4831                    window_ref: Some(WindowReference::Direct(base_window)),
4832                    partition_by: Vec::new(),
4833                    order_by: Vec::new(),
4834                    frame: None,
4835                })
4836            }
4837        } else {
4838            None
4839        };
4840
4841        let span = start.merge(end);
4842        self.checked_expr(
4843            Expr::FunctionCall {
4844                name,
4845                args,
4846                distinct,
4847                order_by,
4848                filter,
4849                over,
4850                span,
4851            },
4852            height,
4853            false,
4854            true,
4855        )
4856    }
4857
4858    fn parse_raise_args(&mut self) -> Result<(RaiseAction, Option<String>), ParseError> {
4859        let action_tok = self.advance_token();
4860        let action = match &action_tok.kind {
4861            TokenKind::KwIgnore => RaiseAction::Ignore,
4862            TokenKind::KwRollback => RaiseAction::Rollback,
4863            TokenKind::KwAbort => RaiseAction::Abort,
4864            TokenKind::KwFail => RaiseAction::Fail,
4865            _ => {
4866                return Err(ParseError::at(
4867                    "expected IGNORE, ROLLBACK, ABORT, or FAIL in RAISE",
4868                    Some(&action_tok),
4869                ));
4870            }
4871        };
4872        if matches!(action, RaiseAction::Ignore) {
4873            return Ok((action, None));
4874        }
4875        self.expect_kind(&TokenKind::Comma)?;
4876        let msg_tok = self.advance_token();
4877        let message = match &msg_tok.kind {
4878            TokenKind::String(s) => s.clone(),
4879            _ => {
4880                return Err(ParseError::at(
4881                    "expected string message in RAISE",
4882                    Some(&msg_tok),
4883                ));
4884            }
4885        };
4886        Ok((action, Some(message)))
4887    }
4888
4889    fn parse_type_name(&mut self) -> Result<TypeName, ParseError> {
4890        let mut parts = Vec::new();
4891        loop {
4892            match self.peek_kind() {
4893                TokenKind::Id(_) | TokenKind::QuotedId(_, _) => {
4894                    let tok = self.advance_token();
4895                    if let TokenKind::Id(s) | TokenKind::QuotedId(s, _) = &tok.kind {
4896                        parts.push(s.to_string());
4897                    } else {
4898                        unreachable!();
4899                    }
4900                }
4901                k if is_nonreserved_kw(k) => {
4902                    let tok = self.advance_token();
4903                    parts.push(kw_to_str(&tok.kind));
4904                }
4905                _ => break,
4906            }
4907        }
4908        if parts.is_empty() {
4909            return Err(self.err_here("expected type name"));
4910        }
4911        let name = parts.join(" ");
4912
4913        let (arg1, arg2) = if self.eat_kind(&TokenKind::LeftParen) {
4914            let a1 = self.parse_type_arg()?;
4915            let a2 = if self.eat_kind(&TokenKind::Comma) {
4916                Some(self.parse_type_arg()?)
4917            } else {
4918                None
4919            };
4920            self.expect_kind(&TokenKind::RightParen)?;
4921            (Some(a1), a2)
4922        } else {
4923            (None, None)
4924        };
4925
4926        Ok(TypeName { name, arg1, arg2 })
4927    }
4928
4929    fn parse_type_arg(&mut self) -> Result<String, ParseError> {
4930        let tok = self.advance_token();
4931        match &tok.kind {
4932            TokenKind::Integer(i) => Ok(i.to_string()),
4933            TokenKind::Float(f) => Ok(f.to_string()),
4934            TokenKind::Minus => {
4935                let next = self.advance_token();
4936                match &next.kind {
4937                    TokenKind::Integer(i) => Ok(format!("-{i}")),
4938                    TokenKind::OversizedInt(s) => Ok(format!("-{s}")),
4939                    TokenKind::Float(f) => Ok(format!("-{f}")),
4940                    _ => Err(ParseError::at(
4941                        "expected number in type argument",
4942                        Some(&next),
4943                    )),
4944                }
4945            }
4946            TokenKind::Plus => {
4947                let next = self.advance_token();
4948                match &next.kind {
4949                    TokenKind::Integer(i) => Ok(format!("+{i}")),
4950                    TokenKind::OversizedInt(s) => Ok(format!("+{s}")),
4951                    TokenKind::Float(f) => Ok(format!("+{f}")),
4952                    _ => Err(ParseError::at(
4953                        "expected number in type argument",
4954                        Some(&next),
4955                    )),
4956                }
4957            }
4958            TokenKind::OversizedInt(s) => Ok(s.clone()),
4959            TokenKind::Id(s) | TokenKind::QuotedId(s, _) => Ok(s.to_string()),
4960            _ => Err(ParseError::at("expected type argument", Some(&tok))),
4961        }
4962    }
4963
4964    /// Subquery parser for EXISTS/IN expression support.
4965    #[cfg(test)]
4966    fn parse_subquery_minimal(&mut self) -> Result<HeightTracked<SelectStatement>, ParseError> {
4967        let with = if self.at_kind(&TokenKind::KwWith) {
4968            Some(ParseMachine::for_with(self).run_with()?)
4969        } else {
4970            None
4971        };
4972        ParseMachine::for_select(self, with).run_select()
4973    }
4974}
4975
4976/// Parse a single expression from raw SQL text.
4977pub fn parse_expr(sql: &str) -> Result<Expr, ParseError> {
4978    let mut parser = Parser::from_sql(sql);
4979    let expr = parser.parse_expr()?;
4980    let _ = parser.eat(&TokenKind::Semicolon);
4981    if !parser.at_eof() {
4982        return Err(parser.err_here(format!(
4983            "unexpected token after expression: {:?}",
4984            parser.peek_kind()
4985        )));
4986    }
4987    Ok(expr)
4988}
4989
4990#[cfg(test)]
4991mod tests {
4992    use super::*;
4993    use crate::parser::ParseErrorKind;
4994    use fsqlite_ast::{BoundCollation, SelectCore, TableOrSubquery};
4995    use fsqlite_types::{SqliteValue, TypeAffinity};
4996
4997    fn parse(sql: &str) -> Expr {
4998        match parse_expr(sql) {
4999            Ok(expr) => expr,
5000            Err(err) => unreachable!("parse error for `{sql}`: {err}"),
5001        }
5002    }
5003
5004    fn repeated_infix_expression(term_count: usize, operator: &str) -> String {
5005        std::iter::repeat_n("1", term_count)
5006            .collect::<Vec<_>>()
5007            .join(operator)
5008    }
5009
5010    fn assert_expression_depth_error(sql: &str) {
5011        let error = parse_expr(sql).expect_err("expression height 1001 must fail closed");
5012        assert_eq!(
5013            error.kind,
5014            ParseErrorKind::ExpressionTooDeep {
5015                max: MAX_PARSE_DEPTH
5016            }
5017        );
5018        assert_eq!(
5019            error.message,
5020            format!(
5021                "Expression tree is too large (maximum depth {})",
5022                MAX_PARSE_DEPTH
5023            )
5024        );
5025        assert!(error.is_expression_too_deep());
5026    }
5027
5028    fn right_deep_binary_expression(height: usize) -> String {
5029        format!("{}1{}", "1 + (".repeat(height - 1), ")".repeat(height - 1))
5030    }
5031
5032    fn single_arg_function_expression(height: usize) -> String {
5033        format!("{}1{}", "abs(".repeat(height - 1), ")".repeat(height - 1))
5034    }
5035
5036    fn scalar_subquery_expression(height: usize) -> String {
5037        format!(
5038            "{}1{}",
5039            "(SELECT ".repeat(height - 1),
5040            ")".repeat(height - 1)
5041        )
5042    }
5043
5044    fn on_one_mib_stack<T: Send + 'static>(task: impl FnOnce() -> T + Send + 'static) -> T {
5045        std::thread::Builder::new()
5046            .stack_size(1024 * 1024)
5047            .spawn(task)
5048            .expect("1 MiB parser thread must spawn")
5049            .join()
5050            .expect("parser task must not overflow or panic")
5051    }
5052
5053    fn parsed_select_height(sql: &str) -> u32 {
5054        let mut parser = Parser::from_sql(sql);
5055        let select = parser
5056            .parse_subquery_minimal()
5057            .expect("SELECT fixture must parse");
5058        assert_eq!(
5059            select.height,
5060            normalized_ast_select_height(&select.value),
5061            "tracked SELECT height diverged from the normalized AST test oracle"
5062        );
5063        select.height
5064    }
5065
5066    fn parsed_expr_height(sql: &str) -> u32 {
5067        let mut parser = Parser::from_sql(sql);
5068        let parsed = parser
5069            .parse_expr_tracked()
5070            .expect("expression fixture must parse");
5071        assert!(
5072            matches!(parser.peek_kind(), TokenKind::Eof | TokenKind::Semicolon),
5073            "expression fixture left an unparsed token: {sql}"
5074        );
5075        assert_eq!(
5076            parsed.height,
5077            normalized_ast_expr_height(&parsed.expr),
5078            "tracked expression height diverged from the normalized AST test oracle: {sql}"
5079        );
5080        parsed.height
5081    }
5082
5083    #[test]
5084    fn bound_outer_value_has_constant_leaf_facts() {
5085        let expr = Expr::BoundOuterValue {
5086            value: SqliteValue::Integer(42),
5087            collation: BoundCollation::Named("NOCASE".to_owned()),
5088            affinity: Some(TypeAffinity::Integer),
5089            span: Span::ZERO,
5090        };
5091
5092        let parsed = ParsedExpr::leaf(expr.clone());
5093        assert_eq!(parsed.height, 1);
5094        assert!(parsed.is_constant);
5095        assert!(!parsed.has_function);
5096
5097        let facts = cached_facts_from_tasks(vec![CachedHeightTask::Expr(&expr)]);
5098        assert_eq!(facts.height, 1);
5099        assert!(facts.is_constant);
5100        assert!(!facts.has_function);
5101    }
5102
5103    fn mixed_deep_expression(height: usize) -> String {
5104        let mut prefix = String::new();
5105        let mut closing_count = 0;
5106        for index in 1..height {
5107            if index % 7 == 0 {
5108                prefix.push('(');
5109                closing_count += 1;
5110            }
5111            match index % 4 {
5112                0 => prefix.push('~'),
5113                1 => {
5114                    prefix.push_str("abs(");
5115                    closing_count += 1;
5116                }
5117                2 => {
5118                    prefix.push_str("(SELECT ");
5119                    closing_count += 1;
5120                }
5121                _ => {
5122                    prefix.push_str("1 + (");
5123                    closing_count += 1;
5124                }
5125            }
5126        }
5127        prefix.push('1');
5128        prefix.push_str(&")".repeat(closing_count));
5129        prefix
5130    }
5131
5132    fn assert_machine_matches_recursive_oracle(sql: &str) {
5133        let mut machine = Parser::from_sql(sql);
5134        let machine_result = machine.parse_expr_tracked();
5135        let machine_pos = machine.pos;
5136        let machine_tail = machine.peek_kind().clone();
5137
5138        let mut oracle = Parser::from_sql(sql);
5139        let oracle_result = oracle.parse_expr_bp(0);
5140        let oracle_pos = oracle.pos;
5141        let oracle_tail = oracle.peek_kind().clone();
5142
5143        assert_eq!(
5144            machine_pos, oracle_pos,
5145            "parser tail position differs: {sql}"
5146        );
5147        assert_eq!(
5148            machine_tail, oracle_tail,
5149            "parser tail token differs: {sql}"
5150        );
5151        match (machine_result, oracle_result) {
5152            (Ok(machine), Ok(oracle)) => {
5153                assert_eq!(machine.expr, oracle.expr, "AST or spans differ: {sql}");
5154                assert_eq!(machine.height, oracle.height, "height differs: {sql}");
5155                assert_eq!(
5156                    machine.is_constant, oracle.is_constant,
5157                    "constant fact differs: {sql}"
5158                );
5159                assert_eq!(
5160                    machine.has_function, oracle.has_function,
5161                    "function fact differs: {sql}"
5162                );
5163                assert_eq!(machine.root, oracle.root, "root fact differs: {sql}");
5164            }
5165            (Err(machine), Err(oracle)) => {
5166                assert_eq!(machine, oracle, "diagnostic differs: {sql}");
5167            }
5168            (Ok(_), Err(_)) | (Err(_), Ok(_)) => {
5169                panic!("machine/oracle result class differs for `{sql}`");
5170            }
5171        }
5172    }
5173
5174    fn assert_select_machine_matches_recursive_oracle(sql: &str) {
5175        let mut machine = Parser::from_sql(sql);
5176        let machine_result = machine.parse_select_stmt_tracked(None);
5177        let machine_pos = machine.pos;
5178        let machine_tail = machine.peek_kind().clone();
5179
5180        let mut oracle = Parser::from_sql(sql);
5181        let oracle_result = oracle.parse_select_stmt_inner_tracked(None);
5182        let oracle_pos = oracle.pos;
5183        let oracle_tail = oracle.peek_kind().clone();
5184
5185        assert_eq!(
5186            machine_pos, oracle_pos,
5187            "SELECT parser tail position differs: {sql}"
5188        );
5189        assert_eq!(
5190            machine_tail, oracle_tail,
5191            "SELECT parser tail token differs: {sql}"
5192        );
5193        match (machine_result, oracle_result) {
5194            (Ok(machine), Ok(oracle)) => {
5195                assert_eq!(machine.value, oracle.value, "SELECT AST differs: {sql}");
5196                assert_eq!(
5197                    machine.height, oracle.height,
5198                    "SELECT height differs: {sql}"
5199                );
5200            }
5201            (Err(machine), Err(oracle)) => {
5202                assert_eq!(machine, oracle, "SELECT diagnostic differs: {sql}");
5203            }
5204            (Ok(_), Err(_)) | (Err(_), Ok(_)) => {
5205                panic!("SELECT machine/oracle result class differs for `{sql}`");
5206            }
5207        }
5208    }
5209
5210    fn bitnot_depth(mut expr: &Expr) -> usize {
5211        let mut depth = 0;
5212        while let Expr::UnaryOp {
5213            op: UnaryOp::BitNot,
5214            expr: inner,
5215            ..
5216        } = expr
5217        {
5218            depth += 1;
5219            expr = inner;
5220        }
5221        depth
5222    }
5223
5224    fn wrap_function_to_height(base: &str, target_height: usize) -> String {
5225        let base_height = parsed_expr_height(base) as usize;
5226        assert!(base_height <= target_height);
5227        let wrappers = target_height - base_height;
5228        format!("{}{base}{}", "abs(".repeat(wrappers), ")".repeat(wrappers))
5229    }
5230
5231    #[test]
5232    fn test_explicit_machine_matches_recursive_oracle_for_shallow_valid_expressions() {
5233        for sql in [
5234            "1",
5235            "-9223372036854775808",
5236            "a.b + c * 2",
5237            "'a'.b + a.'b'",
5238            "attach.x",
5239            "filter.x",
5240            "true.x",
5241            "with.x",
5242            "a.current_date",
5243            "NOT a = b",
5244            "x IS NULL < 2",
5245            "x IS NOT DISTINCT FROM y",
5246            "CAST(x + 1 AS DECIMAL(10, 2))",
5247            "CASE x WHEN 1 THEN y ELSE z END",
5248            "x NOT BETWEEN 1 AND 2",
5249            "x IN (1, 2 + 3)",
5250            "(SELECT 1, 2) IN ((1, 2))",
5251            "(a, b) IN ((SELECT 1))",
5252            "(a, b) IN ((SELECT 1, 2, 3))",
5253            "(a, b) IN ((SELECT 1), (SELECT 2))",
5254            "(SELECT * FROM t) IN (1)",
5255            "x IN (SELECT y FROM t WHERE z > 0 ORDER BY y LIMIT 1)",
5256            "EXISTS (SELECT 1 FROM t WHERE x = y)",
5257            "(1, 2 + 3)",
5258            "value COLLATE \"my col\"",
5259            "doc -> '$.x' || suffix",
5260            "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)",
5261        ] {
5262            assert_machine_matches_recursive_oracle(sql);
5263        }
5264    }
5265
5266    #[test]
5267    fn test_explicit_machine_matches_recursive_oracle_for_shallow_malformed_expressions() {
5268        for sql in [
5269            "+",
5270            "CASE END",
5271            "CASE WHEN 1 2 END",
5272            "CASE WHEN 1 THEN 2",
5273            "CAST(1 INTEGER)",
5274            "f(DISTINCT)",
5275            "(1,)",
5276            "a.",
5277            "a.select",
5278            "a.nothing",
5279            "cast.x",
5280            "current_date.x",
5281            "raise.x",
5282            "transaction.x",
5283            "a BETWEEN 1 2",
5284            "a IN (1,)",
5285            "(a, b) IN (1)",
5286            "(a, b) IN (+(SELECT 1, 2))",
5287            "(a, b) IN ((1, 2), 3)",
5288            "(a, b) NOT IN ((1, 2, 3))",
5289            "(SELECT 1, 2) IN (1)",
5290            "(SELECT 1, 2) IN ((1, 2), 3)",
5291            "(SELECT 1, 2) NOT IN ((1, 2, 3))",
5292            "a LIKE",
5293            "EXISTS (SELECT)",
5294            "count(* ORDER BY x)",
5295            "t.*",
5296        ] {
5297            assert_machine_matches_recursive_oracle(sql);
5298        }
5299    }
5300
5301    #[test]
5302    fn public_parse_expr_requires_eof_after_one_optional_terminator() {
5303        assert_eq!(
5304            parse_expr("1;")
5305                .expect("one trailing expression terminator must remain valid")
5306                .to_string(),
5307            "1"
5308        );
5309
5310        for (sql, unexpected) in [("1; 2", "2"), ("1; SELECT 2", "SELECT"), ("1;;", ";")] {
5311            let error = parse_expr(sql)
5312                .expect_err("tokens after the optional expression terminator must be rejected");
5313            assert_eq!(error.kind, ParseErrorKind::Syntax);
5314            assert!(
5315                error.message.contains("unexpected token after expression"),
5316                "unexpected diagnostic for `{sql}`: {error:?}"
5317            );
5318            assert_eq!(
5319                &sql[error.span.start as usize..error.span.end as usize],
5320                unexpected,
5321                "the diagnostic for `{sql}` must point at the first forbidden token"
5322            );
5323        }
5324    }
5325
5326    #[test]
5327    fn test_explicit_select_machine_matches_recursive_shallow_oracle() {
5328        for sql in [
5329            "SELECT 1",
5330            "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",
5331            "SELECT * FROM a CROSS JOIN b ON a.id = b.id",
5332            "SELECT * FROM a, b",
5333            "SELECT * FROM a, b ON a.id = b.id",
5334            "SELECT * FROM a, b USING(id)",
5335            "SELECT filter.* FROM t AS filter",
5336            "SELECT attach.* FROM t AS \"attach\"",
5337            "SELECT attach.x FROM (SELECT 1 AS x) AS \"attach\"",
5338            "SELECT filter.x FROM (SELECT 1 AS x) AS \"filter\"",
5339            "SELECT 't'.*, t.'select' FROM t",
5340            "SELECT 1 'single quoted'",
5341            "SELECT 1 attach",
5342            "SELECT 1 window",
5343            "SELECT * FROM (SELECT 1) 'single quoted'",
5344            "SELECT * FROM (SELECT 1) match",
5345            "SELECT sum(1) OVER attach WINDOW attach AS ()",
5346            "SELECT sum(1) OVER (attach) WINDOW attach AS ()",
5347            "SELECT sum(x) OVER (ROWS BETWEEN 1 PRECEDING AND 2 PRECEDING) FROM t",
5348            "SELECT sum(x) OVER (RANGE BETWEEN CURRENT ROW AND 1 FOLLOWING) FROM t",
5349            "VALUES (1, 2), (3, 4)",
5350            "VALUES (1) ORDER BY 1",
5351            "VALUES (1) LIMIT 1",
5352            "VALUES (1) UNION SELECT 2 ORDER BY 1 LIMIT 1",
5353            "SELECT 1 UNION ALL SELECT 2 INTERSECT SELECT 3",
5354            "SELECT 1 UNION VALUES (2), (3) ORDER BY 1",
5355            "SELECT FROM t",
5356            "SELECT nothing.* FROM t AS \"nothing\"",
5357            "SELECT sum(1) OVER filter",
5358            "SELECT sum(x) OVER (ROWS 1 FOLLOWING) FROM t",
5359            "SELECT sum(x) OVER (ROWS BETWEEN CURRENT ROW AND 1 PRECEDING) FROM t",
5360            "VALUES 1",
5361        ] {
5362            assert_select_machine_matches_recursive_oracle(sql);
5363        }
5364    }
5365
5366    #[test]
5367    fn test_threshold_unary_precedence_associativity_and_spans_are_stable() {
5368        for unary_count in [63_u32, 64] {
5369            let sql = format!("{}1 + (1)", "~".repeat(unary_count as usize));
5370            let parsed = parse_expr(&sql).expect("threshold unary-plus expression must parse");
5371            assert_eq!(
5372                parsed.span(),
5373                Span::new(0, unary_count + 6),
5374                "grouping delimiters must not change the established root span"
5375            );
5376            let Expr::BinaryOp {
5377                left,
5378                op: BinaryOp::Add,
5379                right,
5380                ..
5381            } = &parsed
5382            else {
5383                panic!("unary prefix must not capture the lower-precedence addition");
5384            };
5385            assert_eq!(bitnot_depth(left), unary_count as usize);
5386            assert!(matches!(
5387                right.as_ref(),
5388                Expr::Literal(Literal::Integer(1), _)
5389            ));
5390
5391            let subtraction = format!("{}10 - 3 - 2", "~".repeat(unary_count as usize));
5392            let subtraction =
5393                parse_expr(&subtraction).expect("threshold subtraction expression must parse");
5394            assert!(matches!(
5395                subtraction,
5396                Expr::BinaryOp {
5397                    op: BinaryOp::Subtract,
5398                    left,
5399                    ..
5400                } if matches!(
5401                    left.as_ref(),
5402                    Expr::BinaryOp {
5403                        op: BinaryOp::Subtract,
5404                        ..
5405                    }
5406                )
5407            ));
5408
5409            let precedence = format!("{}1 + 2 * 3", "~".repeat(unary_count as usize));
5410            let precedence =
5411                parse_expr(&precedence).expect("threshold precedence expression must parse");
5412            assert!(matches!(
5413                precedence,
5414                Expr::BinaryOp {
5415                    op: BinaryOp::Add,
5416                    right,
5417                    ..
5418                } if matches!(
5419                    right.as_ref(),
5420                    Expr::BinaryOp {
5421                        op: BinaryOp::Multiply,
5422                        ..
5423                    }
5424                )
5425            ));
5426
5427            let parenthesized = format!(
5428                "{}1{}",
5429                "(".repeat(unary_count as usize),
5430                ")".repeat(unary_count as usize)
5431            );
5432            let parenthesized =
5433                parse_expr(&parenthesized).expect("threshold parentheses must parse");
5434            assert_eq!(
5435                parenthesized.span(),
5436                Span::new(unary_count, unary_count + 1)
5437            );
5438        }
5439    }
5440
5441    #[test]
5442    fn test_shallow_machine_uses_only_inline_control_and_value_storage() {
5443        PARSE_MACHINE_STACK_SPILLS.set(0);
5444        let expr = parse_expr("a + b * 2").expect("shallow expression must parse");
5445        assert_eq!(expr.to_string(), "a + b * 2");
5446        assert_eq!(
5447            PARSE_MACHINE_STACK_SPILLS.get(),
5448            0,
5449            "representative shallow parsing must not allocate parser stack spill storage"
5450        );
5451    }
5452
5453    #[test]
5454    fn test_formatter_precedence_associativity_and_migration_scale_stability() {
5455        for (sql, expected) in [
5456            ("a + b * 2", "a + b * 2"),
5457            ("a * (b + c)", "a * (b + c)"),
5458            ("(a - b) - c", "a - b - c"),
5459            ("a - (b - c)", "a - (b - c)"),
5460            ("a / (b / c)", "a / (b / c)"),
5461        ] {
5462            let expr = parse_expr(sql).expect("precedence fixture must parse");
5463            let rendered = expr.to_string();
5464            assert_eq!(rendered, expected);
5465            let reparsed = parse_expr(&rendered).expect("formatted fixture must reparse");
5466            assert_eq!(reparsed.to_string(), rendered);
5467        }
5468
5469        const TERM_COUNT: usize = MAX_PARSE_DEPTH as usize;
5470        for operator in ["AND", "OR"] {
5471            let mut sql = String::new();
5472            for _ in 1..TERM_COUNT {
5473                sql.push_str("flag ");
5474                sql.push_str(operator);
5475                sql.push_str(" (");
5476            }
5477            sql.push_str("flag");
5478            sql.push_str(&")".repeat(TERM_COUNT - 1));
5479
5480            let expr = parse_expr(&sql).expect("migration-scale associative chain must parse");
5481            let rendered = expr.to_string();
5482            assert_eq!(rendered.matches(operator).count(), TERM_COUNT - 1);
5483            assert!(
5484                !rendered.contains('('),
5485                "associative {operator} chain must have a flat canonical form"
5486            );
5487            let reparsed = parse_expr(&rendered).expect("flat migration-scale chain must reparse");
5488            assert_eq!(
5489                reparsed.to_string(),
5490                rendered,
5491                "format-parse-format must be byte-stable for {operator}"
5492            );
5493        }
5494    }
5495
5496    #[test]
5497    fn test_expression_height_exact_1000_1001_flat_infix_boundary() {
5498        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5499        let at_limit = repeated_infix_expression(LIMIT, " + ");
5500        let expr = parse_expr(&at_limit).expect("1000-term left-associated tree has height 1000");
5501        let rendered = expr.to_string();
5502        parse_expr(&rendered).expect("formatted height-1000 expression must remain parseable");
5503
5504        let over_limit = repeated_infix_expression(LIMIT + 1, " + ");
5505        assert_expression_depth_error(&over_limit);
5506    }
5507
5508    #[test]
5509    fn test_expression_height_exact_1000_1001_unary_boundary() {
5510        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5511        let at_limit = format!("{}1", "~".repeat(LIMIT - 1));
5512        parse_expr(&at_limit).expect("999 unary nodes plus one leaf have height 1000");
5513
5514        let over_limit = format!("{}1", "~".repeat(LIMIT));
5515        assert_expression_depth_error(&over_limit);
5516    }
5517
5518    #[test]
5519    fn test_expression_height_exact_1000_1001_not_boundary() {
5520        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5521        let at_limit = format!("{}1", "NOT ".repeat(LIMIT - 1));
5522        parse_expr(&at_limit).expect("999 NOT nodes plus one leaf have height 1000");
5523
5524        let over_limit = format!("{}1", "NOT ".repeat(LIMIT));
5525        assert_expression_depth_error(&over_limit);
5526    }
5527
5528    #[test]
5529    fn test_expression_parenthesis_chain_is_stack_safe() {
5530        const PAREN_PAIRS: usize = MAX_PARSE_DEPTH as usize * 2;
5531        let deeply_parenthesized =
5532            format!("{}1{}", "(".repeat(PAREN_PAIRS), ")".repeat(PAREN_PAIRS));
5533        parse_expr(&deeply_parenthesized)
5534            .expect("parentheses do not add AST height or consume the native call stack");
5535    }
5536
5537    #[test]
5538    fn test_expression_prefix_frames_preserve_grouping_and_row_values() {
5539        let grouped = parse_expr("-((1 + 2)) * 3").expect("grouped unary expression must parse");
5540        assert!(matches!(
5541            grouped,
5542            Expr::BinaryOp {
5543                left,
5544                op: BinaryOp::Multiply,
5545                ..
5546            } if matches!(
5547                left.as_ref(),
5548                Expr::UnaryOp {
5549                    op: UnaryOp::Negate,
5550                    ..
5551                }
5552            )
5553        ));
5554
5555        let row = parse_expr("((1), 2)").expect("nested row value must parse");
5556        assert!(matches!(row, Expr::RowValue(values, _) if values.len() == 2));
5557    }
5558
5559    #[test]
5560    fn test_expression_iterative_prefix_frames_preserve_minimum_integer_literal() {
5561        let mut parser = Parser::from_sql("-9223372036854775808");
5562        let parsed = parser
5563            .parse_expr_tracked()
5564            .expect("minimum signed integer literal must parse");
5565        assert!(matches!(
5566            &parsed.expr,
5567            Expr::Literal(Literal::Integer(i64::MIN), _)
5568        ));
5569        assert_eq!(parsed.height, 1);
5570        assert_eq!(parsed.expr.to_string(), "-9223372036854775808");
5571    }
5572
5573    #[test]
5574    fn test_serializer_regression_double_negated_minimum_integer_round_trips() {
5575        let expr = parse_expr("- -9223372036854775808")
5576            .expect("double-negated minimum integer must parse");
5577        let rendered = expr.to_string();
5578        assert_eq!(rendered, "-(-9223372036854775808)");
5579        let reparsed = parse_expr(&rendered).expect("rendered double negation must remain SQL");
5580        assert_eq!(expr, reparsed);
5581    }
5582
5583    #[test]
5584    fn test_serializer_regression_quoted_collation_name_round_trips() {
5585        let expr =
5586            parse_expr("value COLLATE \"my col\"").expect("quoted collation name must parse");
5587        let rendered = expr.to_string();
5588        assert_eq!(rendered, "value COLLATE \"my col\"");
5589        let reparsed = parse_expr(&rendered).expect("rendered collation must remain SQL");
5590        assert_eq!(expr, reparsed);
5591    }
5592
5593    #[test]
5594    fn test_expression_height_mixed_prefix_and_flat_reductions() {
5595        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5596        const PREFIX_COUNT: usize = 499;
5597        let at_limit = format!(
5598            "{}{}",
5599            "~".repeat(PREFIX_COUNT),
5600            repeated_infix_expression(LIMIT - PREFIX_COUNT, " - ")
5601        );
5602        parse_expr(&at_limit)
5603            .expect("mixed unary and left-associated reductions have exact height 1000");
5604
5605        let over_limit = format!(
5606            "{}{}",
5607            "~".repeat(PREFIX_COUNT),
5608            repeated_infix_expression(LIMIT - PREFIX_COUNT + 1, " - ")
5609        );
5610        assert_expression_depth_error(&over_limit);
5611    }
5612
5613    #[test]
5614    fn test_expression_height_container_adds_one_level() {
5615        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5616        let at_limit = format!("abs({})", repeated_infix_expression(LIMIT - 1, " + "));
5617        parse_expr(&at_limit).expect("function node over height-999 argument has height 1000");
5618
5619        let over_limit = format!("abs({})", repeated_infix_expression(LIMIT, " + "));
5620        assert_expression_depth_error(&over_limit);
5621    }
5622
5623    #[test]
5624    fn test_right_deep_binary_exact_1000_1001_boundary_on_one_mib_stack() {
5625        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5626        let at_limit = right_deep_binary_expression(LIMIT);
5627        let expr = on_one_mib_stack(move || {
5628            parse_expr(&at_limit).expect("right-deep height-1000 expression must parse")
5629        });
5630        assert_expression_depth_error(&right_deep_binary_expression(LIMIT + 1));
5631        drop(expr);
5632    }
5633
5634    #[test]
5635    fn test_single_arg_function_exact_1000_1001_boundary_on_one_mib_stack() {
5636        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5637        let at_limit = single_arg_function_expression(LIMIT);
5638        let expr = on_one_mib_stack(move || {
5639            parse_expr(&at_limit).expect("nested function height-1000 expression must parse")
5640        });
5641        assert_expression_depth_error(&single_arg_function_expression(LIMIT + 1));
5642        drop(expr);
5643    }
5644
5645    #[test]
5646    fn test_scalar_subquery_exact_1000_1001_boundary_on_one_mib_stack() {
5647        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5648        let at_limit = scalar_subquery_expression(LIMIT);
5649        let rendered_selects = on_one_mib_stack(move || {
5650            let expr =
5651                parse_expr(&at_limit).expect("scalar subquery height-1000 expression must parse");
5652            let rendered = expr.to_string();
5653            let select_count = rendered.matches("(SELECT ").count();
5654            drop(rendered);
5655            drop(expr);
5656            select_count
5657        });
5658        assert_eq!(rendered_selects, LIMIT - 1);
5659        assert_expression_depth_error(&scalar_subquery_expression(LIMIT + 1));
5660    }
5661
5662    #[test]
5663    fn test_signed_minimum_and_qualified_bases_round_trip_on_one_mib_stack() {
5664        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5665        for (base, canonical_base) in [
5666            ("-9223372036854775808", "-9223372036854775808"),
5667            ("schema.column", "schema.\"column\""),
5668        ] {
5669            let at_limit = wrap_function_to_height(base, LIMIT);
5670            let (height, rendered) = on_one_mib_stack(move || {
5671                let expr = parse_expr(&at_limit).expect("height-1000 base expression must parse");
5672                let height = normalized_ast_expr_height(&expr);
5673                let rendered = expr.to_string();
5674                let reparsed =
5675                    parse_expr(&rendered).expect("formatted height-1000 base must reparse");
5676                assert_eq!(normalized_ast_expr_height(&reparsed), MAX_PARSE_DEPTH);
5677                assert_eq!(rendered, reparsed.to_string());
5678                drop(reparsed);
5679                drop(expr);
5680                (height, rendered)
5681            });
5682            assert_eq!(height, MAX_PARSE_DEPTH);
5683            assert!(rendered.contains(canonical_base));
5684
5685            let over_limit = wrap_function_to_height(base, LIMIT + 1);
5686            let error = on_one_mib_stack(move || {
5687                parse_expr(&over_limit).expect_err("height-1001 base must fail closed")
5688            });
5689            assert_eq!(
5690                error.kind,
5691                ParseErrorKind::ExpressionTooDeep {
5692                    max: MAX_PARSE_DEPTH
5693                }
5694            );
5695        }
5696    }
5697
5698    #[test]
5699    fn test_mixed_deep_height_1000_parse_walk_format_and_drop_on_one_mib_stack() {
5700        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5701        let at_limit = mixed_deep_expression(LIMIT);
5702        let (height, rendered_len, walk_visits) = on_one_mib_stack(move || {
5703            let expr = parse_expr(&at_limit)
5704                .expect("mixed unary/function/subquery/binary height-1000 expression must parse");
5705            HEIGHT_WALK_VISITS.set(0);
5706            let height = normalized_ast_expr_height(&expr);
5707            let walk_visits = HEIGHT_WALK_VISITS.get();
5708            let rendered = expr.to_string();
5709            let rendered_len = rendered.len();
5710            drop(rendered);
5711            drop(expr);
5712            (height, rendered_len, walk_visits)
5713        });
5714        assert_eq!(height, MAX_PARSE_DEPTH);
5715        assert!(rendered_len > LIMIT);
5716        assert!(
5717            walk_visits <= at_limit_token_bound(LIMIT),
5718            "heap-backed cached-height walk must remain linear: {walk_visits} visits"
5719        );
5720
5721        let over_limit = mixed_deep_expression(LIMIT + 1);
5722        let error = on_one_mib_stack(move || {
5723            parse_expr(&over_limit).expect_err("mixed height-1001 expression must fail closed")
5724        });
5725        assert_eq!(
5726            error.kind,
5727            ParseErrorKind::ExpressionTooDeep {
5728                max: MAX_PARSE_DEPTH
5729            }
5730        );
5731    }
5732
5733    #[test]
5734    fn test_mixed_select_shape_round_trips_and_drops_on_one_mib_stack_at_exact_limit() {
5735        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5736        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";
5737        let at_limit = wrap_function_to_height(base, LIMIT);
5738        let (height, rendered_len) = on_one_mib_stack(move || {
5739            let expr = parse_expr(&at_limit)
5740                .expect("mixed CASE/BETWEEN/IN/CTE/JOIN/window height-1000 expression must parse");
5741            assert_eq!(normalized_ast_expr_height(&expr), MAX_PARSE_DEPTH);
5742            let rendered = expr.to_string();
5743            let reparsed =
5744                parse_expr(&rendered).expect("formatted mixed height-1000 expression must reparse");
5745            assert_eq!(normalized_ast_expr_height(&reparsed), MAX_PARSE_DEPTH);
5746            let rerendered = reparsed.to_string();
5747            assert_eq!(rendered, rerendered);
5748            let rendered_len = rendered.len();
5749            drop(rerendered);
5750            drop(reparsed);
5751            drop(rendered);
5752            drop(expr);
5753            (MAX_PARSE_DEPTH, rendered_len)
5754        });
5755        assert_eq!(height, MAX_PARSE_DEPTH);
5756        assert!(rendered_len > LIMIT);
5757
5758        let over_limit = wrap_function_to_height(base, LIMIT + 1);
5759        let error = on_one_mib_stack(move || {
5760            parse_expr(&over_limit).expect_err("mixed height-1001 expression must fail closed")
5761        });
5762        assert_eq!(
5763            error.kind,
5764            ParseErrorKind::ExpressionTooDeep {
5765                max: MAX_PARSE_DEPTH
5766            }
5767        );
5768        assert_eq!(
5769            error.message,
5770            format!(
5771                "Expression tree is too large (maximum depth {})",
5772                MAX_PARSE_DEPTH
5773            )
5774        );
5775    }
5776
5777    const fn at_limit_token_bound(height: usize) -> usize {
5778        height * 12
5779    }
5780
5781    #[test]
5782    fn test_998_wrapper_aggregate_filter_machine_steps_are_linear() {
5783        const WRAPPERS: usize = 998;
5784        let sql = format!(
5785            "{}1{} FILTER (WHERE 1)",
5786            "abs(".repeat(WRAPPERS),
5787            ")".repeat(WRAPPERS)
5788        );
5789        let token_count = Parser::from_sql(&sql).tokens.len();
5790        PARSE_MACHINE_STEPS.set(0);
5791        let expr = parse_expr(&sql).expect("near-match aggregate FILTER expression must parse");
5792        let visits = PARSE_MACHINE_STEPS.get();
5793        assert!(
5794            visits <= token_count.saturating_mul(8),
5795            "explicit parser machine revisited tokens superlinearly: {visits} steps for {token_count} tokens"
5796        );
5797        drop(expr);
5798    }
5799
5800    #[test]
5801    fn test_aggregate_order_by_auxiliary_roots_do_not_rescan_descendants() {
5802        let mut sql = "1".to_owned();
5803        for _ in 0..64 {
5804            sql = format!("f(0 ORDER BY {sql})");
5805        }
5806
5807        HEIGHT_WALK_VISITS.set(0);
5808        parse_expr(&sql).expect("nested aggregate ORDER BY expression must parse");
5809        assert_eq!(
5810            HEIGHT_WALK_VISITS.get(),
5811            0,
5812            "independent aggregate ORDER BY roots must not walk completed ASTs"
5813        );
5814    }
5815
5816    #[test]
5817    fn test_subquery_height_contract_matches_sqlite_height_of_select_fields() {
5818        for (sql, expected_height) in [
5819            ("SELECT 1 + 2 + 3", 3),
5820            ("SELECT 0 WHERE 1 + 2 + 3", 3),
5821            ("SELECT 0 GROUP BY 1 + 2 + 3 HAVING 1 + 2 + 3", 3),
5822            (
5823                "SELECT 0 ORDER BY 1 + 2 + 3 LIMIT 1 + 2 + 3 OFFSET 1 + 2 + 3",
5824                4,
5825            ),
5826            ("VALUES (1 + 2 + 3)", 3),
5827            ("SELECT 0 UNION ALL SELECT 1 + 2 + 3", 3),
5828        ] {
5829            assert_eq!(
5830                parsed_select_height(sql),
5831                expected_height,
5832                "official SELECT expression-height field was omitted: {sql}"
5833            );
5834        }
5835
5836        for sql in [
5837            "WITH c AS (SELECT 1 + 2 + 3) SELECT 0",
5838            "SELECT 0 FROM (SELECT 1 + 2 + 3)",
5839            "SELECT 0 FROM json_each(1 + 2 + 3)",
5840            "SELECT 0 FROM a JOIN b ON 1 + 2 + 3",
5841            "SELECT 0 WINDOW w AS (PARTITION BY 1 + 2 + 3 ORDER BY 1 + 2 + 3)",
5842        ] {
5843            assert_eq!(
5844                parsed_select_height(sql),
5845                1,
5846                "independent SQL root was incorrectly charged to its enclosing SELECT: {sql}"
5847            );
5848        }
5849    }
5850
5851    #[test]
5852    fn test_cached_height_matches_sqlite_grammar_rewrites() {
5853        for (sql, expected_height) in [
5854            ("NOT 1", 2),
5855            ("NOT EXISTS (SELECT 1)", 3),
5856            ("1 BETWEEN 2 AND 3", 2),
5857            ("1 NOT BETWEEN 2 AND 3", 3),
5858            ("1 LIKE 2", 2),
5859            ("1 NOT LIKE 2", 3),
5860            ("1 IN ()", 1),
5861            ("1 NOT IN ()", 1),
5862            ("abs(1) IN ()", 3),
5863            ("abs(1) NOT IN ()", 3),
5864            ("1 IN (2)", 3),
5865            ("1 IN (+2)", 4),
5866            ("1 NOT IN (2)", 4),
5867            ("1 IN (2, 3)", 2),
5868            ("1 NOT IN (2, 3)", 3),
5869            ("1 IN (SELECT 1 + 2)", 3),
5870            ("1 IN ((SELECT 1 + 2))", 3),
5871            ("(1, 2) IN ((3, 4))", 2),
5872            ("+1", 2),
5873            ("++1", 2),
5874            ("-+1", 2),
5875            ("(1 + 2 + 3, 4)", 1),
5876            ("(1 + 2 + 3, 4) + 5", 2),
5877        ] {
5878            assert_eq!(
5879                parsed_expr_height(sql),
5880                expected_height,
5881                "cached grammar height mismatch: {sql}"
5882            );
5883        }
5884    }
5885
5886    #[test]
5887    fn test_function_cached_height_excludes_auxiliary_expression_roots() {
5888        for (sql, expected_height) in [
5889            ("sum(1 + 2 + 3)", 4),
5890            ("sum(1 ORDER BY 1 + 2 + 3)", 2),
5891            ("sum(1) FILTER (WHERE 1 + 2 + 3)", 2),
5892            ("sum(1) OVER (ORDER BY 1 + 2 + 3)", 2),
5893            (
5894                "sum(1 ORDER BY 1 + 2 + 3) FILTER (WHERE 1 + 2 + 3) \
5895                 OVER (ORDER BY 1 + 2 + 3)",
5896                2,
5897            ),
5898        ] {
5899            assert_eq!(
5900                parsed_expr_height(sql),
5901                expected_height,
5902                "auxiliary root leaked into the aggregate argument height: {sql}"
5903            );
5904        }
5905    }
5906
5907    #[test]
5908    fn test_function_auxiliary_roots_have_independent_1000_1001_boundaries() {
5909        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5910        let at_limit = repeated_infix_expression(LIMIT, " + ");
5911        let over_limit = repeated_infix_expression(LIMIT + 1, " + ");
5912        for sql in [
5913            format!("sum(1) FILTER (WHERE {at_limit})"),
5914            format!("row_number() OVER (ORDER BY {at_limit})"),
5915            format!("group_concat(1 ORDER BY {at_limit})"),
5916        ] {
5917            parse_expr(&sql).expect("height-1000 auxiliary root must remain independently valid");
5918        }
5919        for sql in [
5920            format!("sum(1) FILTER (WHERE {over_limit})"),
5921            format!("row_number() OVER (ORDER BY {over_limit})"),
5922            format!("group_concat(1 ORDER BY {over_limit})"),
5923        ] {
5924            assert_expression_depth_error(&sql);
5925        }
5926    }
5927
5928    #[test]
5929    fn test_select_auxiliary_roots_have_independent_1000_1001_boundaries() {
5930        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5931        let at_limit = repeated_infix_expression(LIMIT, " + ");
5932        let over_limit = repeated_infix_expression(LIMIT + 1, " + ");
5933        for sql in [
5934            format!("(WITH c AS (SELECT {at_limit}) SELECT 0)"),
5935            format!("(SELECT 0 FROM (SELECT {at_limit}))"),
5936            format!("(SELECT 0 FROM json_each({at_limit}))"),
5937            format!("(SELECT 0 FROM a JOIN b ON {at_limit})"),
5938            format!("(SELECT 0 WINDOW w AS (ORDER BY {at_limit}))"),
5939        ] {
5940            parse_expr(&sql).expect("height-1000 independent SELECT root must remain valid");
5941        }
5942        for sql in [
5943            format!("(WITH c AS (SELECT {over_limit}) SELECT 0)"),
5944            format!("(SELECT 0 FROM (SELECT {over_limit}))"),
5945            format!("(SELECT 0 FROM json_each({over_limit}))"),
5946            format!("(SELECT 0 FROM a JOIN b ON {over_limit})"),
5947            format!("(SELECT 0 WINDOW w AS (ORDER BY {over_limit}))"),
5948        ] {
5949            assert_expression_depth_error(&sql);
5950        }
5951    }
5952
5953    #[test]
5954    fn test_nested_subquery_height_is_threaded_without_ast_rescans() {
5955        let sql = scalar_subquery_expression(63);
5956        HEIGHT_WALK_VISITS.set(0);
5957        parse_expr(&sql).expect("tracked nested scalar-subquery height must parse");
5958        assert_eq!(
5959            HEIGHT_WALK_VISITS.get(),
5960            0,
5961            "nested subqueries must return tracked SELECT height in O(1) per parent"
5962        );
5963    }
5964
5965    #[test]
5966    fn test_subquery_height_contract_exact_1000_1001_boundary() {
5967        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
5968        let inner_at_limit = repeated_infix_expression(LIMIT - 1, " + ");
5969        for sql in [
5970            format!("(SELECT {inner_at_limit})"),
5971            format!("EXISTS (SELECT {inner_at_limit})"),
5972            format!("1 IN (SELECT {inner_at_limit})"),
5973        ] {
5974            parse_expr(&sql).expect("height-999 SELECT under one expression node must be accepted");
5975        }
5976
5977        let inner_over_limit = repeated_infix_expression(LIMIT, " + ");
5978        for sql in [
5979            format!("(SELECT {inner_over_limit})"),
5980            format!("EXISTS (SELECT {inner_over_limit})"),
5981            format!("1 IN (SELECT {inner_over_limit})"),
5982        ] {
5983            assert_expression_depth_error(&sql);
5984        }
5985    }
5986
5987    // ── Precedence tests (normative invariants) ─────────────────────────
5988
5989    #[test]
5990    fn test_not_lower_precedence_than_comparison() {
5991        // NOT x = y → NOT (x = y)
5992        let expr = parse("NOT x = y");
5993        match &expr {
5994            Expr::UnaryOp {
5995                op: UnaryOp::Not,
5996                expr: inner,
5997                ..
5998            } => match inner.as_ref() {
5999                Expr::BinaryOp {
6000                    op: BinaryOp::Eq, ..
6001                } => {}
6002                other => unreachable!("expected Eq inside NOT, got {other:?}"),
6003            },
6004            other => unreachable!("expected NOT(Eq), got {other:?}"),
6005        }
6006    }
6007
6008    #[test]
6009    fn test_unary_binds_tighter_than_collate() {
6010        // -x COLLATE NOCASE → (-x) COLLATE NOCASE
6011        let expr = parse("-x COLLATE NOCASE");
6012        match &expr {
6013            Expr::Collate {
6014                expr: inner,
6015                collation,
6016                ..
6017            } => {
6018                assert_eq!(collation, "NOCASE");
6019                assert!(matches!(
6020                    inner.as_ref(),
6021                    Expr::UnaryOp {
6022                        op: UnaryOp::Negate,
6023                        ..
6024                    }
6025                ));
6026            }
6027            other => unreachable!("expected COLLATE(Negate), got {other:?}"),
6028        }
6029    }
6030
6031    #[test]
6032    fn test_arithmetic_precedence() {
6033        // 1 + 2 * 3 → 1 + (2 * 3)
6034        let expr = parse("1 + 2 * 3");
6035        match &expr {
6036            Expr::BinaryOp {
6037                op: BinaryOp::Add,
6038                left,
6039                right,
6040                ..
6041            } => {
6042                assert!(matches!(
6043                    left.as_ref(),
6044                    Expr::Literal(Literal::Integer(1), _)
6045                ));
6046                assert!(matches!(
6047                    right.as_ref(),
6048                    Expr::BinaryOp {
6049                        op: BinaryOp::Multiply,
6050                        ..
6051                    }
6052                ));
6053            }
6054            other => unreachable!("expected Add(1, Mul(2,3)), got {other:?}"),
6055        }
6056    }
6057
6058    #[test]
6059    fn test_and_higher_than_or() {
6060        // a OR b AND c → a OR (b AND c)
6061        let expr = parse("a OR b AND c");
6062        match &expr {
6063            Expr::BinaryOp {
6064                op: BinaryOp::Or,
6065                right,
6066                ..
6067            } => {
6068                assert!(matches!(
6069                    right.as_ref(),
6070                    Expr::BinaryOp {
6071                        op: BinaryOp::And,
6072                        ..
6073                    }
6074                ));
6075            }
6076            other => unreachable!("expected Or(a, And(b,c)), got {other:?}"),
6077        }
6078    }
6079
6080    // ── CAST ────────────────────────────────────────────────────────────
6081
6082    #[test]
6083    fn test_cast_expression() {
6084        let expr = parse("CAST(42 AS INTEGER)");
6085        match &expr {
6086            Expr::Cast {
6087                expr: inner,
6088                type_name,
6089                ..
6090            } => {
6091                assert!(matches!(
6092                    inner.as_ref(),
6093                    Expr::Literal(Literal::Integer(42), _)
6094                ));
6095                assert_eq!(type_name.name, "INTEGER");
6096            }
6097            other => unreachable!("expected Cast, got {other:?}"),
6098        }
6099    }
6100
6101    #[test]
6102    fn test_cast_float_argument() {
6103        // CAST(x AS DECIMAL(10.5, -2.5))
6104        let expr = parse("CAST(x AS DECIMAL(10.5, -2.5))");
6105        match &expr {
6106            Expr::Cast { type_name, .. } => {
6107                assert_eq!(type_name.name, "DECIMAL");
6108                assert_eq!(type_name.arg1.as_deref(), Some("10.5"));
6109                assert_eq!(type_name.arg2.as_deref(), Some("-2.5"));
6110            }
6111            other => unreachable!("expected Cast with float args, got {other:?}"),
6112        }
6113    }
6114
6115    #[test]
6116    fn test_cast_signed_args() {
6117        // CAST(x AS NUMERIC(+5, -5))
6118        let expr = parse("CAST(x AS NUMERIC(+5, -5))");
6119        match &expr {
6120            Expr::Cast { type_name, .. } => {
6121                assert_eq!(type_name.name, "NUMERIC");
6122                assert_eq!(type_name.arg1.as_deref(), Some("+5"));
6123                assert_eq!(type_name.arg2.as_deref(), Some("-5"));
6124            }
6125            other => unreachable!("expected Cast with signed args, got {other:?}"),
6126        }
6127    }
6128
6129    // ── CASE ────────────────────────────────────────────────────────────
6130
6131    #[test]
6132    fn test_case_when_simple() {
6133        let expr = parse(
6134            "CASE x WHEN 1 THEN 'one' WHEN 2 THEN 'two' \
6135             ELSE 'other' END",
6136        );
6137        match &expr {
6138            Expr::Case {
6139                operand: Some(op),
6140                whens,
6141                else_expr: Some(_),
6142                ..
6143            } => {
6144                assert!(matches!(op.as_ref(), Expr::Column(..)));
6145                assert_eq!(whens.len(), 2);
6146            }
6147            other => unreachable!("expected simple CASE, got {other:?}"),
6148        }
6149    }
6150
6151    #[test]
6152    fn test_case_when_searched() {
6153        let expr = parse(
6154            "CASE WHEN x > 0 THEN 'pos' WHEN x < 0 THEN 'neg' \
6155             ELSE 'zero' END",
6156        );
6157        match &expr {
6158            Expr::Case {
6159                operand: None,
6160                whens,
6161                else_expr: Some(_),
6162                ..
6163            } => {
6164                assert_eq!(whens.len(), 2);
6165                assert!(matches!(
6166                    &whens[0].0,
6167                    Expr::BinaryOp {
6168                        op: BinaryOp::Gt,
6169                        ..
6170                    }
6171                ));
6172            }
6173            other => unreachable!("expected searched CASE, got {other:?}"),
6174        }
6175    }
6176
6177    // ── EXISTS ──────────────────────────────────────────────────────────
6178
6179    #[test]
6180    fn test_exists_subquery() {
6181        let expr = parse("EXISTS (SELECT 1)");
6182        assert!(matches!(expr, Expr::Exists { not: false, .. }));
6183    }
6184
6185    #[test]
6186    fn test_not_exists_subquery() {
6187        let expr = parse("NOT EXISTS (SELECT 1)");
6188        assert!(matches!(expr, Expr::Exists { not: true, .. }));
6189    }
6190
6191    #[test]
6192    fn test_exists_subquery_supports_qualified_table_with_alias() {
6193        let expr = parse("EXISTS (SELECT 1 FROM main.users AS u WHERE u.id = 1)");
6194        match expr {
6195            Expr::Exists { subquery, .. } => match subquery.body.select {
6196                SelectCore::Select {
6197                    from: Some(from), ..
6198                } => match from.source {
6199                    TableOrSubquery::Table { name, alias, .. } => {
6200                        assert_eq!(name.schema.as_deref(), Some("main"));
6201                        assert_eq!(name.name, "users");
6202                        assert_eq!(alias.as_deref(), Some("u"));
6203                    }
6204                    other => unreachable!("expected table source, got {other:?}"),
6205                },
6206                other => unreachable!("expected SELECT core with FROM, got {other:?}"),
6207            },
6208            other => unreachable!("expected EXISTS subquery, got {other:?}"),
6209        }
6210    }
6211
6212    // ── IN ──────────────────────────────────────────────────────────────
6213
6214    #[test]
6215    fn test_in_expr_list() {
6216        let expr = parse("x IN (1, 2, 3)");
6217        match &expr {
6218            Expr::In {
6219                not: false,
6220                set: InSet::List(items),
6221                ..
6222            } => assert_eq!(items.len(), 3),
6223            other => unreachable!("expected IN list, got {other:?}"),
6224        }
6225    }
6226
6227    #[test]
6228    fn test_explicit_row_value_in_list_rejects_mismatched_element_arities() {
6229        for (sql, expected_message) in [
6230            (
6231                "(a, b, c) IN ((1, 2))",
6232                "IN(...) element has 2 terms - expected 3",
6233            ),
6234            (
6235                "(a, b) IN ((1, 2, 3))",
6236                "IN(...) element has 3 terms - expected 2",
6237            ),
6238            ("(a, b) IN (1)", "IN(...) element has 1 term - expected 2"),
6239            (
6240                "(a, b) IN ((1, 2), 3)",
6241                "IN(...) element has 1 term - expected 2",
6242            ),
6243            (
6244                "(a, b) NOT IN ((1, 2), (3, 4, 5))",
6245                "IN(...) element has 3 terms - expected 2",
6246            ),
6247            (
6248                "0 AND (a, b) IN (1)",
6249                "IN(...) element has 1 term - expected 2",
6250            ),
6251            (
6252                "(a, b) IN (+(SELECT 1, 2))",
6253                "IN(...) element has 1 term - expected 2",
6254            ),
6255        ] {
6256            let error = parse_expr(sql).expect_err("mismatched vector IN arity must fail parsing");
6257            assert_eq!(
6258                error.kind,
6259                ParseErrorKind::Syntax,
6260                "unexpected kind for `{sql}`"
6261            );
6262            assert_eq!(
6263                error.message, expected_message,
6264                "unexpected error for `{sql}`"
6265            );
6266        }
6267    }
6268
6269    #[test]
6270    fn test_subquery_lhs_defers_in_list_arity_to_semantic_resolution() {
6271        for sql in [
6272            "(SELECT 1, 2) IN (1)",
6273            "(SELECT 1, 2) IN ((1, 2, 3))",
6274            "(VALUES (1, 2, 3)) IN ((1, 2))",
6275            "(SELECT 1, 2 UNION ALL SELECT 3, 4) NOT IN (5)",
6276            "0 AND (SELECT 1, 2) IN (1)",
6277            "(SELECT 1, 2) IN (nosuch_vector_function())",
6278        ] {
6279            let expr = parse_expr(sql).unwrap_or_else(|error| {
6280                panic!("subquery-expression IN semantics must be deferred for `{sql}`: {error}")
6281            });
6282            assert!(
6283                matches!(expr, Expr::In { .. } | Expr::BinaryOp { .. }),
6284                "unexpected AST for `{sql}`"
6285            );
6286        }
6287    }
6288
6289    #[test]
6290    fn test_vector_in_list_accepts_matching_empty_and_singleton_subquery_forms() {
6291        for sql in [
6292            "(a, b) IN ((1, 2), (3, 4))",
6293            "(a, b) NOT IN ((1, 2), (3, 4))",
6294            "(a, b) IN ()",
6295            "(a, b) IN ((SELECT 1, 2))",
6296            "(a, b) IN ((SELECT 1))",
6297            "(a, b) IN ((SELECT 1, 2, 3))",
6298            // Multi-element subquery lists defer their arity to the semantic
6299            // resolver (subquery widths need name resolution), so the parser
6300            // accepts them; `(a, b) IN ((SELECT 1), (SELECT 2))` then fails as a
6301            // FunctionError, not a parse error.
6302            "(a, b) IN ((SELECT 1), (SELECT 2))",
6303            "(SELECT 1, 2) IN ((1, 2), (3, 4))",
6304            "(VALUES (1, 2), (3, 4)) NOT IN ((1, 2))",
6305            "(SELECT 1, 2 UNION ALL SELECT 3, 4) IN ((1, 2))",
6306            // Star widths require schema lookup and must not be guessed here.
6307            "(SELECT * FROM t) IN (1)",
6308            "(SELECT t.* FROM t) IN ((1, 2, 3))",
6309            // Preserve subquery column-count diagnostics for both RHS forms.
6310            "(SELECT 1, 2) IN ((SELECT 1))",
6311            "(SELECT 1, 2) IN (SELECT 1)",
6312        ] {
6313            let expr = parse_expr(sql).unwrap_or_else(|error| {
6314                panic!("matching vector IN list must parse for `{sql}`: {error}")
6315            });
6316            assert!(
6317                matches!(expr, Expr::In { .. }),
6318                "unexpected AST for `{sql}`"
6319            );
6320        }
6321    }
6322
6323    #[test]
6324    fn test_vector_in_list_trailing_comma_syntax_error_precedes_arity() {
6325        let error = parse_expr("(a, b) IN (1,)")
6326            .expect_err("a trailing comma in an IN list must fail parsing");
6327        assert_eq!(error.kind, ParseErrorKind::Syntax);
6328        assert_eq!(error.message, "unexpected token in expression: RightParen");
6329    }
6330
6331    #[test]
6332    fn test_statement_parsers_reject_vector_in_list_arity_mismatches() {
6333        for (sql, expected_message) in [
6334            (
6335                "SELECT (a, b) IN (1) FROM t",
6336                "IN(...) element has 1 term - expected 2",
6337            ),
6338            (
6339                "UPDATE t SET flag = (a, b) IN ((1, 2, 3))",
6340                "IN(...) element has 3 terms - expected 2",
6341            ),
6342            (
6343                "DELETE FROM t WHERE (a, b) NOT IN ((1, 2), 3)",
6344                "IN(...) element has 1 term - expected 2",
6345            ),
6346        ] {
6347            let error = Parser::from_sql(sql)
6348                .parse_statement()
6349                .expect_err("statement parser must reject mismatched vector IN arity");
6350            assert_eq!(
6351                error.kind,
6352                ParseErrorKind::Syntax,
6353                "unexpected kind for `{sql}`"
6354            );
6355            assert_eq!(
6356                error.message, expected_message,
6357                "unexpected error for `{sql}`"
6358            );
6359        }
6360    }
6361
6362    #[test]
6363    fn test_statement_parsers_defer_subquery_in_list_arity() {
6364        for sql in [
6365            "SELECT (SELECT 1, 2) IN (1)",
6366            "UPDATE t SET flag = (SELECT 1, 2) IN ((1, 2, 3))",
6367            "DELETE FROM t WHERE 0 AND (SELECT 1, 2) NOT IN ((1, 2), 3)",
6368        ] {
6369            Parser::from_sql(sql)
6370                .parse_statement()
6371                .unwrap_or_else(|error| {
6372                    panic!("statement semantics must be deferred for `{sql}`: {error}")
6373                });
6374        }
6375    }
6376
6377    #[test]
6378    fn test_in_subquery() {
6379        let expr = parse("x IN (SELECT y FROM t)");
6380        assert!(matches!(
6381            expr,
6382            Expr::In {
6383                not: false,
6384                set: InSet::Subquery(_),
6385                ..
6386            }
6387        ));
6388    }
6389
6390    #[test]
6391    fn test_in_subquery_with_order_by_and_limit() {
6392        // This is the pattern used in mcp-agent-mail-db prune queries
6393        let expr =
6394            parse("id NOT IN (SELECT id FROM search_recipes ORDER BY updated_ts DESC LIMIT 5)");
6395        match &expr {
6396            Expr::In {
6397                not: true,
6398                set: InSet::Subquery(stmt),
6399                ..
6400            } => {
6401                assert_eq!(stmt.order_by.len(), 1, "ORDER BY should be parsed");
6402                assert!(stmt.limit.is_some(), "LIMIT should be parsed");
6403            }
6404            other => unreachable!("expected NOT IN subquery, got {other:?}"),
6405        }
6406    }
6407
6408    #[test]
6409    fn test_in_subquery_supports_group_by_and_having() {
6410        let expr = parse("x IN (SELECT y FROM t GROUP BY y HAVING COUNT(*) > 1)");
6411        match expr {
6412            Expr::In {
6413                set: InSet::Subquery(stmt),
6414                ..
6415            } => match stmt.body.select {
6416                SelectCore::Select {
6417                    group_by, having, ..
6418                } => {
6419                    assert_eq!(group_by.len(), 1, "GROUP BY should be parsed");
6420                    assert!(having.is_some(), "HAVING should be parsed");
6421                }
6422                SelectCore::Values(_) => unreachable!("expected SELECT core"),
6423            },
6424            other => unreachable!("expected IN subquery, got {other:?}"),
6425        }
6426    }
6427
6428    #[test]
6429    fn test_not_in() {
6430        let expr = parse("x NOT IN (1, 2)");
6431        assert!(matches!(expr, Expr::In { not: true, .. }));
6432    }
6433
6434    #[test]
6435    fn test_in_table_name() {
6436        let expr = parse("x IN t");
6437        assert!(matches!(
6438            expr,
6439            Expr::In {
6440                not: false,
6441                set: InSet::Table(_),
6442                ..
6443            }
6444        ));
6445    }
6446
6447    #[test]
6448    fn test_not_in_table_name() {
6449        let expr = parse("x NOT IN t");
6450        assert!(matches!(
6451            expr,
6452            Expr::In {
6453                not: true,
6454                set: InSet::Table(_),
6455                ..
6456            }
6457        ));
6458    }
6459
6460    #[test]
6461    fn test_in_schema_table_name() {
6462        let expr = parse("x IN main.t");
6463        match expr {
6464            Expr::In {
6465                set: InSet::Table(name),
6466                ..
6467            } => {
6468                assert_eq!(name.schema.as_deref(), Some("main"));
6469                assert_eq!(name.name, "t");
6470            }
6471            other => unreachable!("expected IN table form, got {other:?}"),
6472        }
6473    }
6474
6475    // ── BETWEEN ─────────────────────────────────────────────────────────
6476
6477    #[test]
6478    fn test_between_and() {
6479        let expr = parse("x BETWEEN 1 AND 10");
6480        assert!(matches!(expr, Expr::Between { not: false, .. }));
6481    }
6482
6483    #[test]
6484    fn test_not_between() {
6485        let expr = parse("x NOT BETWEEN 1 AND 10");
6486        assert!(matches!(expr, Expr::Between { not: true, .. }));
6487    }
6488
6489    #[test]
6490    fn test_between_does_not_consume_outer_and() {
6491        // x BETWEEN 1 AND 10 AND y = 1 → (BETWEEN) AND (y = 1)
6492        let expr = parse("x BETWEEN 1 AND 10 AND y = 1");
6493        match &expr {
6494            Expr::BinaryOp {
6495                op: BinaryOp::And,
6496                left,
6497                ..
6498            } => assert!(matches!(left.as_ref(), Expr::Between { .. })),
6499            other => unreachable!("expected AND(BETWEEN, Eq), got {other:?}"),
6500        }
6501    }
6502
6503    // ── LIKE / GLOB ─────────────────────────────────────────────────────
6504
6505    #[test]
6506    fn test_like_pattern() {
6507        let expr = parse("name LIKE '%foo%'");
6508        assert!(matches!(
6509            expr,
6510            Expr::Like {
6511                op: LikeOp::Like,
6512                not: false,
6513                escape: None,
6514                ..
6515            }
6516        ));
6517    }
6518
6519    #[test]
6520    fn test_like_escape() {
6521        let expr = parse("name LIKE '%\\%%' ESCAPE '\\'");
6522        assert!(matches!(
6523            expr,
6524            Expr::Like {
6525                op: LikeOp::Like,
6526                escape: Some(_),
6527                ..
6528            }
6529        ));
6530    }
6531
6532    #[test]
6533    fn test_glob_pattern() {
6534        let expr = parse("path GLOB '*.rs'");
6535        assert!(matches!(
6536            expr,
6537            Expr::Like {
6538                op: LikeOp::Glob,
6539                not: false,
6540                ..
6541            }
6542        ));
6543    }
6544
6545    #[test]
6546    fn test_glob_character_class() {
6547        let expr = parse("name GLOB '[a-z]*'");
6548        match &expr {
6549            Expr::Like {
6550                op: LikeOp::Glob,
6551                pattern,
6552                ..
6553            } => assert!(matches!(
6554                pattern.as_ref(),
6555                Expr::Literal(Literal::String(s), _) if s == "[a-z]*"
6556            )),
6557            other => unreachable!("expected GLOB, got {other:?}"),
6558        }
6559    }
6560
6561    // ── COLLATE ─────────────────────────────────────────────────────────
6562
6563    #[test]
6564    fn test_collate_override() {
6565        let expr = parse("name COLLATE NOCASE");
6566        match &expr {
6567            Expr::Collate { collation, .. } => {
6568                assert_eq!(collation, "NOCASE");
6569            }
6570            other => unreachable!("expected COLLATE, got {other:?}"),
6571        }
6572    }
6573
6574    // ── JSON operators ──────────────────────────────────────────────────
6575
6576    #[test]
6577    fn test_json_arrow_operator() {
6578        let expr = parse("data -> 'key'");
6579        assert!(matches!(
6580            expr,
6581            Expr::JsonAccess {
6582                arrow: JsonArrow::Arrow,
6583                ..
6584            }
6585        ));
6586    }
6587
6588    #[test]
6589    fn test_json_double_arrow_operator() {
6590        let expr = parse("data ->> 'key'");
6591        assert!(matches!(
6592            expr,
6593            Expr::JsonAccess {
6594                arrow: JsonArrow::DoubleArrow,
6595                ..
6596            }
6597        ));
6598    }
6599
6600    // ── IS NULL / IS NOT ─────────────────────────────────────────────────────
6601
6602    #[test]
6603    fn test_is_null() {
6604        assert!(matches!(
6605            parse("42"),
6606            Expr::Literal(Literal::Integer(42), _)
6607        ));
6608        assert!(matches!(parse("3.14"), Expr::Literal(Literal::Float(_), _)));
6609        assert!(matches!(
6610            parse("'hello'"),
6611            Expr::Literal(Literal::String(_), _)
6612        ));
6613        assert!(matches!(parse("NULL"), Expr::Literal(Literal::Null, _)));
6614        assert!(matches!(parse("TRUE"), Expr::Literal(Literal::True, _)));
6615        assert!(matches!(parse("FALSE"), Expr::Literal(Literal::False, _)));
6616    }
6617
6618    // ── Issue #122: postfix null-test vs `=` precedence and round-trip ──
6619
6620    /// `a IS NULL = b IS NULL` (no parentheses) groups left-associatively:
6621    /// `((a IS NULL) = b) IS NULL`. Verified against the C SQLite CLI:
6622    /// `SELECT 200 IS NULL = 'ok' IS NULL` yields 0 (not 1), because the
6623    /// null-test and `=` share one left-associative precedence level.
6624    #[test]
6625    fn test_isnull_eq_isnull_unparenthesized_left_associative() {
6626        let expr = parse("a IS NULL = b IS NULL");
6627        match &expr {
6628            Expr::IsNull {
6629                expr: inner,
6630                not: false,
6631                ..
6632            } => match inner.as_ref() {
6633                Expr::BinaryOp {
6634                    op: BinaryOp::Eq,
6635                    left,
6636                    right,
6637                    ..
6638                } => {
6639                    assert!(
6640                        matches!(left.as_ref(), Expr::IsNull { not: false, .. }),
6641                        "expected (a IS NULL) on the left, got {left:?}"
6642                    );
6643                    assert!(
6644                        matches!(right.as_ref(), Expr::Column(..)),
6645                        "expected bare column b on the right, got {right:?}"
6646                    );
6647                }
6648                other => unreachable!("expected Eq inside IsNull, got {other:?}"),
6649            },
6650            other => unreachable!("expected IsNull(Eq(IsNull(a), b)), got {other:?}"),
6651        }
6652    }
6653
6654    /// `(a IS NULL) = (b IS NULL)` must parse as Eq of two null-tests, and
6655    /// the display round-trip must preserve that grouping (issue #122: the
6656    /// serializer used to strip these parentheses, silently inverting CHECK
6657    /// constraints of the form `(a IS NULL) = (b IS NULL)`).
6658    #[test]
6659    fn test_isnull_eq_isnull_parenthesized_round_trip() {
6660        let assert_shape = |expr: &Expr| match expr {
6661            Expr::BinaryOp {
6662                op: BinaryOp::Eq,
6663                left,
6664                right,
6665                ..
6666            } => {
6667                assert!(
6668                    matches!(left.as_ref(), Expr::IsNull { not: false, .. }),
6669                    "expected IsNull on the left, got {left:?}"
6670                );
6671                assert!(
6672                    matches!(right.as_ref(), Expr::IsNull { not: false, .. }),
6673                    "expected IsNull on the right, got {right:?}"
6674                );
6675            }
6676            other => unreachable!("expected Eq(IsNull, IsNull), got {other:?}"),
6677        };
6678        let expr = parse("(a IS NULL) = (b IS NULL)");
6679        assert_shape(&expr);
6680        let rendered = expr.to_string();
6681        assert_eq!(rendered, "a IS NULL = (b IS NULL)");
6682        let reparsed = parse(&rendered);
6683        assert_shape(&reparsed);
6684        assert_eq!(reparsed.to_string(), rendered, "round-trip not idempotent");
6685    }
6686
6687    /// An operator binding tighter than IS attaches to the NULL literal, so
6688    /// no null-test fold happens: `1 IS NULL < 2` is `1 IS (NULL < 2)`.
6689    /// Verified against the C SQLite CLI: `SELECT 1 IS NULL < 2` yields 0
6690    /// (`1 IS NULL` would give 0, then `0 < 2` would give 1).
6691    #[test]
6692    fn test_is_null_followed_by_tighter_operator_binds_to_null() {
6693        let expr = parse("1 IS NULL < 2");
6694        match &expr {
6695            Expr::BinaryOp {
6696                op: BinaryOp::Is,
6697                right,
6698                ..
6699            } => assert!(
6700                matches!(
6701                    right.as_ref(),
6702                    Expr::BinaryOp {
6703                        op: BinaryOp::Lt,
6704                        ..
6705                    }
6706                ),
6707                "expected Lt(NULL, 2) on the right of IS, got {right:?}"
6708            ),
6709            other => unreachable!("expected Is(1, Lt(NULL, 2)), got {other:?}"),
6710        }
6711    }
6712
6713    /// `x IS (NULL)` folds to a null-test just like `x IS NULL`, matching
6714    /// SQLite's binaryToUnaryIfNull (the fold keys on the resolved RHS
6715    /// expression, not on the raw token).
6716    #[test]
6717    fn test_is_parenthesized_null_folds_to_isnull() {
6718        assert!(matches!(
6719            parse("x IS (NULL)"),
6720            Expr::IsNull { not: false, .. }
6721        ));
6722        assert!(matches!(
6723            parse("x IS NOT (NULL)"),
6724            Expr::IsNull { not: true, .. }
6725        ));
6726    }
6727
6728    #[test]
6729    fn test_placeholders() {
6730        assert!(matches!(
6731            parse("?"),
6732            Expr::Placeholder(PlaceholderType::Anonymous, _)
6733        ));
6734        assert!(matches!(
6735            parse("?1"),
6736            Expr::Placeholder(PlaceholderType::Numbered(1), _)
6737        ));
6738        assert!(matches!(
6739            parse(":name"),
6740            Expr::Placeholder(PlaceholderType::ColonNamed(_), _)
6741        ));
6742    }
6743
6744    // ── Column references ───────────────────────────────────────────────
6745
6746    #[test]
6747    fn test_column_bare() {
6748        match &parse("x") {
6749            Expr::Column(
6750                ColumnRef {
6751                    table: None,
6752                    column,
6753                },
6754                _,
6755            ) => assert_eq!(column.as_ref(), "x"),
6756            other => unreachable!("expected bare column, got {other:?}"),
6757        }
6758    }
6759
6760    #[test]
6761    fn test_column_qualified() {
6762        match &parse("t.x") {
6763            Expr::Column(
6764                ColumnRef {
6765                    table: Some(t),
6766                    column,
6767                },
6768                _,
6769            ) => {
6770                assert_eq!(t.as_ref(), "t");
6771                assert_eq!(column.as_ref(), "x");
6772            }
6773            other => unreachable!("expected qualified column, got {other:?}"),
6774        }
6775    }
6776
6777    #[test]
6778    fn test_qualified_column_retains_dot_height_and_exact_boundary() {
6779        assert_eq!(parsed_expr_height("x"), 1);
6780        assert_eq!(parsed_expr_height("t.x"), 2);
6781
6782        const LIMIT: usize = MAX_PARSE_DEPTH as usize;
6783        let at_limit = format!("{}t.x", "~".repeat(LIMIT - 2));
6784        parse_expr(&at_limit).expect("998 unary nodes plus qualified column have height 1000");
6785
6786        let over_limit = format!("{}t.x", "~".repeat(LIMIT - 1));
6787        assert_expression_depth_error(&over_limit);
6788    }
6789
6790    // ── Concat / precedence ─────────────────────────────────────────────
6791
6792    #[test]
6793    fn test_concat_higher_than_add() {
6794        // a + b || c → a + (b || c) since || binds tighter
6795        let expr = parse("a + b || c");
6796        match &expr {
6797            Expr::BinaryOp {
6798                op: BinaryOp::Add,
6799                right,
6800                ..
6801            } => assert!(matches!(
6802                right.as_ref(),
6803                Expr::BinaryOp {
6804                    op: BinaryOp::Concat,
6805                    ..
6806                }
6807            )),
6808            other => unreachable!("expected Add(a, Concat(b,c)), got {other:?}"),
6809        }
6810    }
6811
6812    // ── Parenthesized ───────────────────────────────────────────────────
6813
6814    #[test]
6815    fn test_parenthesized() {
6816        // (1 + 2) * 3 → Mul(Add(1,2), 3)
6817        let expr = parse("(1 + 2) * 3");
6818        match &expr {
6819            Expr::BinaryOp {
6820                op: BinaryOp::Multiply,
6821                left,
6822                ..
6823            } => assert!(matches!(
6824                left.as_ref(),
6825                Expr::BinaryOp {
6826                    op: BinaryOp::Add,
6827                    ..
6828                }
6829            )),
6830            other => unreachable!("expected Mul(Add, 3), got {other:?}"),
6831        }
6832    }
6833
6834    // ── IS / IS NOT ─────────────────────────────────────────────────────
6835
6836    #[test]
6837    fn test_is_operator() {
6838        assert!(matches!(
6839            parse("a IS b"),
6840            Expr::BinaryOp {
6841                op: BinaryOp::Is,
6842                ..
6843            }
6844        ));
6845    }
6846
6847    #[test]
6848    fn test_is_not_operator() {
6849        assert!(matches!(
6850            parse("a IS NOT b"),
6851            Expr::BinaryOp {
6852                op: BinaryOp::IsNot,
6853                ..
6854            }
6855        ));
6856    }
6857
6858    // ── Bitwise ─────────────────────────────────────────────────────────
6859
6860    #[test]
6861    fn test_bitwise_ops() {
6862        // & and | share the same precedence (left-associative)
6863        let expr = parse("a & b | c");
6864        match &expr {
6865            Expr::BinaryOp {
6866                op: BinaryOp::BitOr,
6867                left,
6868                ..
6869            } => assert!(
6870                matches!(
6871                    left.as_ref(),
6872                    Expr::BinaryOp {
6873                        op: BinaryOp::BitAnd,
6874                        ..
6875                    }
6876                ),
6877                "bitwise operators should be left-associative"
6878            ),
6879            other => unreachable!("expected BitOr(BitAnd, c), got {other:?}"),
6880        }
6881    }
6882
6883    #[test]
6884    fn test_bitnot() {
6885        assert!(matches!(
6886            parse("~x"),
6887            Expr::UnaryOp {
6888                op: UnaryOp::BitNot,
6889                ..
6890            }
6891        ));
6892    }
6893
6894    // ── Complex expressions ─────────────────────────────────────────────
6895
6896    #[test]
6897    fn test_complex_where_clause() {
6898        let expr = parse("a > 1 AND b LIKE '%test%' OR NOT c IS NULL");
6899        assert!(matches!(
6900            expr,
6901            Expr::BinaryOp {
6902                op: BinaryOp::Or,
6903                ..
6904            }
6905        ));
6906    }
6907
6908    #[test]
6909    fn test_not_like_pattern() {
6910        assert!(matches!(
6911            parse("name NOT LIKE '%foo'"),
6912            Expr::Like {
6913                op: LikeOp::Like,
6914                not: true,
6915                ..
6916            }
6917        ));
6918    }
6919
6920    #[test]
6921    fn test_subquery_expr() {
6922        assert!(matches!(parse("(SELECT 1)"), Expr::Subquery(..)));
6923    }
6924
6925    // ── bd-kzat: §10.2 Pratt Precedence Validation ─────────────────────
6926    //
6927    // Systematic tests for ALL 11 operator precedence levels.
6928    // Each level gets a dedicated associativity test and a boundary test
6929    // against the adjacent level.
6930
6931    // Level 1: OR — left-associative
6932    #[test]
6933    fn test_pratt_level1_or_left_assoc() {
6934        // a OR b OR c → (a OR b) OR c
6935        let expr = parse("a OR b OR c");
6936        match &expr {
6937            Expr::BinaryOp {
6938                op: BinaryOp::Or,
6939                left,
6940                ..
6941            } => assert!(
6942                matches!(
6943                    left.as_ref(),
6944                    Expr::BinaryOp {
6945                        op: BinaryOp::Or,
6946                        ..
6947                    }
6948                ),
6949                "OR should be left-associative"
6950            ),
6951            other => unreachable!("expected Or(Or(a,b), c), got {other:?}"),
6952        }
6953    }
6954
6955    // Level 2: AND — left-associative, tighter than OR
6956    #[test]
6957    fn test_pratt_level2_and_left_assoc() {
6958        // a AND b AND c → (a AND b) AND c
6959        let expr = parse("a AND b AND c");
6960        match &expr {
6961            Expr::BinaryOp {
6962                op: BinaryOp::And,
6963                left,
6964                ..
6965            } => assert!(
6966                matches!(
6967                    left.as_ref(),
6968                    Expr::BinaryOp {
6969                        op: BinaryOp::And,
6970                        ..
6971                    }
6972                ),
6973                "AND should be left-associative"
6974            ),
6975            other => unreachable!("expected And(And(a,b), c), got {other:?}"),
6976        }
6977    }
6978
6979    // Level 3: NOT — prefix, higher than AND, lower than equality
6980    #[test]
6981    fn test_pratt_level3_not_higher_than_and() {
6982        // NOT a AND b → (NOT a) AND b
6983        let expr = parse("NOT a AND b");
6984        match &expr {
6985            Expr::BinaryOp {
6986                op: BinaryOp::And,
6987                left,
6988                ..
6989            } => assert!(
6990                matches!(
6991                    left.as_ref(),
6992                    Expr::UnaryOp {
6993                        op: UnaryOp::Not,
6994                        ..
6995                    }
6996                ),
6997                "NOT should bind tighter than AND"
6998            ),
6999            other => unreachable!("expected And(Not(a), b), got {other:?}"),
7000        }
7001    }
7002
7003    // Level 4: Equality/membership — left-associative
7004    #[test]
7005    fn test_pratt_level4_equality_left_assoc() {
7006        // a = b != c → (a = b) != c
7007        let expr = parse("a = b != c");
7008        match &expr {
7009            Expr::BinaryOp {
7010                op: BinaryOp::Ne,
7011                left,
7012                ..
7013            } => assert!(
7014                matches!(
7015                    left.as_ref(),
7016                    Expr::BinaryOp {
7017                        op: BinaryOp::Eq,
7018                        ..
7019                    }
7020                ),
7021                "equality operators should be left-associative at same level"
7022            ),
7023            other => unreachable!("expected Ne(Eq(a,b), c), got {other:?}"),
7024        }
7025    }
7026
7027    // Level 4 vs Level 5: THE CRITICAL BOUNDARY
7028    // Equality (level 4) and relational (level 5) are SEPARATE levels
7029    // per canonical upstream SQLite grammar.
7030    #[test]
7031    fn test_pratt_level4_vs_level5_eq_lt_boundary() {
7032        // a = b < c MUST parse as a = (b < c), NOT (a = b) < c
7033        // This is the normative invariant from §10.2.
7034        let expr = parse("a = b < c");
7035        match &expr {
7036            Expr::BinaryOp {
7037                op: BinaryOp::Eq,
7038                right,
7039                ..
7040            } => assert!(
7041                matches!(
7042                    right.as_ref(),
7043                    Expr::BinaryOp {
7044                        op: BinaryOp::Lt,
7045                        ..
7046                    }
7047                ),
7048                "a = b < c MUST parse as a = (b < c): relational binds tighter"
7049            ),
7050            other => unreachable!("expected Eq(a, Lt(b,c)), got {other:?}"),
7051        }
7052    }
7053
7054    // Reverse direction of the same boundary
7055    #[test]
7056    fn test_pratt_level4_vs_level5_ne_ge_boundary() {
7057        // a != b >= c → a != (b >= c)
7058        let expr = parse("a != b >= c");
7059        match &expr {
7060            Expr::BinaryOp {
7061                op: BinaryOp::Ne,
7062                right,
7063                ..
7064            } => assert!(
7065                matches!(
7066                    right.as_ref(),
7067                    Expr::BinaryOp {
7068                        op: BinaryOp::Ge,
7069                        ..
7070                    }
7071                ),
7072                "a != b >= c must parse as a != (b >= c)"
7073            ),
7074            other => unreachable!("expected Ne(Ge(b,c)), got {other:?}"),
7075        }
7076    }
7077
7078    // Level 5: Relational — left-associative
7079    #[test]
7080    fn test_pratt_level5_relational_left_assoc() {
7081        // a < b >= c → (a < b) >= c
7082        let expr = parse("a < b >= c");
7083        match &expr {
7084            Expr::BinaryOp {
7085                op: BinaryOp::Ge,
7086                left,
7087                ..
7088            } => assert!(
7089                matches!(
7090                    left.as_ref(),
7091                    Expr::BinaryOp {
7092                        op: BinaryOp::Lt,
7093                        ..
7094                    }
7095                ),
7096                "relational operators should be left-associative"
7097            ),
7098            other => unreachable!("expected Ge(Lt(a,b), c), got {other:?}"),
7099        }
7100    }
7101
7102    // Level 6: Bitwise — tighter than relational
7103    #[test]
7104    fn test_pratt_level6_bitwise_tighter_than_comparison() {
7105        // a < b & c → a < (b & c)
7106        let expr = parse("a < b & c");
7107        match &expr {
7108            Expr::BinaryOp {
7109                op: BinaryOp::Lt,
7110                right,
7111                ..
7112            } => assert!(
7113                matches!(
7114                    right.as_ref(),
7115                    Expr::BinaryOp {
7116                        op: BinaryOp::BitAnd,
7117                        ..
7118                    }
7119                ),
7120                "bitwise should bind tighter than relational"
7121            ),
7122            other => unreachable!("expected Lt(a, BitAnd(b,c)), got {other:?}"),
7123        }
7124    }
7125
7126    // Level 6: Shift operators left-associative
7127    #[test]
7128    fn test_pratt_level6_shifts_left_assoc() {
7129        // a << b >> c → (a << b) >> c
7130        let expr = parse("a << b >> c");
7131        match &expr {
7132            Expr::BinaryOp {
7133                op: BinaryOp::ShiftRight,
7134                left,
7135                ..
7136            } => assert!(
7137                matches!(
7138                    left.as_ref(),
7139                    Expr::BinaryOp {
7140                        op: BinaryOp::ShiftLeft,
7141                        ..
7142                    }
7143                ),
7144                "shift operators should be left-associative"
7145            ),
7146            other => unreachable!("expected ShiftRight(ShiftLeft(a,b), c), got {other:?}"),
7147        }
7148    }
7149
7150    // Level 7: Addition/subtraction — left-associative, tighter than bitwise
7151    #[test]
7152    fn test_pratt_level7_add_sub_left_assoc() {
7153        // a + b - c → (a + b) - c
7154        let expr = parse("a + b - c");
7155        match &expr {
7156            Expr::BinaryOp {
7157                op: BinaryOp::Subtract,
7158                left,
7159                ..
7160            } => assert!(
7161                matches!(
7162                    left.as_ref(),
7163                    Expr::BinaryOp {
7164                        op: BinaryOp::Add,
7165                        ..
7166                    }
7167                ),
7168                "add/sub should be left-associative"
7169            ),
7170            other => unreachable!("expected Sub(Add(a,b), c), got {other:?}"),
7171        }
7172    }
7173
7174    #[test]
7175    fn test_pratt_level7_add_sub_left_assoc_reverse() {
7176        // a - b + c → (a - b) + c
7177        let expr = parse("a - b + c");
7178        match &expr {
7179            Expr::BinaryOp {
7180                op: BinaryOp::Add,
7181                left,
7182                ..
7183            } => assert!(
7184                matches!(
7185                    left.as_ref(),
7186                    Expr::BinaryOp {
7187                        op: BinaryOp::Subtract,
7188                        ..
7189                    }
7190                ),
7191                "add/sub should be left-associative"
7192            ),
7193            other => unreachable!("expected Add(Sub(a,b), c), got {other:?}"),
7194        }
7195    }
7196
7197    #[test]
7198    fn test_pratt_level9_concat_tighter_than_mul() {
7199        // a * b || c → a * (b || c)
7200        let expr = parse("a * b || c");
7201        match &expr {
7202            Expr::BinaryOp {
7203                op: BinaryOp::Multiply,
7204                right,
7205                ..
7206            } => assert!(
7207                matches!(
7208                    right.as_ref(),
7209                    Expr::BinaryOp {
7210                        op: BinaryOp::Concat,
7211                        ..
7212                    }
7213                ),
7214                "concat should bind tighter than multiply"
7215            ),
7216            other => unreachable!("expected Mul(a, Concat(b,c)), got {other:?}"),
7217        }
7218    }
7219
7220    // Level 8: Multiplication/division/modulo — left-associative
7221    #[test]
7222    fn test_pratt_level8_mul_div_left_assoc() {
7223        // a * b / c → (a * b) / c
7224        let expr = parse("a * b / c");
7225        match &expr {
7226            Expr::BinaryOp {
7227                op: BinaryOp::Divide,
7228                left,
7229                ..
7230            } => assert!(
7231                matches!(
7232                    left.as_ref(),
7233                    Expr::BinaryOp {
7234                        op: BinaryOp::Multiply,
7235                        ..
7236                    }
7237                ),
7238                "mul/div should be left-associative"
7239            ),
7240            other => unreachable!("expected Div(Mul(a,b), c), got {other:?}"),
7241        }
7242    }
7243
7244    #[test]
7245    fn test_pratt_level8_modulo() {
7246        // a * b % c → (a * b) % c
7247        let expr = parse("a * b % c");
7248        match &expr {
7249            Expr::BinaryOp {
7250                op: BinaryOp::Modulo,
7251                left,
7252                ..
7253            } => assert!(
7254                matches!(
7255                    left.as_ref(),
7256                    Expr::BinaryOp {
7257                        op: BinaryOp::Multiply,
7258                        ..
7259                    }
7260                ),
7261                "modulo and multiply at same level, left-associative"
7262            ),
7263            other => unreachable!("expected Mod(Mul(a,b), c), got {other:?}"),
7264        }
7265    }
7266
7267    // Level 9: Concatenation (||) — left-associative, tighter than mul
7268    #[test]
7269    fn test_pratt_level9_concat_left_assoc() {
7270        // a || b || c → (a || b) || c
7271        let expr = parse("a || b || c");
7272        match &expr {
7273            Expr::BinaryOp {
7274                op: BinaryOp::Concat,
7275                left,
7276                ..
7277            } => assert!(
7278                matches!(
7279                    left.as_ref(),
7280                    Expr::BinaryOp {
7281                        op: BinaryOp::Concat,
7282                        ..
7283                    }
7284                ),
7285                "concatenation should be left-associative"
7286            ),
7287            other => unreachable!("expected Concat(Concat(a,b), c), got {other:?}"),
7288        }
7289    }
7290
7291    #[test]
7292    fn test_pratt_level9_concat_left_assoc_reverse() {
7293        // a || b || c → (a || b) || c
7294        let expr = parse("a || b || c");
7295        match &expr {
7296            Expr::BinaryOp {
7297                op: BinaryOp::Concat,
7298                left,
7299                ..
7300            } => assert!(
7301                matches!(
7302                    left.as_ref(),
7303                    Expr::BinaryOp {
7304                        op: BinaryOp::Concat,
7305                        ..
7306                    }
7307                ),
7308                "concatenation should be left-associative"
7309            ),
7310            other => unreachable!("expected Concat(Concat(a,b), c), got {other:?}"),
7311        }
7312    }
7313
7314    // Level 10: COLLATE — postfix, tighter than concat
7315    #[test]
7316    fn test_pratt_level10_collate_tighter_than_concat() {
7317        // a || b COLLATE NOCASE → a || (b COLLATE NOCASE)
7318        let expr = parse("a || b COLLATE NOCASE");
7319        match &expr {
7320            Expr::BinaryOp {
7321                op: BinaryOp::Concat,
7322                right,
7323                ..
7324            } => assert!(
7325                matches!(right.as_ref(), Expr::Collate { .. }),
7326                "COLLATE should bind tighter than concat"
7327            ),
7328            other => unreachable!("expected Concat(a, Collate(b)), got {other:?}"),
7329        }
7330    }
7331
7332    // Level 11: Unary prefix (- + ~) — tightest of all
7333    #[test]
7334    fn test_pratt_level11_unary_negate_tightest() {
7335        // -a * b → (-a) * b
7336        let expr = parse("-a * b");
7337        match &expr {
7338            Expr::BinaryOp {
7339                op: BinaryOp::Multiply,
7340                left,
7341                ..
7342            } => assert!(
7343                matches!(
7344                    left.as_ref(),
7345                    Expr::UnaryOp {
7346                        op: UnaryOp::Negate,
7347                        ..
7348                    }
7349                ),
7350                "unary minus should bind tighter than multiply"
7351            ),
7352            other => unreachable!("expected Mul(Negate(a), b), got {other:?}"),
7353        }
7354    }
7355
7356    #[test]
7357    fn test_pratt_level11_bitnot_tightest() {
7358        // ~a + b → (~a) + b
7359        let expr = parse("~a + b");
7360        match &expr {
7361            Expr::BinaryOp {
7362                op: BinaryOp::Add,
7363                left,
7364                ..
7365            } => assert!(
7366                matches!(
7367                    left.as_ref(),
7368                    Expr::UnaryOp {
7369                        op: UnaryOp::BitNot,
7370                        ..
7371                    }
7372                ),
7373                "bitwise NOT should bind tighter than addition"
7374            ),
7375            other => unreachable!("expected Add(BitNot(a), b), got {other:?}"),
7376        }
7377    }
7378
7379    // ESCAPE is NOT a standalone infix operator — it's suffix of LIKE/GLOB
7380    #[test]
7381    fn test_pratt_escape_not_infix_operator() {
7382        // a LIKE b ESCAPE c → Like(a, b, escape=c)
7383        let expr = parse("a LIKE b ESCAPE c");
7384        match &expr {
7385            Expr::Like {
7386                escape: Some(esc), ..
7387            } => assert!(
7388                matches!(esc.as_ref(), Expr::Column(_, _)),
7389                "ESCAPE should be parsed as suffix of LIKE, not standalone infix"
7390            ),
7391            other => unreachable!("expected Like with escape, got {other:?}"),
7392        }
7393    }
7394
7395    #[test]
7396    fn test_pratt_escape_glob_not_infix() {
7397        // a GLOB b ESCAPE c → Like(a, b, op=Glob, escape=c)
7398        let expr = parse("a GLOB b ESCAPE c");
7399        match &expr {
7400            Expr::Like {
7401                op: LikeOp::Glob,
7402                escape: Some(_),
7403                ..
7404            } => {}
7405            other => unreachable!("expected Glob with escape, got {other:?}"),
7406        }
7407    }
7408
7409    // Error recovery: multiple errors collected in one pass
7410    #[test]
7411    fn test_pratt_error_recovery_multiple_errors() {
7412        use crate::parser::Parser;
7413        let mut p = Parser::from_sql("SELECT +; SELECT *; SELECT 1");
7414        let (stmts, errs) = p.parse_all();
7415        // SELECT + fails (missing operand), SELECT * fails (no FROM for bare *),
7416        // SELECT 1 should succeed.
7417        assert!(
7418            !stmts.is_empty(),
7419            "should recover and parse at least one valid statement"
7420        );
7421        assert!(
7422            !errs.is_empty(),
7423            "should collect at least one error from malformed statements"
7424        );
7425    }
7426
7427    // Complex mixed expression: full 11-level test
7428    #[test]
7429    fn test_pratt_complex_mixed_all_levels() {
7430        // NOT a = b + c * -d OR e < f AND g LIKE h
7431        // → (NOT (a = (b + (c * (-d))))) OR ((e < f) AND (g LIKE h))
7432        let expr = parse("NOT a = b + c * -d OR e < f AND g LIKE h");
7433        // Top level: OR
7434        match &expr {
7435            Expr::BinaryOp {
7436                op: BinaryOp::Or,
7437                left,
7438                right,
7439                ..
7440            } => {
7441                // left = NOT (a = (b + (c * (-d))))
7442                assert!(
7443                    matches!(
7444                        left.as_ref(),
7445                        Expr::UnaryOp {
7446                            op: UnaryOp::Not,
7447                            ..
7448                        }
7449                    ),
7450                    "left of OR should be NOT(...)"
7451                );
7452                // right = (e < f) AND (g LIKE h)
7453                match right.as_ref() {
7454                    Expr::BinaryOp {
7455                        op: BinaryOp::And,
7456                        left: and_left,
7457                        right: and_right,
7458                        ..
7459                    } => {
7460                        assert!(
7461                            matches!(
7462                                and_left.as_ref(),
7463                                Expr::BinaryOp {
7464                                    op: BinaryOp::Lt,
7465                                    ..
7466                                }
7467                            ),
7468                            "left of AND should be Lt(e,f)"
7469                        );
7470                        assert!(
7471                            matches!(and_right.as_ref(), Expr::Like { .. }),
7472                            "right of AND should be Like(g,h)"
7473                        );
7474                    }
7475                    other => unreachable!("expected And(Lt, Like), got {other:?}"),
7476                }
7477
7478                // Drill into the NOT to verify deeper structure:
7479                // NOT → Eq → right = Add → right = Mul → right = Negate
7480                if let Expr::UnaryOp {
7481                    expr: not_inner, ..
7482                } = left.as_ref()
7483                {
7484                    if let Expr::BinaryOp {
7485                        op: BinaryOp::Eq,
7486                        right: eq_right,
7487                        ..
7488                    } = not_inner.as_ref()
7489                    {
7490                        if let Expr::BinaryOp {
7491                            op: BinaryOp::Add,
7492                            right: add_right,
7493                            ..
7494                        } = eq_right.as_ref()
7495                        {
7496                            if let Expr::BinaryOp {
7497                                op: BinaryOp::Multiply,
7498                                right: mul_right,
7499                                ..
7500                            } = add_right.as_ref()
7501                            {
7502                                assert!(
7503                                    matches!(
7504                                        mul_right.as_ref(),
7505                                        Expr::UnaryOp {
7506                                            op: UnaryOp::Negate,
7507                                            ..
7508                                        }
7509                                    ),
7510                                    "deepest: negate"
7511                                );
7512                            } else {
7513                                unreachable!("expected Mul in add_right");
7514                            }
7515                        } else {
7516                            unreachable!("expected Add in eq_right");
7517                        }
7518                    } else {
7519                        unreachable!("expected Eq inside NOT");
7520                    }
7521                }
7522            }
7523            other => unreachable!("expected Or(Not(...), And(...)), got {other:?}"),
7524        }
7525    }
7526
7527    // JSON operators share precedence with concat and associate left-to-right.
7528    #[test]
7529    fn test_pratt_json_same_precedence_as_concat() {
7530        // a || b -> c parses as (a || b) -> c.
7531        let expr = parse("a || b -> c");
7532        match &expr {
7533            Expr::JsonAccess {
7534                expr: left,
7535                path: right,
7536                arrow: JsonArrow::Arrow,
7537                ..
7538            } => {
7539                assert!(
7540                    matches!(
7541                        left.as_ref(),
7542                        Expr::BinaryOp {
7543                            op: BinaryOp::Concat,
7544                            ..
7545                        }
7546                    ),
7547                    "left side should be concat expression"
7548                );
7549                assert!(
7550                    matches!(right.as_ref(), Expr::Column(_, _)),
7551                    "path should remain the right-hand expression"
7552                );
7553            }
7554            other => unreachable!("expected JsonAccess(Concat(a,b), c), got {other:?}"),
7555        }
7556    }
7557
7558    #[test]
7559    fn test_pratt_double_arrow_same_precedence_as_concat() {
7560        let expr = parse("a || b ->> c");
7561        assert!(
7562            matches!(
7563                expr,
7564                Expr::JsonAccess {
7565                    arrow: JsonArrow::DoubleArrow,
7566                    ..
7567                }
7568            ),
7569            "double-arrow should parse as JsonAccess at the same precedence level as concat"
7570        );
7571    }
7572}