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