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