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