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` — column metadata of the target.
1926 Describe,
1927 /// `DESC` — the short spelling of DuckDB's `DESCRIBE` utility.
1928 Desc,
1929 /// `SHOW` — the unqualified list forms (`SHOW databases`, `SHOW tables`) and
1930 /// `SHOW <table>`.
1931 Show,
1932 /// `SUMMARIZE` — summary statistics of the target.
1933 Summarize,
1934}
1935
1936#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1937#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1938#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1939/// The SQL show ref target forms represented by the AST.
1940pub enum ShowRefTarget<X: Extension = NoExt> {
1941 /// Bare `DESCRIBE` or `DESC`, which DuckDB parses before rejecting at a later semantic
1942 /// stage.
1943 Empty {
1944 /// Source location and node identity.
1945 meta: Meta,
1946 },
1947 /// `DESCRIBE <query>` / `SUMMARIZE <query>` — the described query (a `SELECT`,
1948 /// `PIVOT`, or `UNPIVOT` body).
1949 Query {
1950 /// Query governed by this node.
1951 query: Box<Query<X>>,
1952 /// Source location and node identity.
1953 meta: Meta,
1954 },
1955 /// `DESCRIBE <table>`, `SHOW <name>` (`SHOW databases`, `SHOW tables`,
1956 /// `SHOW <table>`) — the named target.
1957 Name {
1958 /// Name referenced by this syntax.
1959 name: ObjectName,
1960 /// Source location and node identity.
1961 meta: Meta,
1962 },
1963}
1964
1965/// The payload of a [`TableFactor::JsonTable`] — SQL/JSON `JSON_TABLE` (SQL:2016,
1966/// PostgreSQL's `JsonTable`).
1967///
1968/// The clause vocabulary is shared with the SQL/JSON expression functions: the
1969/// [`context`](Self::context) reuses [`JsonValueExpr`] (`<doc> [FORMAT JSON …]`), the
1970/// [`passing`](Self::passing) list reuses [`JsonPassingArg`], and the top-level
1971/// [`on_error`](Self::on_error) reuses [`JsonBehavior`] — no parallel copies. The row
1972/// [`path`](Self::path) and every column path are restricted to *string literals* at parse
1973/// (PostgreSQL: "only string constants are supported in JSON_TABLE path" — a bare column or
1974/// operator rejects), so they hold a string-literal [`Expr`].
1975#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1976#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1977#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1978pub struct JsonTable<X: Extension = NoExt> {
1979 /// Whether the lateral form was present in the source.
1980 pub lateral: bool,
1981 /// The JSON document value; see [`JsonValueExpr`].
1982 pub context: JsonValueExpr<X>,
1983 /// The row path — a string literal (PostgreSQL restricts it to a string constant).
1984 pub path: Box<Expr<X>>,
1985 /// Optional path name for this syntax.
1986 pub path_name: Option<Ident>,
1987 /// passing in source order.
1988 pub passing: ThinVec<JsonPassingArg<X>>,
1989 /// Non-empty — PostgreSQL rejects `COLUMNS ()`.
1990 pub columns: ThinVec<JsonTableColumn<X>>,
1991 /// The top-level `<behaviour> ON ERROR`; there is no top-level `ON EMPTY`.
1992 pub on_error: Option<JsonBehavior<X>>,
1993 /// Source location and node identity.
1994 pub meta: Meta,
1995}
1996
1997/// One column of a [`JsonTable`] `COLUMNS` specification (PostgreSQL's `JsonTableColumn`,
1998/// tagged by `coltype`).
1999///
2000/// The wrapper/quotes/behaviour clauses reuse the SQL/JSON expression-function nodes. The
2001/// per-kind clause legality is enforced at parse (matching PostgreSQL): only a
2002/// [`Regular`](Self::Regular) column takes `FORMAT`/wrapper/quotes/`ON EMPTY`;
2003/// [`Exists`](Self::Exists) takes only `ON ERROR`; [`Nested`](Self::Nested) takes no
2004/// behaviours and recurses through its own `COLUMNS`.
2005#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2006#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2007#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2008pub enum JsonTableColumn<X: Extension = NoExt> {
2009 /// `<name> FOR ORDINALITY` — a 1-based row-sequence column; no type, no other clauses.
2010 ForOrdinality {
2011 /// Name referenced by this syntax.
2012 name: Ident,
2013 /// Source location and node identity.
2014 meta: Meta,
2015 },
2016 /// `<name> <type> [FORMAT JSON …] [PATH <string>] [wrapper] [quotes] [<b> ON EMPTY]
2017 /// [<b> ON ERROR]` — a value column projecting the JSON at its path.
2018 Regular {
2019 /// Name referenced by this syntax.
2020 name: Ident,
2021 /// Data type named by this syntax.
2022 data_type: Box<DataType<X>>,
2023 /// Optional format for this syntax.
2024 format: Option<JsonFormat>,
2025 /// `PATH <string>` — a string literal; `None` uses the implicit `$.<name>` path.
2026 path: Option<Box<Expr<X>>>,
2027 /// The `WITH`/`WITHOUT WRAPPER` behaviour; see [`JsonWrapperBehavior`].
2028 wrapper: JsonWrapperBehavior,
2029 /// The `KEEP`/`OMIT QUOTES` behaviour; see [`JsonQuotesBehavior`].
2030 quotes: JsonQuotesBehavior,
2031 /// Optional on empty for this syntax.
2032 on_empty: Option<JsonBehavior<X>>,
2033 /// Optional on error for this syntax.
2034 on_error: Option<JsonBehavior<X>>,
2035 /// Source location and node identity.
2036 meta: Meta,
2037 },
2038 /// `<name> <type> EXISTS [PATH <string>] [<b> ON ERROR]` — a boolean-ish column testing
2039 /// whether the path matches; takes neither `FORMAT`/wrapper/quotes nor `ON EMPTY`.
2040 Exists {
2041 /// Name referenced by this syntax.
2042 name: Ident,
2043 /// Data type named by this syntax.
2044 data_type: Box<DataType<X>>,
2045 /// Optional path for this syntax.
2046 path: Option<Box<Expr<X>>>,
2047 /// Optional on error for this syntax.
2048 on_error: Option<JsonBehavior<X>>,
2049 /// Source location and node identity.
2050 meta: Meta,
2051 },
2052 /// `NESTED [PATH] <string> [AS <name>] COLUMNS ( … )` — a sub-table joined against a
2053 /// nested path, recursing through its own column list (PostgreSQL requires the nested
2054 /// `COLUMNS` to be present and non-empty).
2055 Nested {
2056 /// The nested JSON path (a string literal).
2057 path: Box<Expr<X>>,
2058 /// Optional path name for this syntax.
2059 path_name: Option<Ident>,
2060 /// Columns in source order.
2061 columns: ThinVec<JsonTableColumn<X>>,
2062 /// Source location and node identity.
2063 meta: Meta,
2064 },
2065}
2066
2067/// The payload of a [`TableFactor::XmlTable`] — SQL/XML `XMLTABLE` (SQL:2006, PostgreSQL's
2068/// `RangeTableFunc`).
2069///
2070/// The [`passing_mechanism_before`](Self::passing_mechanism_before)/`_after` reuse the
2071/// [`XmlPassingMechanism`] of `xmlexists` (`PASSING [BY REF|VALUE] doc [BY REF|VALUE]`);
2072/// PostgreSQL admits a mechanism on either side and normalizes it away, so it is preserved
2073/// only for round-trip fidelity. The [`row_expr`](Self::row_expr) and
2074/// [`document`](Self::document) are `c_expr` operands (a bare `a || b` rejects; parenthesize
2075/// to re-admit a full expression), matching the engine.
2076#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2077#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2078#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2079pub struct XmlTable<X: Extension = NoExt> {
2080 /// Whether the lateral form was present in the source.
2081 pub lateral: bool,
2082 /// namespaces in source order.
2083 pub namespaces: ThinVec<XmlNamespace<X>>,
2084 /// The row-generating XPath (a `c_expr`).
2085 pub row_expr: Box<Expr<X>>,
2086 /// The `PASSING` document (a `c_expr`).
2087 pub document: Box<Expr<X>>,
2088 /// Optional passing mechanism before for this syntax.
2089 pub passing_mechanism_before: Option<XmlPassingMechanism>,
2090 /// Optional passing mechanism after for this syntax.
2091 pub passing_mechanism_after: Option<XmlPassingMechanism>,
2092 /// Columns in source order.
2093 pub columns: ThinVec<XmlTableColumn<X>>,
2094 /// Source location and node identity.
2095 pub meta: Meta,
2096}
2097
2098/// One `XMLNAMESPACES` declaration inside an [`XmlTable`]: `<uri> AS <name>` or
2099/// `DEFAULT <uri>`.
2100#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2101#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2102#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2103pub struct XmlNamespace<X: Extension = NoExt> {
2104 /// The namespace URI expression.
2105 pub uri: Box<Expr<X>>,
2106 /// `None` for the `DEFAULT <uri>` (unnamed) form.
2107 pub name: Option<Ident>,
2108 /// Source location and node identity.
2109 pub meta: Meta,
2110}
2111
2112/// One column of an [`XmlTable`] `COLUMNS` specification (PostgreSQL's `RangeTableFuncCol`).
2113///
2114/// The regular-column options (`PATH`/`DEFAULT`/`NULL`/`NOT NULL`) are order-free at parse —
2115/// PostgreSQL normalizes them into fixed node fields — so they are stored positionally here
2116/// and re-rendered in canonical order. PostgreSQL rejects a repeated `PATH`/`DEFAULT` and a
2117/// conflicting/redundant `NULL`/`NOT NULL` at parse, which the parser reproduces.
2118#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2119#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2120#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2121pub enum XmlTableColumn<X: Extension = NoExt> {
2122 /// `<name> FOR ORDINALITY` — a 1-based row-sequence column; no type or options.
2123 ForOrdinality {
2124 /// Name referenced by this syntax.
2125 name: Ident,
2126 /// Source location and node identity.
2127 meta: Meta,
2128 },
2129 /// `<name> <type> [PATH <b_expr>] [DEFAULT <b_expr>] [NULL | NOT NULL]`.
2130 Regular {
2131 /// Name referenced by this syntax.
2132 name: Ident,
2133 /// Data type named by this syntax.
2134 data_type: Box<DataType<X>>,
2135 /// Optional path for this syntax.
2136 path: Option<Box<Expr<X>>>,
2137 /// Optional default for this syntax.
2138 default: Option<Box<Expr<X>>>,
2139 /// `Some(true)` for `NOT NULL`, `Some(false)` for `NULL`, `None` when unwritten.
2140 not_null: Option<bool>,
2141 /// Source location and node identity.
2142 meta: Meta,
2143 },
2144}
2145
2146/// The payload of a [`TableFactor::OpenJson`] — SQL Server's `OPENJSON` rowset function
2147/// (sqlparser-rs's `TableFactor::OpenJsonTable`).
2148///
2149/// Reshaped from sqlparser-rs per ADR-0011: its `json_expr: Expr` / `json_path: Option<Value>` /
2150/// per-column `path: Option<String>` become span-bearing [`Expr`] holders here — the row
2151/// [`path`](Self::path) and every column path are *string literals* (matching JSON_TABLE's
2152/// [`JsonTable::path`](JsonTable::path)), so they round-trip from their spans — and the column
2153/// list is a [`ThinVec`]. An absent `WITH` clause is the empty [`columns`](Self::columns) (MSSQL
2154/// rejects an empty `WITH ()`, so a present clause is always non-empty); the default
2155/// `key`/`value`/`type` schema then applies.
2156#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2157#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2158#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2159pub struct OpenJson<X: Extension = NoExt> {
2160 /// The JSON source expression (a column, variable, or literal), evaluated to a JSON
2161 /// string. Unrestricted — any [`Expr`], unlike the string-literal-only paths.
2162 pub json_expr: Box<Expr<X>>,
2163 /// The optional `, <path>` second argument — a string-literal JSON path selecting the
2164 /// array/object to iterate; `None` iterates the root value.
2165 pub path: Option<Box<Expr<X>>>,
2166 /// The `WITH (…)` explicit column schema; empty when the clause is absent.
2167 pub columns: ThinVec<OpenJsonColumn<X>>,
2168 /// Source location and node identity.
2169 pub meta: Meta,
2170}
2171
2172/// One column of an [`OpenJson`] `WITH (…)` schema — `<name> <type> [<path>] [AS JSON]`
2173/// (sqlparser-rs's `OpenJsonTableColumn`).
2174#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2175#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2176#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2177pub struct OpenJsonColumn<X: Extension = NoExt> {
2178 /// Name referenced by this syntax.
2179 pub name: Ident,
2180 /// Data type named by this syntax.
2181 pub data_type: Box<DataType<X>>,
2182 /// The optional `<column_path>` string literal; `None` uses the implicit `$.<name>` path.
2183 pub path: Option<Box<Expr<X>>>,
2184 /// The `AS JSON` marker — the column holds nested JSON (MSSQL requires an
2185 /// `nvarchar(max)` type).
2186 pub as_json: bool,
2187 /// Source location and node identity.
2188 pub meta: Meta,
2189}
2190
2191#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2192#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2193#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2194/// An SQL join.
2195pub struct Join<X: Extension = NoExt> {
2196 /// The right-hand table factor being joined.
2197 pub relation: TableFactor<X>,
2198 /// The join operator — side and constraint; see [`JoinOperator`].
2199 pub operator: JoinOperator<X>,
2200 /// Source location and node identity.
2201 pub meta: Meta,
2202}
2203
2204#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2205#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2206#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2207/// The SQL join operator forms represented by the AST.
2208pub enum JoinOperator<X: Extension = NoExt> {
2209 /// An `[INNER] JOIN` — keeps only row pairs that satisfy the constraint.
2210 Inner {
2211 /// MySQL `STRAIGHT_JOIN`: an inner join that additionally forces the
2212 /// optimizer to read the left table before the right. It is semantically a
2213 /// plain `INNER JOIN`, so it is the canonical inner-join shape
2214 /// carrying this surface tag (a join-order hint) rather than a new operator
2215 /// variant — mirroring how [`SetQuantifier::All`] preserves an explicit `ALL`.
2216 /// `false` is a bare `[INNER] JOIN`; only MySQL parses the `true` spelling
2217 /// (gated by [`JoinSyntax::straight_join`](crate::dialect::TableExpressionSyntax)).
2218 straight: bool,
2219 /// Whether the redundant `INNER` keyword was written (`INNER JOIN` vs a bare
2220 /// `JOIN` — the two are exact synonyms). A source-fidelity render replays it; a
2221 /// target re-spell and the redacted fingerprint drop it. Always `false` under
2222 /// `straight` (`STRAIGHT_JOIN` is its own keyword, never spelled `INNER`).
2223 inner: bool,
2224 /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2225 constraint: JoinConstraint<X>,
2226 /// Source location and node identity.
2227 meta: Meta,
2228 },
2229 /// A `LEFT [OUTER] JOIN` — keeps every left row, NULL-padding unmatched right columns.
2230 LeftOuter {
2231 /// Whether the redundant `OUTER` keyword was written (`LEFT OUTER JOIN` vs a
2232 /// bare `LEFT JOIN`). Fidelity only, like [`Inner::inner`](Self::Inner::inner).
2233 outer: bool,
2234 /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2235 constraint: JoinConstraint<X>,
2236 /// Source location and node identity.
2237 meta: Meta,
2238 },
2239 /// A `RIGHT [OUTER] JOIN` — keeps every right row, NULL-padding unmatched left columns.
2240 RightOuter {
2241 /// Whether the redundant `OUTER` keyword was written (`RIGHT OUTER JOIN`).
2242 outer: bool,
2243 /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2244 constraint: JoinConstraint<X>,
2245 /// Source location and node identity.
2246 meta: Meta,
2247 },
2248 /// A `FULL [OUTER] JOIN` — keeps all rows from both sides, NULL-padding non-matches.
2249 FullOuter {
2250 /// Whether the redundant `OUTER` keyword was written (`FULL OUTER JOIN`).
2251 outer: bool,
2252 /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2253 constraint: JoinConstraint<X>,
2254 /// Source location and node identity.
2255 meta: Meta,
2256 },
2257 /// DuckDB `ASOF [INNER|LEFT|RIGHT|FULL [OUTER]] JOIN`: an inexact-match temporal
2258 /// join pairing each left row with the nearest right row under the `ON`
2259 /// inequality. A new operator (nearest-match semantics), not a spelling of the
2260 /// side joins — DuckDB serializes it as an orthogonal `ref_type: ASOF` on top of
2261 /// the `join_type` side, mirrored here as [`kind`](Self::AsOf::kind). The engine
2262 /// *parse*-requires an `ON`/`USING` constraint (a bare `ASOF JOIN` is a syntax
2263 /// error), so the parser never builds [`JoinConstraint::None`] here; the
2264 /// inequality requirement itself is bind-time (`ASOF JOIN … ON a = b` parses,
2265 /// then fails DuckDB's binder), so an equality constraint still parses. Gated by
2266 /// [`JoinSyntax::asof_join`](crate::dialect::TableExpressionSyntax).
2267 AsOf {
2268 /// Which side the `ASOF` join keeps; see [`AsOfJoinKind`].
2269 kind: AsOfJoinKind,
2270 /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2271 constraint: JoinConstraint<X>,
2272 /// Source location and node identity.
2273 meta: Meta,
2274 },
2275 /// A `CROSS JOIN` — the unconstrained Cartesian product.
2276 Cross {
2277 /// Source location and node identity.
2278 meta: Meta,
2279 },
2280 /// DuckDB `POSITIONAL JOIN`: pairs rows by position (first with first, …). Like
2281 /// [`Cross`](Self::Cross) it never carries a constraint — the engine
2282 /// parse-rejects a trailing `ON`/`USING` and any side keyword (`POSITIONAL LEFT
2283 /// JOIN` is a syntax error) — so the variant has no constraint or kind field.
2284 /// Gated by
2285 /// [`JoinSyntax::positional_join`](crate::dialect::TableExpressionSyntax).
2286 Positional {
2287 /// Source location and node identity.
2288 meta: Meta,
2289 },
2290 /// DuckDB `[ASOF|NATURAL] SEMI JOIN`: a semi-join — keeps each left row that has
2291 /// at least one right match, projecting left columns only. DuckDB serializes it as
2292 /// a `join_type: SEMI` (engine-verified on 1.5.4), *mutually exclusive* with the
2293 /// `INNER`/`LEFT`/`RIGHT`/`FULL` sides (`LEFT SEMI JOIN` is a syntax error), so it
2294 /// is a new operator rather than a side spelling. It composes only with the
2295 /// `REGULAR`, `NATURAL` (`NATURAL SEMI JOIN`, carried as
2296 /// [`JoinConstraint::Natural`]) and `ASOF` (`ASOF SEMI JOIN`,
2297 /// [`asof`](Self::Semi::asof) `= true`) ref-types — never a side, `CROSS`, or
2298 /// `POSITIONAL`. Like [`AsOf`](Self::AsOf) it *parse*-requires an `ON`/`USING`
2299 /// constraint (a bare `SEMI JOIN` is a syntax error) unless `NATURAL` supplies the
2300 /// match, so the parser never builds [`JoinConstraint::None`] here. `ASOF` and
2301 /// `NATURAL` never co-occur (both engine parse-rejected), so `asof: true` always
2302 /// carries an `ON`/`USING` constraint.
2303 ///
2304 /// The [`side`](Self::Semi::side) axis records the Spark/Hive/Databricks *sided*
2305 /// spelling — `LEFT SEMI JOIN` / `RIGHT SEMI JOIN` — as one operator with DuckDB's
2306 /// side-less `SEMI JOIN` rather than a separate variant (the
2307 /// [`AsOfJoinKind`]/[`ApplyKind`] axis precedent): all three are the same semi-join,
2308 /// differing only in whether an explicit side keyword is written and which side's
2309 /// rows are tested. The two spellings come from different engine families and are
2310 /// gated apart — DuckDB's side-less form by
2311 /// [`JoinSyntax::semi_anti_join`](crate::dialect::TableExpressionSyntax),
2312 /// the sided form by
2313 /// [`JoinSyntax::sided_semi_anti_join`](crate::dialect::TableExpressionSyntax)
2314 /// (DuckDB engine-parse-rejects `LEFT SEMI JOIN`). The two axes are mutually
2315 /// exclusive: [`SemiAntiSide::Left`]/[`Right`](SemiAntiSide::Right) never carry the
2316 /// DuckDB-only `ASOF`/`NATURAL` compositions, so a sided operator always has
2317 /// `asof: false` and an `ON`/`USING` constraint (Spark requires the qualifier).
2318 Semi {
2319 /// Whether the asof form was present in the source.
2320 asof: bool,
2321 /// Which sided spelling (`LEFT`/`RIGHT`/side-less); see [`SemiAntiSide`].
2322 side: SemiAntiSide,
2323 /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2324 constraint: JoinConstraint<X>,
2325 /// Source location and node identity.
2326 meta: Meta,
2327 },
2328 /// DuckDB `[ASOF|NATURAL] ANTI JOIN` / Spark `[LEFT|RIGHT] ANTI JOIN`: an anti-join —
2329 /// keeps each left row with *no* right match. The [`Semi`](Self::Semi) counterpart:
2330 /// identical grammar (DuckDB serializes `join_type: ANTI`) with the opposite
2331 /// membership test, so see [`Semi`](Self::Semi) for the composition, constraint,
2332 /// `asof`-flag, and [`side`](Self::Anti::side) rules and the two gates.
2333 Anti {
2334 /// Whether the asof form was present in the source.
2335 asof: bool,
2336 /// Which sided spelling (`LEFT`/`RIGHT`/side-less); see [`SemiAntiSide`].
2337 side: SemiAntiSide,
2338 /// The join condition (`ON`/`USING`/`NATURAL`/none); see [`JoinConstraint`].
2339 constraint: JoinConstraint<X>,
2340 /// Source location and node identity.
2341 meta: Meta,
2342 },
2343 /// MSSQL `CROSS APPLY` / `OUTER APPLY`: an implicitly-correlated (lateral) join
2344 /// whose right operand — a derived table `(SELECT …)` or a table-valued function
2345 /// call — may reference columns of the left source. Like [`Cross`](Self::Cross) it
2346 /// never carries an `ON`/`USING` constraint (the correlation is positional, in the
2347 /// right operand's own references), so the variant holds no constraint. The
2348 /// [`kind`](Self::Apply::kind) axis is the `CROSS`/`OUTER` flavour — inner-style vs
2349 /// left-style row preservation — mirroring how [`AsOf`](Self::AsOf) records its side
2350 /// on a `kind` rather than splitting into per-spelling variants: `CROSS APPLY` and
2351 /// `OUTER APPLY` are one grammar production differing only by that keyword. Gated by
2352 /// [`JoinSyntax::apply_join`](crate::dialect::TableExpressionSyntax).
2353 Apply {
2354 /// The `CROSS`/`OUTER` apply flavour; see [`ApplyKind`].
2355 kind: ApplyKind,
2356 /// Source location and node identity.
2357 meta: Meta,
2358 },
2359}
2360
2361/// The flavour of a MSSQL [`JoinOperator::Apply`] operator.
2362///
2363/// `CROSS`/`OUTER` are the two spellings of the single `APPLY` grammar production
2364/// (a lateral join over a right table factor), differing only in row preservation —
2365/// `CROSS` drops left rows whose right operand is empty, `OUTER` keeps them with
2366/// nulls — so they are one operator with a two-value axis, not two operators (the
2367/// [`AsOfJoinKind`] precedent). Only `CROSS` is wired into the parser today; `OUTER`
2368/// is the sibling `planner-parity-join-outer-apply` extension, and the enum carries
2369/// it so that landing is a parser-only change with no AST/render churn.
2370#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2371#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2372#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2373pub enum ApplyKind {
2374 /// `CROSS APPLY` — evaluate the right side per left row, like a `LATERAL` inner join.
2375 Cross,
2376 /// `OUTER APPLY` — like `CROSS APPLY` but keeps left rows with no right match (`LATERAL` left join).
2377 Outer,
2378}
2379
2380/// The side spelling of a [`JoinOperator::Semi`]/[`JoinOperator::Anti`] operator.
2381///
2382/// DuckDB spells the semi-/anti-join side-*less* (`SEMI JOIN` / `ANTI JOIN`, a
2383/// left-semi/left-anti by definition) and composes it with the `NATURAL`/`ASOF`
2384/// ref-types; Spark/Hive/Databricks instead *require* an explicit side keyword
2385/// (`LEFT SEMI JOIN`, `RIGHT ANTI JOIN`) and never compose with those ref-types. The
2386/// two are the same operator differing only in this surface axis, so — like
2387/// [`ApplyKind`]/[`AsOfJoinKind`] — the side rides a `kind`-style axis rather than
2388/// splitting into per-spelling variants.
2389///
2390/// [`Sideless`](Self::Sideless) is the DuckDB spelling (its `asof` flag may then be
2391/// set). [`Left`](Self::Left) is Spark's `LEFT` — semantically the same left-semi as
2392/// [`Sideless`](Self::Sideless), differing only in whether the keyword is written;
2393/// [`Right`](Self::Right) is the mirror `RIGHT` (right-row test), genuinely distinct
2394/// semantics. A sided value always carries `asof: false` and an `ON`/`USING`
2395/// constraint (Spark requires the qualifier and has no `ASOF`/`NATURAL`).
2396///
2397/// Only [`Sideless`](Self::Sideless) and [`Left`](Self::Left) are wired into the
2398/// parser today; [`Right`](Self::Right) — and the [`Anti`](JoinOperator::Anti)
2399/// pairing of both sides — ships here so the sibling `RIGHT SEMI` / `LEFT ANTI` /
2400/// `RIGHT ANTI` tickets land as parser-only changes with no AST/render churn (the
2401/// [`ApplyKind::Outer`] precedent).
2402#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2403#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2404#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2405pub enum SemiAntiSide {
2406 /// No side keyword — a plain `SEMI`/`ANTI` join.
2407 Sideless,
2408 /// `LEFT SEMI`/`LEFT ANTI`.
2409 Left,
2410 /// `RIGHT SEMI`/`RIGHT ANTI`.
2411 Right,
2412}
2413
2414/// The side of a DuckDB [`JoinOperator::AsOf`] join.
2415///
2416/// `ASOF` composes with all four standard sides (engine-verified on DuckDB 1.5.4,
2417/// including the `OUTER` spellings) but not with `NATURAL`/`CROSS`, so this is a
2418/// dedicated four-side kind rather than a nested [`JoinOperator`]. `Inner` covers
2419/// both the bare `ASOF JOIN` and the explicit `ASOF INNER JOIN` spelling (the
2420/// canonical shape records the side, not the spelling, like the side joins).
2421#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2422#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2423#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2424pub enum AsOfJoinKind {
2425 /// `ASOF [INNER] JOIN`.
2426 Inner,
2427 /// `ASOF LEFT JOIN`.
2428 Left,
2429 /// `ASOF RIGHT JOIN`.
2430 Right,
2431 /// `ASOF FULL JOIN`.
2432 Full,
2433}
2434
2435#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2436#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2437#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2438/// The SQL join constraint forms represented by the AST.
2439pub enum JoinConstraint<X: Extension = NoExt> {
2440 /// An `ON <predicate>` join condition.
2441 On {
2442 /// Expression evaluated by this syntax.
2443 expr: Expr<X>,
2444 /// Source location and node identity.
2445 meta: Meta,
2446 },
2447 /// A `USING (col, …)` join condition — equate the named common columns.
2448 Using {
2449 /// Columns in source order.
2450 columns: ThinVec<Ident>,
2451 /// Alias assigned by this syntax.
2452 alias: Option<Ident>,
2453 /// Source location and node identity.
2454 meta: Meta,
2455 },
2456 /// A `NATURAL` join — an implicit equi-join on all same-named columns.
2457 Natural {
2458 /// Source location and node identity.
2459 meta: Meta,
2460 },
2461 /// No join constraint (a `CROSS JOIN` or comma join).
2462 None {
2463 /// Source location and node identity.
2464 meta: Meta,
2465 },
2466}
2467
2468#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2469#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2470#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2471/// An SQL order by expr.
2472pub struct OrderByExpr<X: Extension = NoExt> {
2473 /// Expression evaluated by this syntax.
2474 pub expr: Expr<X>,
2475 /// Whether the asc form was present in the source.
2476 pub asc: Option<bool>,
2477 /// PostgreSQL `USING <operator>` sort form (`gram.y` `sortby: a_expr USING
2478 /// qual_all_Op opt_nulls_order`): sort by a named ordering operator instead of
2479 /// `ASC`/`DESC`. Mutually exclusive with [`asc`](Self::asc), which stays `None`
2480 /// when this is `Some`; boxed since it is a rare tail on a common sort key.
2481 pub using: Option<Box<OrderByUsing>>,
2482 /// Whether the nulls first form was present in the source.
2483 pub nulls_first: Option<bool>,
2484 /// Source location and node identity.
2485 pub meta: Meta,
2486}
2487
2488/// The `USING <qual_all_Op>` operator of a PostgreSQL `ORDER BY` sort key.
2489///
2490/// `qual_all_Op` is a possibly schema-qualified operator: the bare `USING <` form
2491/// carries no [`schema`](Self::schema) node at all, while `USING
2492/// OPERATOR(pg_catalog.<)` records the qualification. Modelled like the operator
2493/// half of [`NamedOperatorExpr`](super::NamedOperatorExpr): the operator
2494/// is always symbolic, never a word, so it is a bare interned [`Symbol`]. `schema`
2495/// is `Option` rather than an empty [`ObjectName`] because every present node must
2496/// carry a real source span (the span-containment walker enforces it) — an empty
2497/// name would have none.
2498#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2499#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2500#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2501pub struct OrderByUsing {
2502 /// Schema qualification (`pg_catalog` in `OPERATOR(pg_catalog.<)`); `None` for
2503 /// the bare `USING <` form.
2504 pub schema: Option<ObjectName>,
2505 /// The operator symbol spelling (`<`, `~<~`), interned exact-case so it
2506 /// round-trips.
2507 pub op: Symbol,
2508 /// Source location and node identity.
2509 pub meta: Meta,
2510}
2511
2512/// DuckDB's `ORDER BY ALL [ASC | DESC] [NULLS FIRST | LAST]` clause mode: sort by
2513/// every projection column, left to right.
2514///
2515/// A first-class marker node, not an [`OrderByExpr`] whose expression spells `ALL`:
2516/// the sort keys are resolved at bind time from the projection, so there is no
2517/// expression to carry, and DuckDB's own tree corroborates the framing (it
2518/// serializes the clause as a single order whose expression is the `COLUMNS(*)`
2519/// star node — a whole-projection expansion, not a column named `all`). The
2520/// direction and nulls modifiers ride the clause exactly as they ride an ordinary
2521/// sort key (`ORDER BY ALL DESC NULLS LAST` is valid; probed on 1.5.4), hence the
2522/// same `asc`/`nulls_first` surface as [`OrderByExpr`] — but no `USING` (DuckDB
2523/// rejects `ORDER BY ALL USING <`). Carries no [`Expr`], so it is not generic over
2524/// the extension parameter.
2525#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2526#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2527#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2528pub struct OrderByAll {
2529 /// `ASC` (`Some(true)`) / `DESC` (`Some(false)`), or `None` when unwritten —
2530 /// recording exactly what the source said, mirroring [`OrderByExpr::asc`].
2531 pub asc: Option<bool>,
2532 /// `NULLS FIRST` (`Some(true)`) / `NULLS LAST` (`Some(false)`), or `None` when
2533 /// unwritten, mirroring [`OrderByExpr::nulls_first`].
2534 pub nulls_first: Option<bool>,
2535 /// Source location and node identity.
2536 pub meta: Meta,
2537}
2538
2539/// Canonical LIMIT/OFFSET node plus original surface spelling.
2540#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2541#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2542#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2543pub struct Limit<X: Extension = NoExt> {
2544 /// Row limit applied to the result.
2545 pub limit: Option<Expr<X>>,
2546 /// Row offset applied before returning results.
2547 pub offset: Option<Expr<X>>,
2548 /// Source spelling used for the syntax.
2549 pub syntax: LimitSyntax,
2550 /// Whether a written `FETCH { FIRST | NEXT } ...` tail chose `WITH TIES`
2551 /// (rows tying the last row's `ORDER BY` key are also returned) over the
2552 /// default `ONLY`, or `None` when no `FETCH` clause was written at all.
2553 ///
2554 /// A third state is load-bearing here, not just `bool`: the SQL:2008
2555 /// `OFFSET ... ROWS` spelling ([`LimitSyntax::FetchFirst`]) admits a `FETCH`
2556 /// tail whose row count is itself optional (`FETCH FIRST ROWS ONLY`,
2557 /// PostgreSQL defaults it to 1), so `limit: None` alone cannot tell "no
2558 /// `FETCH` clause" (`OFFSET 5 ROWS`, unbounded) apart from "`FETCH` written
2559 /// with no count" (`OFFSET 5 ROWS FETCH FIRST ROWS ONLY`, bounded to 1) —
2560 /// two different result sets. `with_ties` disambiguates them instead: `None`
2561 /// is the former, `Some(_)` the latter. Always `None` under
2562 /// [`LimitSyntax::LimitOffset`], which has no `FETCH` tail at all.
2563 pub with_ties: Option<bool>,
2564 /// DuckDB's percentage row limit: the count is a *fraction of rows* rather than a
2565 /// row number (`LIMIT 40 PERCENT`, `LIMIT 35%` — return 40%/35% of the result).
2566 /// `None` is the ordinary row-count limit; `Some(_)` records which surface spelling
2567 /// wrote the marker so the renderer round-trips it (the [`LimitSyntax`]
2568 /// precedent). Only meaningful with a written [`limit`](Self::limit) count under
2569 /// [`LimitSyntax::LimitOffset`]; gated to DuckDB via
2570 /// [`QueryTailSyntax::limit_percent`](crate::dialect::SelectSyntax).
2571 pub percent: Option<LimitPercent>,
2572 /// Surface spelling of a written `FETCH { FIRST | NEXT } … { ROW | ROWS }` tail:
2573 /// which of the interchangeable `FIRST`/`NEXT` and `ROW`/`ROWS` synonyms the source
2574 /// wrote, so a source-fidelity render replays them. Meaningful only under
2575 /// [`LimitSyntax::FetchFirst`] with a written `FETCH` (`with_ties` is `Some`); the
2576 /// canonical render (and a target re-spell / the redacted fingerprint) emit the
2577 /// canonical `FETCH FIRST … ROWS`. One byte, so it rides the struct's existing
2578 /// padding — the leaner axis-per-field alternative crossed an alignment word.
2579 pub fetch_spelling: FetchSpelling,
2580 /// Source location and node identity.
2581 pub meta: Meta,
2582}
2583
2584/// Surface spelling of a written `FETCH { FIRST | NEXT } … { ROW | ROWS }` tail
2585/// ([`Limit::fetch_spelling`]).
2586///
2587/// `FIRST`/`NEXT` and `ROW`/`ROWS` are interchangeable noise words; the canonical AST
2588/// keeps one shape and this tag records the written pair so a source-fidelity render
2589/// replays it. The two axes are folded onto one 1-byte enum (rather than two `bool`
2590/// fields) so the tag rides [`Limit`]'s existing padding instead of growing the node
2591/// (ADR-0007). A fidelity tag — a target re-spell and the redacted fingerprint emit the
2592/// canonical [`FirstRows`](Self::FirstRows).
2593#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
2594#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2595#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2596pub enum FetchSpelling {
2597 /// The canonical `FETCH FIRST … ROWS`.
2598 #[default]
2599 FirstRows,
2600 /// `FETCH FIRST … ROW` (singular row word).
2601 FirstRow,
2602 /// `FETCH NEXT … ROWS`.
2603 NextRows,
2604 /// `FETCH NEXT … ROW`.
2605 NextRow,
2606}
2607
2608impl FetchSpelling {
2609 /// Build the tag from the two written-spelling axes: `next` selects `NEXT` over
2610 /// `FIRST`, `row_singular` the singular `ROW` over `ROWS`.
2611 pub fn from_axes(next: bool, row_singular: bool) -> Self {
2612 match (next, row_singular) {
2613 (false, false) => Self::FirstRows,
2614 (false, true) => Self::FirstRow,
2615 (true, false) => Self::NextRows,
2616 (true, true) => Self::NextRow,
2617 }
2618 }
2619
2620 /// The written keyword: `"FETCH NEXT"` or the canonical `"FETCH FIRST"`.
2621 pub fn fetch_keyword(self) -> &'static str {
2622 match self {
2623 Self::NextRows | Self::NextRow => "FETCH NEXT",
2624 Self::FirstRows | Self::FirstRow => "FETCH FIRST",
2625 }
2626 }
2627
2628 /// The written row word (with surrounding spaces): `" ROW "` or the canonical
2629 /// `" ROWS "`.
2630 pub fn row_word(self) -> &'static str {
2631 match self {
2632 Self::FirstRow | Self::NextRow => " ROW ",
2633 Self::FirstRows | Self::NextRows => " ROWS ",
2634 }
2635 }
2636}
2637
2638/// Surface syntax used to write a canonical [`Limit`].
2639#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2640#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2641#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2642pub enum LimitSyntax {
2643 /// Source used the `LIMIT OFFSET` spelling.
2644 LimitOffset,
2645 /// MySQL/MariaDB/SQLite `LIMIT <offset>, <count>` — the comma spelling of
2646 /// `LIMIT <count> OFFSET <offset>` (the offset binds first, the count second). The
2647 /// same row limit, folded onto the canonical [`Limit`] shape; this variant records
2648 /// the comma spelling so a source-fidelity render replays `LIMIT <offset>, <count>`
2649 /// while a target re-spell and the redacted fingerprint emit the canonical
2650 /// `LIMIT <count> OFFSET <offset>`.
2651 CommaOffset,
2652 /// Source used the `FETCH FIRST` spelling.
2653 FetchFirst,
2654}
2655
2656/// Surface spelling of a DuckDB percentage-limit marker ([`Limit::percent`]).
2657///
2658/// One percent semantic, two spellings kept as data (mirroring
2659/// [`LimitSyntax`]/[`RollupSpelling`]): the `%` operator (`LIMIT 35%`) and the
2660/// `PERCENT` keyword (`LIMIT 40 PERCENT`). The two are interchangeable in DuckDB —
2661/// both return the same fraction of rows — so canonicalizing them onto one node keeps
2662/// the differential oracle comparing one shape; the tag exists only so rendering
2663/// reproduces the written marker rather than normalizing every form to one spelling.
2664#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2665#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2666#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2667pub enum LimitPercent {
2668 /// The `%` operator spelling: `LIMIT 35%` (the whitespace-insensitive `LIMIT 20 %`
2669 /// canonicalizes onto this — the space before `%` is not a distinct spelling).
2670 Symbol,
2671 /// The `PERCENT` keyword spelling: `LIMIT 40 PERCENT`.
2672 Keyword,
2673}
2674
2675/// ClickHouse `LIMIT n [OFFSET m] BY expr, …` — per-group row limiting.
2676///
2677/// Keeps the first `n` rows for each distinct value of the `by` expression list
2678/// (with an optional `OFFSET m` skip *within* each group), a wholly different
2679/// operation from the ordinary [`Limit`] tail that bounds the result as a whole. A
2680/// query may carry **both**, in that order: `SELECT … ORDER BY … LIMIT 2 BY x LIMIT
2681/// 10` limits to two rows per `x`, then caps the whole result at ten. So this is its
2682/// own [`Query::limit_by`] field, never folded onto the `Limit` shape — the two
2683/// clauses coexist and mean different things.
2684///
2685/// Gated by [`QueryTailSyntax::limit_by_clause`](crate::dialect::SelectSyntax); no
2686/// shipped preset spells it but Lenient (the permissive union). The `by` list is
2687/// always non-empty (the `BY` keyword requires at least one expression).
2688#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2689#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2690#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2691pub struct LimitBy<X: Extension = NoExt> {
2692 /// The per-group row count `n`; always written (`LIMIT BY` has no bare form).
2693 pub limit: Expr<X>,
2694 /// The `OFFSET m` skip applied within each group before the `n` rows are kept;
2695 /// `None` when unwritten. Rendered as `OFFSET m`, the canonical spelling.
2696 pub offset: Option<Expr<X>>,
2697 /// The `BY expr, …` grouping expressions; always at least one.
2698 pub by: ThinVec<Expr<X>>,
2699 /// Source location and node identity.
2700 pub meta: Meta,
2701}
2702
2703/// One ClickHouse `SETTINGS` pair: `<name> = <value>` (`max_threads = 8`,
2704/// `join_algorithm = 'auto'`), an element of [`Query::settings`].
2705///
2706/// ClickHouse's grammar is `identifier '=' literal` — the value is a scalar literal
2707/// (number, string, boolean). It is modelled as a general [`Expr`] (the `SecretOption`
2708/// precedent — a `<name> <value>` option whose value is likewise a general expression),
2709/// so the corpus literals round-trip while the wider expression grammar is the recorded
2710/// acceptance bound.
2711#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2712#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2713#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2714pub struct Setting<X: Extension = NoExt> {
2715 /// The setting name (`max_threads`); a bare identifier.
2716 pub name: Ident,
2717 /// The assigned value; ClickHouse writes a literal, modelled as a general [`Expr`].
2718 pub value: Expr<X>,
2719 /// Source location and node identity.
2720 pub meta: Meta,
2721}
2722
2723/// ClickHouse `FORMAT <name>` — the output-format clause that closes a query
2724/// ([`Query::format`]), naming the serialization of the result (`FORMAT JSON`,
2725/// `FORMAT CSV`, `FORMAT TabSeparated`, `FORMAT Null`).
2726///
2727/// The format name is a bare identifier, case-sensitive (`JSON` ≠ `json` to
2728/// ClickHouse), never a string literal — so it is carried as an [`Ident`] preserving
2729/// the source spelling, not a [`Literal`]. `Null` is an ordinary format name here, not
2730/// the null literal. Carries no [`Expr`], so it is not generic over the extension
2731/// parameter (the [`OrderByAll`] precedent).
2732///
2733/// Gated by [`QueryTailSyntax::format_clause`](crate::dialect::SelectSyntax); no shipped
2734/// preset spells it but Lenient (the permissive union). There is no ClickHouse oracle,
2735/// so the accepted grammar is the recorded acceptance bound.
2736#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2737#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2738#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2739pub struct FormatClause {
2740 /// The output-format name (`JSON`, `TabSeparated`, `Null`); a bare, case-sensitive
2741 /// identifier.
2742 pub name: Ident,
2743 /// Source location and node identity.
2744 pub meta: Meta,
2745}
2746
2747/// MSSQL `FOR XML` / `FOR JSON` result-shaping tail on a [`Query`]
2748/// ([`Query::for_clause`]): `SELECT … FOR XML {RAW|AUTO|EXPLICIT|PATH} [, …]` and
2749/// `SELECT … FOR JSON {AUTO|PATH} [, …]`, which serialize the result set as XML or
2750/// JSON instead of a rowset.
2751///
2752/// # Parity with sqlparser-rs `ForClause`
2753///
2754/// Mirrors sqlparser-rs's `ForClause` enum (its `Xml { for_xml, elements,
2755/// binary_base64, root, r#type }` / `Json { for_json, root, include_null_values,
2756/// without_array_wrapper }` variants), with three deliberate reshapings:
2757/// - The `RAW`/`AUTO`/`EXPLICIT`/`PATH` and `AUTO`/`PATH` selectors are their own
2758/// [`ForXmlMode`] / [`ForJsonMode`] axes carrying the optional `('name')` element
2759/// name on the arms that take one (`RAW`/`PATH`), rather than sqlparser-rs's
2760/// flatter `ForXml`/`ForJson` with a separate name — the name is a property of the
2761/// mode, so it rides the mode arm (the canonical-shape doctrine, ADR-0011).
2762/// - `ELEMENTS` is [`Option`]`<`[`ForXmlElements`]`>` — `None` for the attribute-centric
2763/// default, `Some` carrying the `XSINIL`/`ABSENT` null-handling refinement —
2764/// where sqlparser-rs drops the refinement onto a bare `elements: bool`.
2765/// - `ROOT ['name']` is [`Option`]`<`[`ForRoot`]`>` (presence = the `ROOT` keyword;
2766/// the inner name is optional) shared by both variants, where sqlparser-rs models
2767/// it per-variant as `root: Option<String>` — losing the bare-`ROOT` vs no-`ROOT`
2768/// distinction our shape keeps.
2769///
2770/// sqlparser-rs's `ForClause::Browse` (`FOR BROWSE`) is out of scope: this ticket
2771/// covers only the `FOR XML`/`FOR JSON` result-shaping tails.
2772///
2773/// # Gating and `FOR` disambiguation
2774///
2775/// Gated by [`QueryTailSyntax::for_xml_json_clause`](crate::dialect::SelectSyntax) —
2776/// on for MSSQL and the permissive Lenient union, off elsewhere; with the gate off the
2777/// `FOR` keyword in this position is left
2778/// unconsumed and surfaces as a clean parse error. `FOR` also introduces the
2779/// row-locking clauses ([`Query::locking`], gated by
2780/// [`QueryTailSyntax::locking_clauses`](crate::dialect::SelectSyntax)); the two share
2781/// the `FOR` lead but **partition on the follow token** — `XML`/`JSON` here versus
2782/// `UPDATE`/`SHARE`/`NO`/`KEY` for locking — so the dispatch is unambiguous under
2783/// every preset combination, including Lenient (the one preset that enables both), and
2784/// needs no [`GrammarConflict`](crate::dialect::GrammarConflict) registry entry.
2785///
2786/// There is no MSSQL oracle, so the accepted grammar is the recorded acceptance bound
2787/// (self-consistent round-trip tests plus the MSSQL `FOR XML`/`FOR JSON` docs cited on
2788/// the gating flag). Options are accepted order-independently and rendered in the
2789/// canonical MSSQL directive order. Carries only mode tags and quoted-name
2790/// [`Literal`]s (no [`Expr`]), so it is not generic over the extension parameter.
2791#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2792#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2793#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2794pub enum ForClause {
2795 /// `FOR XML {RAW|AUTO|EXPLICIT|PATH} [, BINARY BASE64] [, TYPE] [, ROOT ['name']]
2796 /// [, ELEMENTS [XSINIL|ABSENT]]`.
2797 Xml {
2798 /// Mode selected by this syntax.
2799 mode: ForXmlMode,
2800 /// The `ELEMENTS [XSINIL|ABSENT]` element-centric directive; `None` for the
2801 /// attribute-centric default.
2802 elements: Option<ForXmlElements>,
2803 /// `BINARY BASE64` — encode binary columns as Base64 rather than a URL
2804 /// reference.
2805 binary_base64: bool,
2806 /// `TYPE` — return the result as an `xml`-typed value rather than text.
2807 typed: bool,
2808 /// `ROOT ['name']` wrapper element; `None` when no `ROOT` directive is written.
2809 root: Option<ForRoot>,
2810 /// Source location and node identity.
2811 meta: Meta,
2812 },
2813 /// `FOR JSON {AUTO|PATH} [, ROOT ['name']] [, INCLUDE_NULL_VALUES]
2814 /// [, WITHOUT_ARRAY_WRAPPER]`.
2815 Json {
2816 /// Mode selected by this syntax.
2817 mode: ForJsonMode,
2818 /// `ROOT ['name']` wrapper property; `None` when no `ROOT` directive is written.
2819 root: Option<ForRoot>,
2820 /// `INCLUDE_NULL_VALUES` — emit `null`-valued properties instead of omitting them.
2821 include_null_values: bool,
2822 /// `WITHOUT_ARRAY_WRAPPER` — emit a single object rather than a JSON array.
2823 without_array_wrapper: bool,
2824 /// Source location and node identity.
2825 meta: Meta,
2826 },
2827}
2828
2829/// The `FOR XML` serialization mode ([`ForClause::Xml`]): `RAW`, `AUTO`, `EXPLICIT`,
2830/// or `PATH`. `RAW` and `PATH` carry an optional `('ElementName')` naming the row
2831/// element (a quoted string [`Literal`]); `AUTO` and `EXPLICIT` take no name.
2832#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2833#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2834#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2835pub enum ForXmlMode {
2836 /// `RAW ['ElementName']` — one `<row>` (or the named element) per result row.
2837 Raw {
2838 /// The `('ElementName')` row-element name; `None` for a bare `RAW`.
2839 name: Option<Literal>,
2840 /// Source location and node identity.
2841 meta: Meta,
2842 },
2843 /// `AUTO` — nest elements to reflect the `FROM` join hierarchy.
2844 Auto {
2845 /// Source location and node identity.
2846 meta: Meta,
2847 },
2848 /// `EXPLICIT` — the query's own universal-table shape defines the XML.
2849 Explicit {
2850 /// Source location and node identity.
2851 meta: Meta,
2852 },
2853 /// `PATH ['ElementName']` — column names as XPath expressions.
2854 Path {
2855 /// The `('ElementName')` row-element name; `None` for a bare `PATH`.
2856 name: Option<Literal>,
2857 /// Source location and node identity.
2858 meta: Meta,
2859 },
2860}
2861
2862/// The `FOR XML … ELEMENTS` null-handling refinement ([`ForClause::Xml`]): a bare
2863/// `ELEMENTS`, or one of the `XSINIL`/`ABSENT` variants. A pure surface tag (no span
2864/// of its own), like [`LockStrength`].
2865#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2866#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2867#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2868pub enum ForXmlElements {
2869 /// Bare `ELEMENTS` — element-centric XML, NULL columns omitted (the `ABSENT`
2870 /// default MSSQL applies when neither refinement is written).
2871 Plain,
2872 /// `ELEMENTS XSINIL` — emit an empty element with `xsi:nil="true"` for NULLs.
2873 XsiNil,
2874 /// `ELEMENTS ABSENT` — omit the element for NULL columns (explicit spelling of
2875 /// the default).
2876 Absent,
2877}
2878
2879/// The `FOR JSON` serialization mode ([`ForClause::Json`]): `AUTO` or `PATH`. Neither
2880/// takes a name (unlike [`ForXmlMode`]), so a pure surface tag.
2881#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2882#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2883#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2884pub enum ForJsonMode {
2885 /// `AUTO` — nest JSON to reflect the `FROM` join hierarchy.
2886 Auto,
2887 /// `PATH` — dotted column aliases define the JSON object shape.
2888 Path,
2889}
2890
2891/// A `ROOT ['name']` directive shared by [`ForClause::Xml`] and [`ForClause::Json`]:
2892/// the `ROOT` keyword wraps the output in a single root element/property, optionally
2893/// named. Its own spanned node so the `ROOT` keyword span is addressable; the
2894/// optional name is a quoted string [`Literal`].
2895#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2896#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2897#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2898pub struct ForRoot {
2899 /// The `('name')` root name; `None` for a bare `ROOT` (MSSQL then names it `root`).
2900 pub name: Option<Literal>,
2901 /// Source location and node identity.
2902 pub meta: Meta,
2903}
2904
2905/// One Hive/Spark `LATERAL VIEW [OUTER] <generator>(args) <alias> [AS <col> [, …]]`
2906/// clause on a [`Select`] ([`Select::lateral_views`]): a table-generating function
2907/// (`explode`, `posexplode`, `json_tuple`, …) whose output rows are cross-joined
2908/// against each input row, with `OUTER` keeping input rows the generator produces no
2909/// rows for (NULL-padded, like an outer join).
2910///
2911/// # Parity with sqlparser-rs `LateralView`
2912///
2913/// Mirrors sqlparser-rs's `LateralView` struct (`lateral_view: Expr`,
2914/// `lateral_view_name: ObjectName`, `lateral_col_alias: Vec<Ident>`, `outer: bool`),
2915/// with two deliberate reshapings (ADR-0011's typed canonical shape) and one rename:
2916/// - `lateral_view: Expr` → [`function`](Self::function)`:` [`FunctionCall`] — both
2917/// grammars require a generator *call* here (Hive `FromClauseParser.g` `lateralView:
2918/// … function tableAlias …`; Spark `SqlBaseParser.g4` `lateralView: LATERAL VIEW
2919/// OUTER? qualifiedName '(' expression* ')' …`), so the typed call node is the
2920/// grammar, where sqlparser-rs's arbitrary expression over-admits non-calls.
2921/// - `lateral_view_name: ObjectName` → [`alias`](Self::alias)`:` [`Ident`] — the
2922/// correlation alias is a single identifier in both grammars (Hive `tableAlias`,
2923/// Spark `tblName=identifier`), never a qualified name.
2924/// - `lateral_col_alias` → [`columns`](Self::columns), a [`ThinVec`]`<`[`Ident`]`>`
2925/// (empty when unwritten — column aliases are optional since Hive 0.12).
2926///
2927/// # Acceptance bound (no Hive/Spark oracle)
2928///
2929/// The table alias is required (both grammars make it non-optional) and the `AS`
2930/// before the column list is optional — Spark's grammar spells `AS?` while Hive's
2931/// requires the keyword, so accepting the bare-column spelling under the one atomic
2932/// flag is a known conservative-direction over-acceptance for the Hive preset
2933/// (the [`JoinSyntax::sided_semi_anti_join`](crate::dialect::TableExpressionSyntax)
2934/// precedent, captured on the owning ticket). Rendering canonicalizes the bare
2935/// spelling to `AS` — a structural, not byte-exact, round-trip (the wildcard-modifier
2936/// precedent). Whether the named function is a genuine UDTF is a bind-time check past
2937/// the parse-level contract.
2938///
2939/// # Gating and `LATERAL` disambiguation
2940///
2941/// Gated by [`SelectSyntax::lateral_view_clause`](crate::dialect::SelectSyntax) — on
2942/// for Hive, Databricks, and the permissive Lenient union, off elsewhere; with the
2943/// gate off the `LATERAL` keyword in this position is left unconsumed and surfaces as
2944/// a clean parse error. `LATERAL` also introduces the standard LATERAL derived-table /
2945/// function factor ([`TableFactorSyntax::lateral`](crate::dialect::TableExpressionSyntax)),
2946/// but the two occupy disjoint grammar positions — a table-factor head (after
2947/// `FROM`/`,`/a join keyword) versus after the *whole* FROM list — and additionally
2948/// partition on the follow token (`VIEW` here; `(` or a function/subquery head there),
2949/// so the dispatch is unambiguous under every preset combination, including Lenient
2950/// (which enables both), and needs no
2951/// [`GrammarConflict`](crate::dialect::GrammarConflict) registry entry.
2952#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2953#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2954#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2955pub struct LateralView<X: Extension = NoExt> {
2956 /// `OUTER` — keep input rows the generator returns no rows for, NULL-padding the
2957 /// generated columns (Hive/Spark's outer-join refinement of the default
2958 /// cross-join semantics).
2959 pub outer: bool,
2960 /// The table-generating function call (`explode(col)`); inline, not boxed,
2961 /// because this node lives only behind [`Select::lateral_views`]' heap allocation
2962 /// (the [`RowsFromItem`] precedent).
2963 pub function: FunctionCall<X>,
2964 /// The required correlation alias naming the generated relation (Hive
2965 /// `tableAlias` / Spark `tblName`).
2966 pub alias: Ident,
2967 /// The `[AS] c1, c2` column aliases naming the generator's output columns; empty
2968 /// when unwritten.
2969 pub columns: ThinVec<Ident>,
2970 /// Source location and node identity.
2971 pub meta: Meta,
2972}
2973
2974/// An Oracle-style hierarchical (recursive) query clause on a [`Select`]
2975/// ([`Select::connect_by`]): the `CONNECT BY [NOCYCLE] <condition>` parent/child walk,
2976/// with an optional `START WITH <condition>` seed selecting the root rows. Rows are
2977/// expanded top-down from each root, following the `CONNECT BY` condition, in which the
2978/// [`UnaryOperator::Prior`](crate::ast::UnaryOperator) operator marks the operand taken
2979/// from the parent row.
2980///
2981/// # Grammar position and clause order
2982///
2983/// The clause sits **after `WHERE` and before `GROUP BY`**, Oracle's syntactic position
2984/// for the `hierarchical_query_clause` (Oracle *Database SQL Language Reference*,
2985/// `SELECT` → `hierarchical_query_clause`). `START WITH` and `CONNECT BY` may be written
2986/// in **either order** — Oracle's grammar admits both
2987/// `START WITH <c> CONNECT BY [NOCYCLE] <c>` and
2988/// `CONNECT BY [NOCYCLE] <c> [START WITH <c>]` — so the written order is recorded by
2989/// [`start_with_leads`](Self::start_with_leads) and round-trips exactly (the
2990/// spelling-fidelity doctrine: an ordering both engines accept is a spelling, not a
2991/// normalization). Snowflake, whose public docs are the citable grammar here (there is
2992/// no Oracle preset/oracle), documents only the `START WITH … CONNECT BY …` order and
2993/// the `[PRIOR] col = [PRIOR] col` equality shape (*Snowflake SQL Reference*,
2994/// `CONNECT BY`); it places the pair right after `FROM`, but modelling the Oracle
2995/// after-`WHERE` position is the strict superset that also accepts the Snowflake
2996/// `FROM … START WITH … CONNECT BY … [WHERE]`-less spelling.
2997///
2998/// # Acceptance bound (no Oracle/Snowflake oracle)
2999///
3000/// - **`NOCYCLE`.** Oracle accepts `CONNECT BY NOCYCLE <cond>` (return rows despite a
3001/// `CONNECT BY` loop); Snowflake's docs explicitly do *not* support `NOCYCLE`. The one
3002/// atomic [`connect_by_clause`](crate::dialect::SelectSyntax) gate accepts the wider
3003/// Oracle bound, a documented conservative-direction over-acceptance under the
3004/// Snowflake preset (the [`LateralView`] `AS`-optional precedent), captured on the
3005/// owning ticket.
3006/// - **`PRIOR`.** Both engines require exactly one `PRIOR` per `CONNECT BY` equality;
3007/// this parser admits it as an ordinary unary operator anywhere in the condition and
3008/// does not enforce the once-per-conjunct rule (a bind-time check past the parse-level
3009/// contract). `PRIOR` is recognized **only inside the `CONNECT BY` condition**, so the
3010/// global expression grammar is unchanged and a bare `prior` stays an ordinary column
3011/// name everywhere else.
3012#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3013#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3014#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3015pub struct HierarchicalClause<X: Extension = NoExt> {
3016 /// The optional `START WITH <condition>` root-row seed; `None` when the clause
3017 /// writes only `CONNECT BY`. Inline (not boxed) because this node lives only behind
3018 /// [`Select::connect_by`]' heap allocation (the [`LateralView`] precedent).
3019 pub start_with: Option<Expr<X>>,
3020 /// `NOCYCLE` — return rows even when the `CONNECT BY` walk hits a loop (Oracle; not
3021 /// Snowflake — see the acceptance-bound doc). Always modifies `CONNECT BY`,
3022 /// whichever order the pair is written in.
3023 pub nocycle: bool,
3024 /// The required `CONNECT BY [NOCYCLE] <condition>` parent/child predicate. Ordinary
3025 /// expression riding the guarded expression path, with
3026 /// [`UnaryOperator::Prior`](crate::ast::UnaryOperator) recognized inside it.
3027 pub connect_by: Expr<X>,
3028 /// `true` when `START WITH` was written **before** `CONNECT BY`, `false` when after
3029 /// (or absent — canonically `false` when [`start_with`](Self::start_with) is `None`,
3030 /// where the order is moot). Preserves the written order for an exact round-trip.
3031 pub start_with_leads: bool,
3032 /// Source location and node identity.
3033 pub meta: Meta,
3034}