Skip to main content

squonk_ast/ast/
query.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Moderately AI Inc.
3
4//! Query-level AST nodes: `Query`, set operations, `SELECT`, CTEs, and locking clauses.
5
6use super::{
7    DataType, DefaultValue, Delete, Expr, Extension, FunctionCall, Ident, Insert, JsonBehavior,
8    JsonFormat, JsonPassingArg, JsonQuotesBehavior, JsonValueExpr, JsonWrapperBehavior, Literal,
9    MatchRecognize, Merge, NamedWindow, NoExt, ObjectName, PipeOperator, Pivot,
10    SemiStructuredPathSegment, SpecialFunctionKeyword, TemporaryTableKind, Unpivot, Update,
11    XmlPassingMechanism,
12};
13use crate::vocab::{Meta, Symbol};
14use thin_vec::ThinVec;
15
16#[derive(Clone, Debug, PartialEq, Eq, Hash)]
17#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
18#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
19/// An SQL query.
20pub struct Query<X: Extension = NoExt> {
21    /// Common table expressions visible to this statement.
22    pub with: Option<With<X>>,
23    /// Statement or query body governed by this node.
24    pub body: SetExpr<X>,
25    /// Ordering terms in source order.
26    pub order_by: ThinVec<OrderByExpr<X>>,
27    /// DuckDB's `ORDER BY ALL` mode — sort by every projection column, left to
28    /// right — with its optional direction/nulls modifiers; `None` for an ordinary
29    /// (or absent) `ORDER BY`. Mutually exclusive with a non-empty
30    /// [`order_by`](Self::order_by): DuckDB rejects mixing `ALL` with explicit sort
31    /// keys (`ORDER BY ALL, x` / `ORDER BY x, ALL` are syntax errors; probed on
32    /// 1.5.4), so `ALL` is a *mode of the whole clause*, never a sort-key
33    /// expression — the column set is unknowable at parse time, so a synthetic key
34    /// list would be a lie (the shape is anchored on the semantics). `Box`ed
35    /// because the clause is rare while `Query` is a hot node (the
36    /// [`Select::into`] precedent). Gated by
37    /// [`GroupingSyntax::order_by_all`](crate::dialect::SelectSyntax); only the
38    /// query-level clause admits `ALL` — DuckDB rejects it in window `ORDER BY`
39    /// ("Cannot ORDER BY ALL in a window expression") and has no DML sort tails.
40    pub order_by_all: Option<Box<OrderByAll>>,
41    /// ClickHouse `LIMIT n [OFFSET m] BY expr, …` — per-group row limiting, written
42    /// after `ORDER BY` and *before* the ordinary [`limit`](Self::limit) tail. Its own
43    /// field because it coexists with `limit` and means something different (see
44    /// [`LimitBy`]); `None` for the common query that writes no `LIMIT BY`. Gated by
45    /// [`QueryTailSyntax::limit_by_clause`](crate::dialect::SelectSyntax) — off for every
46    /// shipped preset but Lenient, so it stays `None` elsewhere. `Box`ed because the
47    /// clause is rare while `Query` is a hot node (the [`order_by_all`](Self::order_by_all)
48    /// precedent) — keeping the 104-byte [`LimitBy`] off the inline `Query` footprint.
49    pub limit_by: Option<Box<LimitBy<X>>>,
50    /// Row limit applied to the result.
51    pub limit: Option<Limit<X>>,
52    /// ClickHouse `SETTINGS name = value, …` — query-level setting overrides written
53    /// after the ordinary [`limit`](Self::limit) tail (`SELECT … LIMIT 10 SETTINGS
54    /// max_threads = 8`). Empty for the common query that writes none — a bare
55    /// [`ThinVec`] costing one null pointer when unused, following the
56    /// [`locking`](Self::locking)/[`pipe_operators`](Self::pipe_operators) list-tail
57    /// precedent rather than a boxed option (a list's own rarity optimization is the
58    /// empty vector, so `Query`'s hot footprint grows by only that one pointer). Gated
59    /// by [`QueryTailSyntax::settings_clause`](crate::dialect::SelectSyntax) — on for
60    /// Lenient only, so it stays empty under every oracle-compared preset.
61    pub settings: ThinVec<Setting<X>>,
62    /// ClickHouse `FORMAT <name>` — the output-format clause that closes the query, the
63    /// last tail of all (after [`settings`](Self::settings)); `None` for the common query
64    /// that names no format. The format name is a bare identifier (`JSON`, `CSV`,
65    /// `TabSeparated`, `Null`), see [`FormatClause`]. Gated by
66    /// [`QueryTailSyntax::format_clause`](crate::dialect::SelectSyntax) — off for every
67    /// shipped preset but Lenient, so it stays `None` elsewhere. `Box`ed because the
68    /// clause is rare while `Query` is a hot node (the
69    /// [`order_by_all`](Self::order_by_all)/[`limit_by`](Self::limit_by) precedent),
70    /// keeping the clause off the inline `Query` footprint at one pointer's cost.
71    pub format: Option<Box<FormatClause>>,
72    /// The row-locking clauses (`FOR UPDATE`/`FOR SHARE`/…) written after `LIMIT`.
73    /// PostgreSQL admits several stacked clauses (`FOR UPDATE OF a FOR SHARE OF b`),
74    /// MySQL exactly one, so this is a list; empty when the query writes none. Gated
75    /// by [`QueryTailSyntax::locking_clauses`](crate::dialect::SelectSyntax). Not generic
76    /// over `X`: a [`LockingClause`] carries only names and surface tags, no expression.
77    pub locking: ThinVec<LockingClause>,
78    /// BigQuery/ZetaSQL `|>` pipe operators applied to this query's result, left to
79    /// right (`FROM t |> WHERE x |> SELECT a`). Empty for an ordinary query — the common
80    /// case, so a bare [`ThinVec`] that costs one null pointer when unused, like
81    /// [`locking`](Self::locking). Each element is one [`PipeOperator`] step. Gated by
82    /// [`QueryTailSyntax::pipe_syntax`](crate::dialect::SelectSyntax); off for every shipped
83    /// preset, so the field stays empty there and the `|>` token never even lexes.
84    pub pipe_operators: ThinVec<PipeOperator<X>>,
85    /// MSSQL `FOR XML …` / `FOR JSON …` result-shaping tail — the last clause of all,
86    /// serializing the result as XML/JSON instead of a rowset; `None` for the common
87    /// query that writes no `FOR XML`/`FOR JSON`. See [`ForClause`]. Gated by
88    /// [`QueryTailSyntax::for_xml_json_clause`](crate::dialect::SelectSyntax) — on for
89    /// MSSQL and Lenient, so it stays `None` elsewhere. `Box`ed because the clause is
90    /// rare while `Query` is a hot node (the
91    /// [`order_by_all`](Self::order_by_all)/[`format`](Self::format) precedent), keeping
92    /// the 92-byte [`ForClause`] off the inline `Query` footprint at one pointer's cost.
93    /// Not generic over `X`: a [`ForClause`] carries only mode tags and quoted names,
94    /// no expression.
95    pub for_clause: Option<Box<ForClause>>,
96    /// Source location and node identity.
97    pub meta: Meta,
98}
99
100/// One row-locking clause on a [`Query`]: `FOR UPDATE`/`FOR SHARE`/… with the
101/// optional `OF <table>, …` target list and a `NOWAIT`/`SKIP LOCKED` wait policy.
102///
103/// PostgreSQL and MySQL share this modern surface (PG `for_locking_clause`), so it
104/// is one canonical shape gated per-dialect rather than a parallel node
105/// per engine. MySQL's legacy `LOCK IN SHARE MODE` is a *spelling* of `FOR SHARE`
106/// (it predates the `FOR SHARE` keyword), folded onto this shape with a
107/// [`LockingSpelling`] tag so the surface round-trips. Carries no [`Expr`], so it is
108/// not generic over the extension parameter.
109#[derive(Clone, Debug, PartialEq, Eq, Hash)]
110#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
111#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
112pub struct LockingClause {
113    /// Which lock strength (`FOR UPDATE`/`FOR SHARE`/…); see [`LockStrength`].
114    pub strength: LockStrength,
115    /// The `OF <table>, …` restriction naming which relations the lock applies to;
116    /// empty when the clause locks every table in the query (no `OF`). PostgreSQL
117    /// maps these to `RangeVar`s (relation names), so [`ObjectName`], not `Ident`.
118    pub of: ThinVec<ObjectName>,
119    /// Optional wait for this syntax.
120    pub wait: Option<LockWait>,
121    /// Exact source spelling retained for faithful rendering.
122    pub spelling: LockingSpelling,
123    /// Source location and node identity.
124    pub meta: Meta,
125}
126
127/// The strength of a [`LockingClause`], strongest first as PostgreSQL orders them.
128///
129/// All four PostgreSQL `for_locking_strength` levels are modelled so the one shape
130/// covers both dialects (MySQL writes only [`Update`](Self::Update) and
131/// [`Share`](Self::Share); the `KEY`/`NO KEY` refinements are PostgreSQL-only).
132#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
133#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
134#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
135pub enum LockStrength {
136    /// `FOR UPDATE` — the strongest row lock.
137    Update,
138    /// `FOR NO KEY UPDATE` — a weaker exclusive lock that still admits `KEY SHARE` (PostgreSQL).
139    NoKeyUpdate,
140    /// `FOR SHARE` (also MySQL's `LOCK IN SHARE MODE`, distinguished by
141    /// [`LockingSpelling`]).
142    Share,
143    /// `FOR KEY SHARE` — the weakest row lock (PostgreSQL).
144    KeyShare,
145}
146
147#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
148#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
149#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
150/// The SQL lock wait forms represented by the AST.
151pub enum LockWait {
152    /// `NOWAIT`: error immediately rather than wait for a conflicting lock.
153    NoWait,
154    /// `SKIP LOCKED`: silently omit rows that are already locked.
155    SkipLocked,
156}
157
158/// Surface syntax that produced a [`LockingClause`].
159///
160/// One `FOR SHARE` semantic, two spellings kept as data (mirroring
161/// [`RollupSpelling`]): the modern `FOR UPDATE`/`FOR SHARE`/… keyword form and
162/// MySQL's legacy `LOCK IN SHARE MODE`. Canonicalizing the legacy spelling onto the
163/// [`LockStrength::Share`] shape lets the differential oracle compare one shape; the
164/// tag exists only so rendering reproduces the written form.
165#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
166#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
167#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
168pub enum LockingSpelling {
169    /// The modern `FOR <strength> [OF …] [NOWAIT|SKIP LOCKED]` keyword form; the
170    /// construction default and the only form PostgreSQL writes.
171    Modern,
172    /// MySQL's legacy `LOCK IN SHARE MODE` (a bare `FOR SHARE` with no `OF`/wait
173    /// tail). Only valid on [`LockStrength::Share`].
174    LockInShareMode,
175}
176
177#[derive(Clone, Debug, PartialEq, Eq, Hash)]
178#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
179#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
180/// The SQL set expr forms represented by the AST.
181pub enum SetExpr<X: Extension = NoExt> {
182    /// A `SELECT` query body.
183    Select {
184        /// The select body; see [`Select`].
185        select: Box<Select<X>>,
186        /// Source location and node identity.
187        meta: Meta,
188    },
189    /// A `VALUES (...)` row-constructor query body.
190    Values {
191        /// Values in source order.
192        values: Box<Values<X>>,
193        /// Source location and node identity.
194        meta: Meta,
195    },
196    /// A parenthesized nested query body.
197    Query {
198        /// Query governed by this node.
199        query: Box<Query<X>>,
200        /// Source location and node identity.
201        meta: Meta,
202    },
203    /// A set operation combining two query bodies (`UNION`/`INTERSECT`/`EXCEPT`).
204    SetOperation {
205        /// Operator applied by this expression.
206        op: SetOperator,
207        /// Whether the all form was present in the source.
208        all: bool,
209        /// DuckDB's `UNION [ALL] BY NAME` modifier: pair the two inputs' columns by
210        /// *name* (padding a side's missing columns with NULL) instead of by
211        /// position. A semantic modifier of the operation — it changes column
212        /// correspondence — so it is a flag on this node, not a spelling tag or a new
213        /// [`SetOperator`] variant (a semantic modifier is a shape field, a
214        /// pure spelling would be a tag). Orthogonal to [`all`](Self::SetOperation::all),
215        /// exactly as DuckDB models it — `UNION [ALL] BY NAME` serializes as
216        /// `setop_type: UNION_BY_NAME` with a separate `setop_all` (probed on 1.5.4).
217        /// DuckDB accepts `BY NAME` on `UNION` only (`INTERSECT`/`EXCEPT BY NAME` are
218        /// syntax errors; probed on 1.5.4), so the parser sets this `true` only for
219        /// [`SetOperator::Union`]. Gated by
220        /// [`SelectSyntax::union_by_name`](crate::dialect::SelectSyntax); `false` for
221        /// every ordinary (positional) set operation.
222        by_name: bool,
223        /// Left-hand operand.
224        left: Box<SetExpr<X>>,
225        /// Right-hand operand.
226        right: Box<SetExpr<X>>,
227        /// Source location and node identity.
228        meta: Meta,
229    },
230    /// DuckDB's `PIVOT` operator standing as a **query body** — the CTE body, the
231    /// `CREATE VIEW`/`CREATE TABLE AS`/`CREATE MACRO … AS TABLE` body, or any other
232    /// query-body position (`WITH p AS (PIVOT t ON x USING sum(y)) SELECT …`,
233    /// `CREATE VIEW v AS PIVOT t ON x IN (…) USING sum(y)`; both probed on 1.5.4).
234    /// DuckDB admits `PIVOT`/`UNPIVOT` at query-body position but *not*
235    /// `DESCRIBE`/`SHOW` (a CTE body with those is `Parser Error: A CTE needs a
236    /// SELECT`), so this is a query-body variant — reusing the shared [`Pivot`] core
237    /// (tagged [`PivotSpelling::Statement`](super::PivotSpelling)) exactly as
238    /// [`Statement::Pivot`](super::Statement) and [`TableFactor::Pivot`] do — rather
239    /// than a general "statement in query position" carrier (which the
240    /// canonical-shape rule bans as a vibe-union, and which the divergent composition
241    /// of `DESCRIBE`/`SHOW` would misrepresent). `Box`ed to keep this hot enum within
242    /// its size budget. Gated by
243    /// [`TableFactorSyntax::pivot`](crate::dialect::TableExpressionSyntax).
244    Pivot {
245        /// The pivot operation; see [`Pivot`].
246        pivot: Box<Pivot<X>>,
247        /// Source location and node identity.
248        meta: Meta,
249    },
250    /// DuckDB's `UNPIVOT` operator standing as a query body — the
251    /// [`Pivot`](Self::Pivot) counterpart, sharing the [`Unpivot`] core with
252    /// [`Statement::Unpivot`](super::Statement) and [`TableFactor::Unpivot`]. Gated by
253    /// [`TableFactorSyntax::unpivot`](crate::dialect::TableExpressionSyntax).
254    Unpivot {
255        /// The unpivot operation; see [`Unpivot`].
256        unpivot: Box<Unpivot<X>>,
257        /// Source location and node identity.
258        meta: Meta,
259    },
260}
261
262#[derive(Clone, Debug, PartialEq, Eq, Hash)]
263#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
264#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
265/// The SQL set operator forms represented by the AST.
266pub enum SetOperator {
267    /// `UNION` — all rows from both inputs (deduplicated unless `ALL`).
268    Union,
269    /// `INTERSECT` — rows present in both inputs.
270    Intersect,
271    /// `EXCEPT` — rows in the left input but not the right.
272    Except,
273}
274
275#[derive(Clone, Debug, PartialEq, Eq, Hash)]
276#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
277#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
278/// An SQL with.
279pub struct With<X: Extension = NoExt> {
280    /// Whether the recursive form was present in the source.
281    pub recursive: bool,
282    /// ctes in source order.
283    pub ctes: ThinVec<Cte<X>>,
284    /// Source location and node identity.
285    pub meta: Meta,
286}
287
288#[derive(Clone, Debug, PartialEq, Eq, Hash)]
289#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
290#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
291/// An SQL cte.
292pub struct Cte<X: Extension = NoExt> {
293    /// Name referenced by this syntax.
294    pub name: Ident,
295    /// Columns in source order.
296    pub columns: ThinVec<Ident>,
297    /// DuckDB's `USING KEY (col, ...)` recursive-CTE key clause, written between the CTE
298    /// column list and `AS` (`WITH RECURSIVE t(a, b) USING KEY (a) AS (…)`). `None` is the
299    /// ordinary CTE; `Some` carries the key columns (always at least one — the parser
300    /// requires a non-empty parenthesized list). Bare CTE-column names ([`Ident`]), never
301    /// expressions, so a reserved word here is a parse error. Gated by
302    /// [`JoinSyntax::recursive_using_key`](crate::dialect::JoinSyntax); a plain
303    /// `Option<ThinVec>` (one pointer, not boxed) keeps the hot node's ADR-0007 budget
304    /// while carrying the rare clause inline.
305    pub using_key: Option<ThinVec<Ident>>,
306    /// Whether the materialized form was present in the source.
307    pub materialized: Option<bool>,
308    /// Statement or query body governed by this node.
309    pub body: CteBody<X>,
310    /// The SQL:2023 `SEARCH { DEPTH | BREADTH } FIRST BY … SET …` recursive-ordering
311    /// clause, written after the body's `)` ([`CteSearchClause`]). Boxed and optional
312    /// because it rides only the rare recursive CTE, so the hot node pays one pointer
313    /// (ADR-0007) rather than the inline clause; gated by
314    /// [`JoinSyntax::recursive_search_cycle`](crate::dialect::TableExpressionSyntax).
315    pub search: Option<Box<CteSearchClause>>,
316    /// The SQL:2023 `CYCLE … SET … [TO … DEFAULT …] USING …` cycle-detection clause
317    /// ([`CteCycleClause`]), written after any [`search`](Self::search) clause — the
318    /// fixed grammar order (`CYCLE … SEARCH …` is a parse error; probed on pg_query 17).
319    /// Boxed and optional for the same reason, under the same gate.
320    pub cycle: Option<Box<CteCycleClause<X>>>,
321    /// Source location and node identity.
322    pub meta: Meta,
323}
324
325/// The SQL:2023 recursive-query result-ordering clause on a [`Cte`]:
326/// `SEARCH { DEPTH | BREADTH } FIRST BY col [, ...] SET seqcol`.
327///
328/// PostgreSQL attaches it after the CTE body's closing `)` (gram.y `opt_search_clause`).
329/// One of the two orders is mandatory — `SEARCH FIRST …` with neither `DEPTH` nor
330/// `BREADTH` is a parse error — and the `SET` sequence column is required. Non-generic:
331/// its column lists are bare names ([`Ident`]), never expressions. Gated by
332/// [`JoinSyntax::recursive_search_cycle`](crate::dialect::TableExpressionSyntax);
333/// admitted at parse even on a non-`RECURSIVE` `WITH` (the recursion requirement is an
334/// analysis check past this crate's parse boundary; probed on pg_query 17).
335#[derive(Clone, Debug, PartialEq, Eq, Hash)]
336#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
337#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
338pub struct CteSearchClause {
339    /// `BREADTH FIRST` (`true`) vs `DEPTH FIRST` (`false`); mirrors PostgreSQL's
340    /// `search_breadth_first` flag.
341    pub breadth_first: bool,
342    /// The `BY col [, ...]` ordering columns — bare CTE column names (`columnList`), so
343    /// a reserved word here (`SEARCH … BY select …`) is a parse error.
344    pub columns: ThinVec<Ident>,
345    /// The `SET seqcol` sequence column the ordering is materialized into.
346    pub set_column: Ident,
347    /// Source location and node identity.
348    pub meta: Meta,
349}
350
351/// The SQL:2023 recursive-query cycle-detection clause on a [`Cte`]:
352/// `CYCLE col [, ...] SET mark [TO value DEFAULT default] USING path`.
353///
354/// PostgreSQL attaches it after any [`CteSearchClause`] (gram.y `opt_cycle_clause`). The
355/// `SET` mark column and the `USING` path column are both required; the `TO … DEFAULT …`
356/// mark values are optional (the short form defaults the mark to boolean `TRUE`/`FALSE`).
357/// Gated like [`CteSearchClause`] by
358/// [`JoinSyntax::recursive_search_cycle`](crate::dialect::TableExpressionSyntax).
359#[derive(Clone, Debug, PartialEq, Eq, Hash)]
360#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
361#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
362pub struct CteCycleClause<X: Extension = NoExt> {
363    /// The `CYCLE col [, ...]` columns compared to detect a repeated row (`columnList`,
364    /// bare names).
365    pub columns: ThinVec<Ident>,
366    /// The `SET mark` cycle-mark column.
367    pub mark_column: Ident,
368    /// The optional `TO value DEFAULT default` mark values ([`CteCycleMark`]); `None` is
369    /// the short `SET mark USING path` form. Modelled as one node, not two independent
370    /// options, because PostgreSQL admits the pair only together — `TO` without `DEFAULT`
371    /// (or the reverse) is a parse error (probed on pg_query 17).
372    pub mark: Option<CteCycleMark<X>>,
373    /// The `USING path` array column recording the traversal path.
374    pub path_column: Ident,
375    /// Source location and node identity.
376    pub meta: Meta,
377}
378
379/// The `TO value DEFAULT default` cycle-mark values of a [`CteCycleClause`].
380///
381/// Both values are PostgreSQL `AexprConst` constants — a literal or a typed-string
382/// constant (`point '(1,1)'`), never a general expression: `TO (1+2)`, a column
383/// reference, `CAST(…)`, a parameter, or a signed number all parse-reject (probed on
384/// pg_query 17). The parser constrains each to that grammar, so a value here is always an
385/// [`Expr::Literal`](super::Expr) or a prefix-typed
386/// [`Expr::Cast`](super::Expr) ([`CastSyntax::PrefixTyped`](super::CastSyntax)); the
387/// nodes are boxed because [`Expr`] is a hot, larger node while the mark is
388/// rare.
389#[derive(Clone, Debug, PartialEq, Eq, Hash)]
390#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
391#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
392pub struct CteCycleMark<X: Extension = NoExt> {
393    /// Value supplied by this syntax.
394    pub value: Box<Expr<X>>,
395    /// The value marking rows not on a cycle (the `DEFAULT` operand).
396    pub default: Box<Expr<X>>,
397    /// Source location and node identity.
398    pub meta: Meta,
399}
400
401/// The parenthesized body of a [`Cte`]: a query, or — PostgreSQL's data-modifying
402/// CTE — a DML statement whose `RETURNING` rows the outer query reads
403/// (`WITH t AS (DELETE FROM x RETURNING *) SELECT * FROM t`).
404///
405/// A closed enum of exactly PostgreSQL's `PreparableStmt` set (`gram.y`
406/// `common_table_expr`: `SELECT`/`INSERT`/`UPDATE`/`DELETE`/`MERGE`, the `MERGE`
407/// arm PG 17+) — never a general statement carrier, so a utility statement in
408/// query position stays unrepresentable (`WITH t AS (VACUUM)` is a PostgreSQL
409/// parse error; probed on pg_query 17). `RETURNING` is *not* required at the
410/// parse layer — PostgreSQL parses a DML body without one and only its *use*
411/// fails at analysis — so the DML arms carry their nodes unrestricted. Each DML
412/// arm reuses its statement family's node, which carries its own leading `WITH`
413/// (`WITH t AS (WITH u AS (…) INSERT …)` nests exactly as PostgreSQL parses it),
414/// boxed to keep `Cte` lean. The DML arms are gated by
415/// [`MutationSyntax::data_modifying_ctes`](crate::dialect::MutationSyntax):
416/// DuckDB parse-rejects them (`A CTE needs a SELECT`, probed on 1.5.4), as do
417/// SQLite and MySQL (`ER_PARSE_ERROR` 1064, probed on mysql:8).
418#[derive(Clone, Debug, PartialEq, Eq, Hash)]
419#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
420#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
421pub enum CteBody<X: Extension = NoExt> {
422    /// A `SELECT`/query CTE body (the common case).
423    Query {
424        /// Query governed by this node.
425        query: Box<Query<X>>,
426        /// Source location and node identity.
427        meta: Meta,
428    },
429    /// A data-modifying `INSERT` CTE body.
430    Insert {
431        /// The `INSERT` statement; see [`Insert`].
432        insert: Box<Insert<X>>,
433        /// Source location and node identity.
434        meta: Meta,
435    },
436    /// A data-modifying `UPDATE` CTE body.
437    Update {
438        /// The `UPDATE` statement; see [`Update`].
439        update: Box<Update<X>>,
440        /// Source location and node identity.
441        meta: Meta,
442    },
443    /// A data-modifying `DELETE` CTE body.
444    Delete {
445        /// The `DELETE` statement; see [`Delete`].
446        delete: Box<Delete<X>>,
447        /// Source location and node identity.
448        meta: Meta,
449    },
450    /// A data-modifying `MERGE` CTE body.
451    Merge {
452        /// The `MERGE` statement; see [`Merge`].
453        merge: Box<Merge<X>>,
454        /// Source location and node identity.
455        meta: Meta,
456    },
457}
458
459impl<X: Extension> CteBody<X> {
460    /// The body's query when it is one — the overwhelmingly common case — else
461    /// `None` for the data-modifying arms (the [`Statement::as_query`](super::Statement)
462    /// counterpart for CTE bodies).
463    pub fn as_query(&self) -> Option<&Query<X>> {
464        match self {
465            Self::Query { query, .. } => Some(query),
466            Self::Insert { .. }
467            | Self::Update { .. }
468            | Self::Delete { .. }
469            | Self::Merge { .. } => None,
470        }
471    }
472}
473
474#[derive(Clone, Debug, PartialEq, Eq, Hash)]
475#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
476#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
477/// An SQL values.
478pub struct Values<X: Extension = NoExt> {
479    /// Whether each row is written with the explicit `ROW( ... )` row constructor
480    /// (MySQL's query-position spelling — `VALUES ROW(1, 2), ROW(3, 4)`) rather than a
481    /// bare `( ... )` row (`VALUES (1, 2), (3, 4)`, the PostgreSQL/DuckDB/SQLite/ANSI
482    /// spelling). A single flag, not a per-row bit, because the spelling is uniform across
483    /// the constructor: MySQL requires `ROW` on every row and syntax-rejects the bare form
484    /// ([`SelectSyntax::values_row_constructor`](crate::dialect::SelectSyntax) off), while
485    /// the others require the bare form (that gate on). Preserving it keeps the `ROW`
486    /// keyword round-tripping — the same [`explicit`](super::UpdateTupleSource::Row) axis
487    /// the `UPDATE ... SET (a, b) = ROW(...)` tuple source and the [`Expr::Row`] value
488    /// constructor already carry.
489    pub explicit_row: bool,
490    /// rows in source order.
491    pub rows: ThinVec<ThinVec<ValuesItem<X>>>,
492    /// Source location and node identity.
493    pub meta: Meta,
494}
495
496/// One item in a `VALUES` query row: an expression or a bare `DEFAULT`.
497///
498/// PostgreSQL admits a bare `DEFAULT` as a `VALUES` row element (it parses to
499/// `SetToDefault`, distinct from a column reference), so a row item is this enum
500/// rather than a plain [`Expr`] — keeping `DEFAULT` out of the general expression
501/// grammar exactly as the INSERT values path does
502/// ([`InsertValue`](super::InsertValue)), and reusing its
503/// [`DefaultValue`] leaf.
504#[derive(Clone, Debug, PartialEq, Eq, Hash)]
505#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
506#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
507pub enum ValuesItem<X: Extension = NoExt> {
508    /// An ordinary value expression.
509    Expr {
510        /// Expression evaluated by this syntax.
511        expr: Expr<X>,
512        /// Source location and node identity.
513        meta: Meta,
514    },
515    /// A bare `DEFAULT` placeholder (PostgreSQL).
516    Default {
517        /// Explicit `DEFAULT` value.
518        default: DefaultValue,
519        /// Source location and node identity.
520        meta: Meta,
521    },
522}
523
524#[derive(Clone, Debug, PartialEq, Eq, Hash)]
525#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
526#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
527/// An SQL select.
528pub struct Select<X: Extension = NoExt> {
529    /// The `ALL` / `DISTINCT` / `DISTINCT ON (...)` set quantifier, or `None` when
530    /// the SELECT writes no quantifier (the implicit `ALL`).
531    pub distinct: Option<SelectDistinct<X>>,
532    /// MySQL's `SELECT STRAIGHT_JOIN ...` modifier — the query-wide form of the
533    /// [`JoinOperator::Inner`] `straight` join-order hint, written after the
534    /// `DISTINCT`/`ALL` quantifier. A flag (not a node) because it is a surface
535    /// modifier that rides `Select` exactly as `distinct` does; only MySQL parses it
536    /// (gated by [`JoinSyntax::straight_join`](crate::dialect::TableExpressionSyntax)).
537    pub straight_join: bool,
538    /// projection in source order.
539    pub projection: ThinVec<SelectItem<X>>,
540    /// PostgreSQL's `SELECT … INTO <table>` create-table target, written between the
541    /// projection and `FROM`; `None` for every standard SELECT. `Box`ed because the
542    /// clause is rare (gated to PostgreSQL via
543    /// [`SelectSyntax::select_into`](crate::dialect::SelectSyntax)) while `Select` is
544    /// a hot node, so the common case pays one pointer, not the inline
545    /// target. This is the *materialize-into-a-new-table* form; the SQL-standard
546    /// `SELECT … INTO <variable>` (PSM host/local-variable assignment) is a different
547    /// construct and is deliberately not modelled here.
548    pub into: Option<Box<IntoTarget>>,
549    /// from in source order.
550    pub from: ThinVec<TableWithJoins<X>>,
551    /// Hive/Spark `LATERAL VIEW [OUTER] explode(col) t AS a, b` generator clauses,
552    /// written after the whole `FROM` clause and before `WHERE`, each cross-joining the
553    /// rows a table-generating function produces; empty for every SELECT that writes
554    /// none. See [`LateralView`]. Gated by
555    /// [`SelectSyntax::lateral_view_clause`](crate::dialect::SelectSyntax) — on for
556    /// Hive/Databricks/Lenient, so the field stays empty elsewhere. A `ThinVec` because
557    /// the clause is repeatable and rare while `Select` is a hot node: the empty vector
558    /// is one pointer (the [`Query::pipe_operators`] precedent), so the common case
559    /// pays one word.
560    pub lateral_views: ThinVec<LateralView<X>>,
561    /// Predicate that filters input rows.
562    pub selection: Option<Expr<X>>,
563    /// The Oracle-style `[START WITH <cond>] CONNECT BY [NOCYCLE] <cond>` hierarchical
564    /// query clause, written after `WHERE` and before `GROUP BY`; `None` for every
565    /// SELECT that writes none. See [`HierarchicalClause`]. Gated by
566    /// [`SelectSyntax::connect_by_clause`](crate::dialect::SelectSyntax) — on for
567    /// Snowflake and the Lenient union, so the field stays `None` elsewhere.
568    /// `Option<Box<…>>` because the clause is rare while `Select` is a hot node: the
569    /// common (absent) case is one null pointer (the [`Select::into`] precedent), and
570    /// boxing the whole `START WITH`/`CONNECT BY` pair keeps both of its inline `Expr`s
571    /// off the `Select` footprint.
572    pub connect_by: Option<Box<HierarchicalClause<X>>>,
573    /// The `GROUP BY` list. Each item is a [`GroupByItem`]: an ordinary grouping
574    /// expression or one of the SQL:1999 grouping-set constructs
575    /// (`ROLLUP`/`CUBE`/`GROUPING SETS`/empty `()`), which PostgreSQL lowers in this
576    /// position and are therefore their own grammar node rather than
577    /// [`FunctionCall`] expressions.
578    pub group_by: ThinVec<GroupByItem<X>>,
579    /// PostgreSQL's `GROUP BY {DISTINCT | ALL} <grouping items>` set-quantifier
580    /// (SQL:2016 feature T434): a quantifier on the *whole* grouping clause that
581    /// governs deduplication of the grouping sets the items generate — `DISTINCT`
582    /// collapses duplicate sets, `ALL` (the default) keeps them. `None` when the
583    /// clause writes no quantifier; `Some(SetQuantifier::All)` /
584    /// `Some(SetQuantifier::Distinct)` record the explicit spelling so rendering
585    /// round-trips it (the [`Select::distinct`] precedent for the projection
586    /// quantifier). The quantifier prefixes the [`group_by`](Self::group_by) list and
587    /// requires it to be non-empty — PostgreSQL rejects a bare `GROUP BY ALL` /
588    /// `GROUP BY DISTINCT` (verified on pg_query PG-17), which is exactly what keeps it
589    /// MECE with [`group_by_all`](Self::group_by_all): this quantifier is *ALL as a
590    /// modifier of a non-empty item list*, whereas DuckDB's `GROUP BY ALL` mode is
591    /// *ALL standing alone as the entire clause* (empty item list). Gated by
592    /// [`GroupingSyntax::group_by_set_quantifier`](crate::dialect::SelectSyntax).
593    pub group_by_quantifier: Option<SetQuantifier>,
594    /// DuckDB's `GROUP BY ALL` mode: group by every non-aggregated projection
595    /// column, resolved at bind time. A flag with an empty
596    /// [`group_by`](Self::group_by) list rather than a [`GroupByItem`] variant
597    /// because `ALL` is a *mode of the whole clause*, never one grouping item —
598    /// DuckDB rejects mixing it with explicit keys or grouping sets (`GROUP BY
599    /// ALL, x` / `GROUP BY ROLLUP(x), ALL` are syntax errors; probed on 1.5.4) —
600    /// and the key list is unknowable at parse time, so a synthetic item would be
601    /// a lie (the resolved shape decision). DuckDB's own tree
602    /// corroborates the mode framing: `GROUP BY ALL` serializes as
603    /// `aggregate_handling: FORCE_AGGREGATES` with empty `group_expressions`. A
604    /// bare flag like [`straight_join`](Self::straight_join); the parser never
605    /// sets it alongside a non-empty `group_by`. `None` when the clause writes no
606    /// `ALL` mode; `Some(_)` records which surface spelling opened it — DuckDB writes
607    /// the mode two interchangeable ways, the keyword `GROUP BY ALL` and the
608    /// shorthand `GROUP BY *` (both bind to "group by every non-aggregated projection
609    /// column"; `*` is bare-only, DuckDB rejects `GROUP BY *, x` — probed on 1.5.4),
610    /// so the spelling is data the renderer round-trips (the [`GroupByAllSpelling`]
611    /// tag) rather than a normalized flag. Gated by
612    /// [`GroupingSyntax::group_by_all`](crate::dialect::SelectSyntax).
613    pub group_by_all: Option<GroupByAllSpelling>,
614    /// Predicate applied after grouping.
615    pub having: Option<Expr<X>>,
616    /// windows in source order.
617    pub windows: ThinVec<NamedWindow<X>>,
618    /// DuckDB's `QUALIFY <predicate>` clause: a filter over window-function results,
619    /// applied after grouping. A distinct slot rather than a spelling of `HAVING`
620    /// (which filters groups) because the two have different semantics; QUALIFY is a
621    /// common cross-dialect extension (Teradata-origin; Snowflake/BigQuery/DuckDB), so
622    /// this is the common shape anchored where the standard is silent. Sits
623    /// after [`windows`](Self::windows) because DuckDB's grammar places the clause
624    /// after the `WINDOW` clause (`… HAVING … WINDOW … QUALIFY …`; `QUALIFY … WINDOW …`
625    /// is a DuckDB syntax error — verified against DuckDB 1.5.4). `Box`ed because the
626    /// clause is rare (gated to DuckDB via
627    /// [`SelectSyntax::qualify`](crate::dialect::SelectSyntax)) while `Select` is a
628    /// hot node, so the common case pays one pointer (the
629    /// [`into`](Self::into) precedent). Whether the predicate references a window
630    /// function is a bind-time check (DuckDB: "at least one window function must
631    /// appear…"), past the parse-level contract — the parser accepts any expression.
632    pub qualify: Option<Box<Expr<X>>>,
633    /// DuckDB's `USING SAMPLE <entry>` query-level sample clause, written after
634    /// [`qualify`](Self::qualify) and before the enclosing query's `ORDER BY`
635    /// (`… QUALIFY … USING SAMPLE 3 ORDER BY …`; the reverse order is a DuckDB syntax
636    /// error, verified against 1.5.4). `Box`ed because the clause is rare (gated to
637    /// DuckDB via [`QueryTailSyntax::using_sample`](crate::dialect::SelectSyntax)) while
638    /// `Select` is a hot node, so the common case pays one pointer (the
639    /// [`qualify`](Self::qualify)/[`into`](Self::into) precedent). `None` when unwritten.
640    pub sample: Option<Box<SampleClause>>,
641    /// The surface syntax that produced this SELECT body — an ordinary `SELECT …`
642    /// or the `TABLE <name>` short form. Kept as data so the renderer round-trips the
643    /// written spelling (the [`LimitSyntax`] precedent).
644    pub spelling: SelectSpelling,
645    /// Source location and node identity.
646    pub meta: Meta,
647}
648
649/// Surface syntax that produced a [`Select`] body.
650///
651/// One SELECT semantic, three spellings kept as data (mirroring
652/// [`LimitSyntax`]/[`RollupSpelling`]): the ordinary `SELECT <projection> …`, the
653/// standard `TABLE <name>` short form (`<explicit table>`), and DuckDB's FROM-first
654/// order (`FROM <tables> [SELECT …]`). All lower to the same star- or explicit-projection
655/// `Select` — PostgreSQL lowers `TABLE <name>` to `SELECT * FROM <name>`, and DuckDB's own
656/// tree serializes `FROM t SELECT x` identically to `SELECT x FROM t` (and bare `FROM t`
657/// identically to `SELECT * FROM t`; probed on 1.5.4). Canonicalizing each surface into
658/// [`Select`] keeps the differential oracle comparing one shape and the set-operation
659/// grammar composing (`TABLE a UNION TABLE b`, `FROM a SELECT x UNION FROM b SELECT y`);
660/// the tag exists only so rendering reproduces the written order rather than normalizing
661/// every form to `SELECT <projection> FROM …`.
662#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
663#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
664#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
665pub enum SelectSpelling {
666    /// An ordinary `SELECT <projection> [FROM …] …` body; the construction default.
667    Select,
668    /// The `TABLE <name>` short form for `SELECT * FROM <name>` (SQL `<explicit
669    /// table>`). The projection is a single wildcard and the `FROM` is the one named
670    /// relation (with its optional PostgreSQL `ONLY`/`*` inheritance marker); the
671    /// renderer re-emits `TABLE <name>`.
672    TableCommand,
673    /// DuckDB's FROM-first order: `FROM <tables> [SELECT [DISTINCT] <projection>] …`,
674    /// where the `FROM` clause leads and the projection follows it — or is omitted, the
675    /// bare `FROM <tables>` form whose implicit projection is a single wildcard. The
676    /// stored [`Select`] is the ordinary shape (`from`, `projection`, and the tail
677    /// clauses fill exactly as for [`Select`](Self::Select)); this tag only records that
678    /// the source wrote the `FROM` first, so the renderer re-emits that order. The bare
679    /// form round-trips to `FROM <tables>` (the `SELECT *` stays implicit) and an explicit
680    /// `FROM <tables> SELECT *` normalizes onto it — one canonical render for the
681    /// wildcard projection, not a second tag state (the corpus writes the bare form far
682    /// more often). Gated by
683    /// [`SelectSyntax::from_first`](crate::dialect::SelectSyntax); reachable only where a
684    /// query primary may begin, so it composes in every query position.
685    FromFirst,
686}
687
688/// Surface spelling of DuckDB's `GROUP BY ALL` mode ([`Select::group_by_all`]).
689///
690/// One mode, two interchangeable spellings kept as data (the [`SelectSpelling`] /
691/// [`RollupSpelling`] precedent): the keyword `GROUP BY ALL` and the shorthand
692/// `GROUP BY *`. Both name the same bind-time instruction — group by every
693/// non-aggregated projection column — so they collapse to one semantic in the
694/// differential oracle; the tag exists only so a source-fidelity render reproduces
695/// the written form rather than normalizing `*` onto `ALL`. A `TargetDialect`
696/// re-spell and the redacted fingerprint canonicalize to `ALL` (the shared
697/// spelling-tag doctrine, keyed on `honours_source_spelling`). Fieldless because
698/// the mode carries no expression — the key list is unknowable at parse time
699/// (the [`Select::group_by_all`] rationale).
700#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
701#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
702#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
703pub enum GroupByAllSpelling {
704    /// The keyword form `GROUP BY ALL`; the construction default.
705    Keyword,
706    /// DuckDB's shorthand `GROUP BY *` — a bare wildcard standing for the whole
707    /// clause (never a grouping key), which the renderer re-emits as `*`.
708    Star,
709}
710
711/// The destination table of a PostgreSQL `SELECT … INTO <table>` query: the new
712/// relation the result rows are materialized into.
713///
714/// This models PostgreSQL's create-table form of `SELECT INTO`, equivalent to
715/// `CREATE TABLE <name> AS <query>`. It reuses [`TemporaryTableKind`] for the
716/// `TEMP`/`TEMPORARY` spelling so the surface round-trips exactly and the temporary
717/// axis stays one canonical shape with `CREATE TABLE`; `None` is a plain
718/// (non-temporary) target. The clause carries no extension data, so it is not
719/// generic over `X`.
720#[derive(Clone, Debug, PartialEq, Eq, Hash)]
721#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
722#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
723pub struct IntoTarget {
724    /// `SELECT … INTO TEMP`/`TEMPORARY <table>` marker; `None` for a permanent table.
725    pub temporary: Option<TemporaryTableKind>,
726    /// Name referenced by this syntax.
727    pub name: ObjectName,
728    /// Source location and node identity.
729    pub meta: Meta,
730}
731
732/// One item in a `GROUP BY` list: an ordinary grouping expression or one of the
733/// SQL:1999 grouping-set constructs.
734///
735/// PostgreSQL's grammar (`group_by_item`) lowers `ROLLUP (…)`, `CUBE (…)`,
736/// `GROUPING SETS (…)`, and the empty grouping set `()` to grouping-set nodes in
737/// GROUP BY item position in *any* case spelling — a user function named
738/// `rollup`/`cube` cannot be called there without quoting it (`"rollup"(…)` stays a
739/// [`FunctionCall`]) — so these are their own grammar position, never
740/// ordinary expressions. Acceptance is gated by
741/// [`GroupingSyntax::grouping_sets`](crate::dialect::SelectSyntax); when off the
742/// keywords fall through to the expression grammar as ordinary function calls, which
743/// is how MySQL — whose only grouping surface is the different `WITH ROLLUP` — reads
744/// them.
745///
746/// `ROLLUP`/`CUBE` take a plain expression list, but `GROUPING SETS` nests PG's
747/// `group_by_list`, so its members are this same node — admitting
748/// `GROUPING SETS (ROLLUP (a, b), (c), ())`.
749#[derive(Clone, Debug, PartialEq, Eq, Hash)]
750#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
751#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
752pub enum GroupByItem<X: Extension = NoExt> {
753    /// An ordinary grouping expression — a column, a parenthesized `(a, b)` row, or
754    /// any other expression (PG's `a_expr` group-by item).
755    Expr {
756        /// Expression evaluated by this syntax.
757        expr: Expr<X>,
758        /// Source location and node identity.
759        meta: Meta,
760    },
761    /// A `ROLLUP` grouping: hierarchical super-aggregate subtotals over the prefixes
762    /// of the listed expressions. `spelling` records the surface that produced it —
763    /// the SQL:1999 item form `ROLLUP (a, b)` or MySQL's trailing `a, b WITH ROLLUP` —
764    /// so rendering round-trips the source; the two are one semantic, spelling kept as
765    /// data (the [`LimitSyntax`] precedent).
766    Rollup {
767        /// exprs in source order.
768        exprs: ThinVec<Expr<X>>,
769        /// Exact source spelling retained for faithful rendering.
770        spelling: RollupSpelling,
771        /// Source location and node identity.
772        meta: Meta,
773    },
774    /// `CUBE (a, b, …)`: super-aggregate subtotals over every subset of the listed
775    /// expressions.
776    Cube {
777        /// exprs in source order.
778        exprs: ThinVec<Expr<X>>,
779        /// Source location and node identity.
780        meta: Meta,
781    },
782    /// `GROUPING SETS (<item>, …)`: an explicit list of grouping sets, each itself a
783    /// grouping item — so `ROLLUP`/`CUBE`/nested `GROUPING SETS`/`()` may appear
784    /// inside.
785    GroupingSets {
786        /// sets in source order.
787        sets: ThinVec<GroupByItem<X>>,
788        /// Source location and node identity.
789        meta: Meta,
790    },
791    /// The empty grouping set `()` — the grand total. Admitted both as a bare
792    /// GROUP BY item and inside `GROUPING SETS` (PG's `empty_grouping_set`).
793    Empty {
794        /// Source location and node identity.
795        meta: Meta,
796    },
797}
798
799/// Surface syntax that produced a [`GroupByItem::Rollup`].
800///
801/// One `ROLLUP` semantic, two spellings kept as data (mirroring
802/// [`LimitSyntax`]): the SQL:1999 item form `ROLLUP (a, b)` and MySQL's trailing
803/// modifier `GROUP BY a, b WITH ROLLUP`. Canonicalizing MySQL's surface into this
804/// same node lets the differential oracle see one shape; the tag exists only so
805/// rendering reproduces the written form.
806#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
807#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
808#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
809pub enum RollupSpelling {
810    /// The SQL:1999 item form `ROLLUP (a, b)`; the construction default.
811    Function,
812    /// MySQL's trailing modifier, written after the key list: `a, b WITH ROLLUP`.
813    WithRollup,
814}
815
816/// Surface spelling for an alias introducer on a [`SelectItem::Expr`] projection
817/// alias or a [`TableAlias`] correlation name.
818///
819/// One canonical alias, kept as data so the source form round-trips: SQL makes the
820/// `AS` keyword optional before an alias (`SELECT a b` == `SELECT a AS b`,
821/// `FROM t u` == `FROM t AS u`), and DuckDB additionally admits a prefix form
822/// (`SELECT alias: expr`). The AST keeps one alias field and this tag records which
823/// introducer the source wrote, mirroring [`EqualsSpelling`](crate::ast::EqualsSpelling)
824/// / [`QuoteStyle`](crate::ast::QuoteStyle): a fieldless `Copy` tag, not a semantic
825/// distinction. All three forms name the same alias.
826///
827/// A synthesized alias (no source, or a rewrite) takes [`As`](Self::As), the
828/// canonical introducer a `TargetDialect` render always emits; a `PreserveSource`
829/// render honours the tag so a bare alias stays bare. [`PrefixColon`](Self::PrefixColon)
830/// is reachable only on a [`SelectItem::Expr`] alias — the table-factor prefix form
831/// (`FROM b : a`) folds onto its trailing alias slot and canonicalizes to `As`, since
832/// its correlation name renders after the relation, not before it.
833#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
834#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
835#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
836pub enum AliasSpelling {
837    /// The alias written with no introducer: `SELECT a b`, `FROM t u`.
838    Bare,
839    /// The explicit `AS` introducer: `SELECT a AS b`, `FROM t AS u`. Also the
840    /// synthetic/canonical default.
841    As,
842    /// DuckDB's prefix form `SELECT alias: expr` — the alias written before the
843    /// value. Reachable only on a [`SelectItem::Expr`] alias.
844    PrefixColon,
845}
846
847#[derive(Clone, Debug, PartialEq, Eq, Hash)]
848#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
849#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
850/// The SQL select item forms represented by the AST.
851pub enum SelectItem<X: Extension = NoExt> {
852    /// An unqualified `*` projection (with optional DuckDB `EXCLUDE`/`REPLACE`/`RENAME` modifiers).
853    Wildcard {
854        /// DuckDB's `EXCLUDE`/`REPLACE`/`RENAME` wildcard modifiers
855        /// ([`SelectSyntax::wildcard_modifiers`](crate::dialect::SelectSyntax)), which
856        /// change *which* columns the `*` expands to. `None` for a plain `*` — the
857        /// overwhelming common case, so the modifiers are boxed off the hot projection
858        /// item rather than widening every wildcard.
859        options: Option<Box<WildcardOptions<X>>>,
860        /// DuckDB's alias on a star projection: `SELECT * AS idx` names *every*
861        /// star-expanded column `idx` (a rename-all, not a struct pack; engine-probed on
862        /// 1.5.4). It rides the same
863        /// [`SelectSyntax::wildcard_modifiers`](crate::dialect::SelectSyntax) gate as the
864        /// modifiers and is written *after* them (`* EXCLUDE (a) AS idx`); `None` for the
865        /// common unaliased `*`.
866        alias: Option<Ident>,
867        /// How the source introduced `alias`. Meaningful only when `alias` is `Some`;
868        /// [`AliasSpelling::As`] (the canonical default) when there is no alias. Only
869        /// [`Bare`](AliasSpelling::Bare) / [`As`](AliasSpelling::As) are reachable here —
870        /// the [`PrefixColon`](AliasSpelling::PrefixColon) form has no star spelling.
871        alias_spelling: AliasSpelling,
872        /// Source location and node identity.
873        meta: Meta,
874    },
875    /// A qualified `t.*` / `s.t.*` projection.
876    QualifiedWildcard {
877        /// The relation the star expands (`t` in `t.*`).
878        name: ObjectName,
879        /// DuckDB's wildcard modifiers on a qualified `t.*`; see
880        /// [`Wildcard`](Self::Wildcard). `None` for a plain `t.*`.
881        options: Option<Box<WildcardOptions<X>>>,
882        /// DuckDB's alias on a qualified star: `SELECT t.* AS x`; see
883        /// [`Wildcard`](Self::Wildcard). `None` for a plain `t.*`.
884        alias: Option<Ident>,
885        /// How the source introduced `alias`; see [`Wildcard`](Self::Wildcard).
886        alias_spelling: AliasSpelling,
887        /// Source location and node identity.
888        meta: Meta,
889    },
890    /// A projected value expression with an optional alias.
891    Expr {
892        /// Expression evaluated by this syntax.
893        expr: Expr<X>,
894        /// Alias assigned by this syntax.
895        alias: Option<Ident>,
896        /// How the source introduced `alias`. Meaningful only when `alias` is
897        /// `Some`; [`AliasSpelling::As`] (the canonical default) when there is no
898        /// alias, and never rendered in that case.
899        alias_spelling: AliasSpelling,
900        /// Source location and node identity.
901        meta: Meta,
902    },
903}
904
905/// DuckDB's wildcard modifiers, the `EXCLUDE`/`REPLACE`/`RENAME` tail that follows a
906/// `*` / `t.*` (and the `COLUMNS(*)` star, [`Expr::Columns`]).
907///
908/// A new canonical shape, not a spelling tag: the modifiers change which
909/// columns the wildcard expands to — genuine semantics with no equivalent standard
910/// spelling to fold onto. DuckDB's own tree confirms the shape, carrying
911/// `exclude_list`/`replace_list`/`rename_list` on its `STAR` node (probed on 1.5.4).
912/// DuckDB fixes the surface order `EXCLUDE`, then `REPLACE`, then `RENAME`, each at
913/// most once (a different order is a syntax error), so the three lists are stored
914/// separately and rendered back in that canonical order; only present lists are
915/// written. Reachable only under
916/// [`SelectSyntax::wildcard_modifiers`](crate::dialect::SelectSyntax).
917#[derive(Clone, Debug, PartialEq, Eq, Hash)]
918#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
919#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
920pub struct WildcardOptions<X: Extension = NoExt> {
921    /// `EXCLUDE (a, t.b)`: columns dropped from the expansion. Each entry is a column
922    /// reference that may be qualified (DuckDB accepts `EXCLUDE (t.a)`), so an
923    /// [`ObjectName`] rather than a bare [`Ident`] — this unifies DuckDB's split
924    /// `exclude_list` (unqualified) / `qualified_exclude_list` (qualified).
925    pub exclude: ThinVec<ObjectName>,
926    /// `REPLACE (expr AS col, …)`: columns whose value is swapped for an expression
927    /// while keeping their position.
928    pub replace: ThinVec<WildcardReplace<X>>,
929    /// `RENAME (col AS new, …)`: columns renamed in the expansion.
930    pub rename: ThinVec<WildcardRename>,
931    /// Source location and node identity.
932    pub meta: Meta,
933}
934
935/// One `REPLACE (expr AS col)` entry of a [`WildcardOptions`]: the replacement
936/// `expr` and the output column `column` it stands in for. DuckDB's replaced column
937/// name is always unqualified (an output label), hence an [`Ident`].
938#[derive(Clone, Debug, PartialEq, Eq, Hash)]
939#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
940#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
941pub struct WildcardReplace<X: Extension = NoExt> {
942    /// Expression evaluated by this syntax.
943    pub expr: Expr<X>,
944    /// Column referenced by this syntax.
945    pub column: Ident,
946    /// Source location and node identity.
947    pub meta: Meta,
948}
949
950/// One `RENAME (col AS new)` entry of a [`WildcardOptions`]: the source `column`
951/// (which DuckDB permits to be qualified, e.g. `t.a`) renamed to the unqualified
952/// output name `alias`.
953#[derive(Clone, Debug, PartialEq, Eq, Hash)]
954#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
955#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
956pub struct WildcardRename {
957    /// Column referenced by this syntax.
958    pub column: ObjectName,
959    /// Alias assigned by this syntax.
960    pub alias: Ident,
961    /// Source location and node identity.
962    pub meta: Meta,
963}
964
965/// The standard SQL set quantifier `ALL` / `DISTINCT`.
966///
967/// `ALL` is the explicit spelling of the default (no deduplication); it is kept
968/// distinct from "no quantifier" so the surface round-trips, mirroring how
969/// [`OrderByExpr::asc`] preserves an explicit `ASC`. This is the quantifier an
970/// aggregate call carries ([`FunctionCall::quantifier`](super::FunctionCall)); a
971/// SELECT list reuses it inside [`SelectDistinct`], which also admits PostgreSQL's
972/// `DISTINCT ON`.
973#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
974#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
975#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
976pub enum SetQuantifier {
977    /// Explicit `ALL` — the default, no deduplication.
978    All,
979    /// `DISTINCT` deduplication.
980    Distinct,
981}
982
983/// A SELECT-list set quantifier: the standard [`SetQuantifier`] (`ALL`/`DISTINCT`)
984/// or PostgreSQL's `DISTINCT ON (<expr>, ...)`.
985///
986/// The enclosing [`Select::distinct`] is `None` when the SELECT writes no
987/// quantifier; the `ON` variant carries the deduplication keys, which only PG's
988/// `DISTINCT ON` admits (so it has no [`SetQuantifier`] counterpart).
989#[derive(Clone, Debug, PartialEq, Eq, Hash)]
990#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
991#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
992pub enum SelectDistinct<X: Extension = NoExt> {
993    /// A standard `ALL` or `DISTINCT` quantifier.
994    Quantifier {
995        /// Whether `ALL` or `DISTINCT`; see [`SetQuantifier`].
996        quantifier: SetQuantifier,
997        /// Source location and node identity.
998        meta: Meta,
999    },
1000    /// PostgreSQL `DISTINCT ON (<expr>, ...)`: deduplicate on the listed keys only.
1001    On {
1002        /// exprs in source order.
1003        exprs: ThinVec<Expr<X>>,
1004        /// Source location and node identity.
1005        meta: Meta,
1006    },
1007}
1008
1009#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1010#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1011#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1012/// An SQL table with joins.
1013pub struct TableWithJoins<X: Extension = NoExt> {
1014    /// The leading table factor (the first `FROM` item).
1015    pub relation: TableFactor<X>,
1016    /// joins in source order.
1017    pub joins: ThinVec<Join<X>>,
1018    /// Source location and node identity.
1019    pub meta: Meta,
1020}
1021
1022#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1023#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1024#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1025/// An SQL table alias.
1026pub struct TableAlias {
1027    /// Name referenced by this syntax.
1028    pub name: Ident,
1029    /// Columns in source order.
1030    pub columns: ThinVec<Ident>,
1031    /// How the source introduced this correlation name (`FROM t AS u` vs
1032    /// `FROM t u`). [`AliasSpelling::As`] for a synthesized alias.
1033    pub spelling: AliasSpelling,
1034    /// Source location and node identity.
1035    pub meta: Meta,
1036}
1037
1038/// Source spelling for PostgreSQL inheritance suppression.
1039#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1040#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1041#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1042pub enum OnlySyntax {
1043    /// Source used the `BARE` spelling.
1044    Bare,
1045    /// Source used the `PARENTHESIZED` spelling.
1046    Parenthesized,
1047}
1048
1049/// PostgreSQL `relation_expr` inheritance marker on a table reference: whether a
1050/// query reaches the relation's descendant (inheritance-child) tables, plus the
1051/// source spelling that asked for it.
1052///
1053/// One canonical shape for the four legal `relation_expr` spellings,
1054/// chosen so the impossible `ONLY name *` combination is structurally
1055/// unrepresentable: the `*` and `ONLY` markers are sibling variants that can
1056/// never co-occur. `Plain` and `Descendants` are semantically identical in
1057/// PostgreSQL (both leave `inh = true`, so a bare `t` and an explicit `t *`
1058/// select the same rows); the distinct variant exists only to round-trip the
1059/// source `*` exactly.
1060#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1061#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1062#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1063pub enum RelationInheritance {
1064    /// Bare `name`: descendant tables included implicitly.
1065    Plain,
1066    /// `name *`: descendant tables included via the explicit legacy star marker.
1067    Descendants,
1068    /// `ONLY name` / `ONLY (name)`: descendant tables suppressed.
1069    Only(OnlySyntax),
1070}
1071
1072#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1073#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1074#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1075/// An SQL table sample.
1076pub struct TableSample<X: Extension = NoExt> {
1077    /// The sampling method name (`BERNOULLI`, `SYSTEM`, …).
1078    pub method: ObjectName,
1079    /// Arguments in source order.
1080    pub args: ThinVec<Expr<X>>,
1081    /// Optional repeatable for this syntax.
1082    pub repeatable: Option<Box<Expr<X>>>,
1083    /// Source location and node identity.
1084    pub meta: Meta,
1085}
1086
1087/// A table version / time-travel modifier on a base-table factor, written between the
1088/// table name and its correlation alias (`FROM t FOR SYSTEM_TIME AS OF <ts> AS e`) — the
1089/// [`version`](TableFactor::Table::version) slot of a [`TableFactor::Table`].
1090///
1091/// Available to a planner as a typed value, so a time-travel query is recognized without
1092/// string-inspecting the source. This reshapes sqlparser-rs's two-arm `TableVersion`
1093/// (`ForSystemTimeAsOf(Expr)` / `Function(Expr)`), which collapses MSSQL's five distinct
1094/// `FOR SYSTEM_TIME` temporal forms into one: each spelling is its own variant here, so a
1095/// planner sees the endpoints (`FROM … TO`, `BETWEEN … AND`, `CONTAINED IN`) directly and
1096/// the renderer round-trips the written form. The Snowflake `AT`/`BEFORE` function form
1097/// (sqlparser-rs's `Function` arm) is out of scope — no shipped preset carries it — and is
1098/// a deferral, not modelled here.
1099///
1100/// Gated by
1101/// [`TableExpressionSyntax::table_version`](crate::dialect::TableExpressionSyntax): on for
1102/// BigQuery (its `FOR SYSTEM_TIME AS OF`), MSSQL (the five temporal-table forms),
1103/// Databricks/Delta (`VERSION`/`TIMESTAMP AS OF`), and Lenient; off elsewhere, where the
1104/// clause keyword is left unconsumed so a query-level `FOR` (locking, `FOR XML`) still
1105/// parses.
1106#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1107#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1108#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1109pub enum TableVersion<X: Extension = NoExt> {
1110    /// `FOR SYSTEM_TIME AS OF <expr>` — the point-in-time snapshot shared by BigQuery
1111    /// (its only spelling) and MSSQL temporal tables.
1112    ForSystemTimeAsOf {
1113        /// Point in time selected by this syntax.
1114        point: Box<Expr<X>>,
1115        /// Source location and node identity.
1116        meta: Meta,
1117    },
1118    /// MSSQL `FOR SYSTEM_TIME FROM <start> TO <end>` — the half-open `[start, end)`
1119    /// row-version range (the endpoint `end` is excluded, unlike `BETWEEN … AND`).
1120    ForSystemTimeFromTo {
1121        /// The range start expression.
1122        start: Box<Expr<X>>,
1123        /// The range end expression.
1124        end: Box<Expr<X>>,
1125        /// Source location and node identity.
1126        meta: Meta,
1127    },
1128    /// MSSQL `FOR SYSTEM_TIME BETWEEN <start> AND <end>` — the closed-on-both-ends
1129    /// `[start, end]` row-version range (`end` included, unlike `FROM … TO`).
1130    ForSystemTimeBetween {
1131        /// The range start expression.
1132        start: Box<Expr<X>>,
1133        /// The range end expression.
1134        end: Box<Expr<X>>,
1135        /// Source location and node identity.
1136        meta: Meta,
1137    },
1138    /// MSSQL `FOR SYSTEM_TIME CONTAINED IN (<start>, <end>)` — rows whose validity
1139    /// period is fully contained within `[start, end]`.
1140    ForSystemTimeContainedIn {
1141        /// The range start expression.
1142        start: Box<Expr<X>>,
1143        /// The range end expression.
1144        end: Box<Expr<X>>,
1145        /// Source location and node identity.
1146        meta: Meta,
1147    },
1148    /// MSSQL `FOR SYSTEM_TIME ALL` — every row version, historical and current; the one
1149    /// endpoint-free temporal form.
1150    ForSystemTimeAll {
1151        /// Source location and node identity.
1152        meta: Meta,
1153    },
1154    /// Delta/Databricks `VERSION AS OF <expr>` — a snapshot selected by table version
1155    /// number.
1156    VersionAsOf {
1157        /// Version selected by this syntax.
1158        version: Box<Expr<X>>,
1159        /// Source location and node identity.
1160        meta: Meta,
1161    },
1162    /// Delta/Databricks `TIMESTAMP AS OF <expr>` — a snapshot selected by timestamp.
1163    TimestampAsOf {
1164        /// Point in time selected by this syntax.
1165        point: Box<Expr<X>>,
1166        /// Source location and node identity.
1167        meta: Meta,
1168    },
1169}
1170
1171/// DuckDB's `USING SAMPLE <entry>` query-level sample specification (its
1172/// `tablesample_entry` grammar).
1173///
1174/// DuckDB admits two surface shapes for the entry that are semantically identical, so
1175/// they fold to this one canonical shape (the renderer re-derives a
1176/// method-first spelling): a count-first `<size> [unit] [ '(' method [',' seed] ')' ]`
1177/// (`USING SAMPLE 3`, `USING SAMPLE 50% (bernoulli)`) and a method-first `method '('
1178/// <size> [unit] ')' [REPEATABLE '(' seed ')']` (`USING SAMPLE reservoir(20 PERCENT)
1179/// REPEATABLE (42)`). The size and seed are always numeric literals (DuckDB rejects a
1180/// negative or general-expression size), so the node carries no expression and is not
1181/// generic over `X`.
1182#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1183#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1184#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1185pub struct SampleClause {
1186    /// The sampling method (`reservoir`/`bernoulli`/`system`), or `None` for the bare
1187    /// count form (`USING SAMPLE 3`). Whether the method led or trailed the count in
1188    /// source is not preserved — the two orders are equivalent and render method-first.
1189    pub method: Option<ObjectName>,
1190    /// The sample size literal (`3`, `50`, `3.5`).
1191    pub size: Literal,
1192    /// The size unit: a bare count, an explicit `ROWS`, or a percentage (`PERCENT`
1193    /// keyword or the `%` sign).
1194    pub unit: SampleUnit,
1195    /// The random seed from `REPEATABLE (seed)` or the inline `(method, seed)` form;
1196    /// `None` when unwritten.
1197    pub seed: Option<Literal>,
1198    /// Source location and node identity.
1199    pub meta: Meta,
1200}
1201
1202/// The size unit of a [`SampleClause`].
1203///
1204/// Four spellings kept as data so the surface round-trips: a bare count
1205/// (`3`), the explicit `3 ROWS`, and the two percentage spellings `50 PERCENT` and
1206/// `50%` — distinct surfaces DuckDB accepts for the same percentage.
1207#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1208#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1209#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1210pub enum SampleUnit {
1211    /// A bare row count with no unit keyword, as in `USING SAMPLE 3`.
1212    Count,
1213    /// An explicit `ROWS` count, as in `USING SAMPLE 3 ROWS`.
1214    Rows,
1215    /// A `PERCENT`-keyword percentage, as in `USING SAMPLE 10 PERCENT`.
1216    Percent,
1217    /// A `%`-sign percentage, as in `USING SAMPLE 10%`.
1218    PercentSign,
1219}
1220
1221/// One typed column in a table function's column definition list, e.g. `id int`
1222/// in `json_to_record(...) AS x(id int, name text)`.
1223///
1224/// PostgreSQL's `TableFuncElement` (`ColId Typename`) is the record-returning
1225/// counterpart to a plain alias column list: every entry carries a [`DataType`],
1226/// so a typed definition is never confused with an untyped alias column name (the
1227/// `columns` of a [`TableAlias`]).
1228#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1229#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1230#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1231pub struct TableFunctionColumn<X: Extension = NoExt> {
1232    /// Name referenced by this syntax.
1233    pub name: Ident,
1234    /// Data type named by this syntax.
1235    pub data_type: DataType<X>,
1236    /// Source location and node identity.
1237    pub meta: Meta,
1238}
1239
1240/// One function inside a `ROWS FROM ( ... )` list with its optional per-function
1241/// column definition list (PostgreSQL `rowsfrom_item`: `func opt_col_def_list`).
1242#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1243#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1244#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1245pub struct RowsFromItem<X: Extension = NoExt> {
1246    /// The function call; see [`FunctionCall`].
1247    pub function: FunctionCall<X>,
1248    /// column defs in source order.
1249    pub column_defs: ThinVec<TableFunctionColumn<X>>,
1250    /// Source location and node identity.
1251    pub meta: Meta,
1252}
1253
1254/// One MySQL index hint on a table factor:
1255/// `{USE|FORCE|IGNORE} {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] (<index>, …)`.
1256///
1257/// A MySQL-only optimizer directive constraining which indexes the planner may
1258/// consider for the table. Carries no [`Expr`], so it is not generic over `X`.
1259#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1260#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1261#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1262pub struct IndexHint {
1263    /// Whether the hint is `USE`/`FORCE`/`IGNORE`; see [`IndexHintAction`].
1264    pub action: IndexHintAction,
1265    /// The `INDEX` vs `KEY` spelling — synonyms in MySQL, kept as data so the surface
1266    /// round-trips (the [`RollupSpelling`] precedent).
1267    pub keyword: IndexHintKeyword,
1268    /// The optional `FOR {JOIN|ORDER BY|GROUP BY}` scope restricting the hint to one
1269    /// planning phase; `None` applies it to all phases.
1270    pub scope: Option<IndexHintScope>,
1271    /// The parenthesized index-name list. Empty models the `USE INDEX ()` form (use
1272    /// no index) — the parentheses are always written, so an empty list is distinct
1273    /// from the hint's absence (which is the empty
1274    /// [`TableFactor::Table::index_hints`] list instead).
1275    pub indexes: ThinVec<Ident>,
1276    /// Source location and node identity.
1277    pub meta: Meta,
1278}
1279
1280/// Which indexes a MySQL [`IndexHint`] tells the planner to use, ignore, or force.
1281#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1282#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1283#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1284pub enum IndexHintAction {
1285    /// `USE INDEX`: consider only the listed indexes (or none, for the empty list).
1286    Use,
1287    /// `IGNORE INDEX`: consider every index except the listed ones.
1288    Ignore,
1289    /// `FORCE INDEX`: use one of the listed indexes, preferring it over a table scan.
1290    Force,
1291}
1292
1293/// The `INDEX` / `KEY` keyword spelling of a MySQL [`IndexHint`] — exact synonyms.
1294#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1295#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1296#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1297pub enum IndexHintKeyword {
1298    /// The `INDEX` spelling.
1299    Index,
1300    /// The `KEY` spelling.
1301    Key,
1302}
1303
1304/// The `FOR {JOIN|ORDER BY|GROUP BY}` scope of a MySQL [`IndexHint`]: the planning
1305/// phase the hint is restricted to.
1306#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1307#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1308#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1309pub enum IndexHintScope {
1310    /// `FOR JOIN`: row retrieval and join processing.
1311    Join,
1312    /// `FOR ORDER BY`: only when resolving the `ORDER BY`.
1313    OrderBy,
1314    /// `FOR GROUP BY`: only when resolving the `GROUP BY`.
1315    GroupBy,
1316}
1317
1318/// A SQLite `INDEXED BY <index>` / `NOT INDEXED` index directive on a base-table factor,
1319/// written after the table name and its optional correlation alias
1320/// (`FROM t AS e INDEXED BY ix`) — the [`indexed_by`](TableFactor::Table::indexed_by) slot
1321/// of a [`TableFactor::Table`].
1322///
1323/// SQLite's `indexed-clause` on a `qualified-table-name`: `INDEXED BY <name>` forces the
1324/// named index for the table, while `NOT INDEXED` forbids any index (a full table scan).
1325/// Distinct from MySQL's [`IndexHint`] and modelled on its own slot rather than folded in:
1326/// a different grammar (`INDEXED BY name` vs `USE|FORCE|IGNORE INDEX (…)`), a single
1327/// directive rather than a comma-joined list, and a single-index-or-none choice rather than
1328/// a planning-scope set — the same modelling split sqlparser-rs draws between its `IndexHint`
1329/// list and its `TableFactor::Table` index directives. Carries no [`Expr`], so it is not
1330/// generic over `X`.
1331///
1332/// Gated by
1333/// [`TableExpressionSyntax::indexed_by`](crate::dialect::TableExpressionSyntax): on for
1334/// SQLite, off elsewhere, where the `INDEXED` keyword is left to the identifier grammar.
1335#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1336#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1337#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1338pub enum IndexedBy {
1339    /// `INDEXED BY <index>` — force the named index for the table scan.
1340    Named {
1341        /// Index referenced by this syntax.
1342        index: Ident,
1343        /// Source location and node identity.
1344        meta: Meta,
1345    },
1346    /// `NOT INDEXED` — forbid any index; force a full table scan.
1347    NotIndexed {
1348        /// Source location and node identity.
1349        meta: Meta,
1350    },
1351}
1352
1353/// One MSSQL / T-SQL table hint inside a `WITH (...)` list on a table factor:
1354/// `FROM t WITH (NOLOCK)`, `FROM t WITH (INDEX(ix), FORCESEEK)`.
1355///
1356/// A T-SQL-only locking / optimizer directive, distinct from the MySQL
1357/// [`IndexHint`] tail (a different dialect, a different grammar position — MySQL's
1358/// juxtaposed after the alias, T-SQL's introduced by `WITH (`). Carries no [`Expr`],
1359/// so it is not generic over `X`. The common documented hints are given typed
1360/// variants so a downstream planner can key on a specific hint
1361/// ([`Keyword`](Self::Keyword)) without re-parsing text; an unrecognized single-word
1362/// hint is preserved verbatim in [`Other`](Self::Other) rather than over-rejecting,
1363/// the same conservative-round-trip stance the MSSQL preset takes elsewhere. Every
1364/// variant carries `meta` (each hint is a directly-addressable span). Gated by
1365/// [`TableExpressionSyntax::table_hints`](crate::dialect::TableExpressionSyntax).
1366#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1367#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1368#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1369pub enum TableHint {
1370    /// A single-keyword hint (`NOLOCK`, `HOLDLOCK`, `TABLOCK`, …); see
1371    /// [`TableHintKeyword`] for the modelled set.
1372    Keyword {
1373        /// Which modelled hint keyword; see [`TableHintKeyword`].
1374        keyword: TableHintKeyword,
1375        /// Source location and node identity.
1376        meta: Meta,
1377    },
1378    /// The `INDEX` access-path hint: `INDEX (<index>, …)`, `INDEX = <index>`, or
1379    /// `INDEX = (<index>, …)`. `equals` records whether the `=` spelling was used so
1380    /// both round-trip; the parenthesized and the bare `INDEX = <index>` forms both
1381    /// fill `indexes`. Numeric index ids (`INDEX(0)`) are a deliberate conservative
1382    /// deferral — only named indexes are modelled here.
1383    Index {
1384        /// Whether the equals form was present in the source.
1385        equals: bool,
1386        /// indexes in source order.
1387        indexes: ThinVec<Ident>,
1388        /// Source location and node identity.
1389        meta: Meta,
1390    },
1391    /// `FORCESEEK [ ( <index> ( <column>, … ) ) ]`: force an index seek, optionally
1392    /// pinned to a named index and a leading column prefix ([`ForceSeekTarget`]).
1393    /// `None` is the bare `FORCESEEK`.
1394    ForceSeek {
1395        /// Object targeted by this syntax.
1396        target: Option<ForceSeekTarget>,
1397        /// Source location and node identity.
1398        meta: Meta,
1399    },
1400    /// An unrecognized single-word hint, preserved verbatim so the surface round-trips.
1401    Other {
1402        /// The unrecognized hint word, preserved verbatim.
1403        ident: Ident,
1404        /// Source location and node identity.
1405        meta: Meta,
1406    },
1407}
1408
1409/// The optional `( <index> ( <column>, … ) )` argument of a MSSQL `FORCESEEK` table
1410/// hint ([`TableHint::ForceSeek`]): the index to seek and the leading key columns.
1411#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1412#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1413#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1414pub struct ForceSeekTarget {
1415    /// The index the seek is forced to use.
1416    pub index: Ident,
1417    /// The leading index-key columns the seek is forced over; non-empty (T-SQL
1418    /// requires at least one column inside the inner parentheses).
1419    pub columns: ThinVec<Ident>,
1420    /// Source location and node identity.
1421    pub meta: Meta,
1422}
1423
1424/// The modelled single-keyword MSSQL table hints ([`TableHint::Keyword`]) — the
1425/// documented locking, isolation, and planner directives. Each spelling round-trips
1426/// verbatim via [`TableHintKeyword::as_str`]; the classifier
1427/// [`TableHintKeyword::from_upper`] maps an uppercased source word back. Words outside
1428/// this set stay [`TableHint::Other`].
1429#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1430#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1431#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1432pub enum TableHintKeyword {
1433    /// `NOLOCK` — read without shared locks (allows dirty reads).
1434    NoLock,
1435    /// `HOLDLOCK` — hold shared locks until the transaction completes (= `SERIALIZABLE`).
1436    HoldLock,
1437    /// `UPDLOCK` — take update locks instead of shared locks.
1438    UpdLock,
1439    /// `XLOCK` — take exclusive locks.
1440    XLock,
1441    /// `ROWLOCK` — force row-level locking granularity.
1442    RowLock,
1443    /// `PAGLOCK` — force page-level locking granularity.
1444    PagLock,
1445    /// `TABLOCK` — force table-level locking.
1446    TabLock,
1447    /// `TABLOCKX` — force an exclusive table-level lock.
1448    TabLockX,
1449    /// `READPAST` — skip rows locked by other transactions.
1450    ReadPast,
1451    /// `READUNCOMMITTED` — read-uncommitted isolation (= `NOLOCK`).
1452    ReadUncommitted,
1453    /// `READCOMMITTED` — read-committed isolation.
1454    ReadCommitted,
1455    /// `READCOMMITTEDLOCK` — read-committed isolation enforced with locking.
1456    ReadCommittedLock,
1457    /// `REPEATABLEREAD` — repeatable-read isolation.
1458    RepeatableRead,
1459    /// `SERIALIZABLE` — serializable isolation.
1460    Serializable,
1461    /// `SNAPSHOT` — snapshot isolation.
1462    Snapshot,
1463    /// `NOWAIT` — error immediately instead of waiting for a conflicting lock.
1464    NoWait,
1465    /// `NOEXPAND` — do not expand indexed views.
1466    NoExpand,
1467    /// `FORCESCAN` — force a scan access path.
1468    ForceScan,
1469    /// `KEEPIDENTITY` — (bulk insert) keep source identity values.
1470    KeepIdentity,
1471    /// `KEEPDEFAULTS` — (bulk insert) apply column defaults for missing values.
1472    KeepDefaults,
1473    /// `IGNORE_CONSTRAINTS` — (bulk insert) skip CHECK/foreign-key enforcement.
1474    IgnoreConstraints,
1475    /// `IGNORE_TRIGGERS` — (bulk insert) skip trigger firing.
1476    IgnoreTriggers,
1477}
1478
1479impl TableHintKeyword {
1480    /// The canonical T-SQL spelling of this hint, rendered verbatim.
1481    pub fn as_str(self) -> &'static str {
1482        match self {
1483            Self::NoLock => "NOLOCK",
1484            Self::HoldLock => "HOLDLOCK",
1485            Self::UpdLock => "UPDLOCK",
1486            Self::XLock => "XLOCK",
1487            Self::RowLock => "ROWLOCK",
1488            Self::PagLock => "PAGLOCK",
1489            Self::TabLock => "TABLOCK",
1490            Self::TabLockX => "TABLOCKX",
1491            Self::ReadPast => "READPAST",
1492            Self::ReadUncommitted => "READUNCOMMITTED",
1493            Self::ReadCommitted => "READCOMMITTED",
1494            Self::ReadCommittedLock => "READCOMMITTEDLOCK",
1495            Self::RepeatableRead => "REPEATABLEREAD",
1496            Self::Serializable => "SERIALIZABLE",
1497            Self::Snapshot => "SNAPSHOT",
1498            Self::NoWait => "NOWAIT",
1499            Self::NoExpand => "NOEXPAND",
1500            Self::ForceScan => "FORCESCAN",
1501            Self::KeepIdentity => "KEEPIDENTITY",
1502            Self::KeepDefaults => "KEEPDEFAULTS",
1503            Self::IgnoreConstraints => "IGNORE_CONSTRAINTS",
1504            Self::IgnoreTriggers => "IGNORE_TRIGGERS",
1505        }
1506    }
1507
1508    /// Classify an already-uppercased source word into a modelled hint keyword, or
1509    /// `None` when it is not one (the caller keeps it as [`TableHint::Other`]).
1510    pub fn from_upper(word: &str) -> Option<Self> {
1511        Some(match word {
1512            "NOLOCK" => Self::NoLock,
1513            "HOLDLOCK" => Self::HoldLock,
1514            "UPDLOCK" => Self::UpdLock,
1515            "XLOCK" => Self::XLock,
1516            "ROWLOCK" => Self::RowLock,
1517            "PAGLOCK" => Self::PagLock,
1518            "TABLOCK" => Self::TabLock,
1519            "TABLOCKX" => Self::TabLockX,
1520            "READPAST" => Self::ReadPast,
1521            "READUNCOMMITTED" => Self::ReadUncommitted,
1522            "READCOMMITTED" => Self::ReadCommitted,
1523            "READCOMMITTEDLOCK" => Self::ReadCommittedLock,
1524            "REPEATABLEREAD" => Self::RepeatableRead,
1525            "SERIALIZABLE" => Self::Serializable,
1526            "SNAPSHOT" => Self::Snapshot,
1527            "NOWAIT" => Self::NoWait,
1528            "NOEXPAND" => Self::NoExpand,
1529            "FORCESCAN" => Self::ForceScan,
1530            "KEEPIDENTITY" => Self::KeepIdentity,
1531            "KEEPDEFAULTS" => Self::KeepDefaults,
1532            "IGNORE_CONSTRAINTS" => Self::IgnoreConstraints,
1533            "IGNORE_TRIGGERS" => Self::IgnoreTriggers,
1534            _ => return None,
1535        })
1536    }
1537}
1538
1539/// Surface syntax that produced a [`TableFactor::Derived`]: the standard
1540/// parenthesized derived table `( <query> )`, or DuckDB's bare `FROM VALUES (…) AS t`
1541/// row-list table factor written *without* the surrounding parentheses.
1542///
1543/// One derived-table semantic, the paren spelling kept as data (the
1544/// [`SelectSpelling`] precedent). The bare form's body is always a
1545/// [`SetExpr::Values`] constructor and it always carries a table alias — DuckDB
1546/// parse-*requires* one (`FROM VALUES (1) t`, never a bare `FROM VALUES (1)`; probed on
1547/// 1.5.4) — so the renderer re-emits `VALUES (…) AS t` with no wrapping parentheses,
1548/// while the [`Parenthesized`](Self::Parenthesized) default re-emits `( <query> )`. The
1549/// tag is load-bearing: a parenthesized `FROM (VALUES (1) )` and a bare `FROM VALUES (1)
1550/// t` both hold a `Values` body, so only this spelling tells the renderer whether to
1551/// wrap. Gated by
1552/// [`TableFactorSyntax::from_values`](crate::dialect::TableExpressionSyntax).
1553#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1554#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1555#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1556pub enum DerivedSpelling {
1557    /// The standard parenthesized derived table `( <query> )`; the construction default.
1558    Parenthesized,
1559    /// DuckDB's bare `FROM VALUES (…) AS t` row-list table factor (no parentheses).
1560    BareValues,
1561}
1562
1563#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1564#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1565#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1566/// The SQL table factor forms represented by the AST.
1567pub enum TableFactor<X: Extension = NoExt> {
1568    /// A named table/relation reference, with optional alias, hints, sampling, and time-travel.
1569    Table {
1570        /// The table name (one or more dot-separated parts).
1571        name: ObjectName,
1572        /// PostgreSQL `ONLY`/`*` inheritance modifier; see [`RelationInheritance`].
1573        inheritance: RelationInheritance,
1574        /// A PartiQL / SUPER JSON path navigating into a semi-structured column at the
1575        /// table-source position (`FROM src[0].a`), attached directly to the table name.
1576        /// Redshift's SUPER navigation and Snowflake's PartiQL access (sqlparser-rs's
1577        /// `TableFactor::Table::json_path`, gated by its `supports_partiql`). The path is
1578        /// entered only by a `[` immediately after the name — a bracket index root, then
1579        /// `.key` / `[index]` suffixes — so a dotted `FROM src.a.b` stays a compound
1580        /// [`name`](Self::Table::name), never a path. Empty when absent (the path is always
1581        /// non-empty when present, so an empty [`ThinVec`] is the unambiguous "no path"
1582        /// sentinel — the same pattern as [`partition`](Self::Table::partition)). Reuses the
1583        /// expression-position [`SemiStructuredPathSegment`]
1584        /// vocabulary. Gated by
1585        /// [`TableExpressionSyntax::table_json_path`](crate::dialect::TableExpressionSyntax).
1586        json_path: ThinVec<SemiStructuredPathSegment<X>>,
1587        /// A version / time-travel modifier (`FOR SYSTEM_TIME AS OF …`, `VERSION AS OF …`),
1588        /// written between the table name and the alias; `None` when absent. `Box`ed to
1589        /// keep this hot enum within its size budget (ADR-0007). Gated by
1590        /// [`TableExpressionSyntax::table_version`](crate::dialect::TableExpressionSyntax);
1591        /// see [`TableVersion`].
1592        version: Option<Box<TableVersion<X>>>,
1593        /// MySQL explicit partition selection `PARTITION (p0, p1)`, written between
1594        /// the table name and the alias; empty when absent. Restricts the scan to the
1595        /// named partitions/subpartitions. Gated by
1596        /// [`TableExpressionSyntax::partition_selection`](crate::dialect::TableExpressionSyntax).
1597        partition: ThinVec<Ident>,
1598        /// Alias assigned by this syntax.
1599        alias: Option<Box<TableAlias>>,
1600        /// SQLite `INDEXED BY <index>` / `NOT INDEXED` index directive, written after the
1601        /// table name and its optional alias (`FROM t AS e INDEXED BY ix`); `None` when
1602        /// absent. `Box`ed to keep this hot enum within its size budget (ADR-0007). A
1603        /// separate axis from MySQL [`index_hints`](Self::Table::index_hints): a different
1604        /// dialect, grammar, and cardinality (see [`IndexedBy`]). Gated by
1605        /// [`TableExpressionSyntax::indexed_by`](crate::dialect::TableExpressionSyntax).
1606        indexed_by: Option<Box<IndexedBy>>,
1607        /// MySQL index hints (`USE|FORCE|IGNORE INDEX|KEY …`), written after the
1608        /// alias; empty when absent. A list because MySQL admits several comma-joined
1609        /// hints on one table. Gated by
1610        /// [`TableExpressionSyntax::index_hints`](crate::dialect::TableExpressionSyntax).
1611        index_hints: ThinVec<IndexHint>,
1612        /// Optional sample for this syntax.
1613        sample: Option<TableSample<X>>,
1614        /// MSSQL / T-SQL `WITH (...)` table hints (`WITH (NOLOCK)`,
1615        /// `WITH (INDEX(ix), FORCESEEK)`), written after the alias and the
1616        /// tablesample clause; empty when absent. A list because T-SQL admits several
1617        /// comma-joined hints in one `WITH (...)`. A separate axis from
1618        /// [`index_hints`](Self::Table::index_hints): a different dialect (T-SQL vs
1619        /// MySQL) and a different grammar position. Gated by
1620        /// [`TableExpressionSyntax::table_hints`](crate::dialect::TableExpressionSyntax).
1621        table_hints: ThinVec<TableHint>,
1622        /// Source location and node identity.
1623        meta: Meta,
1624    },
1625    /// A derived table — a parenthesized subquery in `FROM`, optionally `LATERAL`.
1626    Derived {
1627        /// Whether the lateral form was present in the source.
1628        lateral: bool,
1629        /// The subquery producing the derived rows.
1630        subquery: Box<Query<X>>,
1631        /// Alias assigned by this syntax.
1632        alias: Option<Box<TableAlias>>,
1633        /// Whether the source wrote the standard parenthesized `( <query> )` or DuckDB's
1634        /// bare `FROM VALUES (…) AS t` row list (no parentheses); see [`DerivedSpelling`].
1635        /// A [`BareValues`](DerivedSpelling::BareValues) factor's `subquery` body is
1636        /// always a [`SetExpr::Values`] and its `alias` is always `Some` (the
1637        /// parser rejects a bare `FROM VALUES` without one), the invariant the
1638        /// [`Render`](crate::render::Render) impl relies on to drop the parentheses.
1639        spelling: DerivedSpelling,
1640        /// Source location and node identity.
1641        meta: Meta,
1642    },
1643    /// A set-returning function used as a table (a table function), optionally `LATERAL`.
1644    Function {
1645        /// Whether the lateral form was present in the source.
1646        lateral: bool,
1647        /// The table-function call; see [`FunctionCall`].
1648        function: Box<FunctionCall<X>>,
1649        /// Whether the with ordinality form was present in the source.
1650        with_ordinality: bool,
1651        /// Alias assigned by this syntax.
1652        alias: Option<Box<TableAlias>>,
1653        /// PostgreSQL `func_alias_clause` column definition list, e.g. the
1654        /// `(id int, name text)` of `func(...) AS x(id int, name text)`. Empty
1655        /// unless the function returns an anonymous record typed at the call site.
1656        column_defs: ThinVec<TableFunctionColumn<X>>,
1657        /// Source location and node identity.
1658        meta: Meta,
1659    },
1660    /// A PostgreSQL `ROWS FROM(f1(…), f2(…))` multi-function table factor.
1661    RowsFrom {
1662        /// Whether the lateral form was present in the source.
1663        lateral: bool,
1664        /// functions in source order.
1665        functions: ThinVec<RowsFromItem<X>>,
1666        /// Whether the with ordinality form was present in the source.
1667        with_ordinality: bool,
1668        /// Alias assigned by this syntax.
1669        alias: Option<Box<TableAlias>>,
1670        /// Source location and node identity.
1671        meta: Meta,
1672    },
1673    /// A first-class `UNNEST(<expr>[, <expr>…])` table factor: an array/collection
1674    /// expression expanded into a relation. Modelled as a dedicated node rather than
1675    /// the generic [`Function`](Self::Function) table function for planner-consumer
1676    /// parity (the downstream planner keys on a distinct `UNNEST`) — even though
1677    /// PostgreSQL itself lowers `FROM unnest(…)` to the same `RangeFunction` as any
1678    /// other set-returning function (its parse tree draws no distinction). Gated by
1679    /// [`TableFactorSyntax::unnest`](crate::dialect::TableExpressionSyntax);
1680    /// reached only when `UNNEST` is immediately followed by `(`, so a bare `UNNEST`
1681    /// stays an ordinary relation name.
1682    Unnest {
1683        /// `LATERAL UNNEST(…)`: the array expressions correlate against earlier FROM
1684        /// items (PostgreSQL `CROSS JOIN LATERAL unnest(t.arr)`).
1685        lateral: bool,
1686        /// The unnested array expressions. PostgreSQL admits several (`unnest(a, b)`,
1687        /// the multi-array zip); DuckDB and BigQuery take exactly one. An empty list
1688        /// models PostgreSQL's degenerate `unnest()` accept.
1689        array_exprs: ThinVec<Expr<X>>,
1690        /// PostgreSQL/DuckDB `WITH ORDINALITY`: append a 1-based ordinal column. BigQuery
1691        /// has no `WITH ORDINALITY` — it spells the same idea `WITH OFFSET` (0-based).
1692        with_ordinality: bool,
1693        /// The correlation alias and its optional untyped column-name list
1694        /// (`AS u(v, ord)`), read *before* the [`with_offset`](Self::Unnest::with_offset)
1695        /// tail so both the PostgreSQL (`… WITH ORDINALITY AS u(…)`) and BigQuery
1696        /// (`… AS u WITH OFFSET`) orderings round-trip.
1697        alias: Option<Box<TableAlias>>,
1698        /// PostgreSQL's typed `func_alias_clause` column-definition list
1699        /// (`unnest(x) AS t(a int)`); empty for the common untyped form. Carried so the
1700        /// rare typed spelling round-trips losslessly rather than over-rejecting.
1701        column_defs: ThinVec<TableFunctionColumn<X>>,
1702        /// BigQuery `WITH OFFSET`: append a 0-based offset column. Gated by
1703        /// [`TableFactorSyntax::unnest_with_offset`](crate::dialect::TableExpressionSyntax)
1704        /// — a preset-less flag (no shipped dialect enables it, mirroring
1705        /// [`QueryTailSyntax::pipe_syntax`](crate::dialect::SelectSyntax)), since only
1706        /// BigQuery/ZetaSQL accepts the tail and there is no BigQuery oracle yet.
1707        with_offset: bool,
1708        /// The BigQuery `WITH OFFSET AS <alias>` column alias; `None` for a bare
1709        /// `WITH OFFSET`, and always `None` when [`with_offset`](Self::Unnest::with_offset)
1710        /// is `false`.
1711        with_offset_alias: Option<Ident>,
1712        /// Source location and node identity.
1713        meta: Meta,
1714    },
1715    /// A parenthesized join nested as a table factor: `(t1 JOIN t2 ON …)`.
1716    NestedJoin {
1717        /// The parenthesized join tree; see [`TableWithJoins`].
1718        table: Box<TableWithJoins<X>>,
1719        /// Alias assigned by this syntax.
1720        alias: Option<Box<TableAlias>>,
1721        /// Source location and node identity.
1722        meta: Meta,
1723    },
1724    /// A bare SQL special value function used as a table reference (PostgreSQL
1725    /// `func_table: func_expr_windowless`, e.g. `SELECT * FROM current_date`):
1726    /// `pg_query` lowers this to a `RangeFunction` wrapping a `SQLValueFunction`,
1727    /// distinct from an ordinary call — mirrors [`Expr::SpecialFunction`], the same
1728    /// grammar production in expression position.
1729    SpecialFunction {
1730        /// Which special-value function; see [`SpecialFunctionKeyword`].
1731        keyword: SpecialFunctionKeyword,
1732        /// The `(precision)` modifier, only valid on the temporal forms (mirrors
1733        /// [`Expr::SpecialFunction`]'s `precision`).
1734        precision: Option<u32>,
1735        /// Alias assigned by this syntax.
1736        alias: Option<Box<TableAlias>>,
1737        /// Source location and node identity.
1738        meta: Meta,
1739    },
1740    /// DuckDB's `<source> PIVOT (<aggregates> FOR <col> IN (<values>) [GROUP BY …])`
1741    /// table factor. The [`Pivot`] core is shared with the leading-keyword
1742    /// [`Statement::Pivot`](super::Statement) (tagged
1743    /// [`PivotSpelling::TableFactor`](super::PivotSpelling)); this position owns the
1744    /// trailing `AS p` alias the statement form has no place for. `Box`ed — like the
1745    /// other payload-bearing variants — to keep this hot enum within its size
1746    /// budget. Gated by
1747    /// [`TableFactorSyntax::pivot`](crate::dialect::TableExpressionSyntax).
1748    Pivot {
1749        /// The pivot operation; see [`Pivot`].
1750        pivot: Box<Pivot<X>>,
1751        /// Alias assigned by this syntax.
1752        alias: Option<Box<TableAlias>>,
1753        /// Source location and node identity.
1754        meta: Meta,
1755    },
1756    /// DuckDB's `<source> UNPIVOT [… NULLS] (<value> FOR <name> IN (<cols>))` table
1757    /// factor — the [`Unpivot`] counterpart of [`Pivot`](Self::Pivot), sharing its core
1758    /// with [`Statement::Unpivot`](super::Statement). Gated by
1759    /// [`TableFactorSyntax::unpivot`](crate::dialect::TableExpressionSyntax).
1760    Unpivot {
1761        /// The unpivot operation; see [`Unpivot`].
1762        unpivot: Box<Unpivot<X>>,
1763        /// Alias assigned by this syntax.
1764        alias: Option<Box<TableAlias>>,
1765        /// Source location and node identity.
1766        meta: Meta,
1767    },
1768    /// The SQL:2016 `<source> MATCH_RECOGNIZE (…)` row-pattern-recognition table factor
1769    /// (Snowflake / Oracle). The [`MatchRecognize`] operator core carries the
1770    /// `PARTITION BY` / `ORDER BY` / `MEASURES` / rows-per-match / after-match-skip /
1771    /// `PATTERN` / `SUBSET` / `DEFINE` clauses; this position owns the trailing `AS mr`
1772    /// alias. `Box`ed — like the other payload-bearing variants — to keep this hot enum
1773    /// within its size budget (ADR-0007). Gated by
1774    /// [`TableFactorSyntax::match_recognize`](crate::dialect::TableExpressionSyntax).
1775    MatchRecognize {
1776        /// The `MATCH_RECOGNIZE` operator; see [`MatchRecognize`].
1777        match_recognize: Box<MatchRecognize<X>>,
1778        /// Alias assigned by this syntax.
1779        alias: Option<Box<TableAlias>>,
1780        /// Source location and node identity.
1781        meta: Meta,
1782    },
1783    /// DuckDB's `DESCRIBE`/`SHOW`/`SUMMARIZE` utility standing as a **table source** —
1784    /// DuckDB's `SHOW_REF` table reference (`FROM (DESCRIBE SELECT …)`,
1785    /// `FROM (DESCRIBE PIVOT …)`, `FROM (SHOW databases)`; all probed on 1.5.4). Unlike
1786    /// [`Pivot`](Self::Pivot)/[`Unpivot`](Self::Unpivot), these are relation-producing
1787    /// constructs, *not* query bodies — DuckDB parse-rejects them at CTE-body position
1788    /// (`A CTE needs a SELECT`) while admitting them here — so they get a table-factor
1789    /// wrapper around the shared [`ShowRef`] core, the shape DuckDB itself uses
1790    /// (its `SHOW_REF` node carries the same kind + target). This position owns the
1791    /// trailing `AS t` alias. `Box`ed to keep this hot enum within its size
1792    /// budget. Gated by
1793    /// [`TableFactorSyntax::show_ref`](crate::dialect::TableExpressionSyntax).
1794    ShowRef {
1795        /// The `DESCRIBE`/`SHOW`/`SUMMARIZE` reference; see [`ShowRef`].
1796        show: Box<ShowRef<X>>,
1797        /// Alias assigned by this syntax.
1798        alias: Option<Box<TableAlias>>,
1799        /// Source location and node identity.
1800        meta: Meta,
1801    },
1802    /// SQL/JSON `JSON_TABLE(…)` table factor (SQL:2016) — a JSON document decomposed into a
1803    /// relation by a `COLUMNS` specification. `Box`ed to keep this hot enum within its size
1804    /// budget. Gated by
1805    /// [`TableFactorSyntax::json_table`](crate::dialect::TableExpressionSyntax).
1806    JsonTable {
1807        /// The `JSON_TABLE(...)` specification; see [`JsonTable`].
1808        json_table: Box<JsonTable<X>>,
1809        /// Alias assigned by this syntax.
1810        alias: Option<Box<TableAlias>>,
1811        /// Source location and node identity.
1812        meta: Meta,
1813    },
1814    /// SQL/XML `XMLTABLE(…)` table factor (SQL:2006) — an XML document decomposed into a
1815    /// relation by an XPath row expression and per-column paths. `Box`ed to keep this hot enum
1816    /// within its size budget. Gated by
1817    /// [`TableFactorSyntax::xml_table`](crate::dialect::TableExpressionSyntax).
1818    XmlTable {
1819        /// The `XMLTABLE(...)` specification; see [`XmlTable`].
1820        xml_table: Box<XmlTable<X>>,
1821        /// Alias assigned by this syntax.
1822        alias: Option<Box<TableAlias>>,
1823        /// Source location and node identity.
1824        meta: Meta,
1825    },
1826    /// SQL Server's `OPENJSON(<json> [, <path>]) [WITH (<col> <type> [<path>] [AS JSON], …)]`
1827    /// table factor — a JSON document parsed into a relation, either with the default
1828    /// key/value/type schema (no `WITH`) or an explicit column schema. `Box`ed to keep this
1829    /// hot enum within its size budget (ADR-0007). Gated by
1830    /// [`TableFactorSyntax::open_json`](crate::dialect::TableFactorSyntax::open_json).
1831    OpenJson {
1832        /// The `OPENJSON(...)` specification; see [`OpenJson`].
1833        open_json: Box<OpenJson<X>>,
1834        /// Alias assigned by this syntax.
1835        alias: Option<Box<TableAlias>>,
1836        /// Source location and node identity.
1837        meta: Meta,
1838    },
1839    /// `TABLE(<expr>)` — an arbitrary expression evaluated as a set-returning table
1840    /// source (sqlparser-rs's `TableFactor::TableFunction`). Distinct from a *named*
1841    /// table function ([`Function`](Self::Function), `FROM f(1)`), whose head is a
1842    /// call, not a parenthesized expression, and from the standalone `TABLE t` query
1843    /// form (`Select::spelling` [`SelectSpelling::TableCommand`](super::SelectSpelling)),
1844    /// which is a statement-level `<explicit table>`, not a `FROM`-position factor at
1845    /// all. Only Snowflake and Oracle document this exact shape and neither carries a
1846    /// differential oracle here, so this is gated
1847    /// [`TableFactorSyntax::table_expr_factor`](crate::dialect::TableFactorSyntax::table_expr_factor),
1848    /// on for Lenient only. `Box`ed to keep this hot enum within its size budget.
1849    TableExpr {
1850        /// Expression evaluated by this syntax.
1851        expr: Box<Expr<X>>,
1852        /// Alias assigned by this syntax.
1853        alias: Option<Box<TableAlias>>,
1854        /// Source location and node identity.
1855        meta: Meta,
1856    },
1857    /// Dialect extension node supplied by the extension type.
1858    Other {
1859        /// The dialect extension node value.
1860        ext: X,
1861        /// Source location and node identity.
1862        meta: Meta,
1863    },
1864}
1865
1866impl<X: Extension> TableFactor<X> {
1867    /// A mutable handle to this factor's correlation-alias slot, or `None` for the
1868    /// extension [`Other`](Self::Other) variant, which carries no alias.
1869    ///
1870    /// Every grammar factor holds an `Option<Box<TableAlias>>`; exposing it uniformly lets
1871    /// a caller inspect or set the alias without matching all twelve variants — used by the
1872    /// DuckDB prefix-colon-alias reader (`FROM <alias> : <factor>`) to attach the alias it
1873    /// parsed ahead of the factor.
1874    pub fn alias_slot_mut(&mut self) -> Option<&mut Option<Box<TableAlias>>> {
1875        match self {
1876            Self::Table { alias, .. }
1877            | Self::Derived { alias, .. }
1878            | Self::Function { alias, .. }
1879            | Self::RowsFrom { alias, .. }
1880            | Self::Unnest { alias, .. }
1881            | Self::NestedJoin { alias, .. }
1882            | Self::SpecialFunction { alias, .. }
1883            | Self::Pivot { alias, .. }
1884            | Self::Unpivot { alias, .. }
1885            | Self::MatchRecognize { alias, .. }
1886            | Self::ShowRef { alias, .. }
1887            | Self::JsonTable { alias, .. }
1888            | Self::XmlTable { alias, .. }
1889            | Self::OpenJson { alias, .. }
1890            | Self::TableExpr { alias, .. } => Some(alias),
1891            Self::Other { .. } => None,
1892        }
1893    }
1894}
1895
1896/// DuckDB's `SHOW_REF` table reference: a `DESCRIBE` / `SHOW` / `SUMMARIZE` utility
1897/// statement standing as a relation-producing table source ([`TableFactor::ShowRef`]).
1898///
1899/// DuckDB models all three uniformly as one `SHOW_REF` node with a `show_type` tag and a
1900/// target that is either a query (`DESCRIBE <query>` / `SUMMARIZE <query>`) or a name
1901/// (`DESCRIBE <table>`, `SHOW databases`) — the canonical shape reproduced here.
1902///
1903/// The same core is reused in two grammar positions: inside a parenthesized `FROM`
1904/// factor ([`TableFactor::ShowRef`]), and — for the `DESCRIBE`/`SUMMARIZE` spellings — as
1905/// a top-level statement ([`Statement::ShowRef`](crate::ast::Statement)), the form DuckDB
1906/// desugars to `SELECT * FROM (<SHOW_REF>)`.
1907#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1908#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1909#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1910pub struct ShowRef<X: Extension = NoExt> {
1911    /// Which utility keyword (`DESCRIBE`/`SHOW`/`SUMMARIZE`); see [`ShowRefKind`].
1912    pub kind: ShowRefKind,
1913    /// Object targeted by this syntax.
1914    pub target: ShowRefTarget<X>,
1915    /// Source location and node identity.
1916    pub meta: Meta,
1917}
1918
1919/// Which utility keyword produced a [`ShowRef`] — DuckDB's `show_type`, kept as data so
1920/// the renderer round-trips the written keyword.
1921#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1922#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1923#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1924pub enum ShowRefKind {
1925    /// `DESCRIBE` (also spelled `DESC`) — column metadata of the target.
1926    Describe,
1927    /// `SHOW` — the unqualified list forms (`SHOW databases`, `SHOW tables`) and
1928    /// `SHOW <table>`.
1929    Show,
1930    /// `SUMMARIZE` — summary statistics of the target.
1931    Summarize,
1932}
1933
1934#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1935#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1936#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1937/// The SQL show ref target forms represented by the AST.
1938pub enum ShowRefTarget<X: Extension = NoExt> {
1939    /// `DESCRIBE <query>` / `SUMMARIZE <query>` — the described query (a `SELECT`,
1940    /// `PIVOT`, or `UNPIVOT` body).
1941    Query {
1942        /// Query governed by this node.
1943        query: Box<Query<X>>,
1944        /// Source location and node identity.
1945        meta: Meta,
1946    },
1947    /// `DESCRIBE <table>`, `SHOW <name>` (`SHOW databases`, `SHOW tables`,
1948    /// `SHOW <table>`) — the named target.
1949    Name {
1950        /// Name referenced by this syntax.
1951        name: ObjectName,
1952        /// Source location and node identity.
1953        meta: Meta,
1954    },
1955}
1956
1957/// The payload of a [`TableFactor::JsonTable`] — SQL/JSON `JSON_TABLE` (SQL:2016,
1958/// PostgreSQL's `JsonTable`).
1959///
1960/// The clause vocabulary is shared with the SQL/JSON expression functions: the
1961/// [`context`](Self::context) reuses [`JsonValueExpr`] (`<doc> [FORMAT JSON …]`), the
1962/// [`passing`](Self::passing) list reuses [`JsonPassingArg`], and the top-level
1963/// [`on_error`](Self::on_error) reuses [`JsonBehavior`] — no parallel copies. The row
1964/// [`path`](Self::path) and every column path are restricted to *string literals* at parse
1965/// (PostgreSQL: "only string constants are supported in JSON_TABLE path" — a bare column or
1966/// operator rejects), so they hold a string-literal [`Expr`].
1967#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1968#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1969#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1970pub struct JsonTable<X: Extension = NoExt> {
1971    /// Whether the lateral form was present in the source.
1972    pub lateral: bool,
1973    /// The JSON document value; see [`JsonValueExpr`].
1974    pub context: JsonValueExpr<X>,
1975    /// The row path — a string literal (PostgreSQL restricts it to a string constant).
1976    pub path: Box<Expr<X>>,
1977    /// Optional path name for this syntax.
1978    pub path_name: Option<Ident>,
1979    /// passing in source order.
1980    pub passing: ThinVec<JsonPassingArg<X>>,
1981    /// Non-empty — PostgreSQL rejects `COLUMNS ()`.
1982    pub columns: ThinVec<JsonTableColumn<X>>,
1983    /// The top-level `<behaviour> ON ERROR`; there is no top-level `ON EMPTY`.
1984    pub on_error: Option<JsonBehavior<X>>,
1985    /// Source location and node identity.
1986    pub meta: Meta,
1987}
1988
1989/// One column of a [`JsonTable`] `COLUMNS` specification (PostgreSQL's `JsonTableColumn`,
1990/// tagged by `coltype`).
1991///
1992/// The wrapper/quotes/behaviour clauses reuse the SQL/JSON expression-function nodes. The
1993/// per-kind clause legality is enforced at parse (matching PostgreSQL): only a
1994/// [`Regular`](Self::Regular) column takes `FORMAT`/wrapper/quotes/`ON EMPTY`;
1995/// [`Exists`](Self::Exists) takes only `ON ERROR`; [`Nested`](Self::Nested) takes no
1996/// behaviours and recurses through its own `COLUMNS`.
1997#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1998#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1999#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2000pub enum JsonTableColumn<X: Extension = NoExt> {
2001    /// `<name> FOR ORDINALITY` — a 1-based row-sequence column; no type, no other clauses.
2002    ForOrdinality {
2003        /// Name referenced by this syntax.
2004        name: Ident,
2005        /// Source location and node identity.
2006        meta: Meta,
2007    },
2008    /// `<name> <type> [FORMAT JSON …] [PATH <string>] [wrapper] [quotes] [<b> ON EMPTY]
2009    /// [<b> ON ERROR]` — a value column projecting the JSON at its path.
2010    Regular {
2011        /// Name referenced by this syntax.
2012        name: Ident,
2013        /// Data type named by this syntax.
2014        data_type: Box<DataType<X>>,
2015        /// Optional format for this syntax.
2016        format: Option<JsonFormat>,
2017        /// `PATH <string>` — a string literal; `None` uses the implicit `$.<name>` path.
2018        path: Option<Box<Expr<X>>>,
2019        /// The `WITH`/`WITHOUT WRAPPER` behaviour; see [`JsonWrapperBehavior`].
2020        wrapper: JsonWrapperBehavior,
2021        /// The `KEEP`/`OMIT QUOTES` behaviour; see [`JsonQuotesBehavior`].
2022        quotes: JsonQuotesBehavior,
2023        /// Optional on empty for this syntax.
2024        on_empty: Option<JsonBehavior<X>>,
2025        /// Optional on error for this syntax.
2026        on_error: Option<JsonBehavior<X>>,
2027        /// Source location and node identity.
2028        meta: Meta,
2029    },
2030    /// `<name> <type> EXISTS [PATH <string>] [<b> ON ERROR]` — a boolean-ish column testing
2031    /// whether the path matches; takes neither `FORMAT`/wrapper/quotes nor `ON EMPTY`.
2032    Exists {
2033        /// Name referenced by this syntax.
2034        name: Ident,
2035        /// Data type named by this syntax.
2036        data_type: Box<DataType<X>>,
2037        /// Optional path for this syntax.
2038        path: Option<Box<Expr<X>>>,
2039        /// Optional on error for this syntax.
2040        on_error: Option<JsonBehavior<X>>,
2041        /// Source location and node identity.
2042        meta: Meta,
2043    },
2044    /// `NESTED [PATH] <string> [AS <name>] COLUMNS ( … )` — a sub-table joined against a
2045    /// nested path, recursing through its own column list (PostgreSQL requires the nested
2046    /// `COLUMNS` to be present and non-empty).
2047    Nested {
2048        /// The nested JSON path (a string literal).
2049        path: Box<Expr<X>>,
2050        /// Optional path name for this syntax.
2051        path_name: Option<Ident>,
2052        /// Columns in source order.
2053        columns: ThinVec<JsonTableColumn<X>>,
2054        /// Source location and node identity.
2055        meta: Meta,
2056    },
2057}
2058
2059/// The payload of a [`TableFactor::XmlTable`] — SQL/XML `XMLTABLE` (SQL:2006, PostgreSQL's
2060/// `RangeTableFunc`).
2061///
2062/// The [`passing_mechanism_before`](Self::passing_mechanism_before)/`_after` reuse the
2063/// [`XmlPassingMechanism`] of `xmlexists` (`PASSING [BY REF|VALUE] doc [BY REF|VALUE]`);
2064/// PostgreSQL admits a mechanism on either side and normalizes it away, so it is preserved
2065/// only for round-trip fidelity. The [`row_expr`](Self::row_expr) and
2066/// [`document`](Self::document) are `c_expr` operands (a bare `a || b` rejects; parenthesize
2067/// to re-admit a full expression), matching the engine.
2068#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2069#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2070#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2071pub struct XmlTable<X: Extension = NoExt> {
2072    /// Whether the lateral form was present in the source.
2073    pub lateral: bool,
2074    /// namespaces in source order.
2075    pub namespaces: ThinVec<XmlNamespace<X>>,
2076    /// The row-generating XPath (a `c_expr`).
2077    pub row_expr: Box<Expr<X>>,
2078    /// The `PASSING` document (a `c_expr`).
2079    pub document: Box<Expr<X>>,
2080    /// Optional passing mechanism before for this syntax.
2081    pub passing_mechanism_before: Option<XmlPassingMechanism>,
2082    /// Optional passing mechanism after for this syntax.
2083    pub passing_mechanism_after: Option<XmlPassingMechanism>,
2084    /// Columns in source order.
2085    pub columns: ThinVec<XmlTableColumn<X>>,
2086    /// Source location and node identity.
2087    pub meta: Meta,
2088}
2089
2090/// One `XMLNAMESPACES` declaration inside an [`XmlTable`]: `<uri> AS <name>` or
2091/// `DEFAULT <uri>`.
2092#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2093#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2094#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2095pub struct XmlNamespace<X: Extension = NoExt> {
2096    /// The namespace URI expression.
2097    pub uri: Box<Expr<X>>,
2098    /// `None` for the `DEFAULT <uri>` (unnamed) form.
2099    pub name: Option<Ident>,
2100    /// Source location and node identity.
2101    pub meta: Meta,
2102}
2103
2104/// One column of an [`XmlTable`] `COLUMNS` specification (PostgreSQL's `RangeTableFuncCol`).
2105///
2106/// The regular-column options (`PATH`/`DEFAULT`/`NULL`/`NOT NULL`) are order-free at parse —
2107/// PostgreSQL normalizes them into fixed node fields — so they are stored positionally here
2108/// and re-rendered in canonical order. PostgreSQL rejects a repeated `PATH`/`DEFAULT` and a
2109/// conflicting/redundant `NULL`/`NOT NULL` at parse, which the parser reproduces.
2110#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2111#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2112#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2113pub enum XmlTableColumn<X: Extension = NoExt> {
2114    /// `<name> FOR ORDINALITY` — a 1-based row-sequence column; no type or options.
2115    ForOrdinality {
2116        /// Name referenced by this syntax.
2117        name: Ident,
2118        /// Source location and node identity.
2119        meta: Meta,
2120    },
2121    /// `<name> <type> [PATH <b_expr>] [DEFAULT <b_expr>] [NULL | NOT NULL]`.
2122    Regular {
2123        /// Name referenced by this syntax.
2124        name: Ident,
2125        /// Data type named by this syntax.
2126        data_type: Box<DataType<X>>,
2127        /// Optional path for this syntax.
2128        path: Option<Box<Expr<X>>>,
2129        /// Optional default for this syntax.
2130        default: Option<Box<Expr<X>>>,
2131        /// `Some(true)` for `NOT NULL`, `Some(false)` for `NULL`, `None` when unwritten.
2132        not_null: Option<bool>,
2133        /// Source location and node identity.
2134        meta: Meta,
2135    },
2136}
2137
2138/// The payload of a [`TableFactor::OpenJson`] — SQL Server's `OPENJSON` rowset function
2139/// (sqlparser-rs's `TableFactor::OpenJsonTable`).
2140///
2141/// Reshaped from sqlparser-rs per ADR-0011: its `json_expr: Expr` / `json_path: Option<Value>` /
2142/// per-column `path: Option<String>` become span-bearing [`Expr`] holders here — the row
2143/// [`path`](Self::path) and every column path are *string literals* (matching JSON_TABLE's
2144/// [`JsonTable::path`](JsonTable::path)), so they round-trip from their spans — and the column
2145/// list is a [`ThinVec`]. An absent `WITH` clause is the empty [`columns`](Self::columns) (MSSQL
2146/// rejects an empty `WITH ()`, so a present clause is always non-empty); the default
2147/// `key`/`value`/`type` schema then applies.
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 OpenJson<X: Extension = NoExt> {
2152    /// The JSON source expression (a column, variable, or literal), evaluated to a JSON
2153    /// string. Unrestricted — any [`Expr`], unlike the string-literal-only paths.
2154    pub json_expr: Box<Expr<X>>,
2155    /// The optional `, <path>` second argument — a string-literal JSON path selecting the
2156    /// array/object to iterate; `None` iterates the root value.
2157    pub path: Option<Box<Expr<X>>>,
2158    /// The `WITH (…)` explicit column schema; empty when the clause is absent.
2159    pub columns: ThinVec<OpenJsonColumn<X>>,
2160    /// Source location and node identity.
2161    pub meta: Meta,
2162}
2163
2164/// One column of an [`OpenJson`] `WITH (…)` schema — `<name> <type> [<path>] [AS JSON]`
2165/// (sqlparser-rs's `OpenJsonTableColumn`).
2166#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2167#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2168#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2169pub struct OpenJsonColumn<X: Extension = NoExt> {
2170    /// Name referenced by this syntax.
2171    pub name: Ident,
2172    /// Data type named by this syntax.
2173    pub data_type: Box<DataType<X>>,
2174    /// The optional `<column_path>` string literal; `None` uses the implicit `$.<name>` path.
2175    pub path: Option<Box<Expr<X>>>,
2176    /// The `AS JSON` marker — the column holds nested JSON (MSSQL requires an
2177    /// `nvarchar(max)` type).
2178    pub as_json: bool,
2179    /// Source location and node identity.
2180    pub meta: Meta,
2181}
2182
2183#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2184#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2185#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2186/// An SQL join.
2187pub struct Join<X: Extension = NoExt> {
2188    /// The right-hand table factor being joined.
2189    pub relation: TableFactor<X>,
2190    /// The join operator — side and constraint; see [`JoinOperator`].
2191    pub operator: JoinOperator<X>,
2192    /// Source location and node identity.
2193    pub meta: Meta,
2194}
2195
2196#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2197#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2198#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2199/// The SQL join operator forms represented by the AST.
2200pub enum JoinOperator<X: Extension = NoExt> {
2201    /// An `[INNER] JOIN` — keeps only row pairs that satisfy the constraint.
2202    Inner {
2203        /// MySQL `STRAIGHT_JOIN`: an inner join that additionally forces the
2204        /// optimizer to read the left table before the right. It is semantically a
2205        /// plain `INNER JOIN`, so it is the canonical inner-join shape
2206        /// carrying this surface tag (a join-order hint) rather than a new operator
2207        /// variant — mirroring how [`SetQuantifier::All`] preserves an explicit `ALL`.
2208        /// `false` is a bare `[INNER] JOIN`; only MySQL parses the `true` spelling
2209        /// (gated by [`JoinSyntax::straight_join`](crate::dialect::TableExpressionSyntax)).
2210        straight: bool,
2211        /// Whether the redundant `INNER` keyword was written (`INNER JOIN` vs a bare
2212        /// `JOIN` — the two are exact synonyms). A source-fidelity render replays it; a
2213        /// target re-spell and the redacted fingerprint drop it. Always `false` under
2214        /// `straight` (`STRAIGHT_JOIN` is its own keyword, never spelled `INNER`).
2215        inner: bool,
2216        /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2217        constraint: JoinConstraint<X>,
2218        /// Source location and node identity.
2219        meta: Meta,
2220    },
2221    /// A `LEFT [OUTER] JOIN` — keeps every left row, NULL-padding unmatched right columns.
2222    LeftOuter {
2223        /// Whether the redundant `OUTER` keyword was written (`LEFT OUTER JOIN` vs a
2224        /// bare `LEFT JOIN`). Fidelity only, like [`Inner::inner`](Self::Inner::inner).
2225        outer: bool,
2226        /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2227        constraint: JoinConstraint<X>,
2228        /// Source location and node identity.
2229        meta: Meta,
2230    },
2231    /// A `RIGHT [OUTER] JOIN` — keeps every right row, NULL-padding unmatched left columns.
2232    RightOuter {
2233        /// Whether the redundant `OUTER` keyword was written (`RIGHT OUTER JOIN`).
2234        outer: bool,
2235        /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2236        constraint: JoinConstraint<X>,
2237        /// Source location and node identity.
2238        meta: Meta,
2239    },
2240    /// A `FULL [OUTER] JOIN` — keeps all rows from both sides, NULL-padding non-matches.
2241    FullOuter {
2242        /// Whether the redundant `OUTER` keyword was written (`FULL OUTER JOIN`).
2243        outer: bool,
2244        /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2245        constraint: JoinConstraint<X>,
2246        /// Source location and node identity.
2247        meta: Meta,
2248    },
2249    /// DuckDB `ASOF [INNER|LEFT|RIGHT|FULL [OUTER]] JOIN`: an inexact-match temporal
2250    /// join pairing each left row with the nearest right row under the `ON`
2251    /// inequality. A new operator (nearest-match semantics), not a spelling of the
2252    /// side joins — DuckDB serializes it as an orthogonal `ref_type: ASOF` on top of
2253    /// the `join_type` side, mirrored here as [`kind`](Self::AsOf::kind). The engine
2254    /// *parse*-requires an `ON`/`USING` constraint (a bare `ASOF JOIN` is a syntax
2255    /// error), so the parser never builds [`JoinConstraint::None`] here; the
2256    /// inequality requirement itself is bind-time (`ASOF JOIN … ON a = b` parses,
2257    /// then fails DuckDB's binder), so an equality constraint still parses. Gated by
2258    /// [`JoinSyntax::asof_join`](crate::dialect::TableExpressionSyntax).
2259    AsOf {
2260        /// Which side the `ASOF` join keeps; see [`AsOfJoinKind`].
2261        kind: AsOfJoinKind,
2262        /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2263        constraint: JoinConstraint<X>,
2264        /// Source location and node identity.
2265        meta: Meta,
2266    },
2267    /// A `CROSS JOIN` — the unconstrained Cartesian product.
2268    Cross {
2269        /// Source location and node identity.
2270        meta: Meta,
2271    },
2272    /// DuckDB `POSITIONAL JOIN`: pairs rows by position (first with first, …). Like
2273    /// [`Cross`](Self::Cross) it never carries a constraint — the engine
2274    /// parse-rejects a trailing `ON`/`USING` and any side keyword (`POSITIONAL LEFT
2275    /// JOIN` is a syntax error) — so the variant has no constraint or kind field.
2276    /// Gated by
2277    /// [`JoinSyntax::positional_join`](crate::dialect::TableExpressionSyntax).
2278    Positional {
2279        /// Source location and node identity.
2280        meta: Meta,
2281    },
2282    /// DuckDB `[ASOF|NATURAL] SEMI JOIN`: a semi-join — keeps each left row that has
2283    /// at least one right match, projecting left columns only. DuckDB serializes it as
2284    /// a `join_type: SEMI` (engine-verified on 1.5.4), *mutually exclusive* with the
2285    /// `INNER`/`LEFT`/`RIGHT`/`FULL` sides (`LEFT SEMI JOIN` is a syntax error), so it
2286    /// is a new operator rather than a side spelling. It composes only with the
2287    /// `REGULAR`, `NATURAL` (`NATURAL SEMI JOIN`, carried as
2288    /// [`JoinConstraint::Natural`]) and `ASOF` (`ASOF SEMI JOIN`,
2289    /// [`asof`](Self::Semi::asof) `= true`) ref-types — never a side, `CROSS`, or
2290    /// `POSITIONAL`. Like [`AsOf`](Self::AsOf) it *parse*-requires an `ON`/`USING`
2291    /// constraint (a bare `SEMI JOIN` is a syntax error) unless `NATURAL` supplies the
2292    /// match, so the parser never builds [`JoinConstraint::None`] here. `ASOF` and
2293    /// `NATURAL` never co-occur (both engine parse-rejected), so `asof: true` always
2294    /// carries an `ON`/`USING` constraint.
2295    ///
2296    /// The [`side`](Self::Semi::side) axis records the Spark/Hive/Databricks *sided*
2297    /// spelling — `LEFT SEMI JOIN` / `RIGHT SEMI JOIN` — as one operator with DuckDB's
2298    /// side-less `SEMI JOIN` rather than a separate variant (the
2299    /// [`AsOfJoinKind`]/[`ApplyKind`] axis precedent): all three are the same semi-join,
2300    /// differing only in whether an explicit side keyword is written and which side's
2301    /// rows are tested. The two spellings come from different engine families and are
2302    /// gated apart — DuckDB's side-less form by
2303    /// [`JoinSyntax::semi_anti_join`](crate::dialect::TableExpressionSyntax),
2304    /// the sided form by
2305    /// [`JoinSyntax::sided_semi_anti_join`](crate::dialect::TableExpressionSyntax)
2306    /// (DuckDB engine-parse-rejects `LEFT SEMI JOIN`). The two axes are mutually
2307    /// exclusive: [`SemiAntiSide::Left`]/[`Right`](SemiAntiSide::Right) never carry the
2308    /// DuckDB-only `ASOF`/`NATURAL` compositions, so a sided operator always has
2309    /// `asof: false` and an `ON`/`USING` constraint (Spark requires the qualifier).
2310    Semi {
2311        /// Whether the asof form was present in the source.
2312        asof: bool,
2313        /// Which sided spelling (`LEFT`/`RIGHT`/side-less); see [`SemiAntiSide`].
2314        side: SemiAntiSide,
2315        /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2316        constraint: JoinConstraint<X>,
2317        /// Source location and node identity.
2318        meta: Meta,
2319    },
2320    /// DuckDB `[ASOF|NATURAL] ANTI JOIN` / Spark `[LEFT|RIGHT] ANTI JOIN`: an anti-join —
2321    /// keeps each left row with *no* right match. The [`Semi`](Self::Semi) counterpart:
2322    /// identical grammar (DuckDB serializes `join_type: ANTI`) with the opposite
2323    /// membership test, so see [`Semi`](Self::Semi) for the composition, constraint,
2324    /// `asof`-flag, and [`side`](Self::Anti::side) rules and the two gates.
2325    Anti {
2326        /// Whether the asof form was present in the source.
2327        asof: bool,
2328        /// Which sided spelling (`LEFT`/`RIGHT`/side-less); see [`SemiAntiSide`].
2329        side: SemiAntiSide,
2330        /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2331        constraint: JoinConstraint<X>,
2332        /// Source location and node identity.
2333        meta: Meta,
2334    },
2335    /// MSSQL `CROSS APPLY` / `OUTER APPLY`: an implicitly-correlated (lateral) join
2336    /// whose right operand — a derived table `(SELECT …)` or a table-valued function
2337    /// call — may reference columns of the left source. Like [`Cross`](Self::Cross) it
2338    /// never carries an `ON`/`USING` constraint (the correlation is positional, in the
2339    /// right operand's own references), so the variant holds no constraint. The
2340    /// [`kind`](Self::Apply::kind) axis is the `CROSS`/`OUTER` flavour — inner-style vs
2341    /// left-style row preservation — mirroring how [`AsOf`](Self::AsOf) records its side
2342    /// on a `kind` rather than splitting into per-spelling variants: `CROSS APPLY` and
2343    /// `OUTER APPLY` are one grammar production differing only by that keyword. Gated by
2344    /// [`JoinSyntax::apply_join`](crate::dialect::TableExpressionSyntax).
2345    Apply {
2346        /// The `CROSS`/`OUTER` apply flavour; see [`ApplyKind`].
2347        kind: ApplyKind,
2348        /// Source location and node identity.
2349        meta: Meta,
2350    },
2351}
2352
2353/// The flavour of a MSSQL [`JoinOperator::Apply`] operator.
2354///
2355/// `CROSS`/`OUTER` are the two spellings of the single `APPLY` grammar production
2356/// (a lateral join over a right table factor), differing only in row preservation —
2357/// `CROSS` drops left rows whose right operand is empty, `OUTER` keeps them with
2358/// nulls — so they are one operator with a two-value axis, not two operators (the
2359/// [`AsOfJoinKind`] precedent). Only `CROSS` is wired into the parser today; `OUTER`
2360/// is the sibling `planner-parity-join-outer-apply` extension, and the enum carries
2361/// it so that landing is a parser-only change with no AST/render churn.
2362#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2363#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2364#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2365pub enum ApplyKind {
2366    /// `CROSS APPLY` — evaluate the right side per left row, like a `LATERAL` inner join.
2367    Cross,
2368    /// `OUTER APPLY` — like `CROSS APPLY` but keeps left rows with no right match (`LATERAL` left join).
2369    Outer,
2370}
2371
2372/// The side spelling of a [`JoinOperator::Semi`]/[`JoinOperator::Anti`] operator.
2373///
2374/// DuckDB spells the semi-/anti-join side-*less* (`SEMI JOIN` / `ANTI JOIN`, a
2375/// left-semi/left-anti by definition) and composes it with the `NATURAL`/`ASOF`
2376/// ref-types; Spark/Hive/Databricks instead *require* an explicit side keyword
2377/// (`LEFT SEMI JOIN`, `RIGHT ANTI JOIN`) and never compose with those ref-types. The
2378/// two are the same operator differing only in this surface axis, so — like
2379/// [`ApplyKind`]/[`AsOfJoinKind`] — the side rides a `kind`-style axis rather than
2380/// splitting into per-spelling variants.
2381///
2382/// [`Sideless`](Self::Sideless) is the DuckDB spelling (its `asof` flag may then be
2383/// set). [`Left`](Self::Left) is Spark's `LEFT` — semantically the same left-semi as
2384/// [`Sideless`](Self::Sideless), differing only in whether the keyword is written;
2385/// [`Right`](Self::Right) is the mirror `RIGHT` (right-row test), genuinely distinct
2386/// semantics. A sided value always carries `asof: false` and an `ON`/`USING`
2387/// constraint (Spark requires the qualifier and has no `ASOF`/`NATURAL`).
2388///
2389/// Only [`Sideless`](Self::Sideless) and [`Left`](Self::Left) are wired into the
2390/// parser today; [`Right`](Self::Right) — and the [`Anti`](JoinOperator::Anti)
2391/// pairing of both sides — ships here so the sibling `RIGHT SEMI` / `LEFT ANTI` /
2392/// `RIGHT ANTI` tickets land as parser-only changes with no AST/render churn (the
2393/// [`ApplyKind::Outer`] precedent).
2394#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2395#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2396#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2397pub enum SemiAntiSide {
2398    /// No side keyword — a plain `SEMI`/`ANTI` join.
2399    Sideless,
2400    /// `LEFT SEMI`/`LEFT ANTI`.
2401    Left,
2402    /// `RIGHT SEMI`/`RIGHT ANTI`.
2403    Right,
2404}
2405
2406/// The side of a DuckDB [`JoinOperator::AsOf`] join.
2407///
2408/// `ASOF` composes with all four standard sides (engine-verified on DuckDB 1.5.4,
2409/// including the `OUTER` spellings) but not with `NATURAL`/`CROSS`, so this is a
2410/// dedicated four-side kind rather than a nested [`JoinOperator`]. `Inner` covers
2411/// both the bare `ASOF JOIN` and the explicit `ASOF INNER JOIN` spelling (the
2412/// canonical shape records the side, not the spelling, like the side joins).
2413#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2414#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2415#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2416pub enum AsOfJoinKind {
2417    /// `ASOF [INNER] JOIN`.
2418    Inner,
2419    /// `ASOF LEFT JOIN`.
2420    Left,
2421    /// `ASOF RIGHT JOIN`.
2422    Right,
2423    /// `ASOF FULL JOIN`.
2424    Full,
2425}
2426
2427#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2428#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2429#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2430/// The SQL join constraint forms represented by the AST.
2431pub enum JoinConstraint<X: Extension = NoExt> {
2432    /// An `ON <predicate>` join condition.
2433    On {
2434        /// Expression evaluated by this syntax.
2435        expr: Expr<X>,
2436        /// Source location and node identity.
2437        meta: Meta,
2438    },
2439    /// A `USING (col, …)` join condition — equate the named common columns.
2440    Using {
2441        /// Columns in source order.
2442        columns: ThinVec<Ident>,
2443        /// Alias assigned by this syntax.
2444        alias: Option<Ident>,
2445        /// Source location and node identity.
2446        meta: Meta,
2447    },
2448    /// A `NATURAL` join — an implicit equi-join on all same-named columns.
2449    Natural {
2450        /// Source location and node identity.
2451        meta: Meta,
2452    },
2453    /// No join constraint (a `CROSS JOIN` or comma join).
2454    None {
2455        /// Source location and node identity.
2456        meta: Meta,
2457    },
2458}
2459
2460#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2461#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2462#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2463/// An SQL order by expr.
2464pub struct OrderByExpr<X: Extension = NoExt> {
2465    /// Expression evaluated by this syntax.
2466    pub expr: Expr<X>,
2467    /// Whether the asc form was present in the source.
2468    pub asc: Option<bool>,
2469    /// PostgreSQL `USING <operator>` sort form (`gram.y` `sortby: a_expr USING
2470    /// qual_all_Op opt_nulls_order`): sort by a named ordering operator instead of
2471    /// `ASC`/`DESC`. Mutually exclusive with [`asc`](Self::asc), which stays `None`
2472    /// when this is `Some`; boxed since it is a rare tail on a common sort key.
2473    pub using: Option<Box<OrderByUsing>>,
2474    /// Whether the nulls first form was present in the source.
2475    pub nulls_first: Option<bool>,
2476    /// Source location and node identity.
2477    pub meta: Meta,
2478}
2479
2480/// The `USING <qual_all_Op>` operator of a PostgreSQL `ORDER BY` sort key.
2481///
2482/// `qual_all_Op` is a possibly schema-qualified operator: the bare `USING <` form
2483/// carries no [`schema`](Self::schema) node at all, while `USING
2484/// OPERATOR(pg_catalog.<)` records the qualification. Modelled like the operator
2485/// half of [`NamedOperatorExpr`](super::NamedOperatorExpr): the operator
2486/// is always symbolic, never a word, so it is a bare interned [`Symbol`]. `schema`
2487/// is `Option` rather than an empty [`ObjectName`] because every present node must
2488/// carry a real source span (the span-containment walker enforces it) — an empty
2489/// name would have none.
2490#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2491#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2492#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2493pub struct OrderByUsing {
2494    /// Schema qualification (`pg_catalog` in `OPERATOR(pg_catalog.<)`); `None` for
2495    /// the bare `USING <` form.
2496    pub schema: Option<ObjectName>,
2497    /// The operator symbol spelling (`<`, `~<~`), interned exact-case so it
2498    /// round-trips.
2499    pub op: Symbol,
2500    /// Source location and node identity.
2501    pub meta: Meta,
2502}
2503
2504/// DuckDB's `ORDER BY ALL [ASC | DESC] [NULLS FIRST | LAST]` clause mode: sort by
2505/// every projection column, left to right.
2506///
2507/// A first-class marker node, not an [`OrderByExpr`] whose expression spells `ALL`:
2508/// the sort keys are resolved at bind time from the projection, so there is no
2509/// expression to carry, and DuckDB's own tree corroborates the framing (it
2510/// serializes the clause as a single order whose expression is the `COLUMNS(*)`
2511/// star node — a whole-projection expansion, not a column named `all`). The
2512/// direction and nulls modifiers ride the clause exactly as they ride an ordinary
2513/// sort key (`ORDER BY ALL DESC NULLS LAST` is valid; probed on 1.5.4), hence the
2514/// same `asc`/`nulls_first` surface as [`OrderByExpr`] — but no `USING` (DuckDB
2515/// rejects `ORDER BY ALL USING <`). Carries no [`Expr`], so it is not generic over
2516/// the extension parameter.
2517#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2518#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2519#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2520pub struct OrderByAll {
2521    /// `ASC` (`Some(true)`) / `DESC` (`Some(false)`), or `None` when unwritten —
2522    /// recording exactly what the source said, mirroring [`OrderByExpr::asc`].
2523    pub asc: Option<bool>,
2524    /// `NULLS FIRST` (`Some(true)`) / `NULLS LAST` (`Some(false)`), or `None` when
2525    /// unwritten, mirroring [`OrderByExpr::nulls_first`].
2526    pub nulls_first: Option<bool>,
2527    /// Source location and node identity.
2528    pub meta: Meta,
2529}
2530
2531/// Canonical LIMIT/OFFSET node plus original surface spelling.
2532#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2533#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2534#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2535pub struct Limit<X: Extension = NoExt> {
2536    /// Row limit applied to the result.
2537    pub limit: Option<Expr<X>>,
2538    /// Row offset applied before returning results.
2539    pub offset: Option<Expr<X>>,
2540    /// Source spelling used for the syntax.
2541    pub syntax: LimitSyntax,
2542    /// Whether a written `FETCH { FIRST | NEXT } ...` tail chose `WITH TIES`
2543    /// (rows tying the last row's `ORDER BY` key are also returned) over the
2544    /// default `ONLY`, or `None` when no `FETCH` clause was written at all.
2545    ///
2546    /// A third state is load-bearing here, not just `bool`: the SQL:2008
2547    /// `OFFSET ... ROWS` spelling ([`LimitSyntax::FetchFirst`]) admits a `FETCH`
2548    /// tail whose row count is itself optional (`FETCH FIRST ROWS ONLY`,
2549    /// PostgreSQL defaults it to 1), so `limit: None` alone cannot tell "no
2550    /// `FETCH` clause" (`OFFSET 5 ROWS`, unbounded) apart from "`FETCH` written
2551    /// with no count" (`OFFSET 5 ROWS FETCH FIRST ROWS ONLY`, bounded to 1) —
2552    /// two different result sets. `with_ties` disambiguates them instead: `None`
2553    /// is the former, `Some(_)` the latter. Always `None` under
2554    /// [`LimitSyntax::LimitOffset`], which has no `FETCH` tail at all.
2555    pub with_ties: Option<bool>,
2556    /// DuckDB's percentage row limit: the count is a *fraction of rows* rather than a
2557    /// row number (`LIMIT 40 PERCENT`, `LIMIT 35%` — return 40%/35% of the result).
2558    /// `None` is the ordinary row-count limit; `Some(_)` records which surface spelling
2559    /// wrote the marker so the renderer round-trips it (the [`LimitSyntax`]
2560    /// precedent). Only meaningful with a written [`limit`](Self::limit) count under
2561    /// [`LimitSyntax::LimitOffset`]; gated to DuckDB via
2562    /// [`QueryTailSyntax::limit_percent`](crate::dialect::SelectSyntax).
2563    pub percent: Option<LimitPercent>,
2564    /// Surface spelling of a written `FETCH { FIRST | NEXT } … { ROW | ROWS }` tail:
2565    /// which of the interchangeable `FIRST`/`NEXT` and `ROW`/`ROWS` synonyms the source
2566    /// wrote, so a source-fidelity render replays them. Meaningful only under
2567    /// [`LimitSyntax::FetchFirst`] with a written `FETCH` (`with_ties` is `Some`); the
2568    /// canonical render (and a target re-spell / the redacted fingerprint) emit the
2569    /// canonical `FETCH FIRST … ROWS`. One byte, so it rides the struct's existing
2570    /// padding — the leaner axis-per-field alternative crossed an alignment word.
2571    pub fetch_spelling: FetchSpelling,
2572    /// Source location and node identity.
2573    pub meta: Meta,
2574}
2575
2576/// Surface spelling of a written `FETCH { FIRST | NEXT } … { ROW | ROWS }` tail
2577/// ([`Limit::fetch_spelling`]).
2578///
2579/// `FIRST`/`NEXT` and `ROW`/`ROWS` are interchangeable noise words; the canonical AST
2580/// keeps one shape and this tag records the written pair so a source-fidelity render
2581/// replays it. The two axes are folded onto one 1-byte enum (rather than two `bool`
2582/// fields) so the tag rides [`Limit`]'s existing padding instead of growing the node
2583/// (ADR-0007). A fidelity tag — a target re-spell and the redacted fingerprint emit the
2584/// canonical [`FirstRows`](Self::FirstRows).
2585#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
2586#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2587#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2588pub enum FetchSpelling {
2589    /// The canonical `FETCH FIRST … ROWS`.
2590    #[default]
2591    FirstRows,
2592    /// `FETCH FIRST … ROW` (singular row word).
2593    FirstRow,
2594    /// `FETCH NEXT … ROWS`.
2595    NextRows,
2596    /// `FETCH NEXT … ROW`.
2597    NextRow,
2598}
2599
2600impl FetchSpelling {
2601    /// Build the tag from the two written-spelling axes: `next` selects `NEXT` over
2602    /// `FIRST`, `row_singular` the singular `ROW` over `ROWS`.
2603    pub fn from_axes(next: bool, row_singular: bool) -> Self {
2604        match (next, row_singular) {
2605            (false, false) => Self::FirstRows,
2606            (false, true) => Self::FirstRow,
2607            (true, false) => Self::NextRows,
2608            (true, true) => Self::NextRow,
2609        }
2610    }
2611
2612    /// The written keyword: `"FETCH NEXT"` or the canonical `"FETCH FIRST"`.
2613    pub fn fetch_keyword(self) -> &'static str {
2614        match self {
2615            Self::NextRows | Self::NextRow => "FETCH NEXT",
2616            Self::FirstRows | Self::FirstRow => "FETCH FIRST",
2617        }
2618    }
2619
2620    /// The written row word (with surrounding spaces): `" ROW "` or the canonical
2621    /// `" ROWS "`.
2622    pub fn row_word(self) -> &'static str {
2623        match self {
2624            Self::FirstRow | Self::NextRow => " ROW ",
2625            Self::FirstRows | Self::NextRows => " ROWS ",
2626        }
2627    }
2628}
2629
2630/// Surface syntax used to write a canonical [`Limit`].
2631#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2632#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2633#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2634pub enum LimitSyntax {
2635    /// Source used the `LIMIT OFFSET` spelling.
2636    LimitOffset,
2637    /// MySQL/MariaDB/SQLite `LIMIT <offset>, <count>` — the comma spelling of
2638    /// `LIMIT <count> OFFSET <offset>` (the offset binds first, the count second). The
2639    /// same row limit, folded onto the canonical [`Limit`] shape; this variant records
2640    /// the comma spelling so a source-fidelity render replays `LIMIT <offset>, <count>`
2641    /// while a target re-spell and the redacted fingerprint emit the canonical
2642    /// `LIMIT <count> OFFSET <offset>`.
2643    CommaOffset,
2644    /// Source used the `FETCH FIRST` spelling.
2645    FetchFirst,
2646}
2647
2648/// Surface spelling of a DuckDB percentage-limit marker ([`Limit::percent`]).
2649///
2650/// One percent semantic, two spellings kept as data (mirroring
2651/// [`LimitSyntax`]/[`RollupSpelling`]): the `%` operator (`LIMIT 35%`) and the
2652/// `PERCENT` keyword (`LIMIT 40 PERCENT`). The two are interchangeable in DuckDB —
2653/// both return the same fraction of rows — so canonicalizing them onto one node keeps
2654/// the differential oracle comparing one shape; the tag exists only so rendering
2655/// reproduces the written marker rather than normalizing every form to one spelling.
2656#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2657#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2658#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2659pub enum LimitPercent {
2660    /// The `%` operator spelling: `LIMIT 35%` (the whitespace-insensitive `LIMIT 20 %`
2661    /// canonicalizes onto this — the space before `%` is not a distinct spelling).
2662    Symbol,
2663    /// The `PERCENT` keyword spelling: `LIMIT 40 PERCENT`.
2664    Keyword,
2665}
2666
2667/// ClickHouse `LIMIT n [OFFSET m] BY expr, …` — per-group row limiting.
2668///
2669/// Keeps the first `n` rows for each distinct value of the `by` expression list
2670/// (with an optional `OFFSET m` skip *within* each group), a wholly different
2671/// operation from the ordinary [`Limit`] tail that bounds the result as a whole. A
2672/// query may carry **both**, in that order: `SELECT … ORDER BY … LIMIT 2 BY x LIMIT
2673/// 10` limits to two rows per `x`, then caps the whole result at ten. So this is its
2674/// own [`Query::limit_by`] field, never folded onto the `Limit` shape — the two
2675/// clauses coexist and mean different things.
2676///
2677/// Gated by [`QueryTailSyntax::limit_by_clause`](crate::dialect::SelectSyntax); no
2678/// shipped preset spells it but Lenient (the permissive union). The `by` list is
2679/// always non-empty (the `BY` keyword requires at least one expression).
2680#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2681#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2682#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2683pub struct LimitBy<X: Extension = NoExt> {
2684    /// The per-group row count `n`; always written (`LIMIT BY` has no bare form).
2685    pub limit: Expr<X>,
2686    /// The `OFFSET m` skip applied within each group before the `n` rows are kept;
2687    /// `None` when unwritten. Rendered as `OFFSET m`, the canonical spelling.
2688    pub offset: Option<Expr<X>>,
2689    /// The `BY expr, …` grouping expressions; always at least one.
2690    pub by: ThinVec<Expr<X>>,
2691    /// Source location and node identity.
2692    pub meta: Meta,
2693}
2694
2695/// One ClickHouse `SETTINGS` pair: `<name> = <value>` (`max_threads = 8`,
2696/// `join_algorithm = 'auto'`), an element of [`Query::settings`].
2697///
2698/// ClickHouse's grammar is `identifier '=' literal` — the value is a scalar literal
2699/// (number, string, boolean). It is modelled as a general [`Expr`] (the `SecretOption`
2700/// precedent — a `<name> <value>` option whose value is likewise a general expression),
2701/// so the corpus literals round-trip while the wider expression grammar is the recorded
2702/// acceptance bound.
2703#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2704#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2705#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2706pub struct Setting<X: Extension = NoExt> {
2707    /// The setting name (`max_threads`); a bare identifier.
2708    pub name: Ident,
2709    /// The assigned value; ClickHouse writes a literal, modelled as a general [`Expr`].
2710    pub value: Expr<X>,
2711    /// Source location and node identity.
2712    pub meta: Meta,
2713}
2714
2715/// ClickHouse `FORMAT <name>` — the output-format clause that closes a query
2716/// ([`Query::format`]), naming the serialization of the result (`FORMAT JSON`,
2717/// `FORMAT CSV`, `FORMAT TabSeparated`, `FORMAT Null`).
2718///
2719/// The format name is a bare identifier, case-sensitive (`JSON` ≠ `json` to
2720/// ClickHouse), never a string literal — so it is carried as an [`Ident`] preserving
2721/// the source spelling, not a [`Literal`]. `Null` is an ordinary format name here, not
2722/// the null literal. Carries no [`Expr`], so it is not generic over the extension
2723/// parameter (the [`OrderByAll`] precedent).
2724///
2725/// Gated by [`QueryTailSyntax::format_clause`](crate::dialect::SelectSyntax); no shipped
2726/// preset spells it but Lenient (the permissive union). There is no ClickHouse oracle,
2727/// so the accepted grammar is the recorded acceptance bound.
2728#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2729#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2730#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2731pub struct FormatClause {
2732    /// The output-format name (`JSON`, `TabSeparated`, `Null`); a bare, case-sensitive
2733    /// identifier.
2734    pub name: Ident,
2735    /// Source location and node identity.
2736    pub meta: Meta,
2737}
2738
2739/// MSSQL `FOR XML` / `FOR JSON` result-shaping tail on a [`Query`]
2740/// ([`Query::for_clause`]): `SELECT … FOR XML {RAW|AUTO|EXPLICIT|PATH} [, …]` and
2741/// `SELECT … FOR JSON {AUTO|PATH} [, …]`, which serialize the result set as XML or
2742/// JSON instead of a rowset.
2743///
2744/// # Parity with sqlparser-rs `ForClause`
2745///
2746/// Mirrors sqlparser-rs's `ForClause` enum (its `Xml { for_xml, elements,
2747/// binary_base64, root, r#type }` / `Json { for_json, root, include_null_values,
2748/// without_array_wrapper }` variants), with three deliberate reshapings:
2749/// - The `RAW`/`AUTO`/`EXPLICIT`/`PATH` and `AUTO`/`PATH` selectors are their own
2750///   [`ForXmlMode`] / [`ForJsonMode`] axes carrying the optional `('name')` element
2751///   name on the arms that take one (`RAW`/`PATH`), rather than sqlparser-rs's
2752///   flatter `ForXml`/`ForJson` with a separate name — the name is a property of the
2753///   mode, so it rides the mode arm (the canonical-shape doctrine, ADR-0011).
2754/// - `ELEMENTS` is [`Option`]`<`[`ForXmlElements`]`>` — `None` for the attribute-centric
2755///   default, `Some` carrying the `XSINIL`/`ABSENT` null-handling refinement —
2756///   where sqlparser-rs drops the refinement onto a bare `elements: bool`.
2757/// - `ROOT ['name']` is [`Option`]`<`[`ForRoot`]`>` (presence = the `ROOT` keyword;
2758///   the inner name is optional) shared by both variants, where sqlparser-rs models
2759///   it per-variant as `root: Option<String>` — losing the bare-`ROOT` vs no-`ROOT`
2760///   distinction our shape keeps.
2761///
2762/// sqlparser-rs's `ForClause::Browse` (`FOR BROWSE`) is out of scope: this ticket
2763/// covers only the `FOR XML`/`FOR JSON` result-shaping tails.
2764///
2765/// # Gating and `FOR` disambiguation
2766///
2767/// Gated by [`QueryTailSyntax::for_xml_json_clause`](crate::dialect::SelectSyntax) —
2768/// on for MSSQL and the permissive Lenient union, off elsewhere; with the gate off the
2769/// `FOR` keyword in this position is left
2770/// unconsumed and surfaces as a clean parse error. `FOR` also introduces the
2771/// row-locking clauses ([`Query::locking`], gated by
2772/// [`QueryTailSyntax::locking_clauses`](crate::dialect::SelectSyntax)); the two share
2773/// the `FOR` lead but **partition on the follow token** — `XML`/`JSON` here versus
2774/// `UPDATE`/`SHARE`/`NO`/`KEY` for locking — so the dispatch is unambiguous under
2775/// every preset combination, including Lenient (the one preset that enables both), and
2776/// needs no [`GrammarConflict`](crate::dialect::GrammarConflict) registry entry.
2777///
2778/// There is no MSSQL oracle, so the accepted grammar is the recorded acceptance bound
2779/// (self-consistent round-trip tests plus the MSSQL `FOR XML`/`FOR JSON` docs cited on
2780/// the gating flag). Options are accepted order-independently and rendered in the
2781/// canonical MSSQL directive order. Carries only mode tags and quoted-name
2782/// [`Literal`]s (no [`Expr`]), so it is not generic over the extension parameter.
2783#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2784#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2785#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2786pub enum ForClause {
2787    /// `FOR XML {RAW|AUTO|EXPLICIT|PATH} [, BINARY BASE64] [, TYPE] [, ROOT ['name']]
2788    /// [, ELEMENTS [XSINIL|ABSENT]]`.
2789    Xml {
2790        /// Mode selected by this syntax.
2791        mode: ForXmlMode,
2792        /// The `ELEMENTS [XSINIL|ABSENT]` element-centric directive; `None` for the
2793        /// attribute-centric default.
2794        elements: Option<ForXmlElements>,
2795        /// `BINARY BASE64` — encode binary columns as Base64 rather than a URL
2796        /// reference.
2797        binary_base64: bool,
2798        /// `TYPE` — return the result as an `xml`-typed value rather than text.
2799        typed: bool,
2800        /// `ROOT ['name']` wrapper element; `None` when no `ROOT` directive is written.
2801        root: Option<ForRoot>,
2802        /// Source location and node identity.
2803        meta: Meta,
2804    },
2805    /// `FOR JSON {AUTO|PATH} [, ROOT ['name']] [, INCLUDE_NULL_VALUES]
2806    /// [, WITHOUT_ARRAY_WRAPPER]`.
2807    Json {
2808        /// Mode selected by this syntax.
2809        mode: ForJsonMode,
2810        /// `ROOT ['name']` wrapper property; `None` when no `ROOT` directive is written.
2811        root: Option<ForRoot>,
2812        /// `INCLUDE_NULL_VALUES` — emit `null`-valued properties instead of omitting them.
2813        include_null_values: bool,
2814        /// `WITHOUT_ARRAY_WRAPPER` — emit a single object rather than a JSON array.
2815        without_array_wrapper: bool,
2816        /// Source location and node identity.
2817        meta: Meta,
2818    },
2819}
2820
2821/// The `FOR XML` serialization mode ([`ForClause::Xml`]): `RAW`, `AUTO`, `EXPLICIT`,
2822/// or `PATH`. `RAW` and `PATH` carry an optional `('ElementName')` naming the row
2823/// element (a quoted string [`Literal`]); `AUTO` and `EXPLICIT` take no name.
2824#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2825#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2826#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2827pub enum ForXmlMode {
2828    /// `RAW ['ElementName']` — one `<row>` (or the named element) per result row.
2829    Raw {
2830        /// The `('ElementName')` row-element name; `None` for a bare `RAW`.
2831        name: Option<Literal>,
2832        /// Source location and node identity.
2833        meta: Meta,
2834    },
2835    /// `AUTO` — nest elements to reflect the `FROM` join hierarchy.
2836    Auto {
2837        /// Source location and node identity.
2838        meta: Meta,
2839    },
2840    /// `EXPLICIT` — the query's own universal-table shape defines the XML.
2841    Explicit {
2842        /// Source location and node identity.
2843        meta: Meta,
2844    },
2845    /// `PATH ['ElementName']` — column names as XPath expressions.
2846    Path {
2847        /// The `('ElementName')` row-element name; `None` for a bare `PATH`.
2848        name: Option<Literal>,
2849        /// Source location and node identity.
2850        meta: Meta,
2851    },
2852}
2853
2854/// The `FOR XML … ELEMENTS` null-handling refinement ([`ForClause::Xml`]): a bare
2855/// `ELEMENTS`, or one of the `XSINIL`/`ABSENT` variants. A pure surface tag (no span
2856/// of its own), like [`LockStrength`].
2857#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2858#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2859#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2860pub enum ForXmlElements {
2861    /// Bare `ELEMENTS` — element-centric XML, NULL columns omitted (the `ABSENT`
2862    /// default MSSQL applies when neither refinement is written).
2863    Plain,
2864    /// `ELEMENTS XSINIL` — emit an empty element with `xsi:nil="true"` for NULLs.
2865    XsiNil,
2866    /// `ELEMENTS ABSENT` — omit the element for NULL columns (explicit spelling of
2867    /// the default).
2868    Absent,
2869}
2870
2871/// The `FOR JSON` serialization mode ([`ForClause::Json`]): `AUTO` or `PATH`. Neither
2872/// takes a name (unlike [`ForXmlMode`]), so a pure surface tag.
2873#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2874#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2875#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2876pub enum ForJsonMode {
2877    /// `AUTO` — nest JSON to reflect the `FROM` join hierarchy.
2878    Auto,
2879    /// `PATH` — dotted column aliases define the JSON object shape.
2880    Path,
2881}
2882
2883/// A `ROOT ['name']` directive shared by [`ForClause::Xml`] and [`ForClause::Json`]:
2884/// the `ROOT` keyword wraps the output in a single root element/property, optionally
2885/// named. Its own spanned node so the `ROOT` keyword span is addressable; the
2886/// optional name is a quoted string [`Literal`].
2887#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2888#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2889#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2890pub struct ForRoot {
2891    /// The `('name')` root name; `None` for a bare `ROOT` (MSSQL then names it `root`).
2892    pub name: Option<Literal>,
2893    /// Source location and node identity.
2894    pub meta: Meta,
2895}
2896
2897/// One Hive/Spark `LATERAL VIEW [OUTER] <generator>(args) <alias> [AS <col> [, …]]`
2898/// clause on a [`Select`] ([`Select::lateral_views`]): a table-generating function
2899/// (`explode`, `posexplode`, `json_tuple`, …) whose output rows are cross-joined
2900/// against each input row, with `OUTER` keeping input rows the generator produces no
2901/// rows for (NULL-padded, like an outer join).
2902///
2903/// # Parity with sqlparser-rs `LateralView`
2904///
2905/// Mirrors sqlparser-rs's `LateralView` struct (`lateral_view: Expr`,
2906/// `lateral_view_name: ObjectName`, `lateral_col_alias: Vec<Ident>`, `outer: bool`),
2907/// with two deliberate reshapings (ADR-0011's typed canonical shape) and one rename:
2908/// - `lateral_view: Expr` → [`function`](Self::function)`:` [`FunctionCall`] — both
2909///   grammars require a generator *call* here (Hive `FromClauseParser.g` `lateralView:
2910///   … function tableAlias …`; Spark `SqlBaseParser.g4` `lateralView: LATERAL VIEW
2911///   OUTER? qualifiedName '(' expression* ')' …`), so the typed call node is the
2912///   grammar, where sqlparser-rs's arbitrary expression over-admits non-calls.
2913/// - `lateral_view_name: ObjectName` → [`alias`](Self::alias)`:` [`Ident`] — the
2914///   correlation alias is a single identifier in both grammars (Hive `tableAlias`,
2915///   Spark `tblName=identifier`), never a qualified name.
2916/// - `lateral_col_alias` → [`columns`](Self::columns), a [`ThinVec`]`<`[`Ident`]`>`
2917///   (empty when unwritten — column aliases are optional since Hive 0.12).
2918///
2919/// # Acceptance bound (no Hive/Spark oracle)
2920///
2921/// The table alias is required (both grammars make it non-optional) and the `AS`
2922/// before the column list is optional — Spark's grammar spells `AS?` while Hive's
2923/// requires the keyword, so accepting the bare-column spelling under the one atomic
2924/// flag is a known conservative-direction over-acceptance for the Hive preset
2925/// (the [`JoinSyntax::sided_semi_anti_join`](crate::dialect::TableExpressionSyntax)
2926/// precedent, captured on the owning ticket). Rendering canonicalizes the bare
2927/// spelling to `AS` — a structural, not byte-exact, round-trip (the wildcard-modifier
2928/// precedent). Whether the named function is a genuine UDTF is a bind-time check past
2929/// the parse-level contract.
2930///
2931/// # Gating and `LATERAL` disambiguation
2932///
2933/// Gated by [`SelectSyntax::lateral_view_clause`](crate::dialect::SelectSyntax) — on
2934/// for Hive, Databricks, and the permissive Lenient union, off elsewhere; with the
2935/// gate off the `LATERAL` keyword in this position is left unconsumed and surfaces as
2936/// a clean parse error. `LATERAL` also introduces the standard LATERAL derived-table /
2937/// function factor ([`TableFactorSyntax::lateral`](crate::dialect::TableExpressionSyntax)),
2938/// but the two occupy disjoint grammar positions — a table-factor head (after
2939/// `FROM`/`,`/a join keyword) versus after the *whole* FROM list — and additionally
2940/// partition on the follow token (`VIEW` here; `(` or a function/subquery head there),
2941/// so the dispatch is unambiguous under every preset combination, including Lenient
2942/// (which enables both), and needs no
2943/// [`GrammarConflict`](crate::dialect::GrammarConflict) registry entry.
2944#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2945#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2946#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2947pub struct LateralView<X: Extension = NoExt> {
2948    /// `OUTER` — keep input rows the generator returns no rows for, NULL-padding the
2949    /// generated columns (Hive/Spark's outer-join refinement of the default
2950    /// cross-join semantics).
2951    pub outer: bool,
2952    /// The table-generating function call (`explode(col)`); inline, not boxed,
2953    /// because this node lives only behind [`Select::lateral_views`]' heap allocation
2954    /// (the [`RowsFromItem`] precedent).
2955    pub function: FunctionCall<X>,
2956    /// The required correlation alias naming the generated relation (Hive
2957    /// `tableAlias` / Spark `tblName`).
2958    pub alias: Ident,
2959    /// The `[AS] c1, c2` column aliases naming the generator's output columns; empty
2960    /// when unwritten.
2961    pub columns: ThinVec<Ident>,
2962    /// Source location and node identity.
2963    pub meta: Meta,
2964}
2965
2966/// An Oracle-style hierarchical (recursive) query clause on a [`Select`]
2967/// ([`Select::connect_by`]): the `CONNECT BY [NOCYCLE] <condition>` parent/child walk,
2968/// with an optional `START WITH <condition>` seed selecting the root rows. Rows are
2969/// expanded top-down from each root, following the `CONNECT BY` condition, in which the
2970/// [`UnaryOperator::Prior`](crate::ast::UnaryOperator) operator marks the operand taken
2971/// from the parent row.
2972///
2973/// # Grammar position and clause order
2974///
2975/// The clause sits **after `WHERE` and before `GROUP BY`**, Oracle's syntactic position
2976/// for the `hierarchical_query_clause` (Oracle *Database SQL Language Reference*,
2977/// `SELECT` → `hierarchical_query_clause`). `START WITH` and `CONNECT BY` may be written
2978/// in **either order** — Oracle's grammar admits both
2979/// `START WITH <c> CONNECT BY [NOCYCLE] <c>` and
2980/// `CONNECT BY [NOCYCLE] <c> [START WITH <c>]` — so the written order is recorded by
2981/// [`start_with_leads`](Self::start_with_leads) and round-trips exactly (the
2982/// spelling-fidelity doctrine: an ordering both engines accept is a spelling, not a
2983/// normalization). Snowflake, whose public docs are the citable grammar here (there is
2984/// no Oracle preset/oracle), documents only the `START WITH … CONNECT BY …` order and
2985/// the `[PRIOR] col = [PRIOR] col` equality shape (*Snowflake SQL Reference*,
2986/// `CONNECT BY`); it places the pair right after `FROM`, but modelling the Oracle
2987/// after-`WHERE` position is the strict superset that also accepts the Snowflake
2988/// `FROM … START WITH … CONNECT BY … [WHERE]`-less spelling.
2989///
2990/// # Acceptance bound (no Oracle/Snowflake oracle)
2991///
2992/// - **`NOCYCLE`.** Oracle accepts `CONNECT BY NOCYCLE <cond>` (return rows despite a
2993///   `CONNECT BY` loop); Snowflake's docs explicitly do *not* support `NOCYCLE`. The one
2994///   atomic [`connect_by_clause`](crate::dialect::SelectSyntax) gate accepts the wider
2995///   Oracle bound, a documented conservative-direction over-acceptance under the
2996///   Snowflake preset (the [`LateralView`] `AS`-optional precedent), captured on the
2997///   owning ticket.
2998/// - **`PRIOR`.** Both engines require exactly one `PRIOR` per `CONNECT BY` equality;
2999///   this parser admits it as an ordinary unary operator anywhere in the condition and
3000///   does not enforce the once-per-conjunct rule (a bind-time check past the parse-level
3001///   contract). `PRIOR` is recognized **only inside the `CONNECT BY` condition**, so the
3002///   global expression grammar is unchanged and a bare `prior` stays an ordinary column
3003///   name everywhere else.
3004#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3005#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3006#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3007pub struct HierarchicalClause<X: Extension = NoExt> {
3008    /// The optional `START WITH <condition>` root-row seed; `None` when the clause
3009    /// writes only `CONNECT BY`. Inline (not boxed) because this node lives only behind
3010    /// [`Select::connect_by`]' heap allocation (the [`LateralView`] precedent).
3011    pub start_with: Option<Expr<X>>,
3012    /// `NOCYCLE` — return rows even when the `CONNECT BY` walk hits a loop (Oracle; not
3013    /// Snowflake — see the acceptance-bound doc). Always modifies `CONNECT BY`,
3014    /// whichever order the pair is written in.
3015    pub nocycle: bool,
3016    /// The required `CONNECT BY [NOCYCLE] <condition>` parent/child predicate. Ordinary
3017    /// expression riding the guarded expression path, with
3018    /// [`UnaryOperator::Prior`](crate::ast::UnaryOperator) recognized inside it.
3019    pub connect_by: Expr<X>,
3020    /// `true` when `START WITH` was written **before** `CONNECT BY`, `false` when after
3021    /// (or absent — canonically `false` when [`start_with`](Self::start_with) is `None`,
3022    /// where the order is moot). Preserves the written order for an exact round-trip.
3023    pub start_with_leads: bool,
3024    /// Source location and node identity.
3025    pub meta: Meta,
3026}