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}