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    /// A PostgreSQL positional parameter whose digit run exceeds [`u32`].
1415    ///
1416    /// PostgreSQL's raw parser accepts these spellings and materializes them through
1417    /// its C `atol` scanner path. Keeping the original digits interned preserves a
1418    /// renderable, round-trippable AST instead of rejecting a parse-valid placeholder
1419    /// or collapsing its engine-specific overflow result into an ordinary index.
1420    PositionalLarge {
1421        /// Decimal digits after the `$` sigil.
1422        digits: Symbol,
1423    },
1424    /// SQLite numbered `?1`, `?123` — the `?`-spelled positional parameter (distinct from
1425    /// PostgreSQL's `$`-spelled [`Positional`](Self::Positional) so it round-trips as `?N`).
1426    /// The carried value is the 1-based index; SQLite restricts it to `1..=32766`
1427    /// (`SQLITE_MAX_VARIABLE_NUMBER`), enforced at parse time.
1428    Numbered(u32),
1429    /// Anonymous positional `?` (ODBC/JDBC); its ordinal is its occurrence order.
1430    Anonymous,
1431    /// A named placeholder bound by name rather than position (`:name`, `@name`).
1432    ///
1433    /// `name` is the interned name *without* its sigil; [`ParameterSigil`] records
1434    /// which sigil spelled it, so a dialect accepting both forms round-trips each
1435    /// exactly. The by-name binding is a distinct meaning from the
1436    /// positional/anonymous forms, hence a value here rather than only a tag.
1437    Named {
1438        /// Name referenced by this syntax.
1439        name: Symbol,
1440        /// Which sigil introduced the name; see [`ParameterSigil`].
1441        sigil: ParameterSigil,
1442    },
1443}
1444
1445/// Which sigil introduced a named parameter placeholder.
1446///
1447/// The surface-form tag on [`ParameterKind::Named`]: one canonical named-parameter
1448/// shape covers the spellings, and this records which the source used —
1449/// the colon form (`:name`; Oracle, SQLite, JDBC/psycopg), the at-sign form
1450/// (`@name`; T-SQL, SQLite), or the dollar form (`$name`; SQLite) — so rendering
1451/// restores the original sigil.
1452#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1453#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1454#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1455pub enum ParameterSigil {
1456    /// `:name`.
1457    Colon,
1458    /// `@name`.
1459    At,
1460    /// `$name` — SQLite's dollar-named placeholder. Disjoint from the PostgreSQL
1461    /// positional `$1` ([`ParameterKind::Positional`]) by its follow byte (an
1462    /// identifier, not a digit).
1463    Dollar,
1464}
1465
1466/// Which session-variable form an [`Expr::SessionVariable`] names.
1467///
1468/// One flat enum over the four valid MySQL spellings, so an impossible pairing — a
1469/// `@`-user variable carrying an explicit system scope — is unrepresentable rather
1470/// than a runtime invariant (make illegal states unrepresentable). The system forms
1471/// differ semantically, not merely in spelling: `@@x` reads the server's implicit
1472/// scope for the variable while `@@global.x` / `@@session.x` force one, so each is
1473/// its own value, and the sigil (`@` vs `@@`) is recovered from the
1474/// variant so every form round-trips.
1475#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1476#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1477#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1478pub enum SessionVariableKind {
1479    /// `@name` — a user-defined session variable.
1480    User,
1481    /// `@@name` — a system variable at the server's implicit scope.
1482    System,
1483    /// `@@global.name` — a system variable read at global scope.
1484    SystemGlobal,
1485    /// `@@session.name` — a system variable read at session scope.
1486    SystemSession,
1487}
1488
1489/// A function or aggregate call: `name([DISTINCT] args | *)` plus the optional
1490/// aggregate modifiers an ordered-set / filtered aggregate carries.
1491///
1492/// Boxed inside [`Expr::Function`] so the hot expression enum stays within its
1493/// size budget. The same canonical shape covers plain scalar calls
1494/// (`COALESCE`, `NULLIF`, `GREATEST`, `LEAST`, …) and aggregates; unused modifier
1495/// fields stay at their empty defaults (one shape per construct).
1496#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1497#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1498#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1499pub struct FunctionCall<X: Extension = NoExt> {
1500    /// Name referenced by this syntax.
1501    pub name: ObjectName,
1502    /// The `ALL` / `DISTINCT` argument quantifier, as in `count(DISTINCT x)` or
1503    /// `count(ALL x)`; `None` for a call written with no quantifier. `DISTINCT ON`
1504    /// is a SELECT-list-only form, so an aggregate never carries it.
1505    pub quantifier: Option<SetQuantifier>,
1506    /// The argument list, each a positional or PostgreSQL named argument (see
1507    /// [`FunctionArg`]). Empty for a no-arg call or a `*` call.
1508    pub args: ThinVec<FunctionArg<X>>,
1509    /// `*` as the sole argument, as in `count(*)`. Mutually exclusive with `args`.
1510    pub wildcard: bool,
1511    /// `ORDER BY` inside an ordered-set aggregate, as in `array_agg(x ORDER BY y)`.
1512    pub order_by: ThinVec<OrderByExpr<X>>,
1513    /// `WITHIN GROUP (ORDER BY <keys>)` — the SQL:2008 ordered-set aggregate clause
1514    /// (T612/T614), as in `percentile_cont(0.5) WITHIN GROUP (ORDER BY x)`. A
1515    /// post-argument clause parsed after the `)`, mirroring the optionality of
1516    /// [`filter`](Self::filter) and [`over`](Self::over); `None` when absent, and the
1517    /// list is non-empty when present (the grammar requires a sort key). Distinct from
1518    /// the in-parenthesis [`order_by`](Self::order_by): PostgreSQL admits at most one of
1519    /// the two, and they render in different positions.
1520    pub within_group: Option<ThinVec<OrderByExpr<X>>>,
1521    /// `SEPARATOR <string>` — the MySQL `GROUP_CONCAT(... SEPARATOR ',')` delimiter,
1522    /// an in-parenthesis argument tail parsed after [`order_by`](Self::order_by) and
1523    /// before the closing `)`. Gated for acceptance by
1524    /// [`AggregateCallSyntax::group_concat_separator`](crate::dialect::ExpressionSyntax):
1525    /// `None` when the dialect leaves the flag off or the clause is unwritten. The
1526    /// delimiter is always a string constant, so it is a bare [`Literal`] rather than a
1527    /// general expression — one canonical field on the shared call shape,
1528    /// not a `GROUP_CONCAT`-specific node.
1529    pub separator: Option<Literal>,
1530    /// `FILTER (WHERE <predicate>)` applied to an aggregate.
1531    pub filter: Option<Box<Expr<X>>>,
1532    /// Whether the [`filter`](Self::filter) clause wrote the standard `WHERE` keyword
1533    /// (`FILTER (WHERE p)`) or DuckDB's keyword-less `FILTER (p)`. A pure fidelity tag —
1534    /// the [`FilterWhereSpelling`] round-trips the surface spelling. Carries the canonical
1535    /// [`Where`](FilterWhereSpelling::Where) when no filter is present.
1536    pub filter_where: FilterWhereSpelling,
1537    /// The `OVER (…)` / `OVER name` window clause that makes this a window
1538    /// function call; `None` for a plain scalar or aggregate call.
1539    pub over: Option<WindowSpec<X>>,
1540    /// `IGNORE NULLS` / `RESPECT NULLS` null-treatment written *inside* the call
1541    /// parentheses, after the argument list (and any in-parenthesis `ORDER BY`), as in
1542    /// DuckDB's `last(s IGNORE NULLS) OVER (…)`. `None` when unwritten. One canonical
1543    /// field on the shared call shape, the [`NullTreatment`] tag recording
1544    /// which the source used so it round-trips. Gated for acceptance by
1545    /// [`AggregateCallSyntax::null_treatment`](crate::dialect::CallSyntax): DuckDB spells the
1546    /// SQL:2016 null-treatment inside the parentheses (the standard's post-`)` position
1547    /// engine-rejects on 1.5.4), so it rides the in-parenthesis tail like
1548    /// [`separator`](Self::separator) rather than a post-argument clause. When the
1549    /// dialect leaves the flag off, `IGNORE`/`RESPECT` is left unconsumed and the
1550    /// unmatched `)` surfaces as a clean parse error.
1551    pub null_treatment: Option<NullTreatment>,
1552    /// The MySQL window-function post-`)` tail — the SQL:2016
1553    /// `[FROM {FIRST | LAST}] [{RESPECT | IGNORE} NULLS]` clauses a null-treatment
1554    /// window function carries *between* its argument `)` and its `OVER` clause, as in
1555    /// `NTH_VALUE(x, 2) FROM FIRST RESPECT NULLS OVER (…)`. `None` when no tail was
1556    /// written. A separate field from the in-parenthesis DuckDB
1557    /// [`null_treatment`](Self::null_treatment) precisely because the surface *position*
1558    /// differs: MySQL spells these after the `)` and rejects the in-paren spelling
1559    /// (`ER_PARSE_ERROR`, 1064), while DuckDB rejects the post-`)` one — so the two
1560    /// positions round-trip as distinct fields on the shared call shape
1561    /// rather than a shared field plus a position tag the DuckDB path would also carry.
1562    /// Gated for acceptance by
1563    /// [`AggregateCallSyntax::window_function_tail`](crate::dialect::CallSyntax): MySQL admits it
1564    /// only on the null-treatment window functions
1565    /// (`LEAD`/`LAG`/`FIRST_VALUE`/`LAST_VALUE`/`NTH_VALUE`, with `FROM FIRST` on
1566    /// `NTH_VALUE` alone), enforced by the parser's window-grammar gate.
1567    pub window_tail: Option<WindowFunctionTail>,
1568    /// Source location and node identity.
1569    pub meta: Meta,
1570}
1571
1572/// One argument of a [`FunctionCall`]: a bare positional value, or a PostgreSQL
1573/// named argument `name => value` / `name := value`, optionally prefixed by the
1574/// `VARIADIC` array-spread marker (`VARIADIC arr`, `VARIADIC name => arr`).
1575///
1576/// One canonical shape covers all three spellings: a positional
1577/// argument is `{ name: None, syntax: Positional, value }`, while a named argument
1578/// carries `name: Some(_)` and an [`ArgSyntax`] surface tag recording which arrow
1579/// the source wrote so it round-trips. `name` being `Some` ⟺ `syntax` is a named
1580/// form is an invariant the parser upholds. The argument is a spanned node so the
1581/// `name => value` extent has side-table identity of its own.
1582#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1583#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1584#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1585pub struct FunctionArg<X: Extension = NoExt> {
1586    /// The argument name for a named argument (`name` in `name => value`);
1587    /// `None` for a positional argument.
1588    pub name: Option<Symbol>,
1589    /// The `VARIADIC` marker that spreads an array over a variadic parameter
1590    /// (`f(a, VARIADIC arr)`). A parse-layer prefix the parser admits only on the
1591    /// *last* argument of a call and never alongside an `ALL`/`DISTINCT` quantifier
1592    /// (both PostgreSQL and DuckDB parse-reject the other positions); it composes with
1593    /// the `name => value` named form, so it is a flag on the argument rather than a
1594    /// separate call-level field. `false` for an ordinary argument.
1595    pub variadic: bool,
1596    /// Which surface form spelled this argument (positional, `=>`, or `:=`).
1597    pub syntax: ArgSyntax,
1598    /// Value supplied by this syntax.
1599    pub value: Expr<X>,
1600    /// Source location and node identity.
1601    pub meta: Meta,
1602}
1603
1604/// How a [`FunctionArg`] was spelled.
1605///
1606/// The surface-form tag for the one canonical [`FunctionArg`] shape,
1607/// mirroring [`CastSyntax`]: a positional argument carries no name, while the two
1608/// PostgreSQL named-argument arrows are otherwise the same construct (`=>` is
1609/// current, `:=` is the deprecated spelling PostgreSQL still accepts), so this
1610/// records which the source used to restore it on render.
1611#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1612#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1613#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1614pub enum ArgSyntax {
1615    /// A bare positional argument, as in `f(x)`.
1616    Positional,
1617    /// A named argument written `name => value`.
1618    Arrow,
1619    /// A named argument written with the deprecated `name := value` spelling.
1620    ColonEquals,
1621}
1622
1623/// The `IGNORE NULLS` / `RESPECT NULLS` null-treatment on a window/aggregate
1624/// [`FunctionCall`] (SQL:2016 window functions; DuckDB spells it inside the call
1625/// parentheses).
1626///
1627/// Two spellings of the same construct kept as data so the surface round-trips
1628/// (the [`ArgSyntax`] precedent): `RESPECT NULLS` is the default the
1629/// engine may canonicalize away, but the parser preserves whichever the source wrote.
1630#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1631#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1632#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1633pub enum NullTreatment {
1634    /// `IGNORE NULLS` — skip null values when computing the window/aggregate result.
1635    IgnoreNulls,
1636    /// `RESPECT NULLS` — include null values (the default).
1637    RespectNulls,
1638}
1639
1640/// The MySQL window-function post-`)` argument tail (SQL:2016):
1641/// `[FROM {FIRST | LAST}] [{RESPECT | IGNORE} NULLS]`, written between a null-treatment
1642/// window function's argument `)` and its `OVER` clause.
1643///
1644/// Both clauses are independently optional and appear in this fixed order (the reverse
1645/// order is a mysql:8 `ER_PARSE_ERROR`); the struct is only constructed when at least
1646/// one is present, so a [`FunctionCall::window_tail`] of `Some` always carries a
1647/// clause. The [`NullTreatment`] value is reused from the in-parenthesis DuckDB form —
1648/// only its surface *position* differs (post-`)` here, in-paren there), which is why
1649/// this is a separate field rather than a shared one.
1650#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1651#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1652#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1653pub struct WindowFunctionTail {
1654    /// `FROM FIRST` / `FROM LAST` — which end of the frame `NTH_VALUE` counts its `N`th
1655    /// row from. `None` when unwritten. On mysql:8 only `FROM FIRST` is accepted (and
1656    /// only on `NTH_VALUE`); `FROM LAST` is grammar-admitted but feature-rejected
1657    /// (`ER_NOT_SUPPORTED_YET`, 1235), so the MySQL parser never produces
1658    /// [`FromFirstLast::Last`].
1659    pub from_first_last: Option<FromFirstLast>,
1660    /// `RESPECT NULLS` / `IGNORE NULLS` — the post-`)` null treatment. `None` when
1661    /// unwritten. On mysql:8 only `RESPECT NULLS` is accepted; `IGNORE NULLS` is
1662    /// grammar-admitted but feature-rejected (`ER_NOT_SUPPORTED_YET`, 1235), so the
1663    /// MySQL parser never produces [`NullTreatment::IgnoreNulls`] in this position.
1664    pub null_treatment: Option<NullTreatment>,
1665}
1666
1667/// `FROM FIRST` / `FROM LAST` — which end of the window frame `NTH_VALUE` counts its
1668/// `N`th row from (SQL:2016 `from_first_last`).
1669///
1670/// Two spellings of the same construct kept as data so the surface round-trips (the
1671/// [`NullTreatment`] precedent). `FROM FIRST` is the default; `FROM LAST` completes the
1672/// surface for a dialect that admits it (mysql:8 feature-rejects it, so its parser only
1673/// ever produces `First`).
1674#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1675#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1676#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1677pub enum FromFirstLast {
1678    /// `FROM FIRST` — count from the first row of the frame (the default).
1679    First,
1680    /// `FROM LAST` — count from the last row of the frame.
1681    Last,
1682}
1683
1684/// A `CASE` expression in either standard form.
1685///
1686/// Searched `CASE WHEN cond THEN result … [ELSE result] END` has no `operand`;
1687/// simple `CASE operand WHEN value THEN result … END` carries the `operand` each
1688/// `WHEN` value is compared against. Boxed inside [`Expr::Case`] to keep the hot
1689/// expression enum within its size budget.
1690#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1691#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1692#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1693pub struct CaseExpr<X: Extension = NoExt> {
1694    /// Optional operand for this syntax.
1695    pub operand: Option<Box<Expr<X>>>,
1696    /// The `WHEN … THEN …` branches, in source order (always at least one).
1697    pub when_clauses: ThinVec<WhenClause<X>>,
1698    /// Optional else result for this syntax.
1699    pub else_result: Option<Box<Expr<X>>>,
1700    /// Source location and node identity.
1701    pub meta: Meta,
1702}
1703
1704#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1705#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1706#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1707/// An SQL when clause.
1708pub struct WhenClause<X: Extension = NoExt> {
1709    /// Predicate that controls this clause.
1710    pub condition: Expr<X>,
1711    /// The value produced when the condition holds.
1712    pub result: Expr<X>,
1713    /// Source location and node identity.
1714    pub meta: Meta,
1715}
1716
1717/// An `EXTRACT(<field> FROM <source>)` expression.
1718///
1719/// `field` is the datetime field name (`YEAR`, `MONTH`, …) interned as an
1720/// identifier; `source` is the value it is pulled from. Boxed inside
1721/// [`Expr::Extract`] to keep the hot expression enum within its size budget.
1722#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1723#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1724#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1725pub struct ExtractExpr<X: Extension = NoExt> {
1726    /// The datetime field name (`YEAR`, `MONTH`, …).
1727    pub field: Ident,
1728    /// The value the field is pulled from.
1729    pub source: Box<Expr<X>>,
1730    /// Source location and node identity.
1731    pub meta: Meta,
1732}
1733
1734/// The SQL/JSON `FORMAT JSON [ENCODING <enc>]` specifier (SQL:2016).
1735///
1736/// The only spellable format is `JSON` (PostgreSQL rejects `FORMAT JSONB` at raw
1737/// parse), so the struct's mere presence records "`FORMAT JSON` was written"; the
1738/// optional `encoding` is the `ENCODING` tail. PostgreSQL validates the encoding
1739/// name against `UTF8`/`UTF16`/`UTF32` *at raw parse* ("unrecognized JSON encoding"),
1740/// so it is a closed [`JsonEncoding`] rather than a free identifier.
1741#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1742#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1743#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1744pub struct JsonFormat {
1745    /// Optional encoding for this syntax.
1746    pub encoding: Option<JsonEncoding>,
1747}
1748
1749/// A SQL/JSON `ENCODING` name — the closed set PostgreSQL accepts at raw parse.
1750#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1751#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1752#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1753pub enum JsonEncoding {
1754    /// `UTF8` encoding.
1755    Utf8,
1756    /// `UTF16` encoding.
1757    Utf16,
1758    /// `UTF32` encoding.
1759    Utf32,
1760}
1761
1762/// A SQL/JSON value expression — an operand with an optional [`JsonFormat`]
1763/// (`<expr> [FORMAT JSON [ENCODING …]]`, PostgreSQL's `JsonValueExpr`).
1764///
1765/// The context item of a query function, each element of a `JSON_ARRAY` value
1766/// list, the value half of an object member, and the argument of `JSON`/
1767/// `JSON_SERIALIZE`/aggregates all take this shape.
1768#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1769#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1770#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1771pub struct JsonValueExpr<X: Extension = NoExt> {
1772    /// Expression evaluated by this syntax.
1773    pub expr: Box<Expr<X>>,
1774    /// Optional format for this syntax.
1775    pub format: Option<JsonFormat>,
1776    /// Source location and node identity.
1777    pub meta: Meta,
1778}
1779
1780#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1781#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1782#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1783/// An SQL json returning.
1784pub struct JsonReturning<X: Extension = NoExt> {
1785    /// Data type named by this syntax.
1786    pub data_type: Box<DataType<X>>,
1787    /// Optional format for this syntax.
1788    pub format: Option<JsonFormat>,
1789    /// Source location and node identity.
1790    pub meta: Meta,
1791}
1792
1793/// One SQL/JSON `PASSING` binding: `<value> [FORMAT JSON] AS <name>`.
1794///
1795/// The `FORMAT` rides the [`JsonValueExpr`] value; the name is a `ColLabel`
1796/// (interns any quote style).
1797#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1798#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1799#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1800pub struct JsonPassingArg<X: Extension = NoExt> {
1801    /// Value supplied by this syntax.
1802    pub value: JsonValueExpr<X>,
1803    /// Name referenced by this syntax.
1804    pub name: Ident,
1805    /// Source location and node identity.
1806    pub meta: Meta,
1807}
1808
1809/// A SQL/JSON `ON EMPTY` / `ON ERROR` behaviour handler (PostgreSQL's
1810/// `JsonBehavior`).
1811///
1812/// One shared grammar backs every slot: PostgreSQL accepts any behaviour in any
1813/// `ON EMPTY`/`ON ERROR` position at raw parse (the per-function legality — e.g.
1814/// `JSON_VALUE` cannot yield `EMPTY ARRAY` — is a parse-*analysis* check, not a
1815/// syntax rule), so the [`JsonBehaviorKind`] set is not restricted per function.
1816/// `default_expr` is `Some` exactly when `kind` is [`Default`](JsonBehaviorKind::Default).
1817#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1818#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1819#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1820pub struct JsonBehavior<X: Extension = NoExt> {
1821    /// Which behaviour applies (`NULL`/`ERROR`/`DEFAULT`/…); see [`JsonBehaviorKind`].
1822    pub kind: JsonBehaviorKind,
1823    /// Optional default expr for this syntax.
1824    pub default_expr: Option<Box<Expr<X>>>,
1825    /// Source location and node identity.
1826    pub meta: Meta,
1827}
1828
1829#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1830#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1831#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1832/// The SQL json behavior kind forms represented by the AST.
1833pub enum JsonBehaviorKind {
1834    /// `ERROR` — raise an error.
1835    Error,
1836    /// `NULL` — yield SQL null.
1837    Null,
1838    /// `TRUE` — yield `TRUE` (`JSON_EXISTS` only).
1839    True,
1840    /// `FALSE` — yield `FALSE` (`JSON_EXISTS` only).
1841    False,
1842    /// `UNKNOWN` — yield unknown (`JSON_EXISTS` only).
1843    Unknown,
1844    /// `EMPTY` — PostgreSQL's shorthand for `EMPTY ARRAY`; kept distinct so it
1845    /// round-trips to the bare spelling.
1846    Empty,
1847    /// `EMPTY ARRAY` — yield an empty JSON array.
1848    EmptyArray,
1849    /// `EMPTY OBJECT` — yield an empty JSON object.
1850    EmptyObject,
1851    /// `DEFAULT <expr>` — the fallback value carried in
1852    /// [`JsonBehavior::default_expr`].
1853    Default,
1854}
1855
1856/// The SQL/JSON `WRAPPER` behaviour of `JSON_QUERY` (SQL:2016).
1857///
1858/// The optional `ARRAY` keyword and the `UNCONDITIONAL` default are semantically
1859/// inert (PostgreSQL normalizes them away), so this closed enum folds
1860/// `WITH [UNCONDITIONAL] [ARRAY] WRAPPER` onto [`Unconditional`](Self::Unconditional)
1861/// and `WITHOUT [ARRAY] WRAPPER` onto [`Without`](Self::Without) — the render
1862/// re-parses to the same value.
1863#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1864#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1865#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1866pub enum JsonWrapperBehavior {
1867    /// No wrapper clause written.
1868    Unspecified,
1869    /// `WITHOUT [ARRAY] WRAPPER`.
1870    Without,
1871    /// `WITH [UNCONDITIONAL] [ARRAY] WRAPPER`.
1872    Unconditional,
1873    /// `WITH CONDITIONAL [ARRAY] WRAPPER`.
1874    Conditional,
1875}
1876
1877/// The SQL/JSON `QUOTES` behaviour of `JSON_QUERY` (SQL:2016).
1878///
1879/// The `ON SCALAR STRING` tail is semantically inert (PostgreSQL normalizes it
1880/// away), so it is not preserved.
1881#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1882#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1883#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1884pub enum JsonQuotesBehavior {
1885    /// No quotes clause written.
1886    Unspecified,
1887    /// `KEEP QUOTES [ON SCALAR STRING]`.
1888    Keep,
1889    /// `OMIT QUOTES [ON SCALAR STRING]`.
1890    Omit,
1891}
1892
1893/// Which SQL/JSON query function a [`JsonFuncExpr`] is.
1894#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1895#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1896#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1897pub enum JsonFuncKind {
1898    /// `JSON_VALUE` — extract a scalar SQL value at the path.
1899    Value,
1900    /// `JSON_QUERY` — extract a JSON fragment at the path.
1901    Query,
1902    /// `JSON_EXISTS` — test whether the path matches.
1903    Exists,
1904}
1905
1906/// A SQL/JSON query function — `JSON_VALUE` / `JSON_QUERY` / `JSON_EXISTS`
1907/// (SQL:2016; PostgreSQL's one `JsonFuncExpr` node with an `op` tag).
1908///
1909/// One canonical shape with a [`JsonFuncKind`] tag covers all three, mirroring the
1910/// engine. The three differ in which trailing clauses their grammar admits — only
1911/// `JSON_QUERY` takes `wrapper`/`quotes`; `JSON_EXISTS` takes neither `returning`
1912/// nor `on_empty`; only `JSON_QUERY`/`JSON_VALUE` take `on_empty` — and the parser
1913/// enforces those restrictions, so an illegal field stays `None`/`Unspecified`.
1914#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1915#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1916#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1917pub struct JsonFuncExpr<X: Extension = NoExt> {
1918    /// Which function this is (`JSON_VALUE`/`JSON_QUERY`/`JSON_EXISTS`); see [`JsonFuncKind`].
1919    pub kind: JsonFuncKind,
1920    /// The JSON value the path is applied to.
1921    pub context: JsonValueExpr<X>,
1922    /// The SQL/JSON path expression.
1923    pub path: Box<Expr<X>>,
1924    /// The `PASSING` argument bindings, in source order.
1925    pub passing: ThinVec<JsonPassingArg<X>>,
1926    /// The optional `RETURNING <type>` clause; see [`JsonReturning`].
1927    pub returning: Option<JsonReturning<X>>,
1928    /// The `WITH`/`WITHOUT WRAPPER` behaviour; see [`JsonWrapperBehavior`].
1929    pub wrapper: JsonWrapperBehavior,
1930    /// The `KEEP`/`OMIT QUOTES` behaviour; see [`JsonQuotesBehavior`].
1931    pub quotes: JsonQuotesBehavior,
1932    /// Optional on empty for this syntax.
1933    pub on_empty: Option<JsonBehavior<X>>,
1934    /// Optional on error for this syntax.
1935    pub on_error: Option<JsonBehavior<X>>,
1936    /// Source location and node identity.
1937    pub meta: Meta,
1938}
1939
1940/// Whether a SQL/JSON object member was spelled `key : value` or `key VALUE value`.
1941///
1942/// The optional leading `KEY` keyword is inert (PostgreSQL normalizes it away) and
1943/// is not preserved.
1944#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1945#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1946#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1947pub enum JsonKeyValueSpelling {
1948    /// `key : value`.
1949    Colon,
1950    /// `key VALUE value`.
1951    Value,
1952}
1953
1954/// One SQL/JSON object member: `[KEY] <key> {: | VALUE} <value> [FORMAT JSON]`.
1955#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1956#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1957#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1958pub struct JsonKeyValue<X: Extension = NoExt> {
1959    /// The member key expression.
1960    pub key: Box<Expr<X>>,
1961    /// Value supplied by this syntax.
1962    pub value: JsonValueExpr<X>,
1963    /// Exact source spelling retained for faithful rendering.
1964    pub spelling: JsonKeyValueSpelling,
1965    /// Source location and node identity.
1966    pub meta: Meta,
1967}
1968
1969/// The SQL/JSON null-handling clause of a constructor: `ABSENT ON NULL` (drop null
1970/// entries) or `NULL ON NULL` (keep them). `None` records that no clause was written
1971/// — the two constructors default oppositely (`JSON_OBJECT` keeps, `JSON_ARRAY`
1972/// drops), so the render must not synthesize the absent clause.
1973#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1974#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1975#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1976pub enum JsonNullClause {
1977    /// `ABSENT ON NULL` — drop members/elements whose value is null.
1978    AbsentOnNull,
1979    /// `NULL ON NULL` — keep members/elements whose value is null.
1980    NullOnNull,
1981}
1982
1983/// A SQL/JSON object constructor: `JSON_OBJECT([members] [null] [unique]
1984/// [RETURNING …])` (SQL:2016).
1985///
1986/// `entries` may be empty (`JSON_OBJECT()` / `JSON_OBJECT(RETURNING …)`).
1987/// `unique_keys` is `Some(true)` for `WITH UNIQUE [KEYS]`, `Some(false)` for
1988/// `WITHOUT UNIQUE [KEYS]`, `None` when unwritten (the inert `KEYS` word is not
1989/// preserved). This is the standard constructor only; the legacy
1990/// `json_object(text[])` function keeps the ordinary [`Expr::Function`] shape.
1991#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1992#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1993#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1994pub struct JsonObjectExpr<X: Extension = NoExt> {
1995    /// entries in source order.
1996    pub entries: ThinVec<JsonKeyValue<X>>,
1997    /// Optional null clause for this syntax.
1998    pub null_clause: Option<JsonNullClause>,
1999    /// Whether the unique keys form was present in the source.
2000    pub unique_keys: Option<bool>,
2001    /// The optional `RETURNING <type>` clause; see [`JsonReturning`].
2002    pub returning: Option<JsonReturning<X>>,
2003    /// Source location and node identity.
2004    pub meta: Meta,
2005}
2006
2007/// The body of a [`JsonArrayExpr`] — a value list or a subquery, PostgreSQL's two
2008/// distinct `JSON_ARRAY` productions.
2009#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2010#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2011#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2012pub enum JsonArrayBody<X: Extension = NoExt> {
2013    /// `JSON_ARRAY(v, … [null])` — a (possibly empty) value list with an optional
2014    /// null-handling clause.
2015    Values {
2016        /// Child items in source order.
2017        items: ThinVec<JsonValueExpr<X>>,
2018        /// Optional null clause for this syntax.
2019        null_clause: Option<JsonNullClause>,
2020        /// Source location and node identity.
2021        meta: Meta,
2022    },
2023    /// `JSON_ARRAY(<query> [FORMAT JSON])` — a subquery whose rows become elements.
2024    Query {
2025        /// Query governed by this node.
2026        query: Box<Query<X>>,
2027        /// Optional format for this syntax.
2028        format: Option<JsonFormat>,
2029        /// Source location and node identity.
2030        meta: Meta,
2031    },
2032}
2033
2034/// A SQL/JSON array constructor: `JSON_ARRAY(<body> [RETURNING …])` (SQL:2016).
2035#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2036#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2037#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2038pub struct JsonArrayExpr<X: Extension = NoExt> {
2039    /// Statement or query body governed by this node.
2040    pub body: JsonArrayBody<X>,
2041    /// The optional `RETURNING <type>` clause; see [`JsonReturning`].
2042    pub returning: Option<JsonReturning<X>>,
2043    /// Source location and node identity.
2044    pub meta: Meta,
2045}
2046
2047/// The body of a [`JsonAggregateExpr`] — the object or array aggregate shape.
2048#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2049#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2050#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2051pub enum JsonAggregateBody<X: Extension = NoExt> {
2052    /// `JSON_OBJECTAGG(k {: | VALUE} v [unique])` — one member, no `ORDER BY`.
2053    Object {
2054        /// The single key/value member; see [`JsonKeyValue`].
2055        entry: JsonKeyValue<X>,
2056        /// Whether the unique keys form was present in the source.
2057        unique_keys: Option<bool>,
2058        /// Source location and node identity.
2059        meta: Meta,
2060    },
2061    /// `JSON_ARRAYAGG(v [ORDER BY …])` — one value with an optional sort.
2062    Array {
2063        /// Value supplied by this syntax.
2064        value: JsonValueExpr<X>,
2065        /// Ordering terms in source order.
2066        order_by: ThinVec<OrderByExpr<X>>,
2067        /// Source location and node identity.
2068        meta: Meta,
2069    },
2070}
2071
2072/// A SQL/JSON aggregate constructor — `JSON_OBJECTAGG` / `JSON_ARRAYAGG`
2073/// (SQL:2016). Shares the ordinary-aggregate `FILTER (WHERE …)` / `OVER (…)` tail.
2074#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2075#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2076#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2077pub struct JsonAggregateExpr<X: Extension = NoExt> {
2078    /// The object or array aggregate body; see [`JsonAggregateBody`].
2079    pub body: JsonAggregateBody<X>,
2080    /// Optional null clause for this syntax.
2081    pub null_clause: Option<JsonNullClause>,
2082    /// The optional `RETURNING <type>` clause; see [`JsonReturning`].
2083    pub returning: Option<JsonReturning<X>>,
2084    /// Optional filter for this syntax.
2085    pub filter: Option<Box<Expr<X>>>,
2086    /// Optional over for this syntax.
2087    pub over: Option<Box<WindowSpec<X>>>,
2088    /// Source location and node identity.
2089    pub meta: Meta,
2090}
2091
2092/// Which bare SQL/JSON constructor a [`JsonConstructorExpr`] is.
2093#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2094#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2095#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2096pub enum JsonConstructorKind {
2097    /// `JSON(<value> [WITH|WITHOUT UNIQUE [KEYS]])`.
2098    Json,
2099    /// `JSON_SCALAR(<expr>)` — a plain argument, no `FORMAT`/`RETURNING`/`UNIQUE`.
2100    Scalar,
2101    /// `JSON_SERIALIZE(<value> [RETURNING <type> [FORMAT JSON]])`.
2102    Serialize,
2103}
2104
2105/// A bare SQL/JSON constructor — `JSON(x)` / `JSON_SCALAR(x)` / `JSON_SERIALIZE(x)`
2106/// (SQL:2016). The unused fields per kind stay `None` (the parser admits only each
2107/// kind's grammar): `unique_keys` is `JSON`-only, `returning` is `JSON_SERIALIZE`-only,
2108/// and `JSON_SCALAR`'s value never carries a `FORMAT`.
2109#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2110#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2111#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2112pub struct JsonConstructorExpr<X: Extension = NoExt> {
2113    /// Which constructor this is (`JSON`/`JSON_SCALAR`/`JSON_SERIALIZE`); see [`JsonConstructorKind`].
2114    pub kind: JsonConstructorKind,
2115    /// Value supplied by this syntax.
2116    pub value: JsonValueExpr<X>,
2117    /// Whether the unique keys form was present in the source.
2118    pub unique_keys: Option<bool>,
2119    /// The optional `RETURNING <type>` clause; see [`JsonReturning`].
2120    pub returning: Option<JsonReturning<X>>,
2121    /// Source location and node identity.
2122    pub meta: Meta,
2123}
2124
2125/// The type constraint of an `IS JSON` predicate: `IS JSON [VALUE|ARRAY|OBJECT|SCALAR]`.
2126#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2127#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2128#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2129pub enum JsonItemType {
2130    /// Bare `IS JSON` — any JSON type.
2131    Any,
2132    /// `IS JSON VALUE` — any JSON scalar, array, or object.
2133    Value,
2134    /// `IS JSON ARRAY` — a JSON array.
2135    Array,
2136    /// `IS JSON OBJECT` — a JSON object.
2137    Object,
2138    /// `IS JSON SCALAR` — a JSON scalar (not an array or object).
2139    Scalar,
2140}
2141
2142/// The SQL/JSON `<expr> IS [NOT] JSON [type] [WITH|WITHOUT UNIQUE [KEYS]]` predicate
2143/// (SQL:2016; PostgreSQL's `JsonIsPredicate`).
2144///
2145/// Binds at `IS`-predicate precedence like the other `IS` tests. `unique_keys`
2146/// records `WITH UNIQUE [KEYS]` (the inert `KEYS` word and the default `WITHOUT`
2147/// spelling are not preserved).
2148#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2149#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2150#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2151pub struct IsJsonExpr<X: Extension = NoExt> {
2152    /// Expression evaluated by this syntax.
2153    pub expr: Box<Expr<X>>,
2154    /// Whether the negated form was present in the source.
2155    pub negated: bool,
2156    /// The asserted JSON type (`VALUE`/`ARRAY`/`OBJECT`/`SCALAR`/any); see [`JsonItemType`].
2157    pub item_type: JsonItemType,
2158    /// Whether the unique keys form was present in the source.
2159    pub unique_keys: bool,
2160    /// Source location and node identity.
2161    pub meta: Meta,
2162}
2163
2164/// A SQL/XML expression function (SQL:2006; PostgreSQL `func_expr_common_subexpr`).
2165///
2166/// One kind-tagged enum for the eight special forms PostgreSQL lowers to `XmlExpr`
2167/// (and `XmlSerialize`): each differs in the clause grammar inside its parens, so a
2168/// per-form variant carries exactly that form's fields (the aggregate `xmlagg` is an
2169/// *ordinary* aggregate, not a keyword special form, so it is not here). The
2170/// contextual keywords each form opens with — `NAME`, `DOCUMENT`/`CONTENT`,
2171/// `VERSION`, `STANDALONE`, `PASSING`, `INDENT`, `WHITESPACE` — stay unreserved
2172/// (usable as ordinary names elsewhere); they are consumed only inside these parens.
2173#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2174#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2175#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2176pub enum XmlFunc<X: Extension = NoExt> {
2177    /// `xmlelement(NAME <name> [, xmlattributes(<attr>, …)] [, <content>, …])`.
2178    /// The `xmlattributes(…)` list, when present, precedes the content list (PostgreSQL
2179    /// rejects content before it); either list may be empty.
2180    Element {
2181        /// Name referenced by this syntax.
2182        name: Ident,
2183        /// attributes in source order.
2184        attributes: ThinVec<XmlAttribute<X>>,
2185        /// content in source order.
2186        content: ThinVec<Expr<X>>,
2187        /// Source location and node identity.
2188        meta: Meta,
2189    },
2190    /// `xmlforest(<value> [AS <name>], …)` — a non-empty list of the same
2191    /// `<value> [AS <name>]` element shape as an `xmlelement` attribute (reusing
2192    /// [`XmlAttribute`]).
2193    Forest {
2194        /// elements in source order.
2195        elements: ThinVec<XmlAttribute<X>>,
2196        /// Source location and node identity.
2197        meta: Meta,
2198    },
2199    /// `xmlconcat(<value>, …)` — a non-empty ordinary expression list.
2200    Concat {
2201        /// Arguments in source order.
2202        args: ThinVec<Expr<X>>,
2203        /// Source location and node identity.
2204        meta: Meta,
2205    },
2206    /// `xmlparse({DOCUMENT | CONTENT} <value> [{PRESERVE | STRIP} WHITESPACE])`.
2207    Parse {
2208        /// Whether the input is a `DOCUMENT` or a `CONTENT` fragment; see [`XmlDocumentOrContent`].
2209        option: XmlDocumentOrContent,
2210        /// The XML value to parse.
2211        arg: Box<Expr<X>>,
2212        /// The `PRESERVE`/`STRIP WHITESPACE` handling; see [`XmlWhitespaceOption`].
2213        whitespace: XmlWhitespaceOption,
2214        /// Source location and node identity.
2215        meta: Meta,
2216    },
2217    /// `xmlpi(NAME <name> [, <content>])` — a single optional content expression.
2218    Pi {
2219        /// Name referenced by this syntax.
2220        name: Ident,
2221        /// Optional content for this syntax.
2222        content: Option<Box<Expr<X>>>,
2223        /// Source location and node identity.
2224        meta: Meta,
2225    },
2226    /// `xmlroot(<value>, VERSION {<expr> | NO VALUE} [, STANDALONE {YES | NO | NO VALUE}])`.
2227    /// The `VERSION` clause is mandatory: `version` is `None` exactly for `NO VALUE`
2228    /// (an unwritten clause is not representable, matching PostgreSQL's grammar).
2229    Root {
2230        /// The XML value to modify.
2231        arg: Box<Expr<X>>,
2232        /// Optional version for this syntax.
2233        version: Option<Box<Expr<X>>>,
2234        /// The `STANDALONE {YES | NO | NO VALUE}` clause; see [`XmlStandalone`].
2235        standalone: XmlStandalone,
2236        /// Source location and node identity.
2237        meta: Meta,
2238    },
2239    /// `xmlserialize({DOCUMENT | CONTENT} <value> AS <type> [[NO] INDENT])`.
2240    Serialize {
2241        /// Whether the input is a `DOCUMENT` or a `CONTENT` fragment; see [`XmlDocumentOrContent`].
2242        option: XmlDocumentOrContent,
2243        /// The XML value to serialize.
2244        arg: Box<Expr<X>>,
2245        /// Data type named by this syntax.
2246        data_type: Box<DataType<X>>,
2247        /// The `[NO] INDENT` option; see [`XmlIndentOption`].
2248        indent: XmlIndentOption,
2249        /// Source location and node identity.
2250        meta: Meta,
2251    },
2252    /// `xmlexists(<path> PASSING [BY {REF | VALUE}] <doc> [BY {REF | VALUE}])`.
2253    /// The passing mechanism is admitted on either side of the document argument
2254    /// (PostgreSQL's `xmlexists_argument`); each side round-trips independently.
2255    Exists {
2256        /// The XPath expression to evaluate.
2257        path: Box<Expr<X>>,
2258        /// Optional mechanism before for this syntax.
2259        mechanism_before: Option<XmlPassingMechanism>,
2260        /// The XML document the path is tested against.
2261        arg: Box<Expr<X>>,
2262        /// Optional mechanism after for this syntax.
2263        mechanism_after: Option<XmlPassingMechanism>,
2264        /// Source location and node identity.
2265        meta: Meta,
2266    },
2267}
2268
2269/// One `<value> [AS <name>]` element of an `xmlelement` attribute list or an
2270/// `xmlforest` list. The `AS <name>` label is a `ColLabel` (any keyword admitted);
2271/// `None` when the element is a bare value (PostgreSQL derives the tag at analysis).
2272#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2273#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2274#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2275pub struct XmlAttribute<X: Extension = NoExt> {
2276    /// Value supplied by this syntax.
2277    pub value: Box<Expr<X>>,
2278    /// Name referenced by this syntax.
2279    pub name: Option<Ident>,
2280    /// Source location and node identity.
2281    pub meta: Meta,
2282}
2283
2284/// The `DOCUMENT` / `CONTENT` mode word of `xmlparse` / `xmlserialize`.
2285#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2286#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2287#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2288pub enum XmlDocumentOrContent {
2289    /// `DOCUMENT` — the input is a single well-formed XML document.
2290    Document,
2291    /// `CONTENT` — the input is an XML content fragment.
2292    Content,
2293}
2294
2295/// The optional whitespace-handling clause of `xmlparse`.
2296#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2297#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2298#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2299pub enum XmlWhitespaceOption {
2300    /// No clause written.
2301    Unspecified,
2302    /// `PRESERVE WHITESPACE`.
2303    Preserve,
2304    /// `STRIP WHITESPACE`.
2305    Strip,
2306}
2307
2308/// The optional indentation clause of `xmlserialize`.
2309#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2310#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2311#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2312pub enum XmlIndentOption {
2313    /// No clause written.
2314    Unspecified,
2315    /// `INDENT`.
2316    Indent,
2317    /// `NO INDENT`.
2318    NoIndent,
2319}
2320
2321/// The optional `STANDALONE` clause value of `xmlroot`.
2322#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2323#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2324#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2325pub enum XmlStandalone {
2326    /// No `STANDALONE` clause written.
2327    Unspecified,
2328    /// `STANDALONE YES`.
2329    Yes,
2330    /// `STANDALONE NO`.
2331    No,
2332    /// `STANDALONE NO VALUE`.
2333    NoValue,
2334}
2335
2336/// The `BY REF` / `BY VALUE` passing mechanism of `xmlexists`. Both spellings are
2337/// admitted at parse (PostgreSQL treats `BY REF` as the default and rejects `BY
2338/// VALUE` only later), so the tag round-trips the exact source word.
2339#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2340#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2341#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2342pub enum XmlPassingMechanism {
2343    /// `BY REF` — pass the XML by reference (PostgreSQL's default).
2344    ByRef,
2345    /// `BY VALUE` — pass the XML by value.
2346    ByValue,
2347}
2348
2349/// A standard-SQL string special form (SQL-92 E021-06/-09/-11 + SQL:1999 T312;
2350/// PostgreSQL's `func_expr_common_subexpr` string productions).
2351///
2352/// One kind-tagged enum for the keyword-argument string functions: each variant
2353/// carries exactly its form's typed fields, and every operand keyword (`FROM`,
2354/// `FOR`, `SIMILAR`, `ESCAPE`, `PLACING`, `IN`, `LEADING`/`TRAILING`/`BOTH`) is
2355/// grammar, not data. The comma plain-call spellings (`substring(x, 1, 2)`,
2356/// `trim(x, y)`, `overlay(a, b, c)`) are *not* here — they stay ordinary
2357/// [`FunctionCall`]s, so the plain-call surface every engine also accepts is
2358/// unaffected by the special forms.
2359#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2360#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2361#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2362pub enum StringFunc<X: Extension = NoExt> {
2363    /// `SUBSTRING(<expr> FROM <start> [FOR <count>])` and its variants: PostgreSQL
2364    /// admits `FOR <count>` alone and the reversed `FOR <count> FROM <start>`
2365    /// spelling (both orders fold onto the same fields and render canonically
2366    /// `FROM`-first), so at least one of `start`/`count` is always present. The
2367    /// string-pattern form `SUBSTRING(x FROM 'pat')`/`… FOR '#'` is this same
2368    /// production (the regex reading is runtime semantics, not grammar).
2369    Substring {
2370        /// Expression evaluated by this syntax.
2371        expr: Box<Expr<X>>,
2372        /// The `FROM <start>` operand, `None` for the `FOR`-only form.
2373        start: Option<Box<Expr<X>>>,
2374        /// The `FOR <count>` operand, `None` for the `FROM`-only form.
2375        count: Option<Box<Expr<X>>>,
2376        /// Source location and node identity.
2377        meta: Meta,
2378    },
2379    /// PostgreSQL's `SUBSTRING(<expr> SIMILAR <pattern> ESCAPE <escape>)` regex
2380    /// form (SQL:1999 `<regular expression substring function>`). All three
2381    /// operands are mandatory (`SUBSTRING(x SIMILAR p)` without `ESCAPE` rejects,
2382    /// engine-verified).
2383    SubstringSimilar {
2384        /// Expression evaluated by this syntax.
2385        expr: Box<Expr<X>>,
2386        /// The SQL-regex pattern.
2387        pattern: Box<Expr<X>>,
2388        /// The escape-character expression.
2389        escape: Box<Expr<X>>,
2390        /// Source location and node identity.
2391        meta: Meta,
2392    },
2393    /// `POSITION(<substr> IN <string>)` (SQL-92 E021-11). The operands are the
2394    /// restricted `b_expr` in PostgreSQL/DuckDB (`POSITION(1 IN 2 OR 3)` rejects)
2395    /// and MySQL's asymmetric `bit_expr IN expr` under
2396    /// [`StringFuncForms::position_asymmetric_operands`](crate::dialect::CallSyntax).
2397    /// There is no plain-call spelling: every keyword-form engine parse-rejects
2398    /// `position(a, b)`.
2399    Position {
2400        /// The substring to search for.
2401        substr: Box<Expr<X>>,
2402        /// The string searched within.
2403        string: Box<Expr<X>>,
2404        /// Source location and node identity.
2405        meta: Meta,
2406    },
2407    /// `OVERLAY(<target> PLACING <replacement> FROM <start> [FOR <count>])`
2408    /// (SQL:1999 T312). `FROM <start>` is mandatory — `OVERLAY(x PLACING y)` and
2409    /// the `FOR`-without-`FROM` form parse-reject on every keyword-form engine.
2410    Overlay {
2411        /// Object targeted by this syntax.
2412        target: Box<Expr<X>>,
2413        /// The replacement string spliced in.
2414        replacement: Box<Expr<X>>,
2415        /// The 1-based start position.
2416        start: Box<Expr<X>>,
2417        /// Optional count for this syntax.
2418        count: Option<Box<Expr<X>>>,
2419        /// Source location and node identity.
2420        meta: Meta,
2421    },
2422    /// `TRIM([{BOTH | LEADING | TRAILING}] [<chars>] FROM <sources>…)` (SQL-92
2423    /// E021-09) plus PostgreSQL's loose `trim_list` tails under
2424    /// [`StringFuncForms::trim_list_syntax`](crate::dialect::CallSyntax): a bare
2425    /// `FROM <list>`, a side without `FROM` (`TRIM(TRAILING ' foo ')`), and a
2426    /// multi-expression list (`TRIM('a' FROM 'b', 'c')`). At least one of
2427    /// `side`/`from` is present — a bare `TRIM(x)`/`TRIM(x, y)` is an ordinary
2428    /// call, never this node.
2429    Trim {
2430        /// The written `BOTH`/`LEADING`/`TRAILING` side, `None` when omitted.
2431        side: Option<TrimSide>,
2432        /// The trim-character expression written before `FROM`; `None` for the
2433        /// bare-`FROM` and side-without-`FROM` forms.
2434        trim_chars: Option<Box<Expr<X>>>,
2435        /// Whether `FROM` was written (distinguishes `TRIM(TRAILING ' foo ')`
2436        /// from `TRIM(TRAILING FROM ' foo ')` — both are valid PostgreSQL with
2437        /// different meanings, so the bit is load-bearing for round-trips).
2438        from: bool,
2439        /// The source expression list after `FROM` (or the bare list when a side
2440        /// is written without `FROM`). PostgreSQL's `trim_list` is an `expr_list`,
2441        /// so more than one source parses there; the restricted dialects hold this
2442        /// to exactly one at parse.
2443        sources: ThinVec<Expr<X>>,
2444        /// Source location and node identity.
2445        meta: Meta,
2446    },
2447    /// PostgreSQL's `COLLATION FOR (<expr>)` — the common-subexpr that reports the
2448    /// collation name derived for its operand. PostgreSQL gives it a dedicated
2449    /// `COLLATION FOR '(' a_expr ')'` production that lowers to a
2450    /// `pg_catalog.pg_collation_for(<expr>)` call, but the surface keyword form is
2451    /// kept here (not folded to a [`FunctionCall`]) so it round-trips as written. The
2452    /// parentheses and single `a_expr` operand are mandatory — `COLLATION FOR 'x'`,
2453    /// `COLLATION FOR ()`, and a two-argument list all parse-reject (engine-verified).
2454    CollationFor {
2455        /// Expression evaluated by this syntax.
2456        expr: Box<Expr<X>>,
2457        /// Source location and node identity.
2458        meta: Meta,
2459    },
2460    /// MySQL's `CONVERT(<expr> USING <charset>)` transcoding form — reinterprets its
2461    /// string operand in another character set (the grammar's
2462    /// `CONVERT '(' expr USING charset_name ')'`). Distinct from the comma-form cast
2463    /// [`CastSyntax::Convert`]: this changes the charset, not the type, so it is a string
2464    /// special form rather than a cast. The operand is a full `a_expr`
2465    /// (`CONVERT(1+2 USING utf8mb4)` parses, engine-verified); `charset` is a MySQL
2466    /// `charset_name` — an `ident_or_text` (a bare or backtick identifier, or a quoted
2467    /// string, round-tripping by the [`Ident`]'s quote style) or the `BINARY` transcoding
2468    /// name (`CONVERT(x USING binary)`), which reaches here as a bare [`Ident`]. Recognized
2469    /// only under [`CallSyntax::convert_function`](crate::dialect::CallSyntax); MySQL-only.
2470    ConvertUsing {
2471        /// Expression evaluated by this syntax.
2472        expr: Box<Expr<X>>,
2473        /// The target character set name.
2474        charset: Ident,
2475        /// Source location and node identity.
2476        meta: Meta,
2477    },
2478    /// MySQL's full-text search `MATCH (<col>, …) AGAINST (<expr> [<modifier>])`
2479    /// (`simple_expr: MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'`).
2480    /// The `columns` are a comma list of column references (bare or 1–3-part dotted
2481    /// [`Expr::Column`]s — an arbitrary expression, literal, function call, or empty
2482    /// list all parse-reject, engine-verified); `against` is a MySQL `bit_expr` (below
2483    /// the comparison level, so a trailing `IN`/`WITH` opens the modifier rather than an
2484    /// `IN` predicate). The optional [`MatchSearchModifier`] is exactly one of the four
2485    /// documented combinations; `None` is the default (no modifier words) and round-trips
2486    /// as written rather than as an explicit `IN NATURAL LANGUAGE MODE`. Recognized only
2487    /// under [`StringFuncForms::match_against`](crate::dialect::CallSyntax); MySQL-only, and
2488    /// distinct from SQLite's infix `<expr> MATCH <expr>` operator. Semantic constraints
2489    /// (a full-text index must cover the columns, the operand must be constant) are a
2490    /// server binding concern, not grammar.
2491    MatchAgainst {
2492        /// Columns in source order.
2493        columns: ThinVec<Expr<X>>,
2494        /// The full-text search expression.
2495        against: Box<Expr<X>>,
2496        /// Optional modifier for this syntax.
2497        modifier: Option<MatchSearchModifier>,
2498        /// Source location and node identity.
2499        meta: Meta,
2500    },
2501    /// `CEIL(<expr> TO <field>)` / `CEILING(<expr> TO <field>)` — a rounding-field
2502    /// keyword form distinct from the ordinary `CEIL(<expr>)` call and the comma-form
2503    /// scale spelling `CEIL(<expr>, <scale>)` (which stays an ordinary [`FunctionCall`]:
2504    /// no probed oracle grammar admits the `TO` tail, engine-verified against pg_query,
2505    /// DuckDB, and mysql:8.4). The field (`DAY`, `HOUR`, …) is stored as written and
2506    /// validated, if at all, by the consuming engine at analysis time, not parse.
2507    /// Recognized only under
2508    /// [`StringFuncForms::ceil_to_field`](crate::dialect::StringFuncForms::ceil_to_field).
2509    CeilTo {
2510        /// Expression evaluated by this syntax.
2511        expr: Box<Expr<X>>,
2512        /// The rounding field (`DAY`, `HOUR`, …).
2513        field: Ident,
2514        /// Exact source spelling retained for faithful rendering.
2515        spelling: CeilSpelling,
2516        /// Source location and node identity.
2517        meta: Meta,
2518    },
2519    /// `FLOOR(<expr> TO <field>)` — a rounding-field keyword form distinct from the
2520    /// ordinary `FLOOR(<expr>)` call and the comma-form scale spelling
2521    /// `FLOOR(<expr>, <scale>)` (which stays an ordinary [`FunctionCall`]: no probed
2522    /// oracle grammar admits the `TO` tail, engine-verified against pg_query, DuckDB, and
2523    /// mysql:8.4). Unlike [`StringFunc::CeilTo`], `FLOOR` has no `FLOORING` synonym, so
2524    /// there is no spelling field to track. The field (`DAY`, `HOUR`, …) is stored as
2525    /// written and validated, if at all, by the consuming engine at analysis time, not
2526    /// parse. Recognized only under
2527    /// [`StringFuncForms::floor_to_field`](crate::dialect::StringFuncForms::floor_to_field).
2528    FloorTo {
2529        /// Expression evaluated by this syntax.
2530        expr: Box<Expr<X>>,
2531        /// The rounding field (`DAY`, `HOUR`, …).
2532        field: Ident,
2533        /// Source location and node identity.
2534        meta: Meta,
2535    },
2536}
2537
2538/// The optional search modifier of a MySQL [`StringFunc::MatchAgainst`] — one of the
2539/// four documented full-text combinations. `WITH QUERY EXPANSION` combines only with
2540/// the (implicit or explicit) natural-language mode; `IN BOOLEAN MODE WITH QUERY
2541/// EXPANSION` parse-rejects (engine-verified).
2542#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2543#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2544#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2545pub enum MatchSearchModifier {
2546    /// `IN NATURAL LANGUAGE MODE`.
2547    NaturalLanguage,
2548    /// `IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION`.
2549    NaturalLanguageQueryExpansion,
2550    /// `IN BOOLEAN MODE`.
2551    Boolean,
2552    /// `WITH QUERY EXPANSION`.
2553    QueryExpansion,
2554}
2555
2556/// The `BOTH` / `LEADING` / `TRAILING` side word of a [`StringFunc::Trim`].
2557#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2558#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2559#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2560pub enum TrimSide {
2561    /// `TRIM(BOTH …)` — trim the character from both ends.
2562    Both,
2563    /// `TRIM(LEADING …)` — trim from the start only.
2564    Leading,
2565    /// `TRIM(TRAILING …)` — trim from the end only.
2566    Trailing,
2567}
2568
2569/// Quantifier used by `op ANY/ALL/SOME (<query>)` predicates.
2570#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2571#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2572#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2573pub enum Quantifier {
2574    /// `ANY` — the predicate holds for at least one row/element.
2575    Any,
2576    /// `ALL` — the predicate holds for every row/element.
2577    All,
2578    /// `SOME` — standard synonym for `ANY`.
2579    Some,
2580}
2581
2582/// Closed operator keys for dialect binding-power tables.
2583#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2584#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2585#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2586pub enum BinaryOperator {
2587    /// `+` — addition.
2588    Plus,
2589    /// `-` — subtraction.
2590    Minus,
2591    /// `*` — multiplication.
2592    Multiply,
2593    /// `/` — division.
2594    Divide,
2595    /// Modulo, spelled `%` everywhere and additionally `MOD` in MySQL. The two
2596    /// spellings are one operator; the [`ModuloSpelling`] tag records
2597    /// which the source used so it round-trips.
2598    Modulo(ModuloSpelling),
2599    /// Integer division — a distinct operator from `/` (it truncates to an integer), so
2600    /// it gets its own key rather than reusing [`Divide`](Self::Divide). Two
2601    /// dialect-disjoint spellings fold onto it: MySQL's `DIV` keyword and
2602    /// DuckDB's `//` symbol; the [`IntegerDivideSpelling`] tag records which the source
2603    /// used so it round-trips. Binds at multiplicative precedence.
2604    IntegerDivide(IntegerDivideSpelling),
2605    /// Arithmetic exponentiation, spelled `^`. A distinct operator from the
2606    /// [`BitwiseXor`](Self::BitwiseXor) `^` (a dialect gives the `^` lexeme one meaning or the
2607    /// other — engine truth): under
2608    /// [`CaretOperator::Exponent`](crate::dialect::CaretOperator)
2609    /// (PostgreSQL/DuckDB) `^` is arithmetic power,
2610    /// and binds at its OWN precedence tier — tighter than `*`/`/`/`%`
2611    /// ([`multiplicative`](crate::precedence::BindingPowerTable::multiplicative)) and looser
2612    /// than the unary sign, left-associative (`2 ^ 3 ^ 2` is `(2 ^ 3) ^ 2`, `2 ^ 3 * 2` is
2613    /// `(2 ^ 3) * 2` — engine-measured on pg_query). Its binding power is the dedicated
2614    /// [`exponent`](crate::precedence::BindingPowerTable::exponent) row, not the multiplicative
2615    /// rank the other arithmetic operators share.
2616    Exponent,
2617    /// `||` — string concatenation.
2618    StringConcat,
2619    /// PostgreSQL `@>` containment — "left contains right" over arrays, ranges, and
2620    /// `jsonb`. Binds at PostgreSQL's "any other operator" precedence (the rank shared
2621    /// with [`StringConcat`](Self::StringConcat)), left-associative.
2622    Contains,
2623    /// PostgreSQL `<@` containment — "left is contained by right", the mirror of
2624    /// [`Contains`](Self::Contains). Same "any other operator" precedence.
2625    ContainedBy,
2626    /// DuckDB `^@` — "left string starts with right string". Binds at the "any other
2627    /// operator" precedence. Gated by [`starts_with_operator`](crate::dialect::OperatorSyntax::starts_with_operator).
2628    StartsWith,
2629    /// The `&&` overlap operator — "do the two operands overlap?" over arrays, ranges
2630    /// (PostgreSQL), and geometries (DuckDB, whose `&&` is bounding-box overlap). The one
2631    /// operator key for the `&&` spelling across dialects that give it this meaning:
2632    /// DuckDB routes it here through [`DoubleAmpersand::Overlaps`], and PostgreSQL's
2633    /// range/array `&&` (deferred) would fold onto the same variant. Binds at the "any
2634    /// other operator" precedence (the [`Contains`](Self::Contains) rank), left-associative.
2635    ///
2636    /// Distinct from [`Overlaps`](Self::Overlaps), the SQL-standard `OVERLAPS` *keyword*
2637    /// period predicate over `(start, end)` rows — a different surface (keyword vs symbol),
2638    /// operand shape (two-element rows vs scalars), and render (`OVERLAPS` vs `&&`).
2639    ///
2640    /// [`DoubleAmpersand::Overlaps`]: crate::dialect::DoubleAmpersand::Overlaps
2641    Overlap,
2642    /// PostgreSQL `->` JSON access — object field / array element returned as
2643    /// `json`/`jsonb`. Same "any other operator" precedence.
2644    JsonGet,
2645    /// PostgreSQL `->>` JSON access — object field / array element returned as `text`,
2646    /// the text-typed form of [`JsonGet`](Self::JsonGet). Same precedence.
2647    JsonGetText,
2648    /// PostgreSQL `?` `jsonb` key/element existence — "does the right text string exist as
2649    /// a top-level key (object) or array element (array) of the left `jsonb`?". Binds at
2650    /// PostgreSQL's "any other operator" precedence (the [`Contains`](Self::Contains) rank),
2651    /// left-associative. Lexed only under [`OperatorSyntax::jsonb_operators`]; the `?` byte
2652    /// is otherwise a stray byte in PostgreSQL (it has no `?` parameter) or the anonymous
2653    /// placeholder elsewhere.
2654    ///
2655    /// [`OperatorSyntax::jsonb_operators`]: crate::dialect::OperatorSyntax::jsonb_operators
2656    JsonExists,
2657    /// PostgreSQL `?|` `jsonb` any-key existence — "does any of the right `text[]` strings
2658    /// exist as a top-level key/element of the left `jsonb`?". Same "any other operator"
2659    /// precedence as [`JsonExists`](Self::JsonExists).
2660    JsonExistsAny,
2661    /// PostgreSQL `?&` `jsonb` all-keys existence — "do all of the right `text[]` strings
2662    /// exist as top-level keys/elements of the left `jsonb`?". Same precedence.
2663    JsonExistsAll,
2664    /// PostgreSQL `@?` — "does the right `jsonpath` return any item for the left `jsonb`?".
2665    /// Same "any other operator" precedence.
2666    JsonPathExists,
2667    /// PostgreSQL `@@` — the match operator: for `jsonb @@ jsonpath` it returns the result
2668    /// of the JSON-path predicate check, and it is also the `tsvector @@ tsquery` full-text
2669    /// search match. One operator key for the shared `@@` spelling; same "any other operator"
2670    /// precedence.
2671    JsonPathMatch,
2672    /// PostgreSQL `#>` — extract the `jsonb` sub-object at the right `text[]` path, returned
2673    /// as `jsonb`. Same "any other operator" precedence.
2674    JsonExtractPath,
2675    /// PostgreSQL `#>>` — extract the `jsonb` sub-object at the right `text[]` path, returned
2676    /// as `text`, the text-typed form of [`JsonExtractPath`](Self::JsonExtractPath). Same
2677    /// precedence.
2678    JsonExtractPathText,
2679    /// PostgreSQL `#-` — delete the field/element of the left `jsonb` at the right `text[]`
2680    /// path. Same "any other operator" precedence. The `#-` lexeme is munched over the two
2681    /// contiguous bytes ahead of the bare `#` bitwise-XOR (engine-verified: `5#-3` is
2682    /// `5 #- 3`, while a space splits it into `#` then `-3`).
2683    JsonDeletePath,
2684    /// Bitwise OR, spelled `|` (PostgreSQL, MySQL, SQLite, DuckDB). In PostgreSQL/SQLite/
2685    /// DuckDB it shares one "bitwise" precedence with `&`/`<<`/`>>` (between additive and
2686    /// comparison); MySQL ranks it strictly looser than `&` (the load-bearing per-dialect
2687    /// precedence split), so its binding power is dialect data
2688    /// ([`BindingPowerTable::bitwise_or`](crate::precedence::BindingPowerTable::bitwise_or)).
2689    BitwiseOr,
2690    /// Bitwise AND, spelled `&` (PostgreSQL, MySQL, SQLite, DuckDB). Shares the one bitwise
2691    /// rank with `|`/`<<`/`>>` in PostgreSQL/SQLite/DuckDB; binds tighter than `|` and
2692    /// looser than the shifts in MySQL (dialect data, see
2693    /// [`BindingPowerTable::bitwise_and`](crate::precedence::BindingPowerTable::bitwise_and)).
2694    BitwiseAnd,
2695    /// Bitwise left shift, spelled `<<` (PostgreSQL, MySQL, SQLite, DuckDB). Grouped with
2696    /// [`BitwiseShiftRight`](Self::BitwiseShiftRight) at one shift rank in every dialect —
2697    /// looser than additive everywhere (`1 << 2 + 3` is `1 << (2 + 3)`, engine-measured on
2698    /// SQLite/PG/DuckDB) — but that rank sits *below* additive and *above* `&` in MySQL
2699    /// (dialect data,
2700    /// [`BindingPowerTable::bitwise_shift`](crate::precedence::BindingPowerTable::bitwise_shift)).
2701    BitwiseShiftLeft,
2702    /// Bitwise right shift, spelled `>>` (PostgreSQL, MySQL, SQLite, DuckDB). The mirror of
2703    /// [`BitwiseShiftLeft`](Self::BitwiseShiftLeft); same shift rank.
2704    BitwiseShiftRight,
2705    /// Bitwise exclusive-or. Two dialect-disjoint spellings fold onto this one operator:
2706    /// PostgreSQL's `#` and MySQL's `^`. The [`BitwiseXorSpelling`] tag is
2707    /// load-bearing for validity, not only fidelity — PostgreSQL rejects `^` as XOR (there
2708    /// `^` is exponentiation) and MySQL treats `#` as a comment — so a normalized render
2709    /// would not re-parse under the dialect that produced it (the same contract
2710    /// [`IsNotDistinctFrom`](Self::IsNotDistinctFrom) keeps). The two spellings *also* bind
2711    /// at different precedences: PostgreSQL's `#` is an "any other operator" (looser than
2712    /// additive), MySQL's `^` binds tighter than `*` — dialect data on
2713    /// [`BindingPowerTable::bitwise_xor`](crate::precedence::BindingPowerTable::bitwise_xor).
2714    /// Distinct from the *logical* [`Xor`](Self::Xor) keyword operator (MySQL `XOR`).
2715    BitwiseXor(BitwiseXorSpelling),
2716    /// Equality, spelled `=` everywhere and additionally `==` in SQLite. The two
2717    /// spellings are one operator; the [`EqualsSpelling`] tag records
2718    /// which the source used so it round-trips.
2719    Eq(EqualsSpelling),
2720    /// Inequality, spelled `<>` (SQL-standard) everywhere and additionally `!=`
2721    /// (C-style) in every bundled dialect. The two spellings are one operator; the
2722    /// [`NotEqSpelling`] tag records which the source used so it round-trips.
2723    NotEq(NotEqSpelling),
2724    /// `<` — less-than.
2725    Lt,
2726    /// `<=` — less-than-or-equal.
2727    LtEq,
2728    /// `>` — greater-than.
2729    Gt,
2730    /// `>=` — greater-than-or-equal.
2731    GtEq,
2732    /// The null-safe inequality predicate `IS DISTINCT FROM` (SQL:1999 T151): true when
2733    /// the operands differ, treating `NULL` as an ordinary comparable value rather than
2734    /// yielding `NULL`. The parser recognizes it in the `IS` predicate arm, not the
2735    /// symbolic-operator loop. Binds at comparison precedence, non-associative like `=`
2736    /// (PostgreSQL `a_expr IS DISTINCT FROM a_expr %prec IS`, gram.y). Two
2737    /// interchangeable-semantics spellings fold onto it: the SQL:1999 `IS DISTINCT FROM`
2738    /// keyword form and SQLite's bare `IS NOT` (`a IS NOT b`, SQLite's general negated
2739    /// `IS`). The [`IsDistinctFromSpelling`] tag records which the source used so the
2740    /// surface round-trips, mirroring the [`IsNotDistinctFrom`](Self::IsNotDistinctFrom)
2741    /// complement.
2742    IsDistinctFrom(IsDistinctFromSpelling),
2743    /// The complement of [`IsDistinctFrom`](Self::IsDistinctFrom): the null-safe
2744    /// equality predicate, true when the operands are equal or both `NULL`. A distinct
2745    /// operator key at the same comparison precedence. Two interchangeable-semantics
2746    /// spellings fold onto it: the SQL:1999 `IS NOT DISTINCT FROM` keyword
2747    /// form (which SQLite's general `IS` also produces), recognized in the `IS`-predicate
2748    /// arm; and MySQL's `<=>` operator, recognized in the symbolic-operator loop. The
2749    /// [`IsNotDistinctFromSpelling`] tag records which the source used. Unlike the other
2750    /// spelling tags this is load-bearing for validity, not only fidelity: MySQL rejects
2751    /// the keyword form and the other dialects reject `<=>`, so the spelling cannot be
2752    /// normalized away without producing input the same dialect fails to re-parse.
2753    IsNotDistinctFrom(IsNotDistinctFromSpelling),
2754    /// MySQL `RLIKE` / `REGEXP` regular-expression match. The two keywords are
2755    /// synonyms folding onto one operator; the [`RegexpSpelling`] tag
2756    /// records which the source used. Binds at comparison precedence (like `LIKE`).
2757    Regexp(RegexpSpelling),
2758    /// SQLite's `GLOB` pattern-match operator — case-sensitive Unix-glob matching
2759    /// (`*`/`?`/`[…]`). A keyword infix operator ([`KeywordOperators::Sqlite`]), the
2760    /// SQLite analogue of MySQL's `RLIKE`. Binds at comparison precedence (like
2761    /// `LIKE`/`REGEXP`).
2762    ///
2763    /// [`KeywordOperators::Sqlite`]: crate::dialect::KeywordOperators::Sqlite
2764    Glob,
2765    /// SQLite's `MATCH` operator — a grammar hook whose meaning is supplied by an
2766    /// application-defined function (FTS, R-Tree). A keyword infix operator
2767    /// ([`KeywordOperators::Sqlite`]) sibling of [`Glob`](Self::Glob); binds at
2768    /// comparison precedence. The bundled engine registers no `match` backing, so a
2769    /// bare `prepare` rejects it — it is grammar-only, guarded by round-trip rather
2770    /// than an accept/reject oracle.
2771    ///
2772    /// [`KeywordOperators::Sqlite`]: crate::dialect::KeywordOperators::Sqlite
2773    Match,
2774    /// The SQL-standard `OVERLAPS` period predicate (SQL:2016 F251): `(s1, e1) OVERLAPS
2775    /// (s2, e2)` — true when the two time periods, each given as a `(start, end |
2776    /// duration)` pair, share any instant. Both operands are exactly-two-element rows
2777    /// ([`Expr::Row`], bare parenthesized pair or `ROW(...)`), a shape the parser enforces
2778    /// (a scalar, a single-element grouping, or a three-element row is a parse error,
2779    /// matching PostgreSQL's `row OVERLAPS row` production and its wrong-arity `ereport`).
2780    /// The boolean result is not itself a row, so the predicate never chains
2781    /// (`x OVERLAPS y OVERLAPS z` rejects) — modelled non-associative. Binds tighter than
2782    /// the comparison operators (`x OVERLAPS y = TRUE` groups `(x OVERLAPS y) = TRUE`) and
2783    /// looser than the arithmetic/`Op` rank, its own PostgreSQL `%nonassoc OVERLAPS` gram.y
2784    /// row. Gated by
2785    /// [`PredicateSyntax::overlaps_period_predicate`](crate::dialect::PredicateSyntax::overlaps_period_predicate).
2786    Overlaps,
2787    /// `AND` — logical conjunction.
2788    And,
2789    /// MySQL `XOR` logical exclusive-or. Binds between `AND` and `OR` in precedence.
2790    Xor,
2791    /// `OR` — logical disjunction.
2792    Or,
2793}
2794
2795/// Surface spelling for the modulo operator [`BinaryOperator::Modulo`].
2796///
2797/// `%` is universal; MySQL additionally spells the same operation with the `MOD`
2798/// keyword. The canonical AST keeps one modulo operator and this tag
2799/// only records which spelling the source used so rendering round-trips exactly.
2800#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2801#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2802#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2803pub enum ModuloSpelling {
2804    /// The `%` operator.
2805    Percent,
2806    /// The MySQL `MOD` keyword.
2807    Mod,
2808}
2809
2810/// Surface spelling for the integer-division operator [`BinaryOperator::IntegerDivide`].
2811///
2812/// Integer division has two dialect-disjoint spellings that fold onto one operator:
2813/// MySQL's `DIV` keyword and DuckDB's `//` symbol. Neither dialect accepts the
2814/// other's spelling (MySQL has no `//` operator — a bare `//` is a syntax error there — and
2815/// DuckDB has no `DIV` keyword), so this tag is load-bearing for validity, not only
2816/// fidelity, mirroring [`BitwiseXorSpelling`]: a normalized render would not re-parse under
2817/// the dialect that produced it.
2818#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2819#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2820#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2821pub enum IntegerDivideSpelling {
2822    /// The MySQL `DIV` keyword.
2823    Div,
2824    /// The DuckDB `//` operator.
2825    SlashSlash,
2826}
2827
2828/// Surface spelling for the equality operator [`BinaryOperator::Eq`].
2829///
2830/// `=` is universal; SQLite additionally spells the same comparison with a doubled
2831/// `==`. The canonical AST keeps one equality operator and this tag only
2832/// records which spelling the source used so rendering round-trips exactly.
2833#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2834#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2835#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2836pub enum EqualsSpelling {
2837    /// The `=` operator.
2838    Single,
2839    /// The SQLite `==` operator.
2840    Double,
2841}
2842
2843/// Whether an aggregate `FILTER (…)` clause wrote the SQL-standard `WHERE` keyword
2844/// before its predicate ([`FunctionCall::filter_where`]).
2845///
2846/// SQL:2003 (and PostgreSQL/SQLite) require `FILTER (WHERE <predicate>)`; DuckDB
2847/// additionally accepts the keyword-less `FILTER (<predicate>)`
2848/// ([`AggregateCallSyntax::filter_optional_where`](crate::dialect::AggregateCallSyntax::filter_optional_where)).
2849/// The canonical AST keeps one filtered-aggregate shape and this tag records which the
2850/// source used so rendering round-trips exactly, mirroring [`EqualsSpelling`]. Only
2851/// meaningful when [`FunctionCall::filter`] is `Some`; a call with no filter carries
2852/// the canonical [`Where`](Self::Where).
2853#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2854#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2855#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2856pub enum FilterWhereSpelling {
2857    /// The SQL-standard `FILTER (WHERE <predicate>)` spelling (the canonical form).
2858    Where,
2859    /// DuckDB's keyword-less `FILTER (<predicate>)` spelling.
2860    Omitted,
2861}
2862
2863/// Surface spelling for the inequality operator [`BinaryOperator::NotEq`].
2864///
2865/// The SQL-standard `<>` is universal; every bundled dialect additionally spells the
2866/// same comparison with the C-style `!=`. The canonical AST keeps one inequality
2867/// operator and this tag only records which spelling the source used so rendering
2868/// round-trips exactly, mirroring [`EqualsSpelling`]. A fidelity tag, not a validity
2869/// one: both spellings parse under every dialect, so the canonical `<>` re-parses
2870/// wherever the source `!=` did.
2871#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2872#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2873#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2874pub enum NotEqSpelling {
2875    /// The SQL-standard `<>` operator (the canonical spelling).
2876    AngleBracket,
2877    /// The C-style `!=` operator.
2878    Bang,
2879}
2880
2881/// Surface spelling for the null-safe inequality operator
2882/// [`BinaryOperator::IsDistinctFrom`].
2883///
2884/// The predicate has two interchangeable-semantics spellings: the SQL:1999
2885/// `IS DISTINCT FROM` keyword form and SQLite's bare `IS NOT` (SQLite's general
2886/// negated `IS`). The canonical AST keeps one operator and this tag records which the
2887/// source used so rendering round-trips, mirroring [`IsNotDistinctFromSpelling`]. Both
2888/// spellings are valid under SQLite (which accepts the explicit keyword form too), so
2889/// this is a fidelity tag rather than a validity one.
2890#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2891#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2892#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2893pub enum IsDistinctFromSpelling {
2894    /// The `IS DISTINCT FROM` keyword form (SQL:1999 T151).
2895    Keyword,
2896    /// SQLite's bare `IS NOT`: `a IS NOT b` is null-safe inequality, folding onto this
2897    /// operator. Renders back as bare `IS NOT` so the surface round-trips.
2898    Is,
2899}
2900
2901/// Surface spelling for the null-safe equality operator
2902/// [`BinaryOperator::IsNotDistinctFrom`].
2903///
2904/// The predicate has two interchangeable-semantics spellings: the SQL:1999
2905/// `IS NOT DISTINCT FROM` keyword form (which SQLite's general `IS` also folds onto)
2906/// and MySQL's `<=>` operator. The canonical AST keeps one operator and this
2907/// tag records which the source used so rendering round-trips. Unlike the sibling
2908/// spelling tags this is load-bearing for validity, not only fidelity: MySQL rejects
2909/// `IS NOT DISTINCT FROM` and the other dialects reject `<=>`, so a normalized render
2910/// would not re-parse under the dialect that produced it.
2911#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2912#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2913#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2914pub enum IsNotDistinctFromSpelling {
2915    /// The `IS NOT DISTINCT FROM` keyword form (SQL:1999 T151).
2916    Keyword,
2917    /// The MySQL `<=>` null-safe-equality operator.
2918    NullSafeEq,
2919    /// SQLite's bare general `IS`: `a IS b` is null-safe equality, folding onto this
2920    /// operator (SQLite also accepts the explicit `IS NOT DISTINCT FROM`, which keeps
2921    /// [`Keyword`](Self::Keyword)). Renders back as bare `IS` so the surface round-trips.
2922    Is,
2923}
2924
2925/// Which truth value an [`Expr::IsTruth`] predicate tests (SQL:2016 F571,
2926/// `<truth value>` in the `<boolean test>` grammar). The three-valued-logic constants:
2927/// `TRUE`, `FALSE`, and `UNKNOWN` (the boolean `NULL`).
2928#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2929#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2930#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2931pub enum TruthValue {
2932    /// `IS [NOT] TRUE`.
2933    True,
2934    /// `IS [NOT] FALSE`.
2935    False,
2936    /// `IS [NOT] UNKNOWN`.
2937    Unknown,
2938}
2939
2940/// Surface spelling for the bitwise exclusive-or operator [`BinaryOperator::BitwiseXor`].
2941///
2942/// XOR has two dialect-disjoint spellings that fold onto one operator:
2943/// PostgreSQL's `#` and MySQL's `^`. Unlike the cosmetic spelling tags, this is
2944/// load-bearing for validity, mirroring [`IsNotDistinctFromSpelling`]: PostgreSQL's `^`
2945/// is *exponentiation* (not XOR) and MySQL's `#` opens a comment, so each dialect rejects
2946/// the other's spelling — a normalized render would not re-parse. The two also differ in
2947/// precedence, which lives on the dialect's binding-power table, not here.
2948#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2949#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2950#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2951pub enum BitwiseXorSpelling {
2952    /// The PostgreSQL `#` operator.
2953    Hash,
2954    /// The MySQL `^` operator.
2955    Caret,
2956}
2957
2958/// Surface spelling for the regex-match operator [`BinaryOperator::Regexp`].
2959///
2960/// MySQL spells regular-expression match two interchangeable ways, `RLIKE` and its
2961/// `REGEXP` synonym. The canonical AST keeps one operator and this tag
2962/// records which keyword the source used so rendering round-trips exactly.
2963#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2964#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2965#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2966pub enum RegexpSpelling {
2967    /// The `RLIKE` keyword.
2968    Rlike,
2969    /// The `REGEXP` keyword.
2970    Regexp,
2971}
2972
2973/// Surface spelling for the pattern-match predicate [`Expr::Like`].
2974///
2975/// SQL spells pattern matching three interchangeable-shape ways — case-sensitive
2976/// `LIKE` (SQL-92 core E021-08), PostgreSQL's case-insensitive `ILIKE`, and the
2977/// regex-flavoured `SIMILAR TO` (SQL:1999 F841). The canonical AST keeps one
2978/// [`Expr::Like`] node and this tag records which keyword the source
2979/// used so rendering round-trips exactly, mirroring [`RegexpSpelling`].
2980#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2981#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2982#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2983pub enum LikeSpelling {
2984    /// The `LIKE` keyword.
2985    Like,
2986    /// The `ILIKE` keyword (PostgreSQL).
2987    ILike,
2988    /// The `SIMILAR TO` keyword pair (SQL:1999 F841).
2989    SimilarTo,
2990}
2991
2992/// Surface spelling for the null test [`Expr::IsNull`].
2993///
2994/// SQL's `<expr> IS [NOT] NULL` has one-word postfix synonyms in PostgreSQL and SQLite —
2995/// `<expr> ISNULL` (for `IS NULL`) and `<expr> NOTNULL` (for `IS NOT NULL`). The canonical
2996/// AST keeps one [`Expr::IsNull`] node with its `negated` flag and this tag records which
2997/// keyword form the source used so rendering round-trips, mirroring [`LikeSpelling`].
2998#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2999#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3000#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3001pub enum NullTestSpelling {
3002    /// The standard two/three-word form `IS NULL` / `IS NOT NULL`.
3003    Is,
3004    /// The one-word postfix synonym `ISNULL` (for `IS NULL`) / `NOTNULL` (for `IS NOT NULL`)
3005    /// — PostgreSQL and SQLite.
3006    Postfix,
3007    /// The two-word postfix synonym `<expr> NOT NULL` (for `IS NOT NULL`) — a `NOT`-led
3008    /// predicate spelling distinct from the one-word [`Postfix`](Self::Postfix) `NOTNULL`.
3009    /// Only ever paired with `negated: true` (there is no un-negated two-word form).
3010    /// SQLite and DuckDB accept it; PostgreSQL, despite the one-word synonyms, rejects it
3011    /// (engine-measured), so it rides its own
3012    /// [`PredicateSyntax::null_test_two_word_postfix`](crate::dialect::PredicateSyntax) gate
3013    /// rather than [`OperatorSyntax::null_test_postfix`](crate::dialect::OperatorSyntax).
3014    PostfixNotNull,
3015}
3016
3017/// Surface spelling for the [`StringFunc::CeilTo`] head word — `CEIL` vs its `CEILING`
3018/// synonym, recorded so rendering round-trips the word the source wrote.
3019#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3020#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3021#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3022pub enum CeilSpelling {
3023    /// Source used the `CEIL` spelling.
3024    Ceil,
3025    /// Source used the `CEILING` spelling.
3026    Ceiling,
3027}
3028
3029/// The Unicode normal form named in an `<expr> IS [NOT] <form> NORMALIZED` test
3030/// ([`Expr::IsNormalized`]).
3031#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3032#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3033#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3034pub enum NormalizationForm {
3035    /// Normalization Form C (canonical composition) — the default when `IS NORMALIZED`
3036    /// names no form.
3037    Nfc,
3038    /// Normalization Form D (canonical decomposition).
3039    Nfd,
3040    /// Normalization Form KC (compatibility composition).
3041    Nfkc,
3042    /// Normalization Form KD (compatibility decomposition).
3043    Nfkd,
3044}
3045
3046/// Unary expression operators.
3047#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3048#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3049#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3050pub enum UnaryOperator {
3051    /// `NOT` — logical negation.
3052    Not,
3053    /// `-` — arithmetic negation.
3054    Minus,
3055    /// `+` — unary plus (identity).
3056    Plus,
3057    /// Bitwise complement, spelled `~` (PostgreSQL, MySQL, SQLite, DuckDB). A prefix
3058    /// operator whose binding power is dialect data
3059    /// ([`prefix_bitwise_not`](crate::precedence::BindingPowerTable::prefix_bitwise_not)):
3060    /// it binds tightly (like the unary sign) in SQLite/MySQL, but in PostgreSQL/DuckDB it
3061    /// is looser than the arithmetic operators and tighter than the binary bitwise family
3062    /// (`~ 1 + 1` is `~ (1 + 1)` but `~ 1 & 3` is `(~ 1) & 3` — engine-measured).
3063    BitwiseNot,
3064    /// Oracle/Snowflake `PRIOR <expr>` — inside a `CONNECT BY` condition, marks the
3065    /// operand taken from the *parent* row of the hierarchical walk (Oracle *SQL
3066    /// Language Reference*, hierarchical queries; Snowflake `CONNECT BY`). It is an
3067    /// expression-level operator meaningful only in that clause: the parser produces it
3068    /// solely while parsing a [`Select::connect_by`](crate::ast::Select) condition
3069    /// (gated by [`SelectSyntax::connect_by_clause`](crate::dialect::SelectSyntax)), so
3070    /// the global expression grammar admits no bare `PRIOR` and a `prior` elsewhere
3071    /// stays an ordinary column name. Binds like the unary sign (tighter than
3072    /// comparison), so `PRIOR a = b` groups as `(PRIOR a) = b`.
3073    Prior,
3074}