Skip to main content

squonk_ast/ast/
expr.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Moderately AI Inc.
3
4//! Scalar-expression AST nodes: the `Expr` tree and its operand shapes.
5
6use super::{
7    DataType, Extension, Ident, IntervalFields, Literal, NoExt, ObjectName, OrderByExpr, Query,
8    SetQuantifier, WildcardOptions, WindowSpec,
9};
10use crate::vocab::{Meta, Symbol};
11use thin_vec::ThinVec;
12
13/// SQL expressions for the M1 parser surface.
14///
15/// Each variant carries `Meta` so the expression node itself has side-table
16/// identity. Child nodes keep their own metadata; `Meta` remains structural
17/// equality-neutral.
18#[derive(Clone, Debug, PartialEq, Eq, Hash)]
19#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
20#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
21pub enum Expr<X: Extension = NoExt> {
22    /// A column reference: a bare or qualified name (`c`, `t.c`, `s.t.c`).
23    Column {
24        /// The referenced name, one or more dot-separated identifier parts.
25        name: ObjectName,
26        /// Source location and node identity.
27        meta: Meta,
28    },
29    /// A literal constant: a number, string, boolean, `NULL`, or typed date/time value.
30    Literal {
31        /// The literal value; see [`Literal`].
32        literal: Literal,
33        /// Source location and node identity.
34        meta: Meta,
35    },
36    /// A binary operator application: `left op right` (`a + b`, `x AND y`, `p || q`).
37    BinaryOp {
38        /// Left-hand operand.
39        left: Box<Expr<X>>,
40        /// The binary operator joining the two operands.
41        op: BinaryOperator,
42        /// Right-hand operand.
43        right: Box<Expr<X>>,
44        /// Source location and node identity.
45        meta: Meta,
46    },
47    /// A unary operator application: prefix (`-x`, `NOT p`, `+n`) or postfix, per the operator.
48    UnaryOp {
49        /// The unary operator applied to the operand.
50        op: UnaryOperator,
51        /// The operand the operator applies to.
52        expr: Box<Expr<X>>,
53        /// Source location and node identity.
54        meta: Meta,
55    },
56    /// A function, aggregate, or window call (`count(*)`, `substr(s, 1, 3)`); see [`FunctionCall`].
57    Function {
58        /// The call's target, arguments, and any `OVER`/`FILTER`/`WITHIN GROUP` clauses; see [`FunctionCall`].
59        call: Box<FunctionCall<X>>,
60        /// Source location and node identity.
61        meta: Meta,
62    },
63    /// A `CASE` expression: searched (`CASE WHEN … THEN …`) or simple (`CASE x WHEN …`); see [`CaseExpr`].
64    Case {
65        /// The optional operand, `WHEN`/`THEN` arms, and optional `ELSE`; see [`CaseExpr`].
66        case: Box<CaseExpr<X>>,
67        /// Source location and node identity.
68        meta: Meta,
69    },
70    /// An `EXTRACT(field FROM source)` datetime-field extraction; see [`ExtractExpr`].
71    Extract {
72        /// The extracted field and the source expression; see [`ExtractExpr`].
73        extract: Box<ExtractExpr<X>>,
74        /// Source location and node identity.
75        meta: Meta,
76    },
77    /// A type cast: standard `CAST(expr AS type)`, PostgreSQL `expr::type`, or DuckDB `TRY_CAST`.
78    Cast {
79        /// The expression being cast.
80        expr: Box<Expr<X>>,
81        /// The target data type.
82        data_type: Box<DataType<X>>,
83        /// Whether the cast was spelled `CAST(expr AS type)` or `expr::type`.
84        syntax: CastSyntax,
85        /// DuckDB's `TRY_CAST(expr AS type)` null-on-failure cast. This is *semantics*,
86        /// not a spelling: a failed `TRY_CAST` yields `NULL` where `CAST` raises, so it
87        /// is a distinct flag rather than a [`CastSyntax`] variant. DuckDB's
88        /// own serialized tree carries the same `try_cast` boolean on its one `CAST`
89        /// node. Always `false` for the `::` and prefix-typed spellings, which have no
90        /// try form.
91        try_cast: bool,
92        /// Source location and node identity.
93        meta: Meta,
94    },
95    /// A null test: `<expr> IS [NOT] NULL`, or its postfix synonyms — one-word
96    /// `<expr> ISNULL` / `<expr> NOTNULL` and the two-word `<expr> NOT NULL`.
97    ///
98    /// `negated` is the `NOT` axis (`IS NOT NULL` / `NOTNULL` / `NOT NULL`);
99    /// [`spelling`](NullTestSpelling) records whether the source wrote the standard
100    /// `IS [NOT] NULL`, the one-word `ISNULL`/`NOTNULL` postfix, or the two-word `NOT NULL`
101    /// postfix so rendering round-trips. The one-word synonyms are gated by
102    /// [`OperatorSyntax::null_test_postfix`](crate::dialect::OperatorSyntax) and the two-word
103    /// form by [`PredicateSyntax::null_test_two_word_postfix`](crate::dialect::PredicateSyntax)
104    /// (they diverge — see the flag docs); the standard spelling is always available.
105    IsNull {
106        /// Expression evaluated by this syntax.
107        expr: Box<Expr<X>>,
108        /// Whether the negated form was present in the source.
109        negated: bool,
110        /// Exact source spelling retained for faithful rendering.
111        spelling: NullTestSpelling,
112        /// Source location and node identity.
113        meta: Meta,
114    },
115    /// A truth-value test: `<expr> IS [NOT] {TRUE | FALSE | UNKNOWN}` (SQL:2016 F571).
116    ///
117    /// The postfix sibling of [`IsNull`](Self::IsNull) — a unary predicate carrying a
118    /// `negated` flag for the `IS NOT` form and binding at comparison precedence — with a
119    /// [`TruthValue`] tag recording which of the three truth values was tested. A distinct
120    /// node rather than a widening of `IsNull`: the standard models truth tests and the null
121    /// test as separate predicates (`<boolean test>` vs `<null predicate>`), and the operand
122    /// is a boolean value here rather than an arbitrary comparable.
123    ///
124    /// Reachable only under
125    /// [`OperatorSyntax::truth_value_tests`](crate::dialect::OperatorSyntax) (ANSI /
126    /// PostgreSQL / MySQL / DuckDB / Lenient). SQLite has no truth-value predicate: its `IS`
127    /// is a general null-safe equality, so it folds `IS TRUE` / `IS FALSE` onto
128    /// [`IsNotDistinctFrom`](BinaryOperator::IsNotDistinctFrom) against the boolean literal and
129    /// reads `IS UNKNOWN` as equality against an identifier named `unknown` (engine-measured
130    /// via rusqlite) — so the flag stays off there and this node is never produced.
131    IsTruth {
132        /// Expression evaluated by this syntax.
133        expr: Box<Expr<X>>,
134        /// Value supplied by this syntax.
135        value: TruthValue,
136        /// Whether the negated form was present in the source.
137        negated: bool,
138        /// Source location and node identity.
139        meta: Meta,
140    },
141    /// A Unicode-normalization test: `<expr> IS [NOT] [NFC|NFD|NFKC|NFKD] NORMALIZED`
142    /// (SQL:2016 T061).
143    ///
144    /// The postfix sibling of [`IsNull`](Self::IsNull)/[`IsTruth`](Self::IsTruth) — a unary
145    /// predicate carrying a `negated` flag for the `IS NOT` form, binding at comparison
146    /// precedence — testing whether a string is in the given Unicode normal form. `form` is
147    /// the optional [`NormalizationForm`]; `None` is the bare `IS NORMALIZED`, which defaults
148    /// to NFC. Gated by
149    /// [`PredicateSyntax::is_normalized`](crate::dialect::PredicateSyntax).
150    IsNormalized {
151        /// Expression evaluated by this syntax.
152        expr: Box<Expr<X>>,
153        /// Optional form for this syntax.
154        form: Option<NormalizationForm>,
155        /// Whether the negated form was present in the source.
156        negated: bool,
157        /// Source location and node identity.
158        meta: Meta,
159    },
160    /// A range test: `<expr> [NOT] BETWEEN [SYMMETRIC] <low> AND <high>`.
161    ///
162    /// `symmetric` records the SQL-standard `SYMMETRIC` modifier, which — unlike the
163    /// default `ASYMMETRIC` — is semantically load-bearing: it permits `low > high` by
164    /// testing the value against the ordered pair, so it is kept as data rather than
165    /// dropped. The explicit `ASYMMETRIC` noise word is the default and is not retained
166    /// (it renders back as the bare form). Gated by
167    /// [`PredicateSyntax::between_symmetric`](crate::dialect::PredicateSyntax).
168    Between {
169        /// Expression evaluated by this syntax.
170        expr: Box<Expr<X>>,
171        /// The lower bound of the range.
172        low: Box<Expr<X>>,
173        /// The upper bound of the range.
174        high: Box<Expr<X>>,
175        /// Whether the negated form was present in the source.
176        negated: bool,
177        /// Whether the symmetric form was present in the source.
178        symmetric: bool,
179        /// Source location and node identity.
180        meta: Meta,
181    },
182    /// A pattern-match predicate: `<expr> [NOT] LIKE|ILIKE|SIMILAR TO <pattern>
183    /// [ESCAPE <c>]`.
184    ///
185    /// One canonical shape covers all three spellings; [`LikeSpelling`]
186    /// records which the source used so it round-trips. Like the other comparison
187    /// predicates ([`Between`](Self::Between)/[`InList`](Self::InList)) it carries a
188    /// `negated` flag for the `NOT` form and binds at comparison precedence. The
189    /// optional `escape` holds the ISO `ESCAPE '<c>'` character (usually a string
190    /// literal); `None` when the source wrote no `ESCAPE`.
191    Like {
192        /// Expression evaluated by this syntax.
193        expr: Box<Expr<X>>,
194        /// The pattern to match against.
195        pattern: Box<Expr<X>>,
196        /// Optional escape for this syntax.
197        escape: Option<Box<Expr<X>>>,
198        /// Whether the negated form was present in the source.
199        negated: bool,
200        /// Exact source spelling retained for faithful rendering.
201        spelling: LikeSpelling,
202        /// Source location and node identity.
203        meta: Meta,
204    },
205    /// A list-membership test: `<expr> [NOT] IN (v1, v2, …)`.
206    InList {
207        /// The value tested for membership.
208        expr: Box<Expr<X>>,
209        /// The candidate values, in source order.
210        list: ThinVec<Expr<X>>,
211        /// Whether the negated form was present in the source.
212        negated: bool,
213        /// Source location and node identity.
214        meta: Meta,
215    },
216    /// A subquery-membership test: `<expr> [NOT] IN (<subquery>)`.
217    InSubquery {
218        /// The value tested for membership.
219        expr: Box<Expr<X>>,
220        /// The subquery whose rows form the candidate set.
221        subquery: Box<Query<X>>,
222        /// Whether the negated form was present in the source.
223        negated: bool,
224        /// Source location and node identity.
225        meta: Meta,
226    },
227    /// DuckDB's unparenthesized `<expr> [NOT] IN <rhs>` list-membership operator, where
228    /// the right operand is a single value expression rather than a parenthesized list
229    /// or subquery: `z IN y` (DuckDB desugars it to `contains(y, z)`), distinct from the
230    /// standard `IN (…)` forms ([`InList`](Self::InList) / [`InSubquery`](Self::InSubquery)).
231    /// A new canonical node, not a widening of `InList`: the two round-trip to
232    /// different surface syntax (`x IN y` vs `x IN (y)`), and DuckDB models them as
233    /// distinct constructs (a `contains` function call vs a `COMPARE_IN`).
234    ///
235    /// `rhs` is DuckDB's restricted `c_expr` right operand: a column/qualified reference,
236    /// function call, subscript (`y[1]`), array/struct/map literal, `CAST`/`CASE`, or a
237    /// parameter — but never a leading constant or unary sign (`IN 4` / `IN 'a'` / `IN -5`
238    /// are DuckDB parser errors, an LALR grammar-generator restriction the parser
239    /// replicates via a leading-token gate). Binds tighter than the comparison operators
240    /// and the standard `IN (list)` predicate (`z = w IN y` is `z = (w IN y)`; measured on
241    /// 1.5.4), left-associative. Reachable only under
242    /// [`PredicateSyntax::unparenthesized_in_list`](crate::dialect::PredicateSyntax)
243    /// (DuckDB / Lenient).
244    InExpr {
245        /// The value tested for membership.
246        expr: Box<Expr<X>>,
247        /// The single-value right operand (DuckDB's restricted `c_expr`).
248        rhs: Box<Expr<X>>,
249        /// Whether the negated form was present in the source.
250        negated: bool,
251        /// Source location and node identity.
252        meta: Meta,
253    },
254    /// An `EXISTS (<subquery>)` test — true when the subquery returns at least one row.
255    Exists {
256        /// The subquery tested for row existence.
257        query: Box<Query<X>>,
258        /// Source location and node identity.
259        meta: Meta,
260    },
261    /// A quantified comparison against a subquery: `<expr> op {ANY | ALL | SOME} (<subquery>)`.
262    QuantifiedComparison {
263        /// Left-hand operand.
264        left: Box<Expr<X>>,
265        /// The comparison operator applied against each row.
266        op: BinaryOperator,
267        /// Whether the comparison is `ANY`/`SOME` or `ALL`.
268        quantifier: Quantifier,
269        /// The subquery supplying the right-hand rows.
270        subquery: Box<Query<X>>,
271        /// Source location and node identity.
272        meta: Meta,
273    },
274    /// A quantified comparison whose right operand is a scalar list/array *value*
275    /// rather than a subquery: DuckDB `ax = ANY (b)` over a `LIST`/`ARRAY` column or
276    /// literal, `x = ANY ([1, 2, 3])`, PostgreSQL `x = ANY (ARRAY[…])`.
277    ///
278    /// A distinct node from [`QuantifiedComparison`](Self::QuantifiedComparison), not a
279    /// widening of its `subquery` field: the reference engine models the two
280    /// as separate constructs — the array form is PostgreSQL's `ScalarArrayOpExpr` (the
281    /// operator is applied elementwise to an array value) while the subquery form is an
282    /// `AnySublink`/`AllSublink` (the operator ranges over a query's rows). The operands
283    /// differ in kind (a value [`Expr`] vs a [`Query`]) and in evaluation, so folding
284    /// them onto one node would misrepresent the engine's shape. The parser splits the
285    /// two on the parenthesized content exactly as `IN (…)` splits `InSubquery` from
286    /// `InList`: a leading query keyword is the subquery form, anything else is this one.
287    ///
288    /// `array` is the operand expression as written (the list literal `[…]`, an
289    /// `ARRAY[…]` constructor, or a bare column). Reachable only under
290    /// [`OperatorSyntax::quantified_comparison_lists`](crate::dialect::OperatorSyntax)
291    /// (DuckDB/PostgreSQL/Lenient); the subquery form's
292    /// [`quantified_comparisons`](crate::dialect::OperatorSyntax::quantified_comparisons)
293    /// gate is a prerequisite (both are on wherever this is).
294    QuantifiedList {
295        /// Left-hand operand.
296        left: Box<Expr<X>>,
297        /// Operator applied by this expression.
298        op: BinaryOperator,
299        /// Whether the quantifier is `ANY`/`SOME` or `ALL`.
300        quantifier: Quantifier,
301        /// The array/list right operand.
302        array: Box<Expr<X>>,
303        /// Source location and node identity.
304        meta: Meta,
305    },
306    /// A pattern-match predicate quantified over an array operand: PostgreSQL
307    /// `<expr> [NOT] LIKE|ILIKE {ANY | ALL | SOME} (<array>)` — `'foo' LIKE ANY
308    /// (ARRAY['%a', '%o'])`.
309    ///
310    /// A distinct node from [`Like`](Self::Like), for the same reason
311    /// [`QuantifiedList`](Self::QuantifiedList) is distinct from a plain
312    /// comparison: PostgreSQL models the plain `<expr> LIKE <pattern>` as an
313    /// `OpExpr` over the `~~` operator, but the quantified form as a
314    /// `ScalarArrayOpExpr` applying `~~`/`~~*` elementwise across an array value.
315    /// The operands differ in kind (a single pattern [`Expr`] vs an array-valued
316    /// operand) and in evaluation, so folding a quantifier onto `Like` would
317    /// misrepresent the engine's shape. `SIMILAR TO` has no quantified form
318    /// (PostgreSQL rejects `SIMILAR TO ANY`), so `spelling` is only ever
319    /// [`Like`](LikeSpelling::Like)/[`ILike`](LikeSpelling::ILike) here.
320    ///
321    /// `pattern` is the array operand as written. Reachable only under
322    /// [`PredicateSyntax::pattern_match_quantifier`](crate::dialect::PredicateSyntax)
323    /// (PostgreSQL/Lenient).
324    QuantifiedLike {
325        /// Left-hand operand.
326        left: Box<Expr<X>>,
327        /// The array of patterns to match against.
328        pattern: Box<Expr<X>>,
329        /// Whether the quantifier is `ANY`/`SOME` or `ALL`.
330        quantifier: Quantifier,
331        /// Whether the negated form was present in the source.
332        negated: bool,
333        /// Exact source spelling retained for faithful rendering.
334        spelling: LikeSpelling,
335        /// Source location and node identity.
336        meta: Meta,
337    },
338    /// A scalar subquery used as a value: `(SELECT …)`.
339    Subquery {
340        /// The parenthesized subquery evaluated as a scalar value.
341        query: Box<Query<X>>,
342        /// Source location and node identity.
343        meta: Meta,
344    },
345    /// A placeholder parameter bound at execute time (`?`, `$1`, `:name`); see [`ParameterKind`].
346    Parameter {
347        /// Which placeholder syntax was used; see [`ParameterKind`].
348        kind: ParameterKind,
349        /// Source location and node identity.
350        meta: Meta,
351    },
352    /// A DuckDB `#n` positional column reference — a select-list column named by its
353    /// 1-based output position, used mainly in `ORDER BY #1` / `GROUP BY #2` but valid
354    /// wherever a value expression is (`SELECT #1 + 1`). A new canonical node,
355    /// not folded onto an integer [`Literal`](Self::Literal): the `#` sigil carries the
356    /// "resolve to the n-th projected column" semantics DuckDB's binder acts on, which a
357    /// bare integer does not. Distinct from a prepared-statement
358    /// [`Parameter`](Self::Parameter) placeholder (a hole bound at execute time) — this
359    /// reads a column already in the query. `index` is the parsed position, always `>= 1`
360    /// (DuckDB rejects `#0` at parse time — "Positional reference node needs to be >= 1").
361    /// Reachable only under
362    /// [`ExpressionSyntax::positional_column`](crate::dialect::ExpressionSyntax), which
363    /// also gates the `#<digits>` lexeme.
364    PositionalColumn {
365        /// The 1-based select-list position (always `>= 1`).
366        index: u32,
367        /// Source location and node identity.
368        meta: Meta,
369    },
370    /// A MySQL session-variable read: a user-defined `@name` variable or a server
371    /// `@@[scope.]name` system variable, evaluated as a value expression.
372    ///
373    /// Distinct from a prepared-statement placeholder ([`Parameter`](Self::Parameter)):
374    /// a placeholder is a hole bound at execute time, whereas this reads the current
375    /// value of the named variable in place. One canonical shape covers all
376    /// four surface forms; the [`SessionVariableKind`] tag records the sigil (`@` vs
377    /// `@@`) and the optional system scope so `@x`, `@@x`, `@@global.x`, and
378    /// `@@session.x` each round-trip exactly. `name` is the interned variable name,
379    /// exact-case (sigil and scope stripped), so it renders verbatim like an
380    /// identifier.
381    SessionVariable {
382        /// Which sigil/scope form (`@`/`@@`/`@@global.`/`@@session.`); see [`SessionVariableKind`].
383        kind: SessionVariableKind,
384        /// Name referenced by this syntax.
385        name: Symbol,
386        /// Source location and node identity.
387        meta: Meta,
388    },
389    /// A PostgreSQL array element/slice access: `base[index]` or
390    /// `base[lower:upper]` (boxed; see [`SubscriptExpr`]).
391    Subscript {
392        /// The array element/slice access; see [`SubscriptExpr`].
393        subscript: Box<SubscriptExpr<X>>,
394        /// Source location and node identity.
395        meta: Meta,
396    },
397    /// A colon-led semi-structured value path: `base:key[0].field` (boxed; see
398    /// [`SemiStructuredAccessExpr`]).
399    SemiStructuredAccess {
400        /// The semi-structured path access; see [`SemiStructuredAccessExpr`].
401        semi_structured_access: Box<SemiStructuredAccessExpr<X>>,
402        /// Source location and node identity.
403        meta: Meta,
404    },
405    /// A PostgreSQL `expr COLLATE collation` collation override (boxed; see
406    /// [`CollateExpr`]).
407    Collate {
408        /// The collation override; see [`CollateExpr`].
409        collate: Box<CollateExpr<X>>,
410        /// Source location and node identity.
411        meta: Meta,
412    },
413    /// A MySQL operator-position interval quantity: `INTERVAL <value> <unit>` — the
414    /// `INTERVAL 3 DAY` operand of MySQL date arithmetic (`d - INTERVAL 3 DAY`,
415    /// `INTERVAL 1 DAY + d`, `DATE_ADD(d, INTERVAL 1 DAY)`, a window-frame bound).
416    ///
417    /// Distinct from the ANSI/PostgreSQL typed-string interval *literal*
418    /// [`LiteralKind::Interval`](crate::ast::LiteralKind::Interval): MySQL's `INTERVAL` is
419    /// not a standalone value but the second operand of the date-add/date-sub production
420    /// (`Item_date_add_interval`), so it carries an arbitrary amount *expression* and a
421    /// mandatory unit keyword rather than a quoted amount string and an optional ANSI
422    /// qualifier. Gated by
423    /// [`ExpressionSyntax::mysql_interval_operator`](crate::dialect::ExpressionSyntax); see
424    /// that flag for the position/unit boundary. The `unit` reuses the shared
425    /// [`IntervalFields`] vocabulary but always renders in MySQL's underscore spelling
426    /// (`DAY_HOUR`, `YEAR_MONTH`), never the ANSI `TO` composite.
427    Interval {
428        /// The interval amount — an arbitrary expression (`3`, `'3-2'`, `?`, `@v`, `n + 1`).
429        value: Box<Expr<X>>,
430        /// The unit keyword (`DAY`, `HOUR_SECOND`, `YEAR_MONTH`, …).
431        unit: IntervalFields,
432        /// Source location and node identity.
433        meta: Meta,
434    },
435    /// A PostgreSQL `expr AT TIME ZONE zone` time-zone conversion (boxed; see
436    /// [`AtTimeZoneExpr`]).
437    AtTimeZone {
438        /// The time-zone conversion; see [`AtTimeZoneExpr`].
439        at_time_zone: Box<AtTimeZoneExpr<X>>,
440        /// Source location and node identity.
441        meta: Meta,
442    },
443    /// An array constructor: PostgreSQL `ARRAY[...]` / `ARRAY(<query>)` or the
444    /// DuckDB bare-bracket list literal `[...]` (boxed; see [`ArrayExpr`]).
445    Array {
446        /// The array/list constructor; see [`ArrayExpr`].
447        array: Box<ArrayExpr<X>>,
448        /// Source location and node identity.
449        meta: Meta,
450    },
451    /// A DuckDB struct literal `{'key': value, …}` (boxed; see [`StructExpr`]).
452    Struct {
453        /// The struct literal; see [`StructExpr`].
454        r#struct: Box<StructExpr<X>>,
455        /// Source location and node identity.
456        meta: Meta,
457    },
458    /// A BigQuery `STRUCT(...)` value constructor: the typeless `STRUCT(1, 2)` /
459    /// `STRUCT(x AS a, y AS b)` forms and the typed `STRUCT<a INT64, b STRING>(1, 'x')`
460    /// form (boxed; see [`StructConstructorExpr`]). Distinct from the DuckDB brace
461    /// literal [`Struct`](Self::Struct) and from an ordinary `struct(...)` catalog-function
462    /// call, which a dialect without
463    /// [`ExpressionSyntax::struct_constructor`](crate::dialect::ExpressionSyntax) keeps as
464    /// a [`Function`](Self::Function).
465    StructConstructor {
466        /// The `STRUCT(...)` constructor; see [`StructConstructorExpr`].
467        constructor: Box<StructConstructorExpr<X>>,
468        /// Source location and node identity.
469        meta: Meta,
470    },
471    /// A DuckDB map literal `MAP {k: v, …}` (boxed; see [`MapExpr`]).
472    ///
473    /// Only the brace form is a dedicated grammar production; the two-list
474    /// `MAP(<keys>, <values>)` spelling is an ordinary call to the (case-insensitive)
475    /// `map` function, so it parses as [`Function`](Self::Function) like any other
476    /// call.
477    Map {
478        /// The map literal; see [`MapExpr`].
479        map: Box<MapExpr<X>>,
480        /// Source location and node identity.
481        meta: Meta,
482    },
483    /// A row constructor: explicit `ROW(...)` or the implicit parenthesized
484    /// `(a, b, …)` form (boxed; see [`RowExpr`]).
485    Row {
486        /// The row constructor; see [`RowExpr`].
487        row: Box<RowExpr<X>>,
488        /// Source location and node identity.
489        meta: Meta,
490    },
491    /// A PostgreSQL composite navigation off a value: `(expr).field` field selection
492    /// or the `(expr).*` / whole-row `tbl.*` star expansion (boxed; see
493    /// [`FieldSelectionExpr`]).
494    FieldSelection {
495        /// The composite field selection; see [`FieldSelectionExpr`].
496        field_selection: Box<FieldSelectionExpr<X>>,
497        /// Source location and node identity.
498        meta: Meta,
499    },
500    /// A general infix operator application — the explicit `a OPERATOR(schema.+) b`
501    /// or a bare symbolic operator `a ~ b` / `a <-> b` (boxed; see [`NamedOperatorExpr`]).
502    NamedOperator {
503        /// The named/infix operator application; see [`NamedOperatorExpr`].
504        named_operator: Box<NamedOperatorExpr<X>>,
505        /// Source location and node identity.
506        meta: Meta,
507    },
508    /// A general prefix operator application: `@ x` (absolute value), `|/ x` (square
509    /// root), `@@ box` (centre), or a fully user-defined prefix operator `@#@ x` (boxed;
510    /// see [`PrefixOperatorExpr`]).
511    PrefixOperator {
512        /// The prefix operator application; see [`PrefixOperatorExpr`].
513        prefix_operator: Box<PrefixOperatorExpr<X>>,
514        /// Source location and node identity.
515        meta: Meta,
516    },
517    /// A general postfix operator application: `10!` (factorial), `1 ~`, `1 <->`, or any
518    /// other trailing symbolic operator (boxed; see [`PostfixOperatorExpr`]). DuckDB is the
519    /// only enabler — it keeps the postfix reading PostgreSQL removed in 14.
520    PostfixOperator {
521        /// The postfix operator application; see [`PostfixOperatorExpr`].
522        postfix_operator: Box<PostfixOperatorExpr<X>>,
523        /// Source location and node identity.
524        meta: Meta,
525    },
526    /// A DuckDB single-arrow lambda: `x -> x + 1` / `(x, y) -> x + y` (boxed; see
527    /// [`LambdaExpr`]).
528    Lambda {
529        /// The lambda expression; see [`LambdaExpr`].
530        lambda: Box<LambdaExpr<X>>,
531        /// Source location and node identity.
532        meta: Meta,
533    },
534    /// DuckDB's `COLUMNS(<selector>)` column-set selector, a star expression that
535    /// stands wherever a value expression does (`SELECT COLUMNS('re')`,
536    /// `sum(COLUMNS(*))`, `COLUMNS(*)::JSON`). A new canonical node, not a function
537    /// call: DuckDB models it as its `STAR` node with `columns: true`, and
538    /// modelling it as a call would lose the star semantics the structural oracle
539    /// compares against. Reachable only under
540    /// [`CallSyntax::columns_expression`](crate::dialect::ExpressionSyntax).
541    ///
542    /// `pattern` is the selector argument: `None` for the star form `COLUMNS(*)` /
543    /// `COLUMNS(t.*)` (mirroring DuckDB's null `expr`), `Some(e)` for
544    /// `COLUMNS(<expr>)` — the regex string `COLUMNS('re')`, the lambda
545    /// `COLUMNS(c -> …)`, the name list `COLUMNS([…])`, or a bare column. `qualifier`
546    /// and `options` belong to the star form only (both always `None` alongside a
547    /// `pattern`): `qualifier` is the relation of a qualified star `COLUMNS(t.*)` —
548    /// exactly one name part, DuckDB rejects `COLUMNS(s.t.*)` (probed on 1.5.4) — and
549    /// `options` carries the `EXCLUDE`/`REPLACE`/`RENAME` modifiers DuckDB allows
550    /// inside the star form (`COLUMNS(* EXCLUDE i)`, `COLUMNS(t.* EXCLUDE (k))`).
551    ///
552    /// `spelling` records which of DuckDB's three surface forms of the star node the
553    /// source used (one-shape-plus-tag), all the same STAR node in the engine:
554    /// [`Columns`](ColumnsSpelling::Columns) is the `COLUMNS(<selector>)` wrapper
555    /// (DuckDB's STAR `columns:true`); [`Unpack`](ColumnsSpelling::Unpack) is the
556    /// `*COLUMNS(<selector>)` spread-into-arguments prefix (same node, unpack flag set)
557    /// admitted in call/`IN`-list argument positions; [`Star`](ColumnsSpelling::Star) is
558    /// the bare `*` / `t.*` written without the wrapper (DuckDB's STAR `columns:false`),
559    /// admitted in the `ORDER BY` and `UNPIVOT` `ON`/`IN` positions. A `Star` spelling
560    /// never carries a `pattern` (the bare star has no selector argument).
561    Columns {
562        /// Optional qualifier for this syntax.
563        qualifier: Option<ObjectName>,
564        /// Optional pattern for this syntax.
565        pattern: Option<Box<Expr<X>>>,
566        /// Options supplied in source order.
567        options: Option<Box<WildcardOptions<X>>>,
568        /// Exact source spelling retained for faithful rendering.
569        spelling: ColumnsSpelling,
570        /// Source location and node identity.
571        meta: Meta,
572    },
573    /// A SQL special value function with a dedicated grammar production rather than
574    /// an ordinary call: a nullary keyword form (`CURRENT_DATE`, `CURRENT_USER`,
575    /// `USER`, …) or one of the temporal forms that take an optional precision
576    /// (`CURRENT_TIME(p)`, `LOCALTIMESTAMP(p)`, …). PostgreSQL lowers these to
577    /// `SQLValueFunction`; the keyword is the whole construct, so there is no
578    /// argument list. Held inline (the keyword tag plus an optional precision is
579    /// small) rather than boxed.
580    SpecialFunction {
581        /// Which special-value function; see [`SpecialFunctionKeyword`].
582        keyword: SpecialFunctionKeyword,
583        /// The `(precision)` modifier, only valid on the temporal forms
584        /// (`CURRENT_TIME`, `CURRENT_TIMESTAMP`, `LOCALTIME`, `LOCALTIMESTAMP`).
585        precision: Option<u32>,
586        /// Source location and node identity.
587        meta: Meta,
588    },
589    /// A SQL/JSON query function: `JSON_VALUE` / `JSON_QUERY` / `JSON_EXISTS`
590    /// (SQL:2016, PostgreSQL's `JsonFuncExpr`; boxed, see [`JsonFuncExpr`]).
591    JsonFunc {
592        /// The SQL/JSON query function; see [`JsonFuncExpr`].
593        json_func: Box<JsonFuncExpr<X>>,
594        /// Source location and node identity.
595        meta: Meta,
596    },
597    /// A SQL/JSON object constructor: `JSON_OBJECT(k : v, …)` (boxed; see
598    /// [`JsonObjectExpr`]).
599    JsonObject {
600        /// The `JSON_OBJECT(...)` constructor; see [`JsonObjectExpr`].
601        json_object: Box<JsonObjectExpr<X>>,
602        /// Source location and node identity.
603        meta: Meta,
604    },
605    /// A SQL/JSON array constructor: `JSON_ARRAY(v, …)` or `JSON_ARRAY(<query>)`
606    /// (boxed; see [`JsonArrayExpr`]).
607    JsonArray {
608        /// The `JSON_ARRAY(...)` constructor; see [`JsonArrayExpr`].
609        json_array: Box<JsonArrayExpr<X>>,
610        /// Source location and node identity.
611        meta: Meta,
612    },
613    /// A SQL/JSON aggregate constructor: `JSON_OBJECTAGG(k : v …)` or
614    /// `JSON_ARRAYAGG(v …)` (boxed; see [`JsonAggregateExpr`]).
615    JsonAggregate {
616        /// The SQL/JSON aggregate; see [`JsonAggregateExpr`].
617        json_aggregate: Box<JsonAggregateExpr<X>>,
618        /// Source location and node identity.
619        meta: Meta,
620    },
621    /// A bare SQL/JSON constructor: `JSON(x)` / `JSON_SCALAR(x)` /
622    /// `JSON_SERIALIZE(x)` (boxed; see [`JsonConstructorExpr`]).
623    JsonConstructor {
624        /// The bare SQL/JSON constructor; see [`JsonConstructorExpr`].
625        json_constructor: Box<JsonConstructorExpr<X>>,
626        /// Source location and node identity.
627        meta: Meta,
628    },
629    /// The SQL/JSON `expr IS [NOT] JSON [type] [WITH|WITHOUT UNIQUE [KEYS]]`
630    /// predicate (boxed; see [`IsJsonExpr`]).
631    IsJson {
632        /// The operand, optional JSON type, and uniqueness clause; see [`IsJsonExpr`].
633        is_json: Box<IsJsonExpr<X>>,
634        /// Source location and node identity.
635        meta: Meta,
636    },
637    /// A SQL/XML expression function — `xmlelement`/`xmlforest`/`xmlconcat`/
638    /// `xmlparse`/`xmlpi`/`xmlroot`/`xmlserialize`/`xmlexists` (SQL:2006, PostgreSQL's
639    /// `XmlExpr`/`XmlSerialize`). One kind-tagged [`XmlFunc`] enum carries all eight
640    /// (boxed; the shapes differ, so a single fat inline variant would tax every
641    /// `Expr`).
642    XmlFunc {
643        /// The specific XML function and its arguments; see [`XmlFunc`].
644        xml_func: Box<XmlFunc<X>>,
645        /// Source location and node identity.
646        meta: Meta,
647    },
648    /// The SQL/XML `<expr> IS [NOT] DOCUMENT` predicate (PostgreSQL's `XmlExpr` with
649    /// `IS_DOCUMENT`). Held inline like [`IsNull`](Self::IsNull) — a boxed operand,
650    /// a negation flag, no clause tail.
651    IsDocument {
652        /// The operand tested for being a valid XML document.
653        expr: Box<Expr<X>>,
654        /// Whether the negated form was present in the source.
655        negated: bool,
656        /// Source location and node identity.
657        meta: Meta,
658    },
659    /// A standard-SQL string special form — the `SUBSTRING`/`POSITION`/`OVERLAY`/
660    /// `TRIM` keyword-argument grammar (SQL-92 E021 + SQL:1999 T312, PostgreSQL's
661    /// `func_expr_common_subexpr` string productions). One kind-tagged [`StringFunc`]
662    /// enum carries all five shapes (boxed; the shapes differ, so a single fat
663    /// inline variant would tax every `Expr`). The comma plain-call spellings
664    /// (`substring(x, 1, 2)`, `trim(x, y)`) stay ordinary [`Function`](Self::Function)
665    /// calls — this node holds only the keyword-argument surface.
666    StringFunc {
667        /// The specific string function and its keyword arguments; see [`StringFunc`].
668        string_func: Box<StringFunc<X>>,
669        /// Source location and node identity.
670        meta: Meta,
671    },
672    /// Dialect extension node supplied by the extension type.
673    Other {
674        /// The dialect extension node value.
675        ext: X,
676        /// Source location and node identity.
677        meta: Meta,
678    },
679}
680
681/// Which of DuckDB's three surface spellings of the [`Expr::Columns`] star node the
682/// source used.
683///
684/// All three lower to the one STAR node in the engine (one-shape-plus-tag);
685/// the tag is what lets the shared shape round-trip to the exact spelling.
686#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
687#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
688#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
689pub enum ColumnsSpelling {
690    /// `COLUMNS(<selector>)` — the wrapped column-set selector (DuckDB's STAR node with
691    /// `columns:true`).
692    Columns,
693    /// `*COLUMNS(<selector>)` — the unpack prefix that spreads the selected columns into
694    /// the enclosing call / `IN`-list argument list (`struct_pack(*COLUMNS(*))`,
695    /// `2 IN (*COLUMNS(*))`). The same wrapped node with DuckDB's unpack flag set; a bare
696    /// `*` never carries it.
697    Unpack,
698    /// A bare `*` / `t.*` written without the `COLUMNS(...)` wrapper (DuckDB's STAR node
699    /// with `columns:false`), admitted in the `ORDER BY` sort key and the `UNPIVOT`
700    /// `ON`/`IN` column positions. Always pattern-free.
701    Star,
702}
703
704/// A SQL special value function keyword (PostgreSQL `SQLValueFunction`).
705///
706/// Each names a dedicated grammar production whose whole spelling is the keyword;
707/// the four temporal forms additionally accept a parenthesized precision, carried
708/// on [`Expr::SpecialFunction`] rather than here so the keyword stays a copy tag.
709#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
710#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
711#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
712pub enum SpecialFunctionKeyword {
713    /// `CURRENT_CATALOG` — the current database/catalog name.
714    CurrentCatalog,
715    /// `CURRENT_DATE` — the current date.
716    CurrentDate,
717    /// `CURRENT_ROLE` — the current role name.
718    CurrentRole,
719    /// `CURRENT_SCHEMA` — the current schema name.
720    CurrentSchema,
721    /// `CURRENT_TIME` — the current time of day (optional precision).
722    CurrentTime,
723    /// `CURRENT_TIMESTAMP` — the current date and time (optional precision).
724    CurrentTimestamp,
725    /// `CURRENT_USER` — the current authorization identifier.
726    CurrentUser,
727    /// `LOCALTIME` — the current local time of day (optional precision).
728    LocalTime,
729    /// `LOCALTIMESTAMP` — the current local date and time (optional precision).
730    LocalTimestamp,
731    /// `SESSION_USER` — the session authorization identifier.
732    SessionUser,
733    /// `SYSTEM_USER` — the operating-system-level user identifier.
734    SystemUser,
735    /// `USER` — synonym for `CURRENT_USER`.
736    User,
737    /// MySQL `UTC_DATE` — the UTC-clock analogue of `CURRENT_DATE`. Nullary.
738    UtcDate,
739    /// MySQL `UTC_TIME` — the UTC-clock analogue of `CURRENT_TIME`; takes an optional
740    /// fractional-seconds precision (`UTC_TIME(6)`).
741    UtcTime,
742    /// MySQL `UTC_TIMESTAMP` — the UTC-clock analogue of `CURRENT_TIMESTAMP`; takes an
743    /// optional fractional-seconds precision (`UTC_TIMESTAMP(6)`).
744    UtcTimestamp,
745}
746
747/// How a cast is spelled.
748///
749/// One canonical [`Expr::Cast`] shape covers all three spellings; this
750/// tag records which surface form the source used so rendering round-trips: the
751/// standard `CAST(expr AS type)` call, the PostgreSQL `expr::type` postfix
752/// operator, or the PostgreSQL prefixed typed string constant `type 'string'`.
753#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
754#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
755#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
756pub enum CastSyntax {
757    /// `CAST(expr AS type)`.
758    Call,
759    /// `expr::type` (PostgreSQL).
760    DoubleColon,
761    /// `type 'string'` — a typed string constant whose semantics are a cast of the
762    /// string to the named type (PostgreSQL's generalized `ConstTypename Sconst` /
763    /// `func_name Sconst`, e.g. `float8 'NaN'`, `int4 '42'`, `double precision
764    /// '1.5'`). The operand is always a string constant; the canonical shape is the
765    /// same [`Expr::Cast`] as `'NaN'::float8` and `CAST('NaN' AS float8)`, so only
766    /// this tag and the rendered spelling distinguish the three.
767    PrefixTyped,
768    /// `CONVERT(<expr>, <type>)` — MySQL's comma-form cast, the same canonical
769    /// [`Expr::Cast`] shape as `CAST(<expr> AS <type>)` with only this tag (and the
770    /// rendered spelling) distinguishing it. The target is the identical restricted
771    /// `cast_type` set as `CAST` under
772    /// [`CallSyntax::restricted_cast_targets`](crate::dialect::CallSyntax) — engine-measured
773    /// on mysql:8.4, `CONVERT(1, INT)` / `CONVERT(x, VARCHAR)` reject exactly as the
774    /// matching `CAST` forms do, and the charset-annotated `CHAR` target rides in free via
775    /// the shared type grammar. Recognized only under
776    /// [`CallSyntax::convert_function`](crate::dialect::CallSyntax); MySQL-only. The
777    /// transcoding `CONVERT(<expr> USING <charset>)` form is a *different* production —
778    /// [`StringFunc::ConvertUsing`], not a cast.
779    Convert,
780}
781
782/// A subscript into an array/list value: an element access `base[index]`, a
783/// two-bound slice `base[lower:upper]`, or a DuckDB three-bound slice with a step
784/// `base[lower:upper:step]`.
785///
786/// [`kind`](SubscriptExpr::kind) distinguishes the three forms. An index carries its
787/// index in `lower` (`upper`/`step` both `None`); a two-bound slice may omit either
788/// bound (`base[lower:]`, `base[:upper]`, `base[:]`); a three-bound slice may omit the
789/// lower bound and the step (`base[:upper:step]`, `base[lower:upper:]`) but not the
790/// middle bound — DuckDB spells an open upper bound as the `-` placeholder, carried
791/// here as `upper == None` (an empty middle `base[lower::step]` is a DuckDB parse
792/// error). Boxed inside [`Expr::Subscript`] to keep the hot expression enum within its
793/// size budget.
794#[derive(Clone, Debug, PartialEq, Eq, Hash)]
795#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
796#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
797pub struct SubscriptExpr<X: Extension = NoExt> {
798    /// The value being subscripted.
799    pub base: Expr<X>,
800    /// The element index ([`SubscriptKind::Index`]) or the slice lower bound, if present.
801    pub lower: Option<Expr<X>>,
802    /// The slice upper bound, if present. Always `None` for an index; under
803    /// [`SubscriptKind::SliceWithStep`] a `None` is the `-` open-upper placeholder (an
804    /// absent middle bound is a parse error there), not an omitted bound.
805    pub upper: Option<Expr<X>>,
806    /// The slice step, if present. Only reachable under [`SubscriptKind::SliceWithStep`],
807    /// and `None` there for an omitted trailing step (`base[lower:upper:]`); always `None`
808    /// for an index or a two-bound slice.
809    pub step: Option<Expr<X>>,
810    /// Which bracketed form the subscript holds; see [`SubscriptKind`].
811    pub kind: SubscriptKind,
812    /// Source location and node identity.
813    pub meta: Meta,
814}
815
816/// Which bracketed form an [`Expr::Subscript`] holds — the colon count in the brackets.
817///
818/// The tag drives rendering: a slice re-emits its `:` separators (and, for a stepped
819/// slice, the `-` open-upper placeholder) that a bare index does not.
820#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
821#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
822#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
823pub enum SubscriptKind {
824    /// `base[index]` — a single element access; the index is in
825    /// [`lower`](SubscriptExpr::lower).
826    Index,
827    /// `base[lower:upper]` — a two-bound slice; either bound may be omitted.
828    Slice,
829    /// `base[lower:upper:step]` — a DuckDB three-bound slice with a step. The lower bound
830    /// and the step may each be omitted; the middle bound may not be empty, so a `None`
831    /// [`upper`](SubscriptExpr::upper) renders as the `-` open-upper placeholder.
832    SliceWithStep,
833}
834
835/// A semi-structured object/array path rooted at a value expression.
836///
837/// The first segment is introduced by `:` and later segments use JSON-path-like
838/// `.`/`[...]` suffixes. Kept distinct from PostgreSQL [`Expr::Subscript`] and
839/// [`Expr::FieldSelection`]: those address SQL arrays/composites, while this node
840/// preserves the Snowflake/Databricks-style semi-structured path surface.
841#[derive(Clone, Debug, PartialEq, Eq, Hash)]
842#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
843#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
844pub struct SemiStructuredAccessExpr<X: Extension = NoExt> {
845    /// The value the path is rooted at.
846    pub base: Expr<X>,
847    /// path in source order.
848    pub path: ThinVec<SemiStructuredPathSegment<X>>,
849    /// Source location and node identity.
850    pub meta: Meta,
851}
852
853/// One segment of a semi-structured value path.
854#[derive(Clone, Debug, PartialEq, Eq, Hash)]
855#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
856#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
857pub enum SemiStructuredPathSegment<X: Extension = NoExt> {
858    /// A field/key segment, written as `:key` for the first segment or `.key` after that.
859    Key {
860        /// The field/key name.
861        key: Ident,
862        /// Source location and node identity.
863        meta: Meta,
864    },
865    /// An array/list index segment, written as `[index]`.
866    Index {
867        /// Index referenced by this syntax.
868        index: Box<Expr<X>>,
869        /// Source location and node identity.
870        meta: Meta,
871    },
872}
873
874/// A PostgreSQL `expr COLLATE collation` collation override.
875///
876/// Boxed inside [`Expr::Collate`] to keep the hot expression enum within its size
877/// budget.
878#[derive(Clone, Debug, PartialEq, Eq, Hash)]
879#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
880#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
881pub struct CollateExpr<X: Extension = NoExt> {
882    /// Expression evaluated by this syntax.
883    pub expr: Expr<X>,
884    /// The collation name.
885    pub collation: ObjectName,
886    /// Source location and node identity.
887    pub meta: Meta,
888}
889
890/// A PostgreSQL `expr AT TIME ZONE zone` time-zone conversion.
891///
892/// Boxed inside [`Expr::AtTimeZone`] to keep the hot expression enum within its
893/// size budget.
894#[derive(Clone, Debug, PartialEq, Eq, Hash)]
895#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
896#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
897pub struct AtTimeZoneExpr<X: Extension = NoExt> {
898    /// Expression evaluated by this syntax.
899    pub expr: Expr<X>,
900    /// The target time zone.
901    pub zone: Expr<X>,
902    /// Source location and node identity.
903    pub meta: Meta,
904}
905
906/// An array/list constructor.
907///
908/// Either an element list (`ARRAY[a, b]`, possibly empty `ARRAY[]`, or the DuckDB
909/// bare-bracket `[a, b]`) or a subquery (`ARRAY(SELECT …)`). Boxed inside
910/// [`Expr::Array`] to keep the hot expression enum within its size budget.
911#[derive(Clone, Debug, PartialEq, Eq, Hash)]
912#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
913#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
914pub enum ArrayExpr<X: Extension = NoExt> {
915    /// `ARRAY[a, b, …]` / `[a, b, …]` (or empty `ARRAY[]` / `[]`).
916    Elements {
917        /// elements in source order.
918        elements: ThinVec<Expr<X>>,
919        /// Whether the `ARRAY` keyword was written (vs. the bare `[…]` form).
920        spelling: ArraySpelling,
921        /// Source location and node identity.
922        meta: Meta,
923    },
924    /// `ARRAY(<query>)` — always the keyword form (DuckDB has no bracket-subquery
925    /// spelling).
926    Subquery {
927        /// Query governed by this node.
928        query: Box<Query<X>>,
929        /// Source location and node identity.
930        meta: Meta,
931    },
932    /// A DuckDB list comprehension `[element for var in source (if filter)?]`
933    /// (boxed; see [`ListComprehension`]). A distinct list-literal production from the
934    /// element/subquery forms above: the same `[…]` bracket opens it, but the
935    /// `for` after the first element selects the comprehension rather than an element
936    /// list. Gated by
937    /// [`collection_literals`](crate::dialect::ExpressionSyntax::collection_literals) —
938    /// the same flag that admits the bracket list.
939    Comprehension {
940        /// The list comprehension; see [`ListComprehension`].
941        comprehension: Box<ListComprehension<X>>,
942        /// Source location and node identity.
943        meta: Meta,
944    },
945}
946
947/// Surface spelling for an array element constructor ([`ArrayExpr::Elements`]).
948///
949/// The keyword `ARRAY[…]` (SQL:1999 / PostgreSQL) and the bare `[…]` (DuckDB) are one
950/// canonical shape; this tag records which the source used so rendering
951/// round-trips exactly. Each spelling has its own acceptance gate —
952/// [`ExpressionSyntax::array_constructor`](crate::dialect::ExpressionSyntax::array_constructor)
953/// for the keyword,
954/// [`collection_literals`](crate::dialect::ExpressionSyntax::collection_literals) for
955/// the bracket — so a dialect can admit either independently.
956#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
957#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
958#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
959pub enum ArraySpelling {
960    /// The `ARRAY[…]` keyword-prefixed constructor.
961    Keyword,
962    /// The DuckDB bare-bracket `[…]` list literal.
963    Bracket,
964}
965
966/// A DuckDB list comprehension `[element for var in source (if filter)?]`.
967///
968/// Python-style syntax DuckDB desugars to `list_transform`/`list_filter`: it maps
969/// `element` over each `var` drawn from `source`, keeping only those matching the
970/// optional `filter`. The canonical node keeps the source spelling so a
971/// comprehension round-trips; DuckDB binds it only inside `COLUMNS(…)` when `source`
972/// is a column star, but that is a bind-time rule past this parse-level shape. Boxed
973/// inside [`ArrayExpr::Comprehension`] to keep the array node within its size budget.
974#[derive(Clone, Debug, PartialEq, Eq, Hash)]
975#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
976#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
977pub struct ListComprehension<X: Extension = NoExt> {
978    /// The output expression evaluated for each iteration.
979    pub element: Box<Expr<X>>,
980    /// Loop variables: one name (`for x in …`) or two (`for x, i in …` — value plus
981    /// 1-based index). DuckDB's binder rejects three or more (lambda max 2); the
982    /// parser admits a non-empty list so the multi-var form is representable.
983    pub vars: ThinVec<Ident>,
984    /// Input source for this syntax.
985    pub source: ComprehensionSource<X>,
986    /// Optional filter for this syntax.
987    pub filter: Option<Box<Expr<X>>>,
988    /// Source location and node identity.
989    pub meta: Meta,
990}
991
992/// The source of a [`ListComprehension`] — the thing iterated over.
993///
994/// A general list-valued expression, or the DuckDB column-star source that is valid
995/// only inside `COLUMNS(…)` (`[x for x in *]`, `[x for x in (* EXCLUDE (i))]`). The star
996/// is a distinct spelling rather than an expression because DuckDB has no bare `*`
997/// value expression — it is grammar special to this slot — and because it cannot
998/// canonicalize to `COLUMNS(*)` (DuckDB rejects a `COLUMNS` nested inside another).
999#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1000#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1001#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1002pub enum ComprehensionSource<X: Extension = NoExt> {
1003    /// A general list-valued expression source (`[y for y in <expr>]`).
1004    Expr {
1005        /// Expression evaluated by this syntax.
1006        expr: Box<Expr<X>>,
1007        /// Source location and node identity.
1008        meta: Meta,
1009    },
1010    /// The DuckDB column-star source: a bare `*` or a parenthesized `(* …)`, optionally
1011    /// carrying the `EXCLUDE`/`REPLACE`/`RENAME` wildcard modifiers (only legal in the
1012    /// parenthesized form). `parenthesized` records the `(…)` spelling so it round-trips.
1013    Star {
1014        /// Whether the parenthesized form was present in the source.
1015        parenthesized: bool,
1016        /// Options supplied in source order.
1017        options: Option<Box<WildcardOptions<X>>>,
1018        /// Source location and node identity.
1019        meta: Meta,
1020    },
1021}
1022
1023/// A DuckDB struct literal `{'key': value, …}`.
1024///
1025/// The fields keep source order (a struct is positional as well as named). Boxed
1026/// inside [`Expr::Struct`] to keep the hot expression enum within its size budget.
1027#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1028#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1029#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1030pub struct StructExpr<X: Extension = NoExt> {
1031    /// fields in source order.
1032    pub fields: ThinVec<StructField<X>>,
1033    /// Source location and node identity.
1034    pub meta: Meta,
1035}
1036
1037/// One `key: value` field of a [`StructExpr`].
1038///
1039/// The key is a field *name*, not a value — DuckDB rejects a non-identifier key
1040/// (`{1: 'x'}` is a syntax error) — so it is an interned [`Symbol`] rather than an
1041/// expression. One canonical shape covers the three key spellings; the
1042/// [`StructKeySpelling`] tag records which the source used so `{'a': 1}`, `{a: 1}`,
1043/// and `{"a": 1}` each round-trip exactly.
1044#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1045#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1046#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1047pub struct StructField<X: Extension = NoExt> {
1048    /// The interned field name.
1049    pub key: Symbol,
1050    /// Source spelling used for the key spelling.
1051    pub key_spelling: StructKeySpelling,
1052    /// Value supplied by this syntax.
1053    pub value: Expr<X>,
1054    /// Source location and node identity.
1055    pub meta: Meta,
1056}
1057
1058/// How a [`StructField`] key was spelled.
1059///
1060/// The surface-form tag for the one canonical struct-field shape,
1061/// mirroring [`CastSyntax`]: DuckDB folds all three to the same field name, so only
1062/// this tag and the rendered spelling distinguish them.
1063#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1064#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1065#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1066pub enum StructKeySpelling {
1067    /// A single-quoted string key, as in `{'key': v}`.
1068    SingleQuoted,
1069    /// A bare identifier key, as in `{key: v}`.
1070    Bare,
1071    /// A double-quoted identifier key, as in `{"key": v}`.
1072    DoubleQuoted,
1073}
1074
1075/// A BigQuery `STRUCT(...)` value constructor — the typeless `STRUCT(1, 2)` /
1076/// `STRUCT(x AS a)` forms and the typed `STRUCT<a INT64, b STRING>(1, 'x')` form.
1077///
1078/// Mirrors sqlparser-rs's `Expr::Struct { values, fields }`, reshaped to this crate's
1079/// canonical-shape-plus-owned-sub-nodes convention: `args` are the positional value
1080/// arguments (each optionally `AS`-aliased in the typeless form, which sqlparser-rs
1081/// spells as a separate `Expr::Named` node), and `fields` are the typed field
1082/// declarations from the `STRUCT<...>` angle-bracket prefix. `fields` is empty for the
1083/// typeless form — BigQuery requires at least one type inside `<...>`, so an empty list
1084/// is the unambiguous "typeless" marker and no separate flag is needed. Boxed inside
1085/// [`Expr::StructConstructor`] to keep the hot expression enum within its size budget.
1086#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1087#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1088#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1089pub struct StructConstructorExpr<X: Extension = NoExt> {
1090    /// fields in source order.
1091    pub fields: ThinVec<StructConstructorField<X>>,
1092    /// Arguments in source order.
1093    pub args: ThinVec<StructConstructorArg<X>>,
1094    /// Source location and node identity.
1095    pub meta: Meta,
1096}
1097
1098/// One typed field declaration of a typed [`StructConstructorExpr`]: `name TYPE` in
1099/// `STRUCT<a INT64>`, or an anonymous `TYPE` in `STRUCT<INT64>` (BigQuery permits both).
1100///
1101/// A dedicated node rather than the type-side
1102/// [`StructTypeField`](crate::ast::StructTypeField): BigQuery's angle-bracket field name
1103/// is *optional*, whereas the DuckDB paren composite type ([`DataType::Struct`](crate::ast::DataType))
1104/// requires it, so the two grammars carry different name arities and cannot share one shape.
1105#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1106#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1107#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1108pub struct StructConstructorField<X: Extension = NoExt> {
1109    /// Name referenced by this syntax.
1110    pub name: Option<Ident>,
1111    /// Data type named by this syntax.
1112    pub ty: DataType<X>,
1113    /// Source location and node identity.
1114    pub meta: Meta,
1115}
1116
1117/// One positional value argument of a [`StructConstructorExpr`], optionally aliased with
1118/// `AS name` (`STRUCT(1 AS a)`).
1119///
1120/// The alias is only grammatical in the typeless form; the typed form takes its field
1121/// names from the `STRUCT<...>` prefix, so `alias` is always `None` there.
1122#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1123#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1124#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1125pub struct StructConstructorArg<X: Extension = NoExt> {
1126    /// Value supplied by this syntax.
1127    pub value: Expr<X>,
1128    /// Alias assigned by this syntax.
1129    pub alias: Option<Ident>,
1130    /// Source location and node identity.
1131    pub meta: Meta,
1132}
1133
1134/// A DuckDB map literal `MAP {k1: v1, k2: v2, …}` (possibly empty `MAP {}`).
1135///
1136/// Unlike a [`StructExpr`] field name, a map key is an arbitrary value expression
1137/// (`MAP {1: 'a'}`, `MAP {[1,2]: 'x'}`), so the entries hold [`MapEntry`] expression
1138/// pairs. Boxed inside [`Expr::Map`] to keep the hot expression enum within its size
1139/// budget.
1140#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1141#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1142#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1143pub struct MapExpr<X: Extension = NoExt> {
1144    /// entries in source order.
1145    pub entries: ThinVec<MapEntry<X>>,
1146    /// Source location and node identity.
1147    pub meta: Meta,
1148}
1149
1150#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1151#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1152#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1153/// An SQL map entry.
1154pub struct MapEntry<X: Extension = NoExt> {
1155    /// The map entry key expression.
1156    pub key: Expr<X>,
1157    /// Value supplied by this syntax.
1158    pub value: Expr<X>,
1159    /// Source location and node identity.
1160    pub meta: Meta,
1161}
1162
1163/// A row constructor: explicit `ROW(a, b, …)` (or empty `ROW()`) or the implicit
1164/// parenthesized `(a, b, …)` form.
1165///
1166/// `explicit` records whether the `ROW` keyword was written, since the two
1167/// spellings are otherwise the same construct. Boxed inside
1168/// [`Expr::Row`] to keep the hot expression enum within its size budget.
1169#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1170#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1171#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1172pub struct RowExpr<X: Extension = NoExt> {
1173    /// fields in source order.
1174    pub fields: ThinVec<Expr<X>>,
1175    /// Whether the `ROW` keyword was written (vs. the implicit `(a, b)` form).
1176    pub explicit: bool,
1177    /// Source location and node identity.
1178    pub meta: Meta,
1179}
1180
1181/// A PostgreSQL composite navigation off a value: `(expr).field`, `(expr).*`, or a
1182/// whole-row `tbl.*` used as a value.
1183///
1184/// PostgreSQL models both selectors as one `.`-indirection off an operand (a
1185/// `String` attribute name or an `A_Star`); this shares that shape, with
1186/// [`selector`](Self::selector) distinguishing the named field from the star. A
1187/// whole-row `tbl.*` written in a *value* position (a `ROW(...)` field, a function
1188/// argument, a cast operand) parses to this node with the bare column as `base` and a
1189/// [`FieldSelector::Star`] selector — distinct from a select-list `tbl.*`, which is a
1190/// [`SelectItem::QualifiedWildcard`](crate::ast::SelectItem::QualifiedWildcard)
1191/// projection target. Boxed inside [`Expr::FieldSelection`] to keep the hot expression
1192/// enum within its size budget.
1193#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1194#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1195#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1196pub struct FieldSelectionExpr<X: Extension = NoExt> {
1197    /// The composite value being navigated.
1198    pub base: Expr<X>,
1199    /// Which selector applies — a named field or the whole-composite star; see [`FieldSelector`].
1200    pub selector: FieldSelector,
1201    /// Source location and node identity.
1202    pub meta: Meta,
1203}
1204
1205/// Which composite selector a [`FieldSelectionExpr`] applies to its `base`.
1206///
1207/// PostgreSQL's `.`-indirection element is either a named attribute (`.field`) or the
1208/// whole-composite star (`.*`); the two are the same grammar production and bind
1209/// identically, so one node carries both with this tag. The star form is gated apart
1210/// from the named form ([`ExpressionSyntax::field_wildcard`](crate::dialect::ExpressionSyntax::field_wildcard)
1211/// vs [`field_selection`](crate::dialect::ExpressionSyntax::field_selection)): DuckDB
1212/// admits `.field` but rejects every `.*` value expansion (engine-probed 1.5.4).
1213#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1214#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1215#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1216pub enum FieldSelector {
1217    /// A named composite attribute: `(expr).field`.
1218    Field {
1219        /// The selected attribute name.
1220        field: Ident,
1221        /// Source location and node identity.
1222        meta: Meta,
1223    },
1224    /// The whole-composite / whole-row star: `(expr).*` or a value-position `tbl.*`.
1225    Star {
1226        /// Source location and node identity.
1227        meta: Meta,
1228    },
1229}
1230
1231/// A PostgreSQL explicit-operator infix application: `a OPERATOR(schema.op) b`.
1232///
1233/// `OPERATOR(...)` names an operator explicitly, optionally schema-qualified
1234/// (`OPERATOR(pg_catalog.+)`), and binds at PostgreSQL's "any other operator" rank
1235/// — the same `%left Op OPERATOR` precedence as `||`. Boxed inside
1236/// [`Expr::NamedOperator`] to keep the hot expression enum within its size budget.
1237#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1238#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1239#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1240pub struct NamedOperatorExpr<X: Extension = NoExt> {
1241    /// Left-hand operand.
1242    pub left: Expr<X>,
1243    /// The optional schema qualification (`pg_catalog` in `OPERATOR(pg_catalog.+)`);
1244    /// an empty name when the operator is unqualified (`OPERATOR(+)`) or bare.
1245    pub schema: ObjectName,
1246    /// The operator symbol spelling (`+`, `->>`, `~`, `<->`), interned exact-case so it
1247    /// round-trips. The operator is always symbolic, never a word, so it is held as
1248    /// a bare [`Symbol`] rather than an [`Ident`].
1249    pub op: Symbol,
1250    /// Right-hand operand.
1251    pub right: Expr<X>,
1252    /// Which surface form spelled the operator application — the explicit
1253    /// `OPERATOR(schema.op)` keyword or a bare `a op b`.
1254    pub spelling: NamedOperatorSpelling,
1255    /// Source location and node identity.
1256    pub meta: Meta,
1257}
1258
1259/// How a [`NamedOperatorExpr`] was spelled — the whole surface form.
1260///
1261/// Under the general operator surface
1262/// ([`OperatorSyntax::custom_operators`](crate::dialect::OperatorSyntax::custom_operators);
1263/// PostgreSQL is the current enabler) a dialect admits ANY operator from its `Op` character
1264/// class over a user-extensible operator set, and a bare `a ~ b` / `a <-> b` / `a @#@ b` is
1265/// the SAME grammar production as the explicit `a OPERATOR(schema.op) b` — both name the
1266/// operator, at the "any other operator" precedence. This tag records which surface the
1267/// source used so rendering round-trips exactly (PostgreSQL's own deparse keeps the bare form
1268/// bare and the qualified form wrapped: `OPERATOR(~)` normalizes to `~`, but
1269/// `OPERATOR(pg_catalog.+)` stays wrapped — engine-measured). The [`Bare`](Self::Bare)
1270/// form is always unqualified (a bare operator carries no schema); the
1271/// [`OperatorKeyword`](Self::OperatorKeyword) form carries the optional schema.
1272#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1273#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1274#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1275pub enum NamedOperatorSpelling {
1276    /// The explicit `a OPERATOR(schema.op) b` keyword form (may be schema-qualified).
1277    OperatorKeyword,
1278    /// A bare `a op b` — a general symbolic operator (regex `~`/`!~`/`~*`/`!~*`,
1279    /// geometric/network/text-search ops, or a fully user-defined operator), munched under
1280    /// [`OperatorSyntax::custom_operators`](crate::dialect::OperatorSyntax::custom_operators).
1281    Bare,
1282}
1283
1284/// A prefix operator application: the symbolic operator token, then its operand, as in
1285/// `@ -5` (absolute value), `|/ 25` (square root), `||/ 27` (cube root), `@@ box`
1286/// (centre point), or a fully user-defined prefix operator `@#@ 24`.
1287///
1288/// Under the general operator surface
1289/// ([`OperatorSyntax::custom_operators`](crate::dialect::OperatorSyntax::custom_operators);
1290/// PostgreSQL is the current enabler) a dialect admits ANY operator from its `Op` character
1291/// class in prefix position (PostgreSQL grammar `qual_Op a_expr %prec Op`), so — like the
1292/// bare infix [`NamedOperatorExpr`] — this is one canonical shape carrying the interned
1293/// operator spelling rather than an enumerated set. It binds at the "any other operator" rank
1294/// ([`BindingPowerTable::any_operator`](crate::precedence::BindingPowerTable::any_operator)),
1295/// the same tier the bare infix operators use. Boxed inside [`Expr::PrefixOperator`] to
1296/// keep the hot expression enum within its size budget. Distinct from the fixed
1297/// [`UnaryOperator`] prefixes (`+`/`-`/`NOT`/`~`), which have their own tight precedence
1298/// and dedicated node.
1299#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1300#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1301#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1302pub struct PrefixOperatorExpr<X: Extension = NoExt> {
1303    /// The operator symbol spelling (`@`, `|/`, `@@`, `@#@`), interned exact-case so it
1304    /// round-trips. Always symbolic, never a word, so a bare [`Symbol`] rather than an
1305    /// [`Ident`].
1306    pub op: Symbol,
1307    /// The operand the prefix operator applies to.
1308    pub operand: Expr<X>,
1309    /// Source location and node identity.
1310    pub meta: Meta,
1311}
1312
1313/// A postfix operator application: the operand, then the trailing symbolic operator token, as
1314/// in `10 !` (factorial), `1 ~`, `1 <->`, `1 &`, or a fully user-defined operator `1 @#@`.
1315///
1316/// DuckDB keeps the generalized postfix reading PostgreSQL removed in version 14
1317/// ([`OperatorSyntax::postfix_operators`](crate::dialect::OperatorSyntax::postfix_operators);
1318/// DuckDB is the only enabler): any operator from its `Op` character class — the general
1319/// residue (the interned [`Symbol`] of a `Custom` operator token, e.g. `!!`/`<->`/`~*`), the
1320/// lone `~`/`!`/`@`, and the dedicated symbolic infix operators (`&`/`|`/`<<`/`>>`/`||`/`<@`/
1321/// `@>`/`^@`) — folds here when no operand follows it. Like the bare infix
1322/// [`NamedOperatorExpr`] it is one canonical shape carrying the interned operator spelling
1323/// rather than an enumerated set. It binds at the "any other operator" left rank
1324/// ([`BindingPowerTable::any_operator`](crate::precedence::BindingPowerTable::any_operator)),
1325/// so a tighter operand groups first (`2 * 3 !` is `(2 * 3)!`) while the postfix stays a
1326/// complete unary token (`1 ! < 2` is `(1!) < 2`). Boxed inside [`Expr::PostfixOperator`] to
1327/// keep the hot expression enum within its size budget. The infix reading wins whenever an
1328/// operand *does* follow (`1 ! + 2` is the infix `1 ! (+2)`), so this node is reached only in
1329/// the operand-absent position.
1330#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1331#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1332#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1333pub struct PostfixOperatorExpr<X: Extension = NoExt> {
1334    /// The operand the postfix operator applies to.
1335    pub operand: Expr<X>,
1336    /// The operator symbol spelling (`!`, `~`, `<->`, `@#@`), interned exact-case so it
1337    /// round-trips. Always symbolic, never a word, so a bare [`Symbol`] rather than an
1338    /// [`Ident`].
1339    pub op: Symbol,
1340    /// Source location and node identity.
1341    pub meta: Meta,
1342}
1343
1344/// A DuckDB single-arrow lambda: bound parameters and a body, as in
1345/// `list_transform([1, 2, 3], x -> x + 1)` or `list_reduce(l, (x, y) -> x + y)`.
1346///
1347/// One canonical shape for the new construct: a lambda has no existing
1348/// shape to fold onto — DuckDB's own tree confirms it with a dedicated `LAMBDA`
1349/// node. The parameters are plain unqualified identifiers, never expressions:
1350/// DuckDB's binder rejects anything else ("Parameters must be unqualified
1351/// comma-separated names like x or (x, y)", probed on 1.5.4), and our parser
1352/// applies that same shape test to the `->` left operand to pick this node over
1353/// the JSON-arrow operator — see
1354/// [`OperatorSyntax::lambda_expressions`](crate::dialect::OperatorSyntax::lambda_expressions)
1355/// for the whole disambiguation story. The `->` binds at the JSON-arrow rank (it is
1356/// the same token), so the body captures everything tighter (`x -> x + 1` is
1357/// `x -> (x + 1)`). Boxed inside [`Expr::Lambda`] to keep the hot expression enum
1358/// within its size budget.
1359#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1360#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1361#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1362pub struct LambdaExpr<X: Extension = NoExt> {
1363    /// The bound parameter names, in source order (always at least one — DuckDB
1364    /// rejects an empty `() -> …` at parse time).
1365    pub params: ThinVec<Ident>,
1366    /// Exact source spelling retained for faithful rendering.
1367    pub spelling: LambdaParamSpelling,
1368    /// The lambda body expression.
1369    pub body: Expr<X>,
1370    /// Source location and node identity.
1371    pub meta: Meta,
1372}
1373
1374/// How a [`LambdaExpr`] was spelled — the whole surface form, arrow versus keyword.
1375///
1376/// The surface-form tag for the one canonical lambda shape, mirroring
1377/// [`ArraySpelling`]. The three arrow forms differ only in how the parameter list is
1378/// delimited: DuckDB accepts a bare single name, a parenthesized list, and — because
1379/// `(x, y)` and `ROW(x, y)` parse to the same row node there — the explicit `ROW(…)`
1380/// keyword form (all three probed against 1.5.4). The [`Keyword`](Self::Keyword) form is
1381/// the python-style spelling `lambda x, y: body` DuckDB 1.3.0 introduced (and now
1382/// prefers over the deprecated arrow): a distinct surface for the same node. The forms
1383/// bind the same parameters, so only this tag and the rendered spelling distinguish them.
1384/// [`Bare`](Self::Bare) implies exactly one parameter; the parser upholds that invariant.
1385#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1386#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1387#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1388pub enum LambdaParamSpelling {
1389    /// A bare single parameter with the arrow, as in `x -> x + 1`.
1390    Bare,
1391    /// A parenthesized list with the arrow, as in `(x, y) -> x + y` (also the single
1392    /// `(x) -> …`).
1393    Parenthesized,
1394    /// The explicit row-keyword list with the arrow, as in `ROW(x, y) -> x + y`.
1395    RowKeyword,
1396    /// The python-style keyword spelling `lambda x, y: body` (comma-separated bare names,
1397    /// a `:` before the body). DuckDB's current preferred spelling.
1398    Keyword,
1399}
1400
1401/// A prepared-statement parameter placeholder.
1402///
1403/// One canonical shape: the placeholder *meaning* is the data, while
1404/// which surface forms a dialect accepts is gated by
1405/// [`ParameterSyntax`](crate::dialect::ParameterSyntax). The positional index, the
1406/// anonymous-by-occurrence form, and a by-name binding are distinct values (not a
1407/// single surface tag) because they differ semantically, not merely in spelling.
1408#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1409#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1410#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1411pub enum ParameterKind {
1412    /// PostgreSQL positional `$1`, `$2`, … — the carried value is the 1-based index.
1413    Positional(u32),
1414    /// SQLite numbered `?1`, `?123` — the `?`-spelled positional parameter (distinct from
1415    /// PostgreSQL's `$`-spelled [`Positional`](Self::Positional) so it round-trips as `?N`).
1416    /// The carried value is the 1-based index; SQLite restricts it to `1..=32766`
1417    /// (`SQLITE_MAX_VARIABLE_NUMBER`), enforced at parse time.
1418    Numbered(u32),
1419    /// Anonymous positional `?` (ODBC/JDBC); its ordinal is its occurrence order.
1420    Anonymous,
1421    /// A named placeholder bound by name rather than position (`:name`, `@name`).
1422    ///
1423    /// `name` is the interned name *without* its sigil; [`ParameterSigil`] records
1424    /// which sigil spelled it, so a dialect accepting both forms round-trips each
1425    /// exactly. The by-name binding is a distinct meaning from the
1426    /// positional/anonymous forms, hence a value here rather than only a tag.
1427    Named {
1428        /// Name referenced by this syntax.
1429        name: Symbol,
1430        /// Which sigil introduced the name; see [`ParameterSigil`].
1431        sigil: ParameterSigil,
1432    },
1433}
1434
1435/// Which sigil introduced a named parameter placeholder.
1436///
1437/// The surface-form tag on [`ParameterKind::Named`]: one canonical named-parameter
1438/// shape covers the spellings, and this records which the source used —
1439/// the colon form (`:name`; Oracle, SQLite, JDBC/psycopg), the at-sign form
1440/// (`@name`; T-SQL, SQLite), or the dollar form (`$name`; SQLite) — so rendering
1441/// restores the original sigil.
1442#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1443#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1444#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1445pub enum ParameterSigil {
1446    /// `:name`.
1447    Colon,
1448    /// `@name`.
1449    At,
1450    /// `$name` — SQLite's dollar-named placeholder. Disjoint from the PostgreSQL
1451    /// positional `$1` ([`ParameterKind::Positional`]) by its follow byte (an
1452    /// identifier, not a digit).
1453    Dollar,
1454}
1455
1456/// Which session-variable form an [`Expr::SessionVariable`] names.
1457///
1458/// One flat enum over the four valid MySQL spellings, so an impossible pairing — a
1459/// `@`-user variable carrying an explicit system scope — is unrepresentable rather
1460/// than a runtime invariant (make illegal states unrepresentable). The system forms
1461/// differ semantically, not merely in spelling: `@@x` reads the server's implicit
1462/// scope for the variable while `@@global.x` / `@@session.x` force one, so each is
1463/// its own value, and the sigil (`@` vs `@@`) is recovered from the
1464/// variant so every form round-trips.
1465#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1466#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1467#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1468pub enum SessionVariableKind {
1469    /// `@name` — a user-defined session variable.
1470    User,
1471    /// `@@name` — a system variable at the server's implicit scope.
1472    System,
1473    /// `@@global.name` — a system variable read at global scope.
1474    SystemGlobal,
1475    /// `@@session.name` — a system variable read at session scope.
1476    SystemSession,
1477}
1478
1479/// A function or aggregate call: `name([DISTINCT] args | *)` plus the optional
1480/// aggregate modifiers an ordered-set / filtered aggregate carries.
1481///
1482/// Boxed inside [`Expr::Function`] so the hot expression enum stays within its
1483/// size budget. The same canonical shape covers plain scalar calls
1484/// (`COALESCE`, `NULLIF`, `GREATEST`, `LEAST`, …) and aggregates; unused modifier
1485/// fields stay at their empty defaults (one shape per construct).
1486#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1487#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1488#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1489pub struct FunctionCall<X: Extension = NoExt> {
1490    /// Name referenced by this syntax.
1491    pub name: ObjectName,
1492    /// The `ALL` / `DISTINCT` argument quantifier, as in `count(DISTINCT x)` or
1493    /// `count(ALL x)`; `None` for a call written with no quantifier. `DISTINCT ON`
1494    /// is a SELECT-list-only form, so an aggregate never carries it.
1495    pub quantifier: Option<SetQuantifier>,
1496    /// The argument list, each a positional or PostgreSQL named argument (see
1497    /// [`FunctionArg`]). Empty for a no-arg call or a `*` call.
1498    pub args: ThinVec<FunctionArg<X>>,
1499    /// `*` as the sole argument, as in `count(*)`. Mutually exclusive with `args`.
1500    pub wildcard: bool,
1501    /// `ORDER BY` inside an ordered-set aggregate, as in `array_agg(x ORDER BY y)`.
1502    pub order_by: ThinVec<OrderByExpr<X>>,
1503    /// `WITHIN GROUP (ORDER BY <keys>)` — the SQL:2008 ordered-set aggregate clause
1504    /// (T612/T614), as in `percentile_cont(0.5) WITHIN GROUP (ORDER BY x)`. A
1505    /// post-argument clause parsed after the `)`, mirroring the optionality of
1506    /// [`filter`](Self::filter) and [`over`](Self::over); `None` when absent, and the
1507    /// list is non-empty when present (the grammar requires a sort key). Distinct from
1508    /// the in-parenthesis [`order_by`](Self::order_by): PostgreSQL admits at most one of
1509    /// the two, and they render in different positions.
1510    pub within_group: Option<ThinVec<OrderByExpr<X>>>,
1511    /// `SEPARATOR <string>` — the MySQL `GROUP_CONCAT(... SEPARATOR ',')` delimiter,
1512    /// an in-parenthesis argument tail parsed after [`order_by`](Self::order_by) and
1513    /// before the closing `)`. Gated for acceptance by
1514    /// [`AggregateCallSyntax::group_concat_separator`](crate::dialect::ExpressionSyntax):
1515    /// `None` when the dialect leaves the flag off or the clause is unwritten. The
1516    /// delimiter is always a string constant, so it is a bare [`Literal`] rather than a
1517    /// general expression — one canonical field on the shared call shape,
1518    /// not a `GROUP_CONCAT`-specific node.
1519    pub separator: Option<Literal>,
1520    /// `FILTER (WHERE <predicate>)` applied to an aggregate.
1521    pub filter: Option<Box<Expr<X>>>,
1522    /// Whether the [`filter`](Self::filter) clause wrote the standard `WHERE` keyword
1523    /// (`FILTER (WHERE p)`) or DuckDB's keyword-less `FILTER (p)`. A pure fidelity tag —
1524    /// the [`FilterWhereSpelling`] round-trips the surface spelling. Carries the canonical
1525    /// [`Where`](FilterWhereSpelling::Where) when no filter is present.
1526    pub filter_where: FilterWhereSpelling,
1527    /// The `OVER (…)` / `OVER name` window clause that makes this a window
1528    /// function call; `None` for a plain scalar or aggregate call.
1529    pub over: Option<WindowSpec<X>>,
1530    /// `IGNORE NULLS` / `RESPECT NULLS` null-treatment written *inside* the call
1531    /// parentheses, after the argument list (and any in-parenthesis `ORDER BY`), as in
1532    /// DuckDB's `last(s IGNORE NULLS) OVER (…)`. `None` when unwritten. One canonical
1533    /// field on the shared call shape, the [`NullTreatment`] tag recording
1534    /// which the source used so it round-trips. Gated for acceptance by
1535    /// [`AggregateCallSyntax::null_treatment`](crate::dialect::CallSyntax): DuckDB spells the
1536    /// SQL:2016 null-treatment inside the parentheses (the standard's post-`)` position
1537    /// engine-rejects on 1.5.4), so it rides the in-parenthesis tail like
1538    /// [`separator`](Self::separator) rather than a post-argument clause. When the
1539    /// dialect leaves the flag off, `IGNORE`/`RESPECT` is left unconsumed and the
1540    /// unmatched `)` surfaces as a clean parse error.
1541    pub null_treatment: Option<NullTreatment>,
1542    /// The MySQL window-function post-`)` tail — the SQL:2016
1543    /// `[FROM {FIRST | LAST}] [{RESPECT | IGNORE} NULLS]` clauses a null-treatment
1544    /// window function carries *between* its argument `)` and its `OVER` clause, as in
1545    /// `NTH_VALUE(x, 2) FROM FIRST RESPECT NULLS OVER (…)`. `None` when no tail was
1546    /// written. A separate field from the in-parenthesis DuckDB
1547    /// [`null_treatment`](Self::null_treatment) precisely because the surface *position*
1548    /// differs: MySQL spells these after the `)` and rejects the in-paren spelling
1549    /// (`ER_PARSE_ERROR`, 1064), while DuckDB rejects the post-`)` one — so the two
1550    /// positions round-trip as distinct fields on the shared call shape
1551    /// rather than a shared field plus a position tag the DuckDB path would also carry.
1552    /// Gated for acceptance by
1553    /// [`AggregateCallSyntax::window_function_tail`](crate::dialect::CallSyntax): MySQL admits it
1554    /// only on the null-treatment window functions
1555    /// (`LEAD`/`LAG`/`FIRST_VALUE`/`LAST_VALUE`/`NTH_VALUE`, with `FROM FIRST` on
1556    /// `NTH_VALUE` alone), enforced by the parser's window-grammar gate.
1557    pub window_tail: Option<WindowFunctionTail>,
1558    /// Source location and node identity.
1559    pub meta: Meta,
1560}
1561
1562/// One argument of a [`FunctionCall`]: a bare positional value, or a PostgreSQL
1563/// named argument `name => value` / `name := value`, optionally prefixed by the
1564/// `VARIADIC` array-spread marker (`VARIADIC arr`, `VARIADIC name => arr`).
1565///
1566/// One canonical shape covers all three spellings: a positional
1567/// argument is `{ name: None, syntax: Positional, value }`, while a named argument
1568/// carries `name: Some(_)` and an [`ArgSyntax`] surface tag recording which arrow
1569/// the source wrote so it round-trips. `name` being `Some` ⟺ `syntax` is a named
1570/// form is an invariant the parser upholds. The argument is a spanned node so the
1571/// `name => value` extent has side-table identity of its own.
1572#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1573#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1574#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1575pub struct FunctionArg<X: Extension = NoExt> {
1576    /// The argument name for a named argument (`name` in `name => value`);
1577    /// `None` for a positional argument.
1578    pub name: Option<Symbol>,
1579    /// The `VARIADIC` marker that spreads an array over a variadic parameter
1580    /// (`f(a, VARIADIC arr)`). A parse-layer prefix the parser admits only on the
1581    /// *last* argument of a call and never alongside an `ALL`/`DISTINCT` quantifier
1582    /// (both PostgreSQL and DuckDB parse-reject the other positions); it composes with
1583    /// the `name => value` named form, so it is a flag on the argument rather than a
1584    /// separate call-level field. `false` for an ordinary argument.
1585    pub variadic: bool,
1586    /// Which surface form spelled this argument (positional, `=>`, or `:=`).
1587    pub syntax: ArgSyntax,
1588    /// Value supplied by this syntax.
1589    pub value: Expr<X>,
1590    /// Source location and node identity.
1591    pub meta: Meta,
1592}
1593
1594/// How a [`FunctionArg`] was spelled.
1595///
1596/// The surface-form tag for the one canonical [`FunctionArg`] shape,
1597/// mirroring [`CastSyntax`]: a positional argument carries no name, while the two
1598/// PostgreSQL named-argument arrows are otherwise the same construct (`=>` is
1599/// current, `:=` is the deprecated spelling PostgreSQL still accepts), so this
1600/// records which the source used to restore it on render.
1601#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1602#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1603#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1604pub enum ArgSyntax {
1605    /// A bare positional argument, as in `f(x)`.
1606    Positional,
1607    /// A named argument written `name => value`.
1608    Arrow,
1609    /// A named argument written with the deprecated `name := value` spelling.
1610    ColonEquals,
1611}
1612
1613/// The `IGNORE NULLS` / `RESPECT NULLS` null-treatment on a window/aggregate
1614/// [`FunctionCall`] (SQL:2016 window functions; DuckDB spells it inside the call
1615/// parentheses).
1616///
1617/// Two spellings of the same construct kept as data so the surface round-trips
1618/// (the [`ArgSyntax`] precedent): `RESPECT NULLS` is the default the
1619/// engine may canonicalize away, but the parser preserves whichever the source wrote.
1620#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1621#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1622#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1623pub enum NullTreatment {
1624    /// `IGNORE NULLS` — skip null values when computing the window/aggregate result.
1625    IgnoreNulls,
1626    /// `RESPECT NULLS` — include null values (the default).
1627    RespectNulls,
1628}
1629
1630/// The MySQL window-function post-`)` argument tail (SQL:2016):
1631/// `[FROM {FIRST | LAST}] [{RESPECT | IGNORE} NULLS]`, written between a null-treatment
1632/// window function's argument `)` and its `OVER` clause.
1633///
1634/// Both clauses are independently optional and appear in this fixed order (the reverse
1635/// order is a mysql:8 `ER_PARSE_ERROR`); the struct is only constructed when at least
1636/// one is present, so a [`FunctionCall::window_tail`] of `Some` always carries a
1637/// clause. The [`NullTreatment`] value is reused from the in-parenthesis DuckDB form —
1638/// only its surface *position* differs (post-`)` here, in-paren there), which is why
1639/// this is a separate field rather than a shared one.
1640#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1641#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1642#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1643pub struct WindowFunctionTail {
1644    /// `FROM FIRST` / `FROM LAST` — which end of the frame `NTH_VALUE` counts its `N`th
1645    /// row from. `None` when unwritten. On mysql:8 only `FROM FIRST` is accepted (and
1646    /// only on `NTH_VALUE`); `FROM LAST` is grammar-admitted but feature-rejected
1647    /// (`ER_NOT_SUPPORTED_YET`, 1235), so the MySQL parser never produces
1648    /// [`FromFirstLast::Last`].
1649    pub from_first_last: Option<FromFirstLast>,
1650    /// `RESPECT NULLS` / `IGNORE NULLS` — the post-`)` null treatment. `None` when
1651    /// unwritten. On mysql:8 only `RESPECT NULLS` is accepted; `IGNORE NULLS` is
1652    /// grammar-admitted but feature-rejected (`ER_NOT_SUPPORTED_YET`, 1235), so the
1653    /// MySQL parser never produces [`NullTreatment::IgnoreNulls`] in this position.
1654    pub null_treatment: Option<NullTreatment>,
1655}
1656
1657/// `FROM FIRST` / `FROM LAST` — which end of the window frame `NTH_VALUE` counts its
1658/// `N`th row from (SQL:2016 `from_first_last`).
1659///
1660/// Two spellings of the same construct kept as data so the surface round-trips (the
1661/// [`NullTreatment`] precedent). `FROM FIRST` is the default; `FROM LAST` completes the
1662/// surface for a dialect that admits it (mysql:8 feature-rejects it, so its parser only
1663/// ever produces `First`).
1664#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1665#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1666#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1667pub enum FromFirstLast {
1668    /// `FROM FIRST` — count from the first row of the frame (the default).
1669    First,
1670    /// `FROM LAST` — count from the last row of the frame.
1671    Last,
1672}
1673
1674/// A `CASE` expression in either standard form.
1675///
1676/// Searched `CASE WHEN cond THEN result … [ELSE result] END` has no `operand`;
1677/// simple `CASE operand WHEN value THEN result … END` carries the `operand` each
1678/// `WHEN` value is compared against. Boxed inside [`Expr::Case`] to keep the hot
1679/// expression enum within its size budget.
1680#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1681#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1682#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1683pub struct CaseExpr<X: Extension = NoExt> {
1684    /// Optional operand for this syntax.
1685    pub operand: Option<Box<Expr<X>>>,
1686    /// The `WHEN … THEN …` branches, in source order (always at least one).
1687    pub when_clauses: ThinVec<WhenClause<X>>,
1688    /// Optional else result for this syntax.
1689    pub else_result: Option<Box<Expr<X>>>,
1690    /// Source location and node identity.
1691    pub meta: Meta,
1692}
1693
1694#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1695#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1696#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1697/// An SQL when clause.
1698pub struct WhenClause<X: Extension = NoExt> {
1699    /// Predicate that controls this clause.
1700    pub condition: Expr<X>,
1701    /// The value produced when the condition holds.
1702    pub result: Expr<X>,
1703    /// Source location and node identity.
1704    pub meta: Meta,
1705}
1706
1707/// An `EXTRACT(<field> FROM <source>)` expression.
1708///
1709/// `field` is the datetime field name (`YEAR`, `MONTH`, …) interned as an
1710/// identifier; `source` is the value it is pulled from. Boxed inside
1711/// [`Expr::Extract`] to keep the hot expression enum within its size budget.
1712#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1713#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1714#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1715pub struct ExtractExpr<X: Extension = NoExt> {
1716    /// The datetime field name (`YEAR`, `MONTH`, …).
1717    pub field: Ident,
1718    /// The value the field is pulled from.
1719    pub source: Box<Expr<X>>,
1720    /// Source location and node identity.
1721    pub meta: Meta,
1722}
1723
1724/// The SQL/JSON `FORMAT JSON [ENCODING <enc>]` specifier (SQL:2016).
1725///
1726/// The only spellable format is `JSON` (PostgreSQL rejects `FORMAT JSONB` at raw
1727/// parse), so the struct's mere presence records "`FORMAT JSON` was written"; the
1728/// optional `encoding` is the `ENCODING` tail. PostgreSQL validates the encoding
1729/// name against `UTF8`/`UTF16`/`UTF32` *at raw parse* ("unrecognized JSON encoding"),
1730/// so it is a closed [`JsonEncoding`] rather than a free identifier.
1731#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1732#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1733#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1734pub struct JsonFormat {
1735    /// Optional encoding for this syntax.
1736    pub encoding: Option<JsonEncoding>,
1737}
1738
1739/// A SQL/JSON `ENCODING` name — the closed set PostgreSQL accepts at raw parse.
1740#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1741#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1742#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1743pub enum JsonEncoding {
1744    /// `UTF8` encoding.
1745    Utf8,
1746    /// `UTF16` encoding.
1747    Utf16,
1748    /// `UTF32` encoding.
1749    Utf32,
1750}
1751
1752/// A SQL/JSON value expression — an operand with an optional [`JsonFormat`]
1753/// (`<expr> [FORMAT JSON [ENCODING …]]`, PostgreSQL's `JsonValueExpr`).
1754///
1755/// The context item of a query function, each element of a `JSON_ARRAY` value
1756/// list, the value half of an object member, and the argument of `JSON`/
1757/// `JSON_SERIALIZE`/aggregates all take this shape.
1758#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1759#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1760#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1761pub struct JsonValueExpr<X: Extension = NoExt> {
1762    /// Expression evaluated by this syntax.
1763    pub expr: Box<Expr<X>>,
1764    /// Optional format for this syntax.
1765    pub format: Option<JsonFormat>,
1766    /// Source location and node identity.
1767    pub meta: Meta,
1768}
1769
1770#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1771#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1772#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1773/// An SQL json returning.
1774pub struct JsonReturning<X: Extension = NoExt> {
1775    /// Data type named by this syntax.
1776    pub data_type: Box<DataType<X>>,
1777    /// Optional format for this syntax.
1778    pub format: Option<JsonFormat>,
1779    /// Source location and node identity.
1780    pub meta: Meta,
1781}
1782
1783/// One SQL/JSON `PASSING` binding: `<value> [FORMAT JSON] AS <name>`.
1784///
1785/// The `FORMAT` rides the [`JsonValueExpr`] value; the name is a `ColLabel`
1786/// (interns any quote style).
1787#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1788#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1789#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1790pub struct JsonPassingArg<X: Extension = NoExt> {
1791    /// Value supplied by this syntax.
1792    pub value: JsonValueExpr<X>,
1793    /// Name referenced by this syntax.
1794    pub name: Ident,
1795    /// Source location and node identity.
1796    pub meta: Meta,
1797}
1798
1799/// A SQL/JSON `ON EMPTY` / `ON ERROR` behaviour handler (PostgreSQL's
1800/// `JsonBehavior`).
1801///
1802/// One shared grammar backs every slot: PostgreSQL accepts any behaviour in any
1803/// `ON EMPTY`/`ON ERROR` position at raw parse (the per-function legality — e.g.
1804/// `JSON_VALUE` cannot yield `EMPTY ARRAY` — is a parse-*analysis* check, not a
1805/// syntax rule), so the [`JsonBehaviorKind`] set is not restricted per function.
1806/// `default_expr` is `Some` exactly when `kind` is [`Default`](JsonBehaviorKind::Default).
1807#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1808#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1809#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1810pub struct JsonBehavior<X: Extension = NoExt> {
1811    /// Which behaviour applies (`NULL`/`ERROR`/`DEFAULT`/…); see [`JsonBehaviorKind`].
1812    pub kind: JsonBehaviorKind,
1813    /// Optional default expr for this syntax.
1814    pub default_expr: Option<Box<Expr<X>>>,
1815    /// Source location and node identity.
1816    pub meta: Meta,
1817}
1818
1819#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1820#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1821#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1822/// The SQL json behavior kind forms represented by the AST.
1823pub enum JsonBehaviorKind {
1824    /// `ERROR` — raise an error.
1825    Error,
1826    /// `NULL` — yield SQL null.
1827    Null,
1828    /// `TRUE` — yield `TRUE` (`JSON_EXISTS` only).
1829    True,
1830    /// `FALSE` — yield `FALSE` (`JSON_EXISTS` only).
1831    False,
1832    /// `UNKNOWN` — yield unknown (`JSON_EXISTS` only).
1833    Unknown,
1834    /// `EMPTY` — PostgreSQL's shorthand for `EMPTY ARRAY`; kept distinct so it
1835    /// round-trips to the bare spelling.
1836    Empty,
1837    /// `EMPTY ARRAY` — yield an empty JSON array.
1838    EmptyArray,
1839    /// `EMPTY OBJECT` — yield an empty JSON object.
1840    EmptyObject,
1841    /// `DEFAULT <expr>` — the fallback value carried in
1842    /// [`JsonBehavior::default_expr`].
1843    Default,
1844}
1845
1846/// The SQL/JSON `WRAPPER` behaviour of `JSON_QUERY` (SQL:2016).
1847///
1848/// The optional `ARRAY` keyword and the `UNCONDITIONAL` default are semantically
1849/// inert (PostgreSQL normalizes them away), so this closed enum folds
1850/// `WITH [UNCONDITIONAL] [ARRAY] WRAPPER` onto [`Unconditional`](Self::Unconditional)
1851/// and `WITHOUT [ARRAY] WRAPPER` onto [`Without`](Self::Without) — the render
1852/// re-parses to the same value.
1853#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1854#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1855#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1856pub enum JsonWrapperBehavior {
1857    /// No wrapper clause written.
1858    Unspecified,
1859    /// `WITHOUT [ARRAY] WRAPPER`.
1860    Without,
1861    /// `WITH [UNCONDITIONAL] [ARRAY] WRAPPER`.
1862    Unconditional,
1863    /// `WITH CONDITIONAL [ARRAY] WRAPPER`.
1864    Conditional,
1865}
1866
1867/// The SQL/JSON `QUOTES` behaviour of `JSON_QUERY` (SQL:2016).
1868///
1869/// The `ON SCALAR STRING` tail is semantically inert (PostgreSQL normalizes it
1870/// away), so it is not preserved.
1871#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1872#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1873#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1874pub enum JsonQuotesBehavior {
1875    /// No quotes clause written.
1876    Unspecified,
1877    /// `KEEP QUOTES [ON SCALAR STRING]`.
1878    Keep,
1879    /// `OMIT QUOTES [ON SCALAR STRING]`.
1880    Omit,
1881}
1882
1883/// Which SQL/JSON query function a [`JsonFuncExpr`] is.
1884#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1885#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1886#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1887pub enum JsonFuncKind {
1888    /// `JSON_VALUE` — extract a scalar SQL value at the path.
1889    Value,
1890    /// `JSON_QUERY` — extract a JSON fragment at the path.
1891    Query,
1892    /// `JSON_EXISTS` — test whether the path matches.
1893    Exists,
1894}
1895
1896/// A SQL/JSON query function — `JSON_VALUE` / `JSON_QUERY` / `JSON_EXISTS`
1897/// (SQL:2016; PostgreSQL's one `JsonFuncExpr` node with an `op` tag).
1898///
1899/// One canonical shape with a [`JsonFuncKind`] tag covers all three, mirroring the
1900/// engine. The three differ in which trailing clauses their grammar admits — only
1901/// `JSON_QUERY` takes `wrapper`/`quotes`; `JSON_EXISTS` takes neither `returning`
1902/// nor `on_empty`; only `JSON_QUERY`/`JSON_VALUE` take `on_empty` — and the parser
1903/// enforces those restrictions, so an illegal field stays `None`/`Unspecified`.
1904#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1905#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1906#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1907pub struct JsonFuncExpr<X: Extension = NoExt> {
1908    /// Which function this is (`JSON_VALUE`/`JSON_QUERY`/`JSON_EXISTS`); see [`JsonFuncKind`].
1909    pub kind: JsonFuncKind,
1910    /// The JSON value the path is applied to.
1911    pub context: JsonValueExpr<X>,
1912    /// The SQL/JSON path expression.
1913    pub path: Box<Expr<X>>,
1914    /// The `PASSING` argument bindings, in source order.
1915    pub passing: ThinVec<JsonPassingArg<X>>,
1916    /// The optional `RETURNING <type>` clause; see [`JsonReturning`].
1917    pub returning: Option<JsonReturning<X>>,
1918    /// The `WITH`/`WITHOUT WRAPPER` behaviour; see [`JsonWrapperBehavior`].
1919    pub wrapper: JsonWrapperBehavior,
1920    /// The `KEEP`/`OMIT QUOTES` behaviour; see [`JsonQuotesBehavior`].
1921    pub quotes: JsonQuotesBehavior,
1922    /// Optional on empty for this syntax.
1923    pub on_empty: Option<JsonBehavior<X>>,
1924    /// Optional on error for this syntax.
1925    pub on_error: Option<JsonBehavior<X>>,
1926    /// Source location and node identity.
1927    pub meta: Meta,
1928}
1929
1930/// Whether a SQL/JSON object member was spelled `key : value` or `key VALUE value`.
1931///
1932/// The optional leading `KEY` keyword is inert (PostgreSQL normalizes it away) and
1933/// is not preserved.
1934#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1935#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1936#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1937pub enum JsonKeyValueSpelling {
1938    /// `key : value`.
1939    Colon,
1940    /// `key VALUE value`.
1941    Value,
1942}
1943
1944/// One SQL/JSON object member: `[KEY] <key> {: | VALUE} <value> [FORMAT JSON]`.
1945#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1946#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1947#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1948pub struct JsonKeyValue<X: Extension = NoExt> {
1949    /// The member key expression.
1950    pub key: Box<Expr<X>>,
1951    /// Value supplied by this syntax.
1952    pub value: JsonValueExpr<X>,
1953    /// Exact source spelling retained for faithful rendering.
1954    pub spelling: JsonKeyValueSpelling,
1955    /// Source location and node identity.
1956    pub meta: Meta,
1957}
1958
1959/// The SQL/JSON null-handling clause of a constructor: `ABSENT ON NULL` (drop null
1960/// entries) or `NULL ON NULL` (keep them). `None` records that no clause was written
1961/// — the two constructors default oppositely (`JSON_OBJECT` keeps, `JSON_ARRAY`
1962/// drops), so the render must not synthesize the absent clause.
1963#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1964#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1965#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1966pub enum JsonNullClause {
1967    /// `ABSENT ON NULL` — drop members/elements whose value is null.
1968    AbsentOnNull,
1969    /// `NULL ON NULL` — keep members/elements whose value is null.
1970    NullOnNull,
1971}
1972
1973/// A SQL/JSON object constructor: `JSON_OBJECT([members] [null] [unique]
1974/// [RETURNING …])` (SQL:2016).
1975///
1976/// `entries` may be empty (`JSON_OBJECT()` / `JSON_OBJECT(RETURNING …)`).
1977/// `unique_keys` is `Some(true)` for `WITH UNIQUE [KEYS]`, `Some(false)` for
1978/// `WITHOUT UNIQUE [KEYS]`, `None` when unwritten (the inert `KEYS` word is not
1979/// preserved). This is the standard constructor only; the legacy
1980/// `json_object(text[])` function keeps the ordinary [`Expr::Function`] shape.
1981#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1982#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1983#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1984pub struct JsonObjectExpr<X: Extension = NoExt> {
1985    /// entries in source order.
1986    pub entries: ThinVec<JsonKeyValue<X>>,
1987    /// Optional null clause for this syntax.
1988    pub null_clause: Option<JsonNullClause>,
1989    /// Whether the unique keys form was present in the source.
1990    pub unique_keys: Option<bool>,
1991    /// The optional `RETURNING <type>` clause; see [`JsonReturning`].
1992    pub returning: Option<JsonReturning<X>>,
1993    /// Source location and node identity.
1994    pub meta: Meta,
1995}
1996
1997/// The body of a [`JsonArrayExpr`] — a value list or a subquery, PostgreSQL's two
1998/// distinct `JSON_ARRAY` productions.
1999#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2000#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2001#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2002pub enum JsonArrayBody<X: Extension = NoExt> {
2003    /// `JSON_ARRAY(v, … [null])` — a (possibly empty) value list with an optional
2004    /// null-handling clause.
2005    Values {
2006        /// Child items in source order.
2007        items: ThinVec<JsonValueExpr<X>>,
2008        /// Optional null clause for this syntax.
2009        null_clause: Option<JsonNullClause>,
2010        /// Source location and node identity.
2011        meta: Meta,
2012    },
2013    /// `JSON_ARRAY(<query> [FORMAT JSON])` — a subquery whose rows become elements.
2014    Query {
2015        /// Query governed by this node.
2016        query: Box<Query<X>>,
2017        /// Optional format for this syntax.
2018        format: Option<JsonFormat>,
2019        /// Source location and node identity.
2020        meta: Meta,
2021    },
2022}
2023
2024/// A SQL/JSON array constructor: `JSON_ARRAY(<body> [RETURNING …])` (SQL:2016).
2025#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2026#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2027#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2028pub struct JsonArrayExpr<X: Extension = NoExt> {
2029    /// Statement or query body governed by this node.
2030    pub body: JsonArrayBody<X>,
2031    /// The optional `RETURNING <type>` clause; see [`JsonReturning`].
2032    pub returning: Option<JsonReturning<X>>,
2033    /// Source location and node identity.
2034    pub meta: Meta,
2035}
2036
2037/// The body of a [`JsonAggregateExpr`] — the object or array aggregate shape.
2038#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2039#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2040#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2041pub enum JsonAggregateBody<X: Extension = NoExt> {
2042    /// `JSON_OBJECTAGG(k {: | VALUE} v [unique])` — one member, no `ORDER BY`.
2043    Object {
2044        /// The single key/value member; see [`JsonKeyValue`].
2045        entry: JsonKeyValue<X>,
2046        /// Whether the unique keys form was present in the source.
2047        unique_keys: Option<bool>,
2048        /// Source location and node identity.
2049        meta: Meta,
2050    },
2051    /// `JSON_ARRAYAGG(v [ORDER BY …])` — one value with an optional sort.
2052    Array {
2053        /// Value supplied by this syntax.
2054        value: JsonValueExpr<X>,
2055        /// Ordering terms in source order.
2056        order_by: ThinVec<OrderByExpr<X>>,
2057        /// Source location and node identity.
2058        meta: Meta,
2059    },
2060}
2061
2062/// A SQL/JSON aggregate constructor — `JSON_OBJECTAGG` / `JSON_ARRAYAGG`
2063/// (SQL:2016). Shares the ordinary-aggregate `FILTER (WHERE …)` / `OVER (…)` tail.
2064#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2065#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2066#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2067pub struct JsonAggregateExpr<X: Extension = NoExt> {
2068    /// The object or array aggregate body; see [`JsonAggregateBody`].
2069    pub body: JsonAggregateBody<X>,
2070    /// Optional null clause for this syntax.
2071    pub null_clause: Option<JsonNullClause>,
2072    /// The optional `RETURNING <type>` clause; see [`JsonReturning`].
2073    pub returning: Option<JsonReturning<X>>,
2074    /// Optional filter for this syntax.
2075    pub filter: Option<Box<Expr<X>>>,
2076    /// Optional over for this syntax.
2077    pub over: Option<Box<WindowSpec<X>>>,
2078    /// Source location and node identity.
2079    pub meta: Meta,
2080}
2081
2082/// Which bare SQL/JSON constructor a [`JsonConstructorExpr`] is.
2083#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2084#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2085#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2086pub enum JsonConstructorKind {
2087    /// `JSON(<value> [WITH|WITHOUT UNIQUE [KEYS]])`.
2088    Json,
2089    /// `JSON_SCALAR(<expr>)` — a plain argument, no `FORMAT`/`RETURNING`/`UNIQUE`.
2090    Scalar,
2091    /// `JSON_SERIALIZE(<value> [RETURNING <type> [FORMAT JSON]])`.
2092    Serialize,
2093}
2094
2095/// A bare SQL/JSON constructor — `JSON(x)` / `JSON_SCALAR(x)` / `JSON_SERIALIZE(x)`
2096/// (SQL:2016). The unused fields per kind stay `None` (the parser admits only each
2097/// kind's grammar): `unique_keys` is `JSON`-only, `returning` is `JSON_SERIALIZE`-only,
2098/// and `JSON_SCALAR`'s value never carries a `FORMAT`.
2099#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2100#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2101#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2102pub struct JsonConstructorExpr<X: Extension = NoExt> {
2103    /// Which constructor this is (`JSON`/`JSON_SCALAR`/`JSON_SERIALIZE`); see [`JsonConstructorKind`].
2104    pub kind: JsonConstructorKind,
2105    /// Value supplied by this syntax.
2106    pub value: JsonValueExpr<X>,
2107    /// Whether the unique keys form was present in the source.
2108    pub unique_keys: Option<bool>,
2109    /// The optional `RETURNING <type>` clause; see [`JsonReturning`].
2110    pub returning: Option<JsonReturning<X>>,
2111    /// Source location and node identity.
2112    pub meta: Meta,
2113}
2114
2115/// The type constraint of an `IS JSON` predicate: `IS JSON [VALUE|ARRAY|OBJECT|SCALAR]`.
2116#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2117#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2118#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2119pub enum JsonItemType {
2120    /// Bare `IS JSON` — any JSON type.
2121    Any,
2122    /// `IS JSON VALUE` — any JSON scalar, array, or object.
2123    Value,
2124    /// `IS JSON ARRAY` — a JSON array.
2125    Array,
2126    /// `IS JSON OBJECT` — a JSON object.
2127    Object,
2128    /// `IS JSON SCALAR` — a JSON scalar (not an array or object).
2129    Scalar,
2130}
2131
2132/// The SQL/JSON `<expr> IS [NOT] JSON [type] [WITH|WITHOUT UNIQUE [KEYS]]` predicate
2133/// (SQL:2016; PostgreSQL's `JsonIsPredicate`).
2134///
2135/// Binds at `IS`-predicate precedence like the other `IS` tests. `unique_keys`
2136/// records `WITH UNIQUE [KEYS]` (the inert `KEYS` word and the default `WITHOUT`
2137/// spelling are not preserved).
2138#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2139#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2140#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2141pub struct IsJsonExpr<X: Extension = NoExt> {
2142    /// Expression evaluated by this syntax.
2143    pub expr: Box<Expr<X>>,
2144    /// Whether the negated form was present in the source.
2145    pub negated: bool,
2146    /// The asserted JSON type (`VALUE`/`ARRAY`/`OBJECT`/`SCALAR`/any); see [`JsonItemType`].
2147    pub item_type: JsonItemType,
2148    /// Whether the unique keys form was present in the source.
2149    pub unique_keys: bool,
2150    /// Source location and node identity.
2151    pub meta: Meta,
2152}
2153
2154/// A SQL/XML expression function (SQL:2006; PostgreSQL `func_expr_common_subexpr`).
2155///
2156/// One kind-tagged enum for the eight special forms PostgreSQL lowers to `XmlExpr`
2157/// (and `XmlSerialize`): each differs in the clause grammar inside its parens, so a
2158/// per-form variant carries exactly that form's fields (the aggregate `xmlagg` is an
2159/// *ordinary* aggregate, not a keyword special form, so it is not here). The
2160/// contextual keywords each form opens with — `NAME`, `DOCUMENT`/`CONTENT`,
2161/// `VERSION`, `STANDALONE`, `PASSING`, `INDENT`, `WHITESPACE` — stay unreserved
2162/// (usable as ordinary names elsewhere); they are consumed only inside these parens.
2163#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2164#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2165#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2166pub enum XmlFunc<X: Extension = NoExt> {
2167    /// `xmlelement(NAME <name> [, xmlattributes(<attr>, …)] [, <content>, …])`.
2168    /// The `xmlattributes(…)` list, when present, precedes the content list (PostgreSQL
2169    /// rejects content before it); either list may be empty.
2170    Element {
2171        /// Name referenced by this syntax.
2172        name: Ident,
2173        /// attributes in source order.
2174        attributes: ThinVec<XmlAttribute<X>>,
2175        /// content in source order.
2176        content: ThinVec<Expr<X>>,
2177        /// Source location and node identity.
2178        meta: Meta,
2179    },
2180    /// `xmlforest(<value> [AS <name>], …)` — a non-empty list of the same
2181    /// `<value> [AS <name>]` element shape as an `xmlelement` attribute (reusing
2182    /// [`XmlAttribute`]).
2183    Forest {
2184        /// elements in source order.
2185        elements: ThinVec<XmlAttribute<X>>,
2186        /// Source location and node identity.
2187        meta: Meta,
2188    },
2189    /// `xmlconcat(<value>, …)` — a non-empty ordinary expression list.
2190    Concat {
2191        /// Arguments in source order.
2192        args: ThinVec<Expr<X>>,
2193        /// Source location and node identity.
2194        meta: Meta,
2195    },
2196    /// `xmlparse({DOCUMENT | CONTENT} <value> [{PRESERVE | STRIP} WHITESPACE])`.
2197    Parse {
2198        /// Whether the input is a `DOCUMENT` or a `CONTENT` fragment; see [`XmlDocumentOrContent`].
2199        option: XmlDocumentOrContent,
2200        /// The XML value to parse.
2201        arg: Box<Expr<X>>,
2202        /// The `PRESERVE`/`STRIP WHITESPACE` handling; see [`XmlWhitespaceOption`].
2203        whitespace: XmlWhitespaceOption,
2204        /// Source location and node identity.
2205        meta: Meta,
2206    },
2207    /// `xmlpi(NAME <name> [, <content>])` — a single optional content expression.
2208    Pi {
2209        /// Name referenced by this syntax.
2210        name: Ident,
2211        /// Optional content for this syntax.
2212        content: Option<Box<Expr<X>>>,
2213        /// Source location and node identity.
2214        meta: Meta,
2215    },
2216    /// `xmlroot(<value>, VERSION {<expr> | NO VALUE} [, STANDALONE {YES | NO | NO VALUE}])`.
2217    /// The `VERSION` clause is mandatory: `version` is `None` exactly for `NO VALUE`
2218    /// (an unwritten clause is not representable, matching PostgreSQL's grammar).
2219    Root {
2220        /// The XML value to modify.
2221        arg: Box<Expr<X>>,
2222        /// Optional version for this syntax.
2223        version: Option<Box<Expr<X>>>,
2224        /// The `STANDALONE {YES | NO | NO VALUE}` clause; see [`XmlStandalone`].
2225        standalone: XmlStandalone,
2226        /// Source location and node identity.
2227        meta: Meta,
2228    },
2229    /// `xmlserialize({DOCUMENT | CONTENT} <value> AS <type> [[NO] INDENT])`.
2230    Serialize {
2231        /// Whether the input is a `DOCUMENT` or a `CONTENT` fragment; see [`XmlDocumentOrContent`].
2232        option: XmlDocumentOrContent,
2233        /// The XML value to serialize.
2234        arg: Box<Expr<X>>,
2235        /// Data type named by this syntax.
2236        data_type: Box<DataType<X>>,
2237        /// The `[NO] INDENT` option; see [`XmlIndentOption`].
2238        indent: XmlIndentOption,
2239        /// Source location and node identity.
2240        meta: Meta,
2241    },
2242    /// `xmlexists(<path> PASSING [BY {REF | VALUE}] <doc> [BY {REF | VALUE}])`.
2243    /// The passing mechanism is admitted on either side of the document argument
2244    /// (PostgreSQL's `xmlexists_argument`); each side round-trips independently.
2245    Exists {
2246        /// The XPath expression to evaluate.
2247        path: Box<Expr<X>>,
2248        /// Optional mechanism before for this syntax.
2249        mechanism_before: Option<XmlPassingMechanism>,
2250        /// The XML document the path is tested against.
2251        arg: Box<Expr<X>>,
2252        /// Optional mechanism after for this syntax.
2253        mechanism_after: Option<XmlPassingMechanism>,
2254        /// Source location and node identity.
2255        meta: Meta,
2256    },
2257}
2258
2259/// One `<value> [AS <name>]` element of an `xmlelement` attribute list or an
2260/// `xmlforest` list. The `AS <name>` label is a `ColLabel` (any keyword admitted);
2261/// `None` when the element is a bare value (PostgreSQL derives the tag at analysis).
2262#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2263#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2264#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2265pub struct XmlAttribute<X: Extension = NoExt> {
2266    /// Value supplied by this syntax.
2267    pub value: Box<Expr<X>>,
2268    /// Name referenced by this syntax.
2269    pub name: Option<Ident>,
2270    /// Source location and node identity.
2271    pub meta: Meta,
2272}
2273
2274/// The `DOCUMENT` / `CONTENT` mode word of `xmlparse` / `xmlserialize`.
2275#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2276#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2277#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2278pub enum XmlDocumentOrContent {
2279    /// `DOCUMENT` — the input is a single well-formed XML document.
2280    Document,
2281    /// `CONTENT` — the input is an XML content fragment.
2282    Content,
2283}
2284
2285/// The optional whitespace-handling clause of `xmlparse`.
2286#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2287#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2288#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2289pub enum XmlWhitespaceOption {
2290    /// No clause written.
2291    Unspecified,
2292    /// `PRESERVE WHITESPACE`.
2293    Preserve,
2294    /// `STRIP WHITESPACE`.
2295    Strip,
2296}
2297
2298/// The optional indentation clause of `xmlserialize`.
2299#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2300#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2301#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2302pub enum XmlIndentOption {
2303    /// No clause written.
2304    Unspecified,
2305    /// `INDENT`.
2306    Indent,
2307    /// `NO INDENT`.
2308    NoIndent,
2309}
2310
2311/// The optional `STANDALONE` clause value of `xmlroot`.
2312#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2313#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2314#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2315pub enum XmlStandalone {
2316    /// No `STANDALONE` clause written.
2317    Unspecified,
2318    /// `STANDALONE YES`.
2319    Yes,
2320    /// `STANDALONE NO`.
2321    No,
2322    /// `STANDALONE NO VALUE`.
2323    NoValue,
2324}
2325
2326/// The `BY REF` / `BY VALUE` passing mechanism of `xmlexists`. Both spellings are
2327/// admitted at parse (PostgreSQL treats `BY REF` as the default and rejects `BY
2328/// VALUE` only later), so the tag round-trips the exact source word.
2329#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2330#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2331#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2332pub enum XmlPassingMechanism {
2333    /// `BY REF` — pass the XML by reference (PostgreSQL's default).
2334    ByRef,
2335    /// `BY VALUE` — pass the XML by value.
2336    ByValue,
2337}
2338
2339/// A standard-SQL string special form (SQL-92 E021-06/-09/-11 + SQL:1999 T312;
2340/// PostgreSQL's `func_expr_common_subexpr` string productions).
2341///
2342/// One kind-tagged enum for the keyword-argument string functions: each variant
2343/// carries exactly its form's typed fields, and every operand keyword (`FROM`,
2344/// `FOR`, `SIMILAR`, `ESCAPE`, `PLACING`, `IN`, `LEADING`/`TRAILING`/`BOTH`) is
2345/// grammar, not data. The comma plain-call spellings (`substring(x, 1, 2)`,
2346/// `trim(x, y)`, `overlay(a, b, c)`) are *not* here — they stay ordinary
2347/// [`FunctionCall`]s, so the plain-call surface every engine also accepts is
2348/// unaffected by the special forms.
2349#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2350#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2351#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2352pub enum StringFunc<X: Extension = NoExt> {
2353    /// `SUBSTRING(<expr> FROM <start> [FOR <count>])` and its variants: PostgreSQL
2354    /// admits `FOR <count>` alone and the reversed `FOR <count> FROM <start>`
2355    /// spelling (both orders fold onto the same fields and render canonically
2356    /// `FROM`-first), so at least one of `start`/`count` is always present. The
2357    /// string-pattern form `SUBSTRING(x FROM 'pat')`/`… FOR '#'` is this same
2358    /// production (the regex reading is runtime semantics, not grammar).
2359    Substring {
2360        /// Expression evaluated by this syntax.
2361        expr: Box<Expr<X>>,
2362        /// The `FROM <start>` operand, `None` for the `FOR`-only form.
2363        start: Option<Box<Expr<X>>>,
2364        /// The `FOR <count>` operand, `None` for the `FROM`-only form.
2365        count: Option<Box<Expr<X>>>,
2366        /// Source location and node identity.
2367        meta: Meta,
2368    },
2369    /// PostgreSQL's `SUBSTRING(<expr> SIMILAR <pattern> ESCAPE <escape>)` regex
2370    /// form (SQL:1999 `<regular expression substring function>`). All three
2371    /// operands are mandatory (`SUBSTRING(x SIMILAR p)` without `ESCAPE` rejects,
2372    /// engine-verified).
2373    SubstringSimilar {
2374        /// Expression evaluated by this syntax.
2375        expr: Box<Expr<X>>,
2376        /// The SQL-regex pattern.
2377        pattern: Box<Expr<X>>,
2378        /// The escape-character expression.
2379        escape: Box<Expr<X>>,
2380        /// Source location and node identity.
2381        meta: Meta,
2382    },
2383    /// `POSITION(<substr> IN <string>)` (SQL-92 E021-11). The operands are the
2384    /// restricted `b_expr` in PostgreSQL/DuckDB (`POSITION(1 IN 2 OR 3)` rejects)
2385    /// and MySQL's asymmetric `bit_expr IN expr` under
2386    /// [`StringFuncForms::position_asymmetric_operands`](crate::dialect::CallSyntax).
2387    /// There is no plain-call spelling: every keyword-form engine parse-rejects
2388    /// `position(a, b)`.
2389    Position {
2390        /// The substring to search for.
2391        substr: Box<Expr<X>>,
2392        /// The string searched within.
2393        string: Box<Expr<X>>,
2394        /// Source location and node identity.
2395        meta: Meta,
2396    },
2397    /// `OVERLAY(<target> PLACING <replacement> FROM <start> [FOR <count>])`
2398    /// (SQL:1999 T312). `FROM <start>` is mandatory — `OVERLAY(x PLACING y)` and
2399    /// the `FOR`-without-`FROM` form parse-reject on every keyword-form engine.
2400    Overlay {
2401        /// Object targeted by this syntax.
2402        target: Box<Expr<X>>,
2403        /// The replacement string spliced in.
2404        replacement: Box<Expr<X>>,
2405        /// The 1-based start position.
2406        start: Box<Expr<X>>,
2407        /// Optional count for this syntax.
2408        count: Option<Box<Expr<X>>>,
2409        /// Source location and node identity.
2410        meta: Meta,
2411    },
2412    /// `TRIM([{BOTH | LEADING | TRAILING}] [<chars>] FROM <sources>…)` (SQL-92
2413    /// E021-09) plus PostgreSQL's loose `trim_list` tails under
2414    /// [`StringFuncForms::trim_list_syntax`](crate::dialect::CallSyntax): a bare
2415    /// `FROM <list>`, a side without `FROM` (`TRIM(TRAILING ' foo ')`), and a
2416    /// multi-expression list (`TRIM('a' FROM 'b', 'c')`). At least one of
2417    /// `side`/`from` is present — a bare `TRIM(x)`/`TRIM(x, y)` is an ordinary
2418    /// call, never this node.
2419    Trim {
2420        /// The written `BOTH`/`LEADING`/`TRAILING` side, `None` when omitted.
2421        side: Option<TrimSide>,
2422        /// The trim-character expression written before `FROM`; `None` for the
2423        /// bare-`FROM` and side-without-`FROM` forms.
2424        trim_chars: Option<Box<Expr<X>>>,
2425        /// Whether `FROM` was written (distinguishes `TRIM(TRAILING ' foo ')`
2426        /// from `TRIM(TRAILING FROM ' foo ')` — both are valid PostgreSQL with
2427        /// different meanings, so the bit is load-bearing for round-trips).
2428        from: bool,
2429        /// The source expression list after `FROM` (or the bare list when a side
2430        /// is written without `FROM`). PostgreSQL's `trim_list` is an `expr_list`,
2431        /// so more than one source parses there; the restricted dialects hold this
2432        /// to exactly one at parse.
2433        sources: ThinVec<Expr<X>>,
2434        /// Source location and node identity.
2435        meta: Meta,
2436    },
2437    /// PostgreSQL's `COLLATION FOR (<expr>)` — the common-subexpr that reports the
2438    /// collation name derived for its operand. PostgreSQL gives it a dedicated
2439    /// `COLLATION FOR '(' a_expr ')'` production that lowers to a
2440    /// `pg_catalog.pg_collation_for(<expr>)` call, but the surface keyword form is
2441    /// kept here (not folded to a [`FunctionCall`]) so it round-trips as written. The
2442    /// parentheses and single `a_expr` operand are mandatory — `COLLATION FOR 'x'`,
2443    /// `COLLATION FOR ()`, and a two-argument list all parse-reject (engine-verified).
2444    CollationFor {
2445        /// Expression evaluated by this syntax.
2446        expr: Box<Expr<X>>,
2447        /// Source location and node identity.
2448        meta: Meta,
2449    },
2450    /// MySQL's `CONVERT(<expr> USING <charset>)` transcoding form — reinterprets its
2451    /// string operand in another character set (the grammar's
2452    /// `CONVERT '(' expr USING charset_name ')'`). Distinct from the comma-form cast
2453    /// [`CastSyntax::Convert`]: this changes the charset, not the type, so it is a string
2454    /// special form rather than a cast. The operand is a full `a_expr`
2455    /// (`CONVERT(1+2 USING utf8mb4)` parses, engine-verified); `charset` is a MySQL
2456    /// `charset_name` — an `ident_or_text` (a bare or backtick identifier, or a quoted
2457    /// string, round-tripping by the [`Ident`]'s quote style) or the `BINARY` transcoding
2458    /// name (`CONVERT(x USING binary)`), which reaches here as a bare [`Ident`]. Recognized
2459    /// only under [`CallSyntax::convert_function`](crate::dialect::CallSyntax); MySQL-only.
2460    ConvertUsing {
2461        /// Expression evaluated by this syntax.
2462        expr: Box<Expr<X>>,
2463        /// The target character set name.
2464        charset: Ident,
2465        /// Source location and node identity.
2466        meta: Meta,
2467    },
2468    /// MySQL's full-text search `MATCH (<col>, …) AGAINST (<expr> [<modifier>])`
2469    /// (`simple_expr: MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'`).
2470    /// The `columns` are a comma list of column references (bare or 1–3-part dotted
2471    /// [`Expr::Column`]s — an arbitrary expression, literal, function call, or empty
2472    /// list all parse-reject, engine-verified); `against` is a MySQL `bit_expr` (below
2473    /// the comparison level, so a trailing `IN`/`WITH` opens the modifier rather than an
2474    /// `IN` predicate). The optional [`MatchSearchModifier`] is exactly one of the four
2475    /// documented combinations; `None` is the default (no modifier words) and round-trips
2476    /// as written rather than as an explicit `IN NATURAL LANGUAGE MODE`. Recognized only
2477    /// under [`StringFuncForms::match_against`](crate::dialect::CallSyntax); MySQL-only, and
2478    /// distinct from SQLite's infix `<expr> MATCH <expr>` operator. Semantic constraints
2479    /// (a full-text index must cover the columns, the operand must be constant) are a
2480    /// server binding concern, not grammar.
2481    MatchAgainst {
2482        /// Columns in source order.
2483        columns: ThinVec<Expr<X>>,
2484        /// The full-text search expression.
2485        against: Box<Expr<X>>,
2486        /// Optional modifier for this syntax.
2487        modifier: Option<MatchSearchModifier>,
2488        /// Source location and node identity.
2489        meta: Meta,
2490    },
2491    /// `CEIL(<expr> TO <field>)` / `CEILING(<expr> TO <field>)` — a rounding-field
2492    /// keyword form distinct from the ordinary `CEIL(<expr>)` call and the comma-form
2493    /// scale spelling `CEIL(<expr>, <scale>)` (which stays an ordinary [`FunctionCall`]:
2494    /// no probed oracle grammar admits the `TO` tail, engine-verified against pg_query,
2495    /// DuckDB, and mysql:8.4). The field (`DAY`, `HOUR`, …) is stored as written and
2496    /// validated, if at all, by the consuming engine at analysis time, not parse.
2497    /// Recognized only under
2498    /// [`StringFuncForms::ceil_to_field`](crate::dialect::StringFuncForms::ceil_to_field).
2499    CeilTo {
2500        /// Expression evaluated by this syntax.
2501        expr: Box<Expr<X>>,
2502        /// The rounding field (`DAY`, `HOUR`, …).
2503        field: Ident,
2504        /// Exact source spelling retained for faithful rendering.
2505        spelling: CeilSpelling,
2506        /// Source location and node identity.
2507        meta: Meta,
2508    },
2509    /// `FLOOR(<expr> TO <field>)` — a rounding-field keyword form distinct from the
2510    /// ordinary `FLOOR(<expr>)` call and the comma-form scale spelling
2511    /// `FLOOR(<expr>, <scale>)` (which stays an ordinary [`FunctionCall`]: no probed
2512    /// oracle grammar admits the `TO` tail, engine-verified against pg_query, DuckDB, and
2513    /// mysql:8.4). Unlike [`StringFunc::CeilTo`], `FLOOR` has no `FLOORING` synonym, so
2514    /// there is no spelling field to track. The field (`DAY`, `HOUR`, …) is stored as
2515    /// written and validated, if at all, by the consuming engine at analysis time, not
2516    /// parse. Recognized only under
2517    /// [`StringFuncForms::floor_to_field`](crate::dialect::StringFuncForms::floor_to_field).
2518    FloorTo {
2519        /// Expression evaluated by this syntax.
2520        expr: Box<Expr<X>>,
2521        /// The rounding field (`DAY`, `HOUR`, …).
2522        field: Ident,
2523        /// Source location and node identity.
2524        meta: Meta,
2525    },
2526}
2527
2528/// The optional search modifier of a MySQL [`StringFunc::MatchAgainst`] — one of the
2529/// four documented full-text combinations. `WITH QUERY EXPANSION` combines only with
2530/// the (implicit or explicit) natural-language mode; `IN BOOLEAN MODE WITH QUERY
2531/// EXPANSION` parse-rejects (engine-verified).
2532#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2533#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2534#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2535pub enum MatchSearchModifier {
2536    /// `IN NATURAL LANGUAGE MODE`.
2537    NaturalLanguage,
2538    /// `IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION`.
2539    NaturalLanguageQueryExpansion,
2540    /// `IN BOOLEAN MODE`.
2541    Boolean,
2542    /// `WITH QUERY EXPANSION`.
2543    QueryExpansion,
2544}
2545
2546/// The `BOTH` / `LEADING` / `TRAILING` side word of a [`StringFunc::Trim`].
2547#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2548#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2549#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2550pub enum TrimSide {
2551    /// `TRIM(BOTH …)` — trim the character from both ends.
2552    Both,
2553    /// `TRIM(LEADING …)` — trim from the start only.
2554    Leading,
2555    /// `TRIM(TRAILING …)` — trim from the end only.
2556    Trailing,
2557}
2558
2559/// Quantifier used by `op ANY/ALL/SOME (<query>)` predicates.
2560#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2561#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2562#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2563pub enum Quantifier {
2564    /// `ANY` — the predicate holds for at least one row/element.
2565    Any,
2566    /// `ALL` — the predicate holds for every row/element.
2567    All,
2568    /// `SOME` — standard synonym for `ANY`.
2569    Some,
2570}
2571
2572/// Closed operator keys for dialect binding-power tables.
2573#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2574#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2575#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2576pub enum BinaryOperator {
2577    /// `+` — addition.
2578    Plus,
2579    /// `-` — subtraction.
2580    Minus,
2581    /// `*` — multiplication.
2582    Multiply,
2583    /// `/` — division.
2584    Divide,
2585    /// Modulo, spelled `%` everywhere and additionally `MOD` in MySQL. The two
2586    /// spellings are one operator; the [`ModuloSpelling`] tag records
2587    /// which the source used so it round-trips.
2588    Modulo(ModuloSpelling),
2589    /// Integer division — a distinct operator from `/` (it truncates to an integer), so
2590    /// it gets its own key rather than reusing [`Divide`](Self::Divide). Two
2591    /// dialect-disjoint spellings fold onto it: MySQL's `DIV` keyword and
2592    /// DuckDB's `//` symbol; the [`IntegerDivideSpelling`] tag records which the source
2593    /// used so it round-trips. Binds at multiplicative precedence.
2594    IntegerDivide(IntegerDivideSpelling),
2595    /// Arithmetic exponentiation, spelled `^`. A distinct operator from the
2596    /// [`BitwiseXor`](Self::BitwiseXor) `^` (a dialect gives the `^` lexeme one meaning or the
2597    /// other — engine truth): under
2598    /// [`CaretOperator::Exponent`](crate::dialect::CaretOperator)
2599    /// (PostgreSQL/DuckDB) `^` is arithmetic power,
2600    /// and binds at its OWN precedence tier — tighter than `*`/`/`/`%`
2601    /// ([`multiplicative`](crate::precedence::BindingPowerTable::multiplicative)) and looser
2602    /// than the unary sign, left-associative (`2 ^ 3 ^ 2` is `(2 ^ 3) ^ 2`, `2 ^ 3 * 2` is
2603    /// `(2 ^ 3) * 2` — engine-measured on pg_query). Its binding power is the dedicated
2604    /// [`exponent`](crate::precedence::BindingPowerTable::exponent) row, not the multiplicative
2605    /// rank the other arithmetic operators share.
2606    Exponent,
2607    /// `||` — string concatenation.
2608    StringConcat,
2609    /// PostgreSQL `@>` containment — "left contains right" over arrays, ranges, and
2610    /// `jsonb`. Binds at PostgreSQL's "any other operator" precedence (the rank shared
2611    /// with [`StringConcat`](Self::StringConcat)), left-associative.
2612    Contains,
2613    /// PostgreSQL `<@` containment — "left is contained by right", the mirror of
2614    /// [`Contains`](Self::Contains). Same "any other operator" precedence.
2615    ContainedBy,
2616    /// DuckDB `^@` — "left string starts with right string". Binds at the "any other
2617    /// operator" precedence. Gated by [`starts_with_operator`](crate::dialect::OperatorSyntax::starts_with_operator).
2618    StartsWith,
2619    /// The `&&` overlap operator — "do the two operands overlap?" over arrays, ranges
2620    /// (PostgreSQL), and geometries (DuckDB, whose `&&` is bounding-box overlap). The one
2621    /// operator key for the `&&` spelling across dialects that give it this meaning:
2622    /// DuckDB routes it here through [`DoubleAmpersand::Overlaps`], and PostgreSQL's
2623    /// range/array `&&` (deferred) would fold onto the same variant. Binds at the "any
2624    /// other operator" precedence (the [`Contains`](Self::Contains) rank), left-associative.
2625    ///
2626    /// Distinct from [`Overlaps`](Self::Overlaps), the SQL-standard `OVERLAPS` *keyword*
2627    /// period predicate over `(start, end)` rows — a different surface (keyword vs symbol),
2628    /// operand shape (two-element rows vs scalars), and render (`OVERLAPS` vs `&&`).
2629    ///
2630    /// [`DoubleAmpersand::Overlaps`]: crate::dialect::DoubleAmpersand::Overlaps
2631    Overlap,
2632    /// PostgreSQL `->` JSON access — object field / array element returned as
2633    /// `json`/`jsonb`. Same "any other operator" precedence.
2634    JsonGet,
2635    /// PostgreSQL `->>` JSON access — object field / array element returned as `text`,
2636    /// the text-typed form of [`JsonGet`](Self::JsonGet). Same precedence.
2637    JsonGetText,
2638    /// PostgreSQL `?` `jsonb` key/element existence — "does the right text string exist as
2639    /// a top-level key (object) or array element (array) of the left `jsonb`?". Binds at
2640    /// PostgreSQL's "any other operator" precedence (the [`Contains`](Self::Contains) rank),
2641    /// left-associative. Lexed only under [`OperatorSyntax::jsonb_operators`]; the `?` byte
2642    /// is otherwise a stray byte in PostgreSQL (it has no `?` parameter) or the anonymous
2643    /// placeholder elsewhere.
2644    ///
2645    /// [`OperatorSyntax::jsonb_operators`]: crate::dialect::OperatorSyntax::jsonb_operators
2646    JsonExists,
2647    /// PostgreSQL `?|` `jsonb` any-key existence — "does any of the right `text[]` strings
2648    /// exist as a top-level key/element of the left `jsonb`?". Same "any other operator"
2649    /// precedence as [`JsonExists`](Self::JsonExists).
2650    JsonExistsAny,
2651    /// PostgreSQL `?&` `jsonb` all-keys existence — "do all of the right `text[]` strings
2652    /// exist as top-level keys/elements of the left `jsonb`?". Same precedence.
2653    JsonExistsAll,
2654    /// PostgreSQL `@?` — "does the right `jsonpath` return any item for the left `jsonb`?".
2655    /// Same "any other operator" precedence.
2656    JsonPathExists,
2657    /// PostgreSQL `@@` — the match operator: for `jsonb @@ jsonpath` it returns the result
2658    /// of the JSON-path predicate check, and it is also the `tsvector @@ tsquery` full-text
2659    /// search match. One operator key for the shared `@@` spelling; same "any other operator"
2660    /// precedence.
2661    JsonPathMatch,
2662    /// PostgreSQL `#>` — extract the `jsonb` sub-object at the right `text[]` path, returned
2663    /// as `jsonb`. Same "any other operator" precedence.
2664    JsonExtractPath,
2665    /// PostgreSQL `#>>` — extract the `jsonb` sub-object at the right `text[]` path, returned
2666    /// as `text`, the text-typed form of [`JsonExtractPath`](Self::JsonExtractPath). Same
2667    /// precedence.
2668    JsonExtractPathText,
2669    /// PostgreSQL `#-` — delete the field/element of the left `jsonb` at the right `text[]`
2670    /// path. Same "any other operator" precedence. The `#-` lexeme is munched over the two
2671    /// contiguous bytes ahead of the bare `#` bitwise-XOR (engine-verified: `5#-3` is
2672    /// `5 #- 3`, while a space splits it into `#` then `-3`).
2673    JsonDeletePath,
2674    /// Bitwise OR, spelled `|` (PostgreSQL, MySQL, SQLite, DuckDB). In PostgreSQL/SQLite/
2675    /// DuckDB it shares one "bitwise" precedence with `&`/`<<`/`>>` (between additive and
2676    /// comparison); MySQL ranks it strictly looser than `&` (the load-bearing per-dialect
2677    /// precedence split), so its binding power is dialect data
2678    /// ([`BindingPowerTable::bitwise_or`](crate::precedence::BindingPowerTable::bitwise_or)).
2679    BitwiseOr,
2680    /// Bitwise AND, spelled `&` (PostgreSQL, MySQL, SQLite, DuckDB). Shares the one bitwise
2681    /// rank with `|`/`<<`/`>>` in PostgreSQL/SQLite/DuckDB; binds tighter than `|` and
2682    /// looser than the shifts in MySQL (dialect data, see
2683    /// [`BindingPowerTable::bitwise_and`](crate::precedence::BindingPowerTable::bitwise_and)).
2684    BitwiseAnd,
2685    /// Bitwise left shift, spelled `<<` (PostgreSQL, MySQL, SQLite, DuckDB). Grouped with
2686    /// [`BitwiseShiftRight`](Self::BitwiseShiftRight) at one shift rank in every dialect —
2687    /// looser than additive everywhere (`1 << 2 + 3` is `1 << (2 + 3)`, engine-measured on
2688    /// SQLite/PG/DuckDB) — but that rank sits *below* additive and *above* `&` in MySQL
2689    /// (dialect data,
2690    /// [`BindingPowerTable::bitwise_shift`](crate::precedence::BindingPowerTable::bitwise_shift)).
2691    BitwiseShiftLeft,
2692    /// Bitwise right shift, spelled `>>` (PostgreSQL, MySQL, SQLite, DuckDB). The mirror of
2693    /// [`BitwiseShiftLeft`](Self::BitwiseShiftLeft); same shift rank.
2694    BitwiseShiftRight,
2695    /// Bitwise exclusive-or. Two dialect-disjoint spellings fold onto this one operator:
2696    /// PostgreSQL's `#` and MySQL's `^`. The [`BitwiseXorSpelling`] tag is
2697    /// load-bearing for validity, not only fidelity — PostgreSQL rejects `^` as XOR (there
2698    /// `^` is exponentiation) and MySQL treats `#` as a comment — so a normalized render
2699    /// would not re-parse under the dialect that produced it (the same contract
2700    /// [`IsNotDistinctFrom`](Self::IsNotDistinctFrom) keeps). The two spellings *also* bind
2701    /// at different precedences: PostgreSQL's `#` is an "any other operator" (looser than
2702    /// additive), MySQL's `^` binds tighter than `*` — dialect data on
2703    /// [`BindingPowerTable::bitwise_xor`](crate::precedence::BindingPowerTable::bitwise_xor).
2704    /// Distinct from the *logical* [`Xor`](Self::Xor) keyword operator (MySQL `XOR`).
2705    BitwiseXor(BitwiseXorSpelling),
2706    /// Equality, spelled `=` everywhere and additionally `==` in SQLite. The two
2707    /// spellings are one operator; the [`EqualsSpelling`] tag records
2708    /// which the source used so it round-trips.
2709    Eq(EqualsSpelling),
2710    /// Inequality, spelled `<>` (SQL-standard) everywhere and additionally `!=`
2711    /// (C-style) in every bundled dialect. The two spellings are one operator; the
2712    /// [`NotEqSpelling`] tag records which the source used so it round-trips.
2713    NotEq(NotEqSpelling),
2714    /// `<` — less-than.
2715    Lt,
2716    /// `<=` — less-than-or-equal.
2717    LtEq,
2718    /// `>` — greater-than.
2719    Gt,
2720    /// `>=` — greater-than-or-equal.
2721    GtEq,
2722    /// The null-safe inequality predicate `IS DISTINCT FROM` (SQL:1999 T151): true when
2723    /// the operands differ, treating `NULL` as an ordinary comparable value rather than
2724    /// yielding `NULL`. The parser recognizes it in the `IS` predicate arm, not the
2725    /// symbolic-operator loop. Binds at comparison precedence, non-associative like `=`
2726    /// (PostgreSQL `a_expr IS DISTINCT FROM a_expr %prec IS`, gram.y). Two
2727    /// interchangeable-semantics spellings fold onto it: the SQL:1999 `IS DISTINCT FROM`
2728    /// keyword form and SQLite's bare `IS NOT` (`a IS NOT b`, SQLite's general negated
2729    /// `IS`). The [`IsDistinctFromSpelling`] tag records which the source used so the
2730    /// surface round-trips, mirroring the [`IsNotDistinctFrom`](Self::IsNotDistinctFrom)
2731    /// complement.
2732    IsDistinctFrom(IsDistinctFromSpelling),
2733    /// The complement of [`IsDistinctFrom`](Self::IsDistinctFrom): the null-safe
2734    /// equality predicate, true when the operands are equal or both `NULL`. A distinct
2735    /// operator key at the same comparison precedence. Two interchangeable-semantics
2736    /// spellings fold onto it: the SQL:1999 `IS NOT DISTINCT FROM` keyword
2737    /// form (which SQLite's general `IS` also produces), recognized in the `IS`-predicate
2738    /// arm; and MySQL's `<=>` operator, recognized in the symbolic-operator loop. The
2739    /// [`IsNotDistinctFromSpelling`] tag records which the source used. Unlike the other
2740    /// spelling tags this is load-bearing for validity, not only fidelity: MySQL rejects
2741    /// the keyword form and the other dialects reject `<=>`, so the spelling cannot be
2742    /// normalized away without producing input the same dialect fails to re-parse.
2743    IsNotDistinctFrom(IsNotDistinctFromSpelling),
2744    /// MySQL `RLIKE` / `REGEXP` regular-expression match. The two keywords are
2745    /// synonyms folding onto one operator; the [`RegexpSpelling`] tag
2746    /// records which the source used. Binds at comparison precedence (like `LIKE`).
2747    Regexp(RegexpSpelling),
2748    /// SQLite's `GLOB` pattern-match operator — case-sensitive Unix-glob matching
2749    /// (`*`/`?`/`[…]`). A keyword infix operator ([`KeywordOperators::Sqlite`]), the
2750    /// SQLite analogue of MySQL's `RLIKE`. Binds at comparison precedence (like
2751    /// `LIKE`/`REGEXP`).
2752    ///
2753    /// [`KeywordOperators::Sqlite`]: crate::dialect::KeywordOperators::Sqlite
2754    Glob,
2755    /// SQLite's `MATCH` operator — a grammar hook whose meaning is supplied by an
2756    /// application-defined function (FTS, R-Tree). A keyword infix operator
2757    /// ([`KeywordOperators::Sqlite`]) sibling of [`Glob`](Self::Glob); binds at
2758    /// comparison precedence. The bundled engine registers no `match` backing, so a
2759    /// bare `prepare` rejects it — it is grammar-only, guarded by round-trip rather
2760    /// than an accept/reject oracle.
2761    ///
2762    /// [`KeywordOperators::Sqlite`]: crate::dialect::KeywordOperators::Sqlite
2763    Match,
2764    /// The SQL-standard `OVERLAPS` period predicate (SQL:2016 F251): `(s1, e1) OVERLAPS
2765    /// (s2, e2)` — true when the two time periods, each given as a `(start, end |
2766    /// duration)` pair, share any instant. Both operands are exactly-two-element rows
2767    /// ([`Expr::Row`], bare parenthesized pair or `ROW(...)`), a shape the parser enforces
2768    /// (a scalar, a single-element grouping, or a three-element row is a parse error,
2769    /// matching PostgreSQL's `row OVERLAPS row` production and its wrong-arity `ereport`).
2770    /// The boolean result is not itself a row, so the predicate never chains
2771    /// (`x OVERLAPS y OVERLAPS z` rejects) — modelled non-associative. Binds tighter than
2772    /// the comparison operators (`x OVERLAPS y = TRUE` groups `(x OVERLAPS y) = TRUE`) and
2773    /// looser than the arithmetic/`Op` rank, its own PostgreSQL `%nonassoc OVERLAPS` gram.y
2774    /// row. Gated by
2775    /// [`PredicateSyntax::overlaps_period_predicate`](crate::dialect::PredicateSyntax::overlaps_period_predicate).
2776    Overlaps,
2777    /// `AND` — logical conjunction.
2778    And,
2779    /// MySQL `XOR` logical exclusive-or. Binds between `AND` and `OR` in precedence.
2780    Xor,
2781    /// `OR` — logical disjunction.
2782    Or,
2783}
2784
2785/// Surface spelling for the modulo operator [`BinaryOperator::Modulo`].
2786///
2787/// `%` is universal; MySQL additionally spells the same operation with the `MOD`
2788/// keyword. The canonical AST keeps one modulo operator and this tag
2789/// only records which spelling the source used so rendering round-trips exactly.
2790#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2791#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2792#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2793pub enum ModuloSpelling {
2794    /// The `%` operator.
2795    Percent,
2796    /// The MySQL `MOD` keyword.
2797    Mod,
2798}
2799
2800/// Surface spelling for the integer-division operator [`BinaryOperator::IntegerDivide`].
2801///
2802/// Integer division has two dialect-disjoint spellings that fold onto one operator:
2803/// MySQL's `DIV` keyword and DuckDB's `//` symbol. Neither dialect accepts the
2804/// other's spelling (MySQL has no `//` operator — a bare `//` is a syntax error there — and
2805/// DuckDB has no `DIV` keyword), so this tag is load-bearing for validity, not only
2806/// fidelity, mirroring [`BitwiseXorSpelling`]: a normalized render would not re-parse under
2807/// the dialect that produced it.
2808#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2809#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2810#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2811pub enum IntegerDivideSpelling {
2812    /// The MySQL `DIV` keyword.
2813    Div,
2814    /// The DuckDB `//` operator.
2815    SlashSlash,
2816}
2817
2818/// Surface spelling for the equality operator [`BinaryOperator::Eq`].
2819///
2820/// `=` is universal; SQLite additionally spells the same comparison with a doubled
2821/// `==`. The canonical AST keeps one equality operator and this tag only
2822/// records which spelling the source used so rendering round-trips exactly.
2823#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2824#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2825#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2826pub enum EqualsSpelling {
2827    /// The `=` operator.
2828    Single,
2829    /// The SQLite `==` operator.
2830    Double,
2831}
2832
2833/// Whether an aggregate `FILTER (…)` clause wrote the SQL-standard `WHERE` keyword
2834/// before its predicate ([`FunctionCall::filter_where`]).
2835///
2836/// SQL:2003 (and PostgreSQL/SQLite) require `FILTER (WHERE <predicate>)`; DuckDB
2837/// additionally accepts the keyword-less `FILTER (<predicate>)`
2838/// ([`AggregateCallSyntax::filter_optional_where`](crate::dialect::AggregateCallSyntax::filter_optional_where)).
2839/// The canonical AST keeps one filtered-aggregate shape and this tag records which the
2840/// source used so rendering round-trips exactly, mirroring [`EqualsSpelling`]. Only
2841/// meaningful when [`FunctionCall::filter`] is `Some`; a call with no filter carries
2842/// the canonical [`Where`](Self::Where).
2843#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2844#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2845#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2846pub enum FilterWhereSpelling {
2847    /// The SQL-standard `FILTER (WHERE <predicate>)` spelling (the canonical form).
2848    Where,
2849    /// DuckDB's keyword-less `FILTER (<predicate>)` spelling.
2850    Omitted,
2851}
2852
2853/// Surface spelling for the inequality operator [`BinaryOperator::NotEq`].
2854///
2855/// The SQL-standard `<>` is universal; every bundled dialect additionally spells the
2856/// same comparison with the C-style `!=`. The canonical AST keeps one inequality
2857/// operator and this tag only records which spelling the source used so rendering
2858/// round-trips exactly, mirroring [`EqualsSpelling`]. A fidelity tag, not a validity
2859/// one: both spellings parse under every dialect, so the canonical `<>` re-parses
2860/// wherever the source `!=` did.
2861#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2862#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2863#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2864pub enum NotEqSpelling {
2865    /// The SQL-standard `<>` operator (the canonical spelling).
2866    AngleBracket,
2867    /// The C-style `!=` operator.
2868    Bang,
2869}
2870
2871/// Surface spelling for the null-safe inequality operator
2872/// [`BinaryOperator::IsDistinctFrom`].
2873///
2874/// The predicate has two interchangeable-semantics spellings: the SQL:1999
2875/// `IS DISTINCT FROM` keyword form and SQLite's bare `IS NOT` (SQLite's general
2876/// negated `IS`). The canonical AST keeps one operator and this tag records which the
2877/// source used so rendering round-trips, mirroring [`IsNotDistinctFromSpelling`]. Both
2878/// spellings are valid under SQLite (which accepts the explicit keyword form too), so
2879/// this is a fidelity tag rather than a validity one.
2880#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2881#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2882#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2883pub enum IsDistinctFromSpelling {
2884    /// The `IS DISTINCT FROM` keyword form (SQL:1999 T151).
2885    Keyword,
2886    /// SQLite's bare `IS NOT`: `a IS NOT b` is null-safe inequality, folding onto this
2887    /// operator. Renders back as bare `IS NOT` so the surface round-trips.
2888    Is,
2889}
2890
2891/// Surface spelling for the null-safe equality operator
2892/// [`BinaryOperator::IsNotDistinctFrom`].
2893///
2894/// The predicate has two interchangeable-semantics spellings: the SQL:1999
2895/// `IS NOT DISTINCT FROM` keyword form (which SQLite's general `IS` also folds onto)
2896/// and MySQL's `<=>` operator. The canonical AST keeps one operator and this
2897/// tag records which the source used so rendering round-trips. Unlike the sibling
2898/// spelling tags this is load-bearing for validity, not only fidelity: MySQL rejects
2899/// `IS NOT DISTINCT FROM` and the other dialects reject `<=>`, so a normalized render
2900/// would not re-parse under the dialect that produced it.
2901#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2902#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2903#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2904pub enum IsNotDistinctFromSpelling {
2905    /// The `IS NOT DISTINCT FROM` keyword form (SQL:1999 T151).
2906    Keyword,
2907    /// The MySQL `<=>` null-safe-equality operator.
2908    NullSafeEq,
2909    /// SQLite's bare general `IS`: `a IS b` is null-safe equality, folding onto this
2910    /// operator (SQLite also accepts the explicit `IS NOT DISTINCT FROM`, which keeps
2911    /// [`Keyword`](Self::Keyword)). Renders back as bare `IS` so the surface round-trips.
2912    Is,
2913}
2914
2915/// Which truth value an [`Expr::IsTruth`] predicate tests (SQL:2016 F571,
2916/// `<truth value>` in the `<boolean test>` grammar). The three-valued-logic constants:
2917/// `TRUE`, `FALSE`, and `UNKNOWN` (the boolean `NULL`).
2918#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2919#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2920#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2921pub enum TruthValue {
2922    /// `IS [NOT] TRUE`.
2923    True,
2924    /// `IS [NOT] FALSE`.
2925    False,
2926    /// `IS [NOT] UNKNOWN`.
2927    Unknown,
2928}
2929
2930/// Surface spelling for the bitwise exclusive-or operator [`BinaryOperator::BitwiseXor`].
2931///
2932/// XOR has two dialect-disjoint spellings that fold onto one operator:
2933/// PostgreSQL's `#` and MySQL's `^`. Unlike the cosmetic spelling tags, this is
2934/// load-bearing for validity, mirroring [`IsNotDistinctFromSpelling`]: PostgreSQL's `^`
2935/// is *exponentiation* (not XOR) and MySQL's `#` opens a comment, so each dialect rejects
2936/// the other's spelling — a normalized render would not re-parse. The two also differ in
2937/// precedence, which lives on the dialect's binding-power table, not here.
2938#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2939#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2940#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2941pub enum BitwiseXorSpelling {
2942    /// The PostgreSQL `#` operator.
2943    Hash,
2944    /// The MySQL `^` operator.
2945    Caret,
2946}
2947
2948/// Surface spelling for the regex-match operator [`BinaryOperator::Regexp`].
2949///
2950/// MySQL spells regular-expression match two interchangeable ways, `RLIKE` and its
2951/// `REGEXP` synonym. The canonical AST keeps one operator and this tag
2952/// records which keyword the source used so rendering round-trips exactly.
2953#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2954#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2955#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2956pub enum RegexpSpelling {
2957    /// The `RLIKE` keyword.
2958    Rlike,
2959    /// The `REGEXP` keyword.
2960    Regexp,
2961}
2962
2963/// Surface spelling for the pattern-match predicate [`Expr::Like`].
2964///
2965/// SQL spells pattern matching three interchangeable-shape ways — case-sensitive
2966/// `LIKE` (SQL-92 core E021-08), PostgreSQL's case-insensitive `ILIKE`, and the
2967/// regex-flavoured `SIMILAR TO` (SQL:1999 F841). The canonical AST keeps one
2968/// [`Expr::Like`] node and this tag records which keyword the source
2969/// used so rendering round-trips exactly, mirroring [`RegexpSpelling`].
2970#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2971#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2972#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2973pub enum LikeSpelling {
2974    /// The `LIKE` keyword.
2975    Like,
2976    /// The `ILIKE` keyword (PostgreSQL).
2977    ILike,
2978    /// The `SIMILAR TO` keyword pair (SQL:1999 F841).
2979    SimilarTo,
2980}
2981
2982/// Surface spelling for the null test [`Expr::IsNull`].
2983///
2984/// SQL's `<expr> IS [NOT] NULL` has one-word postfix synonyms in PostgreSQL and SQLite —
2985/// `<expr> ISNULL` (for `IS NULL`) and `<expr> NOTNULL` (for `IS NOT NULL`). The canonical
2986/// AST keeps one [`Expr::IsNull`] node with its `negated` flag and this tag records which
2987/// keyword form the source used so rendering round-trips, mirroring [`LikeSpelling`].
2988#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2989#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2990#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2991pub enum NullTestSpelling {
2992    /// The standard two/three-word form `IS NULL` / `IS NOT NULL`.
2993    Is,
2994    /// The one-word postfix synonym `ISNULL` (for `IS NULL`) / `NOTNULL` (for `IS NOT NULL`)
2995    /// — PostgreSQL and SQLite.
2996    Postfix,
2997    /// The two-word postfix synonym `<expr> NOT NULL` (for `IS NOT NULL`) — a `NOT`-led
2998    /// predicate spelling distinct from the one-word [`Postfix`](Self::Postfix) `NOTNULL`.
2999    /// Only ever paired with `negated: true` (there is no un-negated two-word form).
3000    /// SQLite and DuckDB accept it; PostgreSQL, despite the one-word synonyms, rejects it
3001    /// (engine-measured), so it rides its own
3002    /// [`PredicateSyntax::null_test_two_word_postfix`](crate::dialect::PredicateSyntax) gate
3003    /// rather than [`OperatorSyntax::null_test_postfix`](crate::dialect::OperatorSyntax).
3004    PostfixNotNull,
3005}
3006
3007/// Surface spelling for the [`StringFunc::CeilTo`] head word — `CEIL` vs its `CEILING`
3008/// synonym, recorded so rendering round-trips the word the source wrote.
3009#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3010#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3011#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3012pub enum CeilSpelling {
3013    /// Source used the `CEIL` spelling.
3014    Ceil,
3015    /// Source used the `CEILING` spelling.
3016    Ceiling,
3017}
3018
3019/// The Unicode normal form named in an `<expr> IS [NOT] <form> NORMALIZED` test
3020/// ([`Expr::IsNormalized`]).
3021#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3022#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3023#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3024pub enum NormalizationForm {
3025    /// Normalization Form C (canonical composition) — the default when `IS NORMALIZED`
3026    /// names no form.
3027    Nfc,
3028    /// Normalization Form D (canonical decomposition).
3029    Nfd,
3030    /// Normalization Form KC (compatibility composition).
3031    Nfkc,
3032    /// Normalization Form KD (compatibility decomposition).
3033    Nfkd,
3034}
3035
3036/// Unary expression operators.
3037#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3038#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3039#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3040pub enum UnaryOperator {
3041    /// `NOT` — logical negation.
3042    Not,
3043    /// `-` — arithmetic negation.
3044    Minus,
3045    /// `+` — unary plus (identity).
3046    Plus,
3047    /// Bitwise complement, spelled `~` (PostgreSQL, MySQL, SQLite, DuckDB). A prefix
3048    /// operator whose binding power is dialect data
3049    /// ([`prefix_bitwise_not`](crate::precedence::BindingPowerTable::prefix_bitwise_not)):
3050    /// it binds tightly (like the unary sign) in SQLite/MySQL, but in PostgreSQL/DuckDB it
3051    /// is looser than the arithmetic operators and tighter than the binary bitwise family
3052    /// (`~ 1 + 1` is `~ (1 + 1)` but `~ 1 & 3` is `(~ 1) & 3` — engine-measured).
3053    BitwiseNot,
3054    /// Oracle/Snowflake `PRIOR <expr>` — inside a `CONNECT BY` condition, marks the
3055    /// operand taken from the *parent* row of the hierarchical walk (Oracle *SQL
3056    /// Language Reference*, hierarchical queries; Snowflake `CONNECT BY`). It is an
3057    /// expression-level operator meaningful only in that clause: the parser produces it
3058    /// solely while parsing a [`Select::connect_by`](crate::ast::Select) condition
3059    /// (gated by [`SelectSyntax::connect_by_clause`](crate::dialect::SelectSyntax)), so
3060    /// the global expression grammar admits no bare `PRIOR` and a `prior` elsewhere
3061    /// stays an ordinary column name. Binds like the unary sign (tighter than
3062    /// comparison), so `PRIOR a = b` groups as `(PRIOR a) = b`.
3063    Prior,
3064}