squonk_ast/ast/ddl.rs
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Moderately AI Inc.
3
4//! DDL statement AST nodes: table, column, and constraint definitions.
5
6use super::{
7 DataType, ExecuteStatement, Expr, Extension, Ident, IntervalFields, LanguageName, Literal,
8 NamedOperatorSpelling, NoExt, ObjectName, Query, RoutineObjectKind, RoutineSignature,
9 Statement,
10};
11use crate::vocab::{Meta, Symbol};
12use thin_vec::ThinVec;
13
14#[derive(Clone, Debug, PartialEq, Eq, Hash)]
15#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
16#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
17/// An SQL create table.
18pub struct CreateTable<X: Extension = NoExt> {
19 /// `CREATE OR REPLACE TABLE` (DuckDB): atomically replaces an existing table.
20 /// Gated by [`CreateTableClauseSyntax::create_or_replace_table`](crate::dialect::CreateTableClauseSyntax::create_or_replace_table) —
21 /// off elsewhere, where `OR REPLACE` before `TABLE` surfaces as a clean parse error
22 /// (only `VIEW`/`FUNCTION` take `OR REPLACE` in the other dialects).
23 pub or_replace: bool,
24 /// Optional temporary for this syntax.
25 pub temporary: Option<TemporaryTableKind>,
26 /// `CREATE UNLOGGED TABLE` (PostgreSQL; also a DuckDB no-op): the table's data is not
27 /// written to the write-ahead log. PostgreSQL's `OptTemp` grammar makes `UNLOGGED` a
28 /// *peer* of `TEMP`/`TEMPORARY` — the two are mutually exclusive, so a set `unlogged`
29 /// never co-occurs with a `Some(temporary)` (`CREATE TEMP UNLOGGED TABLE` is a raw-parse
30 /// error, reproduced here). Gated for acceptance by
31 /// [`CreateTableClauseSyntax::unlogged_tables`](crate::dialect::CreateTableClauseSyntax::unlogged_tables) — off
32 /// elsewhere (ANSI/MySQL/SQLite), where `UNLOGGED` after `CREATE` surfaces as a clean
33 /// parse error.
34 pub unlogged: bool,
35 /// Whether the if not exists form was present in the source.
36 pub if_not_exists: bool,
37 /// Name referenced by this syntax.
38 pub name: ObjectName,
39 /// Statement or query body governed by this node.
40 pub body: CreateTableBody<X>,
41 /// The `INHERITS (<parent>, ...)` legacy table-inheritance clause (PostgreSQL), naming the
42 /// parent tables this table inherits columns and constraints from. It follows the
43 /// [`Definition`](CreateTableBody::Definition) `(elements)` body and precedes both
44 /// [`partition_by`](Self::partition_by) and the trailing [`options`](Self::options)
45 /// (PostgreSQL grammar order: `(…) INHERITS (…) PARTITION BY … WITH … ON COMMIT … TABLESPACE
46 /// …`). Empty when no clause is written — an `INHERITS ()` with no parent is a raw-parse
47 /// error, so a non-empty list always corresponds to a written clause and no separate
48 /// `Option` is needed. Only the `(elements)` definition body carries it (the `PARTITION OF`,
49 /// `OF <type>`, and `AS <query>` bodies do not), so it is empty for those. Gated for
50 /// acceptance by
51 /// [`CreateTableClauseSyntax::table_inheritance`](crate::dialect::CreateTableClauseSyntax::table_inheritance) — off
52 /// elsewhere, where the `INHERITS` keyword is left unconsumed and surfaces as a clean parse
53 /// error.
54 pub inherits: ThinVec<ObjectName>,
55 /// The trailing `PARTITION BY {LIST | RANGE | HASH} (<key>, ...)` declarative-partitioning
56 /// clause (PostgreSQL), marking this table as a partitioned *parent*. It follows the
57 /// [`Definition`](CreateTableBody::Definition) body (`CREATE TABLE t (...) PARTITION BY
58 /// LIST (a)`) and the [`PartitionOf`](CreateTableBody::PartitionOf) child body (a
59 /// sub-partitioned child, `... FOR VALUES IN (1) PARTITION BY RANGE (b)`); it never rides
60 /// an `AS <query>` body. Boxed as a cold clause so a non-partitioned table pays only a
61 /// null pointer. `None` when the table is not a partition parent. Gated for acceptance by
62 /// [`CreateTableClauseSyntax::declarative_partitioning`](crate::dialect::CreateTableClauseSyntax::declarative_partitioning) —
63 /// off elsewhere, where the `PARTITION` keyword is left unconsumed and surfaces as a clean
64 /// parse error.
65 pub partition_by: Option<Box<PartitionSpec<X>>>,
66 /// The `USING <access_method>` table access-method clause (PostgreSQL): the storage
67 /// engine backing the table (`heap`, an extension-provided method). In PostgreSQL's
68 /// `CreateStmt` grammar it sits *after* the body, [`inherits`](Self::inherits), and
69 /// [`partition_by`](Self::partition_by) but *before* the trailing
70 /// [`options`](Self::options) (`(…) INHERITS (…) PARTITION BY … USING heap WITH (…) ON
71 /// COMMIT … TABLESPACE …`) — a `WITH (…)` before `USING` is a raw-parse error, so parsing
72 /// it here reproduces that order. On an `AS <query>` body the slot instead precedes the
73 /// `AS` (`CreateTableAsStmt`: `CREATE TABLE t [(cols)] USING m [WITH (…)] AS query`; a
74 /// `USING` *after* the query is a raw-parse error). The method is a single (optionally
75 /// quoted) identifier — a qualified `schema.method` is a raw-parse error. `None` when the
76 /// clause is unwritten. Boxed as a cold clause (rarely written) so a plain `CREATE TABLE`
77 /// pays only a null pointer, keeping this node within its size budget (the box/inline
78 /// budget). Gated for acceptance by
79 /// [`CreateTableClauseSyntax::table_access_method`](crate::dialect::CreateTableClauseSyntax::table_access_method) — off
80 /// elsewhere, where the `USING` keyword is left unconsumed and surfaces as a clean parse
81 /// error.
82 pub access_method: Option<Box<Ident>>,
83 /// Options supplied in source order.
84 pub options: ThinVec<CreateTableOption<X>>,
85 /// Source location and node identity.
86 pub meta: Meta,
87}
88
89/// Surface spelling for temporary table creation.
90#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
91#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
92#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
93pub enum TemporaryTableKind {
94 /// `TEMP` — a session-local temporary table (the short spelling).
95 Temp,
96 /// `TEMPORARY` — a session-local temporary table.
97 Temporary,
98}
99
100/// The `CREATE TABLE` body: a `(elements)` definition, an `AS <query>`, a `PARTITION OF`
101/// declarative-partitioning child, or an `OF <type>` typed table.
102#[derive(Clone, Debug, PartialEq, Eq, Hash)]
103#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
104#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
105pub enum CreateTableBody<X: Extension = NoExt> {
106 /// A parenthesized `(col …, constraint …)` column/constraint definition list.
107 Definition {
108 /// elements in source order.
109 elements: ThinVec<TableElement<X>>,
110 /// Source location and node identity.
111 meta: Meta,
112 },
113 /// A `CREATE TABLE … AS <query>` — populate the table from a query.
114 AsQuery {
115 /// Columns in source order.
116 columns: ThinVec<Ident>,
117 /// Query governed by this node.
118 query: Box<Query<X>>,
119 /// Whether the with data form was present in the source.
120 with_data: Option<bool>,
121 /// Source location and node identity.
122 meta: Meta,
123 },
124 /// `AS EXECUTE <prepared> [(<arg>, ...)] [WITH [NO] DATA]` — a CTAS whose source rows come
125 /// from running a prepared statement rather than an inline query (PostgreSQL's
126 /// `CreateTableAsStmt` over an `ExecuteStmt`, `CREATE TABLE t AS EXECUTE plan(1, 2)`). The
127 /// optional [`columns`](Self::AsExecute::columns) rename the result columns (empty when the
128 /// `(...)` list is absent) exactly as on [`AsQuery`](Self::AsQuery), and the same trailing
129 /// `WITH [NO] DATA` populate flag rides here. The prepared-statement source is a reused
130 /// [`ExecuteStatement`] (name + positional args), boxed to keep this variant within the enum's
131 /// size budget. Gated for acceptance by
132 /// [`CreateTableClauseSyntax::create_table_as_execute`](crate::dialect::CreateTableClauseSyntax::create_table_as_execute); off
133 /// elsewhere, where `EXECUTE` after `AS` is left unconsumed and surfaces as a clean parse
134 /// error (the inline-query CTAS path takes over and rejects the `EXECUTE` keyword).
135 AsExecute {
136 /// Columns in source order.
137 columns: ThinVec<Ident>,
138 /// The prepared statement executed to populate the table; see [`ExecuteStatement`].
139 execute: Box<ExecuteStatement<X>>,
140 /// Whether the with data form was present in the source.
141 with_data: Option<bool>,
142 /// Source location and node identity.
143 meta: Meta,
144 },
145 /// `PARTITION OF <parent> [(<augmentation>, ...)] <bound>` — a declarative-partitioning
146 /// *child*, whose column shape is inherited from `parent` (PostgreSQL). The optional
147 /// parenthesized augmentation list carries per-column overrides (a bare `ColId` with a
148 /// `[WITH OPTIONS]` constraint list — *no* type, so each [`ColumnDef`] leaves
149 /// [`data_type`](ColumnDef::data_type) `None`) and table constraints; it reuses
150 /// [`TableElement`] and stays empty when the `(...)` is absent (PostgreSQL rejects an empty
151 /// `()`). The `bound` is the mandatory `FOR VALUES …` / `DEFAULT` partition-bound spec.
152 /// Gated for acceptance by
153 /// [`CreateTableClauseSyntax::declarative_partitioning`](crate::dialect::CreateTableClauseSyntax::declarative_partitioning); off
154 /// elsewhere, `PARTITION` after the table name surfaces as a clean parse error.
155 PartitionOf {
156 /// The parent partitioned table (`PARTITION OF <parent>`).
157 parent: ObjectName,
158 /// elements in source order.
159 elements: ThinVec<TableElement<X>>,
160 // Boxed as the fat child (a `PartitionBound` is ~48 B, the widest field here): inlining
161 // it would set `CreateTableBody`'s width and tax the warm `Definition`/`AsQuery` paths,
162 // so a partition child pays one cold allocation instead — the same reason `AsQuery` boxes
163 // its `Query` (ADR-0007, box/inline budget).
164 /// The partition bound (`FOR VALUES …` / `DEFAULT`); see [`PartitionBound`].
165 bound: Box<PartitionBound<X>>,
166 /// Source location and node identity.
167 meta: Meta,
168 },
169 /// `OF <type_name> [(<augmentation>, ...)]` — a *typed table* whose column shape is drawn
170 /// from a composite type (PostgreSQL). Like [`PartitionOf`](Self::PartitionOf) the optional
171 /// parenthesized list *augments* rather than declares: each element is a bare `ColId` with an
172 /// optional `WITH OPTIONS` noise phrase and a constraint list (*no* type — the column's type
173 /// comes from `type_name`, so each [`ColumnDef`] leaves [`data_type`](ColumnDef::data_type)
174 /// `None`) or a table constraint. It reuses [`TableElement`] and stays empty when the `(...)`
175 /// is absent (PostgreSQL rejects an empty `()`). Unlike the `(elements)`
176 /// [`Definition`](Self::Definition) body, the `OF` form takes no `INHERITS` clause (`CREATE
177 /// TABLE t OF ty INHERITS (p)` is a raw-parse error), so [`CreateTable::inherits`] is always
178 /// empty for it. Gated for acceptance by
179 /// [`CreateTableClauseSyntax::typed_tables`](crate::dialect::CreateTableClauseSyntax::typed_tables); off elsewhere,
180 /// `OF` after the table name surfaces as a clean parse error.
181 OfType {
182 /// The composite type the table is defined `OF` (`CREATE TABLE t OF <type>`).
183 type_name: ObjectName,
184 /// elements in source order.
185 elements: ThinVec<TableElement<X>>,
186 /// Source location and node identity.
187 meta: Meta,
188 },
189 /// `LIKE <source>` / `(LIKE <source>)` — MySQL's statement-level table-clone body, which
190 /// copies the whole definition of an existing table (`CREATE TABLE t LIKE src`). Unlike
191 /// PostgreSQL's [`TableElement::Like`] copy *element* (which sits inside a `(elements)`
192 /// [`Definition`](Self::Definition) list, carries `{INCLUDING | EXCLUDING} <feature>`
193 /// options, and can co-occur with other elements — gated by
194 /// [`CreateTableClauseSyntax::like_source_table`](crate::dialect::CreateTableClauseSyntax::like_source_table)), this is a
195 /// whole-statement production that replaces the entire body: exactly one bare source name,
196 /// no feature-copy options, no co-element, no trailing table options — MySQL rejects `LIKE
197 /// src ENGINE=…`, `(LIKE src, x INT)`, and `(LIKE src INCLUDING ALL)` all as
198 /// `ER_PARSE_ERROR`. MySQL spells it two ways, differing only by optional parentheses
199 /// ([`parenthesized`](Self::LikeSource::parenthesized) records which so the source form
200 /// round-trips); both admit a qualified source name and compose freely with `IF NOT EXISTS`
201 /// and `TEMPORARY`. Gated for acceptance by
202 /// [`CreateTableClauseSyntax::statement_level_table_like`](crate::dialect::CreateTableClauseSyntax::statement_level_table_like);
203 /// off elsewhere, `LIKE` after the table name (or as the first token inside `(`) is left
204 /// unconsumed and surfaces as a clean parse error.
205 LikeSource {
206 /// Input source for this syntax.
207 source: ObjectName,
208 /// Whether the source was written parenthesized (`(LIKE src)`) rather than bare
209 /// (`LIKE src`). Both are the same MySQL production; this preserves the spelling.
210 parenthesized: bool,
211 /// Source location and node identity.
212 meta: Meta,
213 },
214}
215
216/// The `PARTITION BY {LIST | RANGE | HASH} (<key>, ...)` declarative-partitioning spec on a
217/// partitioned parent (or a sub-partitioned child). PostgreSQL-only; see
218/// [`CreateTable::partition_by`].
219///
220/// PostgreSQL 17 validates the strategy word in the grammar action (`parsePartitionStrategy`),
221/// so an unrecognized strategy (`PARTITION BY foo (a)`) is a raw-parse error — hence
222/// [`strategy`](Self::strategy) is a closed [`PartitionStrategy`] enum, not a free identifier.
223#[derive(Clone, Debug, PartialEq, Eq, Hash)]
224#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
225#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
226pub struct PartitionSpec<X: Extension = NoExt> {
227 /// The partitioning strategy (`LIST`/`RANGE`/`HASH`); see [`PartitionStrategy`].
228 pub strategy: PartitionStrategy,
229 /// The partition key: one or more [`PartitionElem`]s (the grammar requires ≥ 1 — an empty
230 /// `()` is a raw-parse error).
231 pub columns: ThinVec<PartitionElem<X>>,
232 /// Source location and node identity.
233 pub meta: Meta,
234}
235
236/// The partitioning strategy keyword. A `Copy` leaf enum whose span rides the owning
237/// [`PartitionSpec`], like [`DropBehavior`].
238#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
239#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
240#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
241pub enum PartitionStrategy {
242 /// `PARTITION BY LIST` — assign rows by a discrete key value.
243 List,
244 /// `PARTITION BY RANGE` — assign rows by a key range.
245 Range,
246 /// `PARTITION BY HASH` — assign rows by a hash of the key.
247 Hash,
248}
249
250/// One partition-key element: a key expression plus its optional `COLLATE` and operator-class
251/// tails (PostgreSQL `part_elem`).
252///
253/// The grammar admits three key forms — a bare column (`a`), a bare `func_expr_windowless`
254/// (`lower(a)`), or a parenthesized expression (`(a + b)`) — the first two rendered bare and
255/// the last parenthesized. [`parenthesized`](Self::parenthesized) records which so the source
256/// form round-trips; the `COLLATE` and operator-class clauses are *separate* `part_elem`
257/// tails (not expression postfixes), so a bare-column key keeps them out of `expr`.
258#[derive(Clone, Debug, PartialEq, Eq, Hash)]
259#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
260#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
261pub struct PartitionElem<X: Extension = NoExt> {
262 /// Expression evaluated by this syntax.
263 pub expr: Expr<X>,
264 /// Whether the key was written parenthesized (`(a_expr)`). A bare column / function call is
265 /// unparenthesized; every other expression form must be parenthesized (the grammar's
266 /// `'(' a_expr ')'` production), and rendering re-adds the parentheses from this flag.
267 pub parenthesized: bool,
268 /// Optional collation for this syntax.
269 pub collation: Option<ObjectName>,
270 /// The trailing operator-class name (`opt_qualified_name`), when written —
271 /// `a part_test_int4_ops`, `a myschema.text_ops`.
272 pub opclass: Option<ObjectName>,
273 /// Source location and node identity.
274 pub meta: Meta,
275}
276
277/// The partition-bound spec of a [`PartitionOf`](CreateTableBody::PartitionOf) child (or an
278/// [`AttachPartition`](AlterTableAction::AttachPartition) action): `FOR VALUES {IN | FROM…TO |
279/// WITH} (…)` or `DEFAULT` (PostgreSQL).
280///
281/// The bound datums are full `a_expr`s (PostgreSQL parses the `minvalue`/`maxvalue` range
282/// sentinels as ordinary column references at raw-parse time, so they need no dedicated node).
283/// The hash bound's `MODULUS m, REMAINDER r` are unsigned integer literals validated in the
284/// grammar action — exactly one of each, either order — so they are modelled as a resolved
285/// pair rather than a raw option list (a missing/duplicate/non-integer value is a raw-parse
286/// error PostgreSQL raises, matched at the same layer).
287#[derive(Clone, Debug, PartialEq, Eq, Hash)]
288#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
289#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
290pub enum PartitionBound<X: Extension = NoExt> {
291 /// `FOR VALUES IN (<expr>, ...)` — a list partition (≥ 1 value).
292 List {
293 /// Values in source order.
294 values: ThinVec<Expr<X>>,
295 /// Source location and node identity.
296 meta: Meta,
297 },
298 /// `FOR VALUES FROM (<expr>, ...) TO (<expr>, ...)` — a range partition (each side ≥ 1).
299 Range {
300 /// from in source order.
301 from: ThinVec<Expr<X>>,
302 /// to in source order.
303 to: ThinVec<Expr<X>>,
304 /// Source location and node identity.
305 meta: Meta,
306 },
307 /// `FOR VALUES WITH (MODULUS <n>, REMAINDER <n>)` — a hash partition.
308 Hash {
309 /// The `MODULUS` of a `FOR VALUES WITH (MODULUS m, REMAINDER r)` hash bound.
310 modulus: Literal,
311 /// The `REMAINDER` of a hash partition bound.
312 remainder: Literal,
313 /// Source location and node identity.
314 meta: Meta,
315 },
316 /// `DEFAULT` — the catch-all partition.
317 Default {
318 /// Source location and node identity.
319 meta: Meta,
320 },
321}
322
323#[derive(Clone, Debug, PartialEq, Eq, Hash)]
324#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
325#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
326/// The SQL table element forms represented by the AST.
327pub enum TableElement<X: Extension = NoExt> {
328 /// A column definition.
329 Column {
330 /// Column referenced by this syntax.
331 column: ColumnDef<X>,
332 /// Source location and node identity.
333 meta: Meta,
334 },
335 /// A table-level constraint.
336 Constraint {
337 /// The table constraint; see [`TableConstraintDef`].
338 constraint: TableConstraintDef<X>,
339 /// Source location and node identity.
340 meta: Meta,
341 },
342 /// `LIKE <source_table> [{INCLUDING | EXCLUDING} <feature> ...]` — the source-table copy
343 /// element (PostgreSQL). Appears *inside* the parenthesized definition list (`CREATE TABLE t
344 /// (a int, LIKE src INCLUDING ALL, b int)`), copying the named table's structure. The
345 /// [`options`](Self::Like::options) are the repeatable, order-free
346 /// `{INCLUDING | EXCLUDING} <feature>` selectors, preserved as written (PostgreSQL folds them
347 /// into a bitmask, but the source sequence round-trips faithfully here); the list is empty
348 /// for a bare `LIKE src`. Gated for acceptance by
349 /// [`CreateTableClauseSyntax::like_source_table`](crate::dialect::CreateTableClauseSyntax::like_source_table) — off
350 /// elsewhere, where `LIKE` at an element position surfaces as a clean parse error. Distinct
351 /// from MySQL's statement-level `CREATE TABLE t LIKE src` (no parentheses), a separate
352 /// production.
353 Like {
354 /// Input source for this syntax.
355 source: ObjectName,
356 /// Options supplied in source order.
357 options: ThinVec<TableLikeOption>,
358 /// Source location and node identity.
359 meta: Meta,
360 },
361}
362
363#[derive(Clone, Debug, PartialEq, Eq, Hash)]
364#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
365#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
366/// An SQL table like option.
367pub struct TableLikeOption {
368 /// Whether the feature is `INCLUDING` or `EXCLUDING`; see [`TableLikeAction`].
369 pub action: TableLikeAction,
370 /// Which feature is copied/omitted; see [`TableLikeFeature`].
371 pub feature: TableLikeFeature,
372 /// Source location and node identity.
373 pub meta: Meta,
374}
375
376/// Whether a [`TableLikeOption`] includes or excludes its feature. A `Copy` leaf enum whose span
377/// rides the owning [`TableLikeOption`], like [`PartitionStrategy`].
378#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
379#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
380#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
381pub enum TableLikeAction {
382 /// `INCLUDING <feature>` — copy the feature from the source table.
383 Including,
384 /// `EXCLUDING <feature>` — do not copy the feature.
385 Excluding,
386}
387
388/// The feature a [`TableLikeOption`] copies (or omits) from the source table (PostgreSQL's
389/// `TableLikeOption` set). A `Copy` leaf enum whose span rides the owning [`TableLikeOption`].
390#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
391#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
392#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
393pub enum TableLikeFeature {
394 /// `COMMENTS` — column and constraint comments.
395 Comments,
396 /// `COMPRESSION` — per-column compression methods.
397 Compression,
398 /// `CONSTRAINTS` — `CHECK` constraints.
399 Constraints,
400 /// `DEFAULTS` — column default expressions.
401 Defaults,
402 /// `GENERATED` — generated-column expressions.
403 Generated,
404 /// `IDENTITY` — identity/serial column specifications.
405 Identity,
406 /// `INDEXES` — indexes plus primary-key and unique constraints.
407 Indexes,
408 /// `STATISTICS` — extended planner statistics.
409 Statistics,
410 /// `STORAGE` — per-column storage settings.
411 Storage,
412 /// `ALL` — every feature (PostgreSQL's `CREATE_TABLE_LIKE_ALL`).
413 All,
414}
415
416#[derive(Clone, Debug, PartialEq, Eq, Hash)]
417#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
418#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
419/// An SQL column def.
420pub struct ColumnDef<X: Extension = NoExt> {
421 /// Name referenced by this syntax.
422 pub name: Ident,
423 /// The column's declared type, or `None` for a SQLite *typeless* column
424 /// (`CREATE TABLE t (a, b)`): SQLite's flexible typing lets a column omit its
425 /// type entirely (the column then takes BLOB affinity). Every other dialect
426 /// requires a type, so the omission is gated for acceptance by
427 /// [`ColumnDefinitionSyntax::typeless_column_definitions`](crate::dialect::ColumnDefinitionSyntax::typeless_column_definitions)
428 /// — off elsewhere, where a missing type surfaces as a clean parse error. The
429 /// omitted and written forms round-trip distinctly (an `Option`, not a synthesized
430 /// default), so a downstream converter sees exactly what the source declared.
431 pub data_type: Option<DataType<X>>,
432 /// The per-column `STORAGE {<strategy> | DEFAULT}` clause (PostgreSQL): the column's
433 /// on-disk storage strategy (`PLAIN` / `EXTERNAL` / `EXTENDED` / `MAIN`, or the `DEFAULT`
434 /// keyword). It is a *fixed-position* clause in PostgreSQL's `columnDef` grammar — after the
435 /// type and before both [`compression`](Self::compression) and the
436 /// [`constraints`](Self::constraints) (`ColId Typename [STORAGE …] [COMPRESSION …]
437 /// ColQualList`) — so a `STORAGE` *after* a constraint, or after `COMPRESSION`, is a
438 /// raw-parse error. The value is an *open* name, not a closed enum: PostgreSQL's grammar is
439 /// `STORAGE {ColId | DEFAULT}` and validates the strategy word at analysis, out of this
440 /// parser's layer (`STORAGE bogus` is engine-measured accepted at raw parse), so any single
441 /// (optionally quoted) identifier — or the `DEFAULT` keyword, interned as written — rides
442 /// here as an [`Ident`], boxed as the cold clause it is (see
443 /// [`compression`](Self::compression)). `None` when unwritten. Gated for acceptance by
444 /// [`ColumnDefinitionSyntax::column_storage`](crate::dialect::ColumnDefinitionSyntax::column_storage) — off
445 /// elsewhere, where the `STORAGE` keyword surfaces as a clean parse error.
446 pub storage: Option<Box<Ident>>,
447 /// The per-column `COMPRESSION <method>` clause (PostgreSQL): the compression method for the
448 /// column's TOAST-able values (`pglz`, `lz4`, or the `DEFAULT` keyword). Like
449 /// [`storage`](Self::storage) a fixed-position clause with the same open
450 /// `{ColId | DEFAULT}` value grammar — after the type and [`storage`](Self::storage), before
451 /// the [`constraints`](Self::constraints) — so a `COMPRESSION` after a constraint or before
452 /// `STORAGE` is a raw-parse error, while a qualified `schema.method` is one too (the value
453 /// is a single identifier). PostgreSQL validates the specific method name at a later stage,
454 /// out of this parser's layer. `None` when unwritten. The name is boxed to keep this cold
455 /// clause from widening the warm, vec-embedded [`ColumnDef`] (the box/inline budget — a wide
456 /// table holds many `ColumnDef`s and `COMPRESSION` is rarely written). Gated for acceptance
457 /// by the same [`ColumnDefinitionSyntax::column_storage`](crate::dialect::ColumnDefinitionSyntax::column_storage)
458 /// flag as `STORAGE` (the two physical-storage attributes travel together).
459 pub compression: Option<Box<Ident>>,
460 /// constraints in source order.
461 pub constraints: ThinVec<ColumnConstraint<X>>,
462 /// Source location and node identity.
463 pub meta: Meta,
464}
465
466#[derive(Clone, Debug, PartialEq, Eq, Hash)]
467#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
468#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
469/// An SQL column constraint.
470pub struct ColumnConstraint<X: Extension = NoExt> {
471 /// Name referenced by this syntax.
472 pub name: Option<Ident>,
473 /// Optional option for this syntax.
474 pub option: ColumnOption<X>,
475 /// The SQLite `ON CONFLICT <resolution>` clause attached to this constraint —
476 /// `a INTEGER UNIQUE ON CONFLICT REPLACE`. Modelled on the constraint wrapper (like
477 /// [`characteristics`](Self::characteristics)) rather than on the individual
478 /// [`ColumnOption`] variants because the conflict clause qualifies a *constraint*
479 /// (SQLite admits it after `NOT NULL` / `UNIQUE` / `PRIMARY KEY` / `CHECK`), and the
480 /// parser only reads it after one of those. `None` when unwritten. Gated for
481 /// acceptance by
482 /// [`ColumnDefinitionSyntax::column_conflict_resolution_clause`](crate::dialect::ColumnDefinitionSyntax::column_conflict_resolution_clause);
483 /// off elsewhere, where `ON CONFLICT` on a column constraint is a clean parse error.
484 pub conflict: Option<ConflictResolution>,
485 /// The trailing `DEFERRABLE`/`INITIALLY` characteristics, when written. Modelled
486 /// on the constraint wrapper rather than on [`ColumnOption::References`] because
487 /// the SQL characteristic qualifies a *constraint* generally (unique / primary
488 /// key / foreign key), not the reference clause specifically. Boxed because it is
489 /// a cold, rarely written clause: the common column constraint then pays only a
490 /// null pointer, keeping this warm node lean (the box/inline budget).
491 pub characteristics: Option<Box<ConstraintCharacteristics>>,
492 /// Source location and node identity.
493 pub meta: Meta,
494}
495
496/// The `<constraint characteristics>` of a constraint: its deferral mode.
497///
498/// `deferrable` records `DEFERRABLE` (`Some(true)`) / `NOT DEFERRABLE`
499/// (`Some(false)`); `initially_deferred` records `INITIALLY DEFERRED` (`Some(true)`)
500/// / `INITIALLY IMMEDIATE` (`Some(false)`). Each stays `None` when its clause is
501/// unwritten so the omitted form and the explicit default round-trip distinctly —
502/// PostgreSQL materializes both to `NOT DEFERRABLE INITIALLY IMMEDIATE`, so the
503/// omitted and default-explicit spellings are representation-equivalent.
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 struct ConstraintCharacteristics {
508 /// Whether the deferrable form was present in the source.
509 pub deferrable: Option<bool>,
510 /// Whether the initially deferred form was present in the source.
511 pub initially_deferred: Option<bool>,
512 /// Source location and node identity.
513 pub meta: Meta,
514}
515
516#[derive(Clone, Debug, PartialEq, Eq, Hash)]
517#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
518#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
519/// The SQL column option forms represented by the AST.
520pub enum ColumnOption<X: Extension = NoExt> {
521 /// An explicit `NULL` — the column is allowed to contain nulls.
522 Null {
523 /// Source location and node identity.
524 meta: Meta,
525 },
526 /// A `NOT NULL` constraint.
527 NotNull {
528 /// Source location and node identity.
529 meta: Meta,
530 },
531 /// A `DEFAULT <expr>` value clause.
532 Default {
533 /// Expression evaluated by this syntax.
534 expr: Box<Expr<X>>,
535 /// Source location and node identity.
536 meta: Meta,
537 },
538 /// A `GENERATED ALWAYS AS (…)` computed column.
539 Generated {
540 /// The generated-column specification; see [`GeneratedColumn`].
541 generated: Box<GeneratedColumn<X>>,
542 /// Source location and node identity.
543 meta: Meta,
544 },
545 /// A `GENERATED … AS IDENTITY` identity column.
546 Identity {
547 /// The identity-column specification; see [`IdentityColumn`].
548 identity: Box<IdentityColumn<X>>,
549 /// Source location and node identity.
550 meta: Meta,
551 },
552 /// An inline `PRIMARY KEY` constraint.
553 PrimaryKey {
554 /// The SQLite `ASC` / `DESC` sort-order qualifier on an inline primary key
555 /// (`id INT PRIMARY KEY DESC`): `Some(true)` for `ASC`, `Some(false)` for
556 /// `DESC`, `None` when unwritten. Gated for acceptance by
557 /// [`ColumnDefinitionSyntax::inline_primary_key_ordering`](crate::dialect::ColumnDefinitionSyntax::inline_primary_key_ordering)
558 /// — off elsewhere, where the trailing `ASC`/`DESC` is a clean parse error.
559 ascending: Option<bool>,
560 /// The `USING INDEX TABLESPACE <name>` index-parameter clause (PostgreSQL): the
561 /// tablespace of the implicit index backing this inline primary key (`a int PRIMARY
562 /// KEY USING INDEX TABLESPACE ts`). `None` when unwritten. Boxed as the cold clause it
563 /// is (the box/inline budget — a wide table holds many `ColumnDef`s and this is rarely
564 /// written), matching [`Collate`](Self::Collate). Gated for acceptance by
565 /// [`ConstraintSyntax::index_constraint_parameters`](crate::dialect::ConstraintSyntax::index_constraint_parameters);
566 /// off elsewhere, where the `USING` keyword after `PRIMARY KEY` is a clean parse error.
567 index_tablespace: Option<Box<Ident>>,
568 /// Source location and node identity.
569 meta: Meta,
570 },
571 /// An inline `UNIQUE` constraint.
572 Unique {
573 /// The `NULLS [NOT] DISTINCT` null-treatment (PostgreSQL 15+): `Some(false)` for
574 /// `NULLS NOT DISTINCT` (nulls collide, so at most one null row), `Some(true)` for the
575 /// explicit `NULLS DISTINCT` default, `None` when unwritten. Gated for acceptance by
576 /// [`ConstraintSyntax::index_constraint_parameters`](crate::dialect::ConstraintSyntax::index_constraint_parameters);
577 /// off elsewhere, where the `NULLS` keyword after `UNIQUE` is a clean parse error.
578 nulls_not_distinct: Option<bool>,
579 /// The `USING INDEX TABLESPACE <name>` index-parameter clause (PostgreSQL), as on
580 /// [`PrimaryKey`](Self::PrimaryKey::index_tablespace). `None` when unwritten. Boxed and
581 /// gated identically.
582 index_tablespace: Option<Box<Ident>>,
583 /// Source location and node identity.
584 meta: Meta,
585 },
586 /// The MySQL `AUTO_INCREMENT` / SQLite `AUTOINCREMENT` column attribute: the
587 /// column's value defaults to the next sequence number. The one canonical shape
588 /// carries a [`spelling`](AutoIncrementSpelling) tag so the two surface forms
589 /// round-trip. Each spelling gates on its own flag: the underscored `AUTO_INCREMENT`
590 /// under `create_table_clause_syntax.table_options` (MySQL) and the joined `AUTOINCREMENT`
591 /// under `column_definition_syntax.joined_autoincrement_attribute` (SQLite). Neither preset
592 /// admits the other's spelling.
593 AutoIncrement {
594 /// Exact source spelling retained for faithful rendering.
595 spelling: AutoIncrementSpelling,
596 /// Source location and node identity.
597 meta: Meta,
598 },
599 /// The column-definition `COLLATE <collation>` clause: the column's default collating
600 /// sequence. One shape spans three dialects that each spell it in a column definition —
601 /// PostgreSQL (`a text COLLATE "C"` / `COLLATE pg_catalog."default"`), SQLite (`a TEXT
602 /// COLLATE NOCASE`), and DuckDB (`a VARCHAR COLLATE nocase`) — interleaving freely with the
603 /// other [`constraints`](ColumnDef::constraints) in PostgreSQL's `ColQualList` order. The
604 /// name is a (possibly qualified, possibly quoted) [`ObjectName`], matching the
605 /// expression-level `COLLATE` ([`ExpressionSyntax::collate`](crate::dialect::ExpressionSyntax));
606 /// SQLite/DuckDB write a single bare identifier (a one-part name), PostgreSQL a full
607 /// `any_name`. Distinct from that expression-level `COLLATE`, which qualifies an expression,
608 /// not a column. Gated for acceptance by
609 /// [`ColumnDefinitionSyntax::column_collation`](crate::dialect::ColumnDefinitionSyntax::column_collation); off
610 /// elsewhere (ANSI/MySQL), where a column-level `COLLATE` is a clean parse error. The
611 /// collation name is boxed to keep this cold clause from widening the warm `ColumnOption`
612 /// enum (the box/inline budget): every other fat variant here boxes its payload, so
613 /// `Collate` matches them and the common unit constraints pay only a null pointer.
614 Collate {
615 /// The collation name.
616 collation: Box<ObjectName>,
617 /// Source location and node identity.
618 meta: Meta,
619 },
620 /// An inline `CHECK (<predicate>)` constraint.
621 Check {
622 /// Expression evaluated by this syntax.
623 expr: Box<Expr<X>>,
624 /// The `NO INHERIT` marker (PostgreSQL/DuckDB): the check is not inherited by child
625 /// tables (`a int CHECK (a > 0) NO INHERIT`). At column level PostgreSQL bakes
626 /// `opt_no_inherit` directly into the `CHECK` production (unlike the table-level
627 /// [`TableConstraintDef`] marker, which rides the shared constraint-attribute slot), and
628 /// no column-level `NOT VALID` exists. Gated for acceptance by
629 /// [`ConstraintSyntax::constraint_no_inherit_not_valid`](crate::dialect::ConstraintSyntax::constraint_no_inherit_not_valid);
630 /// off elsewhere, where `NO INHERIT` after a column `CHECK` is a clean parse error.
631 no_inherit: bool,
632 /// Source location and node identity.
633 meta: Meta,
634 },
635 /// An inline `REFERENCES <table> (<cols>)` foreign-key constraint.
636 References {
637 // Boxed to keep `ColumnOption` lean: a `ForeignKeyRef` (48 B) is the only
638 // fat payload among these variants (every other is boxed or a unit), so
639 // inlining it set the whole enum's width. `REFERENCES` is a cold DDL path, so
640 // one allocation is a cheap price for the per-node saving — the fat-variant
641 // skew ADR-0007 calls out, data-backed by the box/inline budget benchmark.
642 /// The foreign-key target table, columns, and options; see [`ForeignKeyRef`].
643 reference: Box<ForeignKeyRef>,
644 /// Source location and node identity.
645 meta: Meta,
646 },
647 /// A trailing bodyless `CONSTRAINT <name>` (SQLite): the [`ColumnConstraint::name`] is
648 /// written but no constraint element follows it before the column/table-element
649 /// terminator. Gated for acceptance by
650 /// [`ConstraintSyntax::bare_constraint_name`](crate::dialect::ConstraintSyntax::bare_constraint_name); off
651 /// elsewhere, where a `CONSTRAINT <name>` with nothing following is a clean parse error.
652 Bare {
653 /// Source location and node identity.
654 meta: Meta,
655 },
656 /// Dialect extension node supplied by the extension type.
657 Other {
658 /// The dialect extension node value.
659 ext: X,
660 /// Source location and node identity.
661 meta: Meta,
662 },
663}
664
665/// Surface spelling for the auto-increment column attribute ([`ColumnOption::AutoIncrement`]).
666///
667/// The same canonical attribute is written `AUTO_INCREMENT` (MySQL, an underscore) or
668/// `AUTOINCREMENT` (SQLite, one solid word); this tag records which the source used so
669/// rendering round-trips exactly, mirroring the [`GeneratedColumnSpelling`] tag. A leaf
670/// `Copy` enum whose span lives on the owning [`ColumnOption`], like [`DropBehavior`].
671#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
672#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
673#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
674pub enum AutoIncrementSpelling {
675 /// The MySQL `AUTO_INCREMENT` spelling (with the underscore).
676 Underscored,
677 /// The SQLite `AUTOINCREMENT` spelling (one solid word).
678 Joined,
679}
680
681use super::ConflictResolution;
682
683/// A foreign-key reference: `REFERENCES <table> [(<column>, ...)]` plus the
684/// optional `<referential triggered action>` clauses.
685///
686/// `match_type`, `on_delete`, and `on_update` stay `None` when their clause is
687/// unwritten — the canonical shape keeps one node for both the column-level
688/// `REFERENCES` form and the table-level `FOREIGN KEY` form. The clauses
689/// are order-independent in the standard, so the parser accepts them in any order
690/// and renders them in a fixed canonical order (`ON DELETE` before `ON UPDATE`); the
691/// [`update_before_delete`](Self::update_before_delete) tag records the one bit of
692/// order the two separate fields otherwise lose, so a source-fidelity render replays
693/// the written order. PostgreSQL always materializes these (defaulting `MATCH SIMPLE`
694/// / `NO ACTION`), so an unwritten clause and the explicit default are
695/// representation-equivalent.
696#[derive(Clone, Debug, PartialEq, Eq, Hash)]
697#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
698#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
699pub struct ForeignKeyRef {
700 /// Table referenced by this syntax.
701 pub table: ObjectName,
702 /// Columns in source order.
703 pub columns: ThinVec<Ident>,
704 /// Optional match type for this syntax.
705 pub match_type: Option<ForeignKeyMatch>,
706 /// The referential actions are boxed because they are usually absent and a
707 /// [`ReferentialAction`] is comparatively large: a bare `REFERENCES` then pays
708 /// only a null pointer, keeping the inline node small where this type is
709 /// embedded in [`ColumnOption`]/[`TableConstraint`] (the same reason those
710 /// enums box their generated-column and identity payloads).
711 pub on_delete: Option<Box<ReferentialAction>>,
712 /// Optional on update for this syntax.
713 pub on_update: Option<Box<ReferentialAction>>,
714 /// Whether the source wrote `ON UPDATE` before `ON DELETE`. Meaningful only when
715 /// both actions are present; the canonical render emits `ON DELETE` first, and a
716 /// source-fidelity render swaps to the written order. Exact-order fidelity — the
717 /// clauses are order-independent, so a target re-spell and the redacted fingerprint
718 /// keep the canonical order.
719 pub update_before_delete: bool,
720 /// Source location and node identity.
721 pub meta: Meta,
722}
723
724/// The `MATCH` mode of a foreign key, governing how a composite key containing
725/// NULLs matches. A bare `REFERENCES` omits it; PostgreSQL reports the omitted
726/// form as `SIMPLE`. A leaf vocabulary enum (no `meta`): like [`DropBehavior`] it
727/// holds no spanned children, so its span lives on the owning [`ForeignKeyRef`].
728#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
729#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
730#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
731pub enum ForeignKeyMatch {
732 /// `MATCH FULL` — a partially-null referencing key is rejected.
733 Full,
734 /// `MATCH PARTIAL` — reserved by the standard; unimplemented by most engines.
735 Partial,
736 /// `MATCH SIMPLE` — the default; a null in any referencing column skips the check.
737 Simple,
738}
739
740/// A foreign-key `<referential action>`: what a delete (`ON DELETE`) or update
741/// (`ON UPDATE`) of the referenced row does to the referencing rows.
742///
743/// `SetNull`/`SetDefault` carry the optional PostgreSQL column list (`SET NULL
744/// (col, ...)`), which restricts the action to a subset of the referencing
745/// columns; the grammar accepts the list only on `ON DELETE`, so it is always
746/// empty for an `ON UPDATE` action and for the bare keyword forms. Each variant
747/// carries `meta` because the action is a span-bearing node — the `SET NULL (col)`
748/// form references spanned [`Ident`]s — mirroring [`IdentityOption`].
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 ReferentialAction {
753 /// `NO ACTION` — reject the change if references remain (deferrable).
754 NoAction {
755 /// Source location and node identity.
756 meta: Meta,
757 },
758 /// `RESTRICT` — reject the change immediately if references remain.
759 Restrict {
760 /// Source location and node identity.
761 meta: Meta,
762 },
763 /// `CASCADE` — propagate the delete/update to the referencing rows.
764 Cascade {
765 /// Source location and node identity.
766 meta: Meta,
767 },
768 /// `SET NULL` — null out the referencing columns (optionally a subset).
769 SetNull {
770 /// Columns in source order.
771 columns: ThinVec<Ident>,
772 /// Source location and node identity.
773 meta: Meta,
774 },
775 /// `SET DEFAULT` — reset the referencing columns to their defaults.
776 SetDefault {
777 /// Columns in source order.
778 columns: ThinVec<Ident>,
779 /// Source location and node identity.
780 meta: Meta,
781 },
782}
783
784#[derive(Clone, Debug, PartialEq, Eq, Hash)]
785#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
786#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
787/// An SQL generated column.
788pub struct GeneratedColumn<X: Extension = NoExt> {
789 /// Expression evaluated by this syntax.
790 pub expr: Expr<X>,
791 /// Optional storage for this syntax.
792 pub storage: Option<GeneratedColumnStorage>,
793 /// Which surface spelled the generation clause: the standard `GENERATED ALWAYS AS`
794 /// or the MySQL/SQLite keywordless `AS` shorthand (see [`GeneratedColumnSpelling`]).
795 pub spelling: GeneratedColumnSpelling,
796 /// Source location and node identity.
797 pub meta: Meta,
798}
799
800#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
801#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
802#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
803/// The SQL generated column storage forms represented by the AST.
804pub enum GeneratedColumnStorage {
805 /// `STORED` — the generated value is materialized on write.
806 Stored,
807 /// `VIRTUAL` — the generated value is computed on read.
808 Virtual,
809}
810
811/// Surface spelling for a [`GeneratedColumn`]'s generation clause.
812///
813/// A stored/virtual generated column is written two interchangeable-shape ways: the
814/// standard `GENERATED ALWAYS AS (<expr>)` and the keywordless `AS (<expr>)` shorthand
815/// (MySQL, SQLite). The canonical AST keeps one [`GeneratedColumn`] node and
816/// this tag records which the source used so rendering round-trips exactly, mirroring
817/// the [`LimitSyntax`](super::LimitSyntax) surface tag. The shorthand is gated for
818/// acceptance by
819/// [`ColumnDefinitionSyntax::generated_column_shorthand`](crate::dialect::ColumnDefinitionSyntax::generated_column_shorthand).
820#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
821#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
822#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
823pub enum GeneratedColumnSpelling {
824 /// The standard `GENERATED ALWAYS AS (<expr>)` spelling.
825 GeneratedAlways,
826 /// The keywordless `AS (<expr>)` shorthand (MySQL, SQLite).
827 Shorthand,
828}
829
830#[derive(Clone, Debug, PartialEq, Eq, Hash)]
831#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
832#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
833/// An SQL identity column.
834pub struct IdentityColumn<X: Extension = NoExt> {
835 /// Whether the identity is `GENERATED ALWAYS` or `BY DEFAULT`; see [`IdentityGeneration`].
836 pub generation: IdentityGeneration,
837 /// Options supplied in source order.
838 pub options: ThinVec<IdentityOption<X>>,
839 /// Source location and node identity.
840 pub meta: Meta,
841}
842
843#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
844#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
845#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
846/// The SQL identity generation forms represented by the AST.
847pub enum IdentityGeneration {
848 /// `GENERATED ALWAYS` — the identity value cannot be overridden on insert.
849 Always,
850 /// `GENERATED BY DEFAULT` — an explicit insert value overrides the sequence.
851 ByDefault,
852}
853
854#[derive(Clone, Debug, PartialEq, Eq, Hash)]
855#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
856#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
857/// The SQL identity option forms represented by the AST.
858pub enum IdentityOption<X: Extension = NoExt> {
859 /// `START WITH <n>` — the sequence's initial value.
860 StartWith {
861 /// Expression evaluated by this syntax.
862 expr: Expr<X>,
863 /// Source location and node identity.
864 meta: Meta,
865 },
866 /// `INCREMENT BY <n>` — the step between successive values.
867 IncrementBy {
868 /// Expression evaluated by this syntax.
869 expr: Expr<X>,
870 /// Source location and node identity.
871 meta: Meta,
872 },
873 /// `MINVALUE <n>` / `NO MINVALUE` — the sequence's lower bound.
874 MinValue {
875 /// Value supplied by this syntax.
876 value: Option<Expr<X>>,
877 /// Source location and node identity.
878 meta: Meta,
879 },
880 /// `MAXVALUE <n>` / `NO MAXVALUE` — the sequence's upper bound.
881 MaxValue {
882 /// Value supplied by this syntax.
883 value: Option<Expr<X>>,
884 /// Source location and node identity.
885 meta: Meta,
886 },
887 /// `CACHE <n>` — how many values to preallocate per session.
888 Cache {
889 /// Expression evaluated by this syntax.
890 expr: Expr<X>,
891 /// Source location and node identity.
892 meta: Meta,
893 },
894 /// `CYCLE` / `NO CYCLE` — whether the sequence wraps around at its bound.
895 Cycle {
896 /// Whether cycling is enabled.
897 cycle: bool,
898 /// Source location and node identity.
899 meta: Meta,
900 },
901}
902
903#[derive(Clone, Debug, PartialEq, Eq, Hash)]
904#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
905#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
906/// An SQL table constraint def.
907pub struct TableConstraintDef<X: Extension = NoExt> {
908 /// Name referenced by this syntax.
909 pub name: Option<Ident>,
910 /// The constraint kind and its data; see [`TableConstraint`].
911 pub constraint: TableConstraint<X>,
912 /// The `NO INHERIT` marker (PostgreSQL/DuckDB): the constraint is not inherited by child
913 /// tables. It rides PostgreSQL's shared `ConstraintAttributeSpec` slot alongside
914 /// [`characteristics`](Self::characteristics), order-free with `NOT VALID` and
915 /// `DEFERRABLE`. PostgreSQL admits it only on `CHECK` constraints (`FOREIGN KEY` /
916 /// `PRIMARY KEY` / `UNIQUE` / `EXCLUDE` reject it in the grammar action — reproduced at
917 /// parse), so a set flag always pairs with a [`TableConstraint::Check`]. Gated for
918 /// acceptance by
919 /// [`ConstraintSyntax::constraint_no_inherit_not_valid`](crate::dialect::ConstraintSyntax::constraint_no_inherit_not_valid).
920 pub no_inherit: bool,
921 /// The `NOT VALID` marker (PostgreSQL/DuckDB): the constraint is recorded but existing rows
922 /// are not checked. Shares the `ConstraintAttributeSpec` slot with
923 /// [`no_inherit`](Self::no_inherit) and [`characteristics`](Self::characteristics).
924 /// PostgreSQL admits it only on `CHECK` and `FOREIGN KEY` constraints (`PRIMARY KEY` /
925 /// `UNIQUE` / `EXCLUDE` reject it — reproduced at parse), and there is no column-level
926 /// form. Gated for acceptance by the same
927 /// [`ConstraintSyntax::constraint_no_inherit_not_valid`](crate::dialect::ConstraintSyntax::constraint_no_inherit_not_valid)
928 /// flag as [`no_inherit`](Self::no_inherit).
929 pub not_valid: bool,
930 /// The trailing `DEFERRABLE`/`INITIALLY` characteristics, when written; boxed as
931 /// the cold clause it is (see [`ColumnConstraint::characteristics`]).
932 pub characteristics: Option<Box<ConstraintCharacteristics>>,
933 /// Source location and node identity.
934 pub meta: Meta,
935}
936
937#[derive(Clone, Debug, PartialEq, Eq, Hash)]
938#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
939#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
940/// The SQL table constraint forms represented by the AST.
941pub enum TableConstraint<X: Extension = NoExt> {
942 /// A `PRIMARY KEY (col, …)` table constraint.
943 PrimaryKey {
944 /// The key columns. Each is an [`IndexColumn`] — the same "indexed-column" shape
945 /// [`CreateIndex`] uses — so a column may carry a `COLLATE <collation>` postfix
946 /// (folded into [`IndexColumn::expr`] as an [`Expr::Collate`](super::Expr)) and an
947 /// `ASC`/`DESC` sort order. The bare-column case is an
948 /// [`Expr::Column`](super::Expr) with `asc`/`nulls_first` unset — the shape every
949 /// dialect produces. The COLLATE/ordering decoration is gated for *acceptance* by
950 /// [`ConstraintSyntax::constraint_column_collate_order`](crate::dialect::ConstraintSyntax::constraint_column_collate_order)
951 /// (SQLite): off elsewhere, where a bare name is the only accepted form and a
952 /// trailing `COLLATE`/`ASC`/`DESC` is a clean parse error. SQLite prohibits general
953 /// expressions and `NULLS FIRST`/`LAST` in this position, so the parser never fills
954 /// `nulls_first` nor produces a non-column/non-collate `expr` here.
955 columns: ThinVec<IndexColumn<X>>,
956 /// The `INCLUDE (<col>, ...)` covering-index columns (PostgreSQL): non-key payload
957 /// columns stored in the primary key's implicit index (`PRIMARY KEY (a) INCLUDE (b)`).
958 /// Empty when no `INCLUDE` clause is written. Gated for acceptance by
959 /// [`ConstraintSyntax::index_constraint_parameters`](crate::dialect::ConstraintSyntax::index_constraint_parameters);
960 /// off elsewhere, where `INCLUDE` after the key list is a clean parse error.
961 include: ThinVec<Ident>,
962 /// Source location and node identity.
963 meta: Meta,
964 },
965 /// A `UNIQUE (col, …)` table constraint.
966 Unique {
967 /// The key columns, as on [`PrimaryKey`](Self::PrimaryKey::columns): [`IndexColumn`]s
968 /// admitting a per-column `COLLATE <collation>` and `ASC`/`DESC` under the same
969 /// [`ConstraintSyntax::constraint_column_collate_order`](crate::dialect::ConstraintSyntax::constraint_column_collate_order)
970 /// gate.
971 columns: ThinVec<IndexColumn<X>>,
972 /// The `NULLS [NOT] DISTINCT` null-treatment (PostgreSQL 15+), as on the column-level
973 /// [`ColumnOption::Unique`]. `Some(false)` = `NULLS NOT DISTINCT`, `Some(true)` = the
974 /// explicit `NULLS DISTINCT` default, `None` = unwritten. Gated for acceptance by
975 /// [`ConstraintSyntax::index_constraint_parameters`](crate::dialect::ConstraintSyntax::index_constraint_parameters).
976 nulls_not_distinct: Option<bool>,
977 /// The `INCLUDE (<col>, ...)` covering-index columns (PostgreSQL), as on
978 /// [`PrimaryKey`](Self::PrimaryKey::include). Empty when unwritten. Same gate.
979 include: ThinVec<Ident>,
980 /// Source location and node identity.
981 meta: Meta,
982 },
983 /// A `CHECK (<predicate>)` table constraint.
984 Check {
985 /// Expression evaluated by this syntax.
986 expr: Box<Expr<X>>,
987 /// Source location and node identity.
988 meta: Meta,
989 },
990 /// A PostgreSQL `EXCLUDE [USING <method>] (<element> WITH <operator> [, ...]) [INCLUDE
991 /// (...)] [WITH (...)] [USING INDEX TABLESPACE ...] [WHERE (...)]` exclusion constraint —
992 /// no two rows may hold values that all pairwise satisfy the named operators. The whole
993 /// clause is boxed into [`ExcludeConstraint`] so this cold, rarely-written variant does not
994 /// widen the warm `TableConstraint` enum (the box/inline budget — the same reason
995 /// [`ForeignKey`](Self::ForeignKey) boxes its `ForeignKeyRef`). Gated for acceptance by
996 /// [`ConstraintSyntax::exclusion_constraints`](crate::dialect::ConstraintSyntax::exclusion_constraints); off
997 /// elsewhere (DuckDB included, which rejects it), where `EXCLUDE` at a constraint position is
998 /// a clean parse error.
999 Exclude {
1000 /// The exclusion-constraint specification; see [`ExcludeConstraint`].
1001 exclude: Box<ExcludeConstraint<X>>,
1002 /// Source location and node identity.
1003 meta: Meta,
1004 },
1005 /// A `FOREIGN KEY (col, …) REFERENCES …` table constraint.
1006 ForeignKey {
1007 /// Columns in source order.
1008 columns: ThinVec<Ident>,
1009 // Boxed for the same fat-variant reason as `ColumnOption::References`: the
1010 // inline `ForeignKeyRef` (48 B) alone set `TableConstraint`'s width — and
1011 // transitively that of the `TableConstraintDef` containing it. Cold DDL path,
1012 // so the lone allocation is cheap (ADR-0007, box/inline budget benchmark).
1013 /// The foreign-key target table, columns, and options; see [`ForeignKeyRef`].
1014 references: Box<ForeignKeyRef>,
1015 /// Source location and node identity.
1016 meta: Meta,
1017 },
1018 /// A trailing bodyless `CONSTRAINT <name>` (SQLite), standalone in the table-element list
1019 /// (`CREATE TABLE t (a INT, CONSTRAINT cn)`) — the [`TableConstraintDef::name`] is written
1020 /// but no constraint element follows it. SQLite also lets the comma separating this from a
1021 /// preceding table constraint be omitted (`UNIQUE(a) CONSTRAINT cn`, engine-measured
1022 /// accepted); the parser's table-element loop handles that comma elision, not this variant.
1023 /// Gated for acceptance by
1024 /// [`ConstraintSyntax::bare_constraint_name`](crate::dialect::ConstraintSyntax::bare_constraint_name); off
1025 /// elsewhere, where a `CONSTRAINT <name>` with nothing following is a clean parse error.
1026 Bare {
1027 /// Source location and node identity.
1028 meta: Meta,
1029 },
1030 /// Dialect extension node supplied by the extension type.
1031 Other {
1032 /// The dialect extension node value.
1033 ext: X,
1034 /// Source location and node identity.
1035 meta: Meta,
1036 },
1037}
1038
1039/// The body of a PostgreSQL [`EXCLUDE`](TableConstraint::Exclude) exclusion constraint.
1040///
1041/// `EXCLUDE [USING <method>] (<element> [, ...]) [INCLUDE (<col>, ...)] [WITH (<param>, ...)]
1042/// [USING INDEX TABLESPACE <name>] [WHERE (<predicate>)]` — the tail clauses appear in that
1043/// fixed PostgreSQL grammar order. Boxed inside [`TableConstraint::Exclude`] so the cold clause
1044/// does not widen the warm constraint enum.
1045#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1046#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1047#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1048pub struct ExcludeConstraint<X: Extension = NoExt> {
1049 /// The `USING <method>` index access method (`gist`, `btree`, ...); `None` for the bare
1050 /// `EXCLUDE (...)` form, which PostgreSQL defaults to `btree`. A single identifier — a
1051 /// qualified method is a raw-parse error.
1052 pub method: Option<Ident>,
1053 /// The exclusion elements, each an index element paired with an operator (`c WITH &&`);
1054 /// non-empty (an empty `()` is a raw-parse error).
1055 pub elements: ThinVec<ExcludeElement<X>>,
1056 /// include in source order.
1057 pub include: ThinVec<Ident>,
1058 /// The `WITH (<param> [= <value>], ...)` index storage parameters (reloptions); empty when
1059 /// unwritten. Reuses [`TableStorageParameter`], the same element the `CREATE TABLE … WITH
1060 /// (…)` option list holds.
1061 pub with_params: ThinVec<TableStorageParameter<X>>,
1062 /// Optional index tablespace for this syntax.
1063 pub index_tablespace: Option<Ident>,
1064 /// The `WHERE (<predicate>)` partial-constraint predicate; `None` when unwritten. Boxed as a
1065 /// cold clause so an exclusion constraint without a predicate pays only a null pointer.
1066 pub predicate: Option<Box<Expr<X>>>,
1067 /// Source location and node identity.
1068 pub meta: Meta,
1069}
1070
1071/// One `<index_element> WITH <operator>` element of an [`ExcludeConstraint`] (PostgreSQL
1072/// `ExclusionConstraintElem`).
1073///
1074/// The index element is PostgreSQL's `index_elem`: a key (a bare column, a bare function call,
1075/// or a parenthesized `(a_expr)` — the [`parenthesized`](Self::parenthesized) flag records which
1076/// so the source form round-trips), then optional `COLLATE`, an operator-class name with
1077/// optional reloptions, and `ASC`/`DESC` + `NULLS FIRST`/`LAST` sort modifiers — exactly the
1078/// [`CreateIndex`] key grammar. The `WITH <operator>` names the exclusion operator.
1079#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1080#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1081#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1082pub struct ExcludeElement<X: Extension = NoExt> {
1083 /// Expression evaluated by this syntax.
1084 pub expr: Expr<X>,
1085 /// Whether the key was written parenthesized (`(a + b)`); a bare column / function call is
1086 /// unparenthesized. Mirrors [`PartitionElem::parenthesized`].
1087 pub parenthesized: bool,
1088 /// Optional collation for this syntax.
1089 pub collation: Option<ObjectName>,
1090 /// The operator-class name (`index_elem` `opt_qualified_name`), when written — `a int4_ops`,
1091 /// `a myschema.text_ops`.
1092 pub opclass: Option<ObjectName>,
1093 /// The operator-class reloptions `(<param> [= <value>], ...)` (`index_elem` `reloptions`),
1094 /// present only with an [`opclass`](Self::opclass); empty otherwise.
1095 pub opclass_params: ThinVec<TableStorageParameter<X>>,
1096 /// `ASC` (`Some(true)`) / `DESC` (`Some(false)`) sort order; `None` when unwritten.
1097 pub asc: Option<bool>,
1098 /// `NULLS FIRST` (`Some(true)`) / `NULLS LAST` (`Some(false)`); `None` when unwritten.
1099 pub nulls_first: Option<bool>,
1100 /// Operator applied by this expression.
1101 pub operator: ExcludeOperator,
1102 /// Source location and node identity.
1103 pub meta: Meta,
1104}
1105
1106/// The `WITH <operator>` operator of an [`ExcludeElement`] — PostgreSQL `any_operator`, a bare
1107/// symbolic operator (`&&`, `=`, `-|-`) or the explicit `OPERATOR(<schema>.<op>)` keyword form.
1108///
1109/// Models the operator the same way [`NamedOperatorExpr`](super::NamedOperatorExpr) does — an
1110/// optional schema qualification, the interned symbolic [`op`](Self::op), and a
1111/// [`spelling`](Self::spelling) tag recording the bare-vs-`OPERATOR(...)` surface — so the source
1112/// form round-trips. A leaf node without `meta`: its span is subsumed by the owning
1113/// [`ExcludeElement`], like [`DropBehavior`].
1114#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1115#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1116#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1117pub struct ExcludeOperator {
1118 /// The optional schema qualification (`pg_catalog` in `OPERATOR(pg_catalog.=)`); an empty
1119 /// name for a bare or unqualified operator.
1120 pub schema: ObjectName,
1121 /// The symbolic operator spelling (`&&`, `=`, `-|-`), interned exact-case.
1122 pub op: Symbol,
1123 /// Which surface spelled the operator — a bare `&&` or the explicit `OPERATOR(...)` keyword.
1124 pub spelling: NamedOperatorSpelling,
1125}
1126
1127#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1128#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1129#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1130/// An SQL create table option.
1131pub struct CreateTableOption<X: Extension = NoExt> {
1132 /// Which option kind (`WITH`/`ON COMMIT`/`TABLESPACE`/…); see [`CreateTableOptionKind`].
1133 pub kind: CreateTableOptionKind<X>,
1134 /// Source location and node identity.
1135 pub meta: Meta,
1136}
1137
1138#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1139#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1140#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1141/// The SQL create table option kind forms represented by the AST.
1142pub enum CreateTableOptionKind<X: Extension = NoExt> {
1143 /// A `WITH (<param> = <value>, …)` storage-parameter clause.
1144 With {
1145 /// params in source order.
1146 params: ThinVec<TableStorageParameter<X>>,
1147 /// Source location and node identity.
1148 meta: Meta,
1149 },
1150 /// An `ON COMMIT {PRESERVE ROWS | DELETE ROWS | DROP}` temporary-table clause.
1151 OnCommit {
1152 /// The on-commit action; see [`OnCommitAction`].
1153 action: OnCommitAction,
1154 /// Source location and node identity.
1155 meta: Meta,
1156 },
1157 /// A `TABLESPACE <name>` clause.
1158 Tablespace {
1159 /// The tablespace name.
1160 tablespace: Ident,
1161 /// Source location and node identity.
1162 meta: Meta,
1163 },
1164 /// A MySQL trailing table option written as an open `<name> [=] <value>` pair —
1165 /// `ENGINE = InnoDB`, `AUTO_INCREMENT = 100`, `DEFAULT CHARSET = utf8mb4`,
1166 /// `COMMENT = '...'`, `ROW_FORMAT = DYNAMIC`, `COLLATE = utf8mb4_general_ci`.
1167 ///
1168 /// Modelled as one canonical name/value pair rather than a variant (or a
1169 /// `CreateTable` field) per option keyword: the MySQL option
1170 /// vocabulary is large and server-version-dependent, so a single open shape
1171 /// round-trips an arbitrary option list — the same reasoning the [`CopyOption`]
1172 /// list follows. The optional `DEFAULT` noise word MySQL accepts before
1173 /// `CHARSET`/`COLLATE` is normalized away by the parser, and the `=` is optional.
1174 /// Gated by `create_table_clause_syntax.table_options`.
1175 ///
1176 /// [`CopyOption`]: super::CopyOption
1177 KeyValue {
1178 // The whole `<name> = <value>` payload is boxed (ADR-0007): a `TableOption`
1179 // (name + a 40 B value + meta) is the only fat payload among these variants, so
1180 // inlining it would set the enum's width and tax the lean `With`/`OnCommit`/
1181 // `Tablespace` variants — and, transitively, every `CreateTableOption` an
1182 // ANSI/PostgreSQL parse stores in its options `ThinVec`. Table options are a
1183 // cold DDL path, so the lone allocation per MySQL option is cheap, the same
1184 // box call `ColumnOption::Identity` makes for its `IdentityColumn`.
1185 /// The `<name> = <value>` option pair; see [`TableOption`].
1186 option: Box<TableOption>,
1187 /// Source location and node identity.
1188 meta: Meta,
1189 },
1190 /// The SQLite `WITHOUT ROWID` trailing table option: the table is stored as a
1191 /// clustered index on its primary key rather than the default implicit `rowid`.
1192 /// A bare keyword-style option (no `= value`), so a variant of its own rather than
1193 /// the MySQL [`KeyValue`](Self::KeyValue) name/value catch-all. SQLite
1194 /// comma-separates these trailing options (`... STRICT, WITHOUT ROWID`). Gated for
1195 /// acceptance by
1196 /// [`CreateTableClauseSyntax::without_rowid_table_option`](crate::dialect::CreateTableClauseSyntax::without_rowid_table_option).
1197 WithoutRowid {
1198 /// Source location and node identity.
1199 meta: Meta,
1200 },
1201 /// The SQLite `STRICT` trailing table option: the table enforces its declared
1202 /// column types instead of SQLite's default flexible typing. Like
1203 /// [`WithoutRowid`](Self::WithoutRowid) a bare keyword-style option, gated for
1204 /// acceptance by
1205 /// [`CreateTableClauseSyntax::strict_table_option`](crate::dialect::CreateTableClauseSyntax::strict_table_option).
1206 Strict {
1207 /// Source location and node identity.
1208 meta: Meta,
1209 },
1210 /// The legacy PostgreSQL `WITHOUT OIDS` trailing option: historically suppressed the
1211 /// system `oid` column. Modern PostgreSQL keeps it in the grammar as an accepted no-op,
1212 /// while the affirmative `WITH OIDS` has no production and *rejects* (both
1213 /// engine-measured). A bare keyword-style option, so a
1214 /// variant of its own rather than the MySQL [`KeyValue`](Self::KeyValue) catch-all. It
1215 /// occupies PostgreSQL's `OptWith` slot — mutually exclusive with a `WITH (…)` storage-
1216 /// parameter list, and it precedes `ON COMMIT` / `TABLESPACE`. Gated for acceptance by
1217 /// [`CreateTableClauseSyntax::without_oids`](crate::dialect::CreateTableClauseSyntax::without_oids).
1218 WithoutOids {
1219 /// Source location and node identity.
1220 meta: Meta,
1221 },
1222}
1223
1224/// One MySQL trailing table option: a `<name> = <value>` pair.
1225///
1226/// The canonical "options as data" shape: `name` is the option keyword as
1227/// written (`ENGINE`, `AUTO_INCREMENT`, `CHARSET`, ...) and `value` is its argument.
1228/// Boxed inside [`CreateTableOptionKind::KeyValue`] to keep that enum lean; mirrors
1229/// the [`CopyOption`](super::CopyOption) list element.
1230#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1231#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1232#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1233pub struct TableOption {
1234 /// Name referenced by this syntax.
1235 pub name: Ident,
1236 /// Value supplied by this syntax.
1237 pub value: TableOptionValue,
1238 /// Source location and node identity.
1239 pub meta: Meta,
1240}
1241
1242/// The value of a [`TableOption`].
1243///
1244/// Three surface forms a downstream converter cannot otherwise recover:
1245/// a bareword/keyword (`InnoDB`, `DYNAMIC`), a string (`COMMENT = '...'`), or a
1246/// number (`AUTO_INCREMENT = 100`). String and numeric values ride their
1247/// [`Literal`]'s `meta.span` and materialise lazily; a bareword keeps its
1248/// source spelling on the [`Ident`]. Mirrors [`CopyOptionValue`](super::CopyOptionValue),
1249/// which omits the numeric form COPY options never take.
1250#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1251#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1252#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1253pub enum TableOptionValue {
1254 /// A bareword/keyword value: `ENGINE = InnoDB`, `ROW_FORMAT = DYNAMIC`.
1255 Word {
1256 /// Identifier-form value.
1257 word: Ident,
1258 /// Source location and node identity.
1259 meta: Meta,
1260 },
1261 /// A string value: `COMMENT = 'text'`.
1262 String {
1263 /// Value supplied by this syntax.
1264 value: Literal,
1265 /// Source location and node identity.
1266 meta: Meta,
1267 },
1268 /// A numeric value: `AUTO_INCREMENT = 100`, `KEY_BLOCK_SIZE = 8`.
1269 Number {
1270 /// Value supplied by this syntax.
1271 value: Literal,
1272 /// Source location and node identity.
1273 meta: Meta,
1274 },
1275}
1276
1277#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1278#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1279#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1280/// The SQL on commit action forms represented by the AST.
1281pub enum OnCommitAction {
1282 /// `ON COMMIT PRESERVE ROWS` — keep the temp table's rows after commit.
1283 PreserveRows,
1284 /// `ON COMMIT DELETE ROWS` — empty the temp table on each commit.
1285 DeleteRows,
1286 /// `ON COMMIT DROP` — drop the temp table at the end of the transaction.
1287 Drop,
1288}
1289
1290#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1291#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1292#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1293/// An SQL table storage parameter.
1294pub struct TableStorageParameter<X: Extension = NoExt> {
1295 /// Name referenced by this syntax.
1296 pub name: ObjectName,
1297 /// Value supplied by this syntax.
1298 pub value: Option<Expr<X>>,
1299 /// Source location and node identity.
1300 pub meta: Meta,
1301}
1302
1303/// `ALTER TABLE [IF EXISTS] <name> <action> [, <action>]...`.
1304///
1305/// The schema-evolution counterpart of [`CreateTable`]. A single statement carries
1306/// one or more comma-separated [`AlterTableAction`]s, matching PostgreSQL's
1307/// multi-action form (`ALTER TABLE t ADD COLUMN a INT, DROP COLUMN b`). The `IF
1308/// EXISTS` prefix is gated by `existence_guards.if_exists` dialect data:
1309/// off under ANSI, on under PostgreSQL. The PostgreSQL-only `ONLY`
1310/// inheritance qualifier is a deliberate follow-up, tracked separately.
1311#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1312#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1313#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1314pub struct AlterTable<X: Extension = NoExt> {
1315 /// Whether the if exists form was present in the source.
1316 pub if_exists: bool,
1317 /// Name referenced by this syntax.
1318 pub name: ObjectName,
1319 /// actions in source order.
1320 pub actions: ThinVec<AlterTableAction<X>>,
1321 /// Source location and node identity.
1322 pub meta: Meta,
1323}
1324
1325/// A column target inside an `ALTER TABLE` action.
1326///
1327/// Most dialects name a top-level column (`c`), while DuckDB also admits dotted paths into
1328/// nested STRUCT fields for selected actions (`s.s2.k`). Kept separate from [`ObjectName`]
1329/// so ALTER-specific nested paths do not imply that table columns generally use relation-name
1330/// semantics.
1331#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1332#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1333#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1334pub struct AlterColumnTarget {
1335 /// parts in source order.
1336 pub parts: ThinVec<Ident>,
1337 /// Source location and node identity.
1338 pub meta: Meta,
1339}
1340
1341/// One action in an `ALTER TABLE` statement.
1342///
1343/// `AddColumn`/`AddConstraint` reuse the `CREATE TABLE` element nodes ([`ColumnDef`]
1344/// and [`TableConstraintDef`]) rather than inventing alter-specific copies, so the
1345/// `Other(X)` extension seams already on [`ColumnOption`]/[`TableConstraint`] cover
1346/// custom DDL attached through an alter too. The optional `COLUMN` noise
1347/// word is not represented: rendering normalizes to the canonical `ADD COLUMN`
1348/// spelling.
1349#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1350#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1351#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1352pub enum AlterTableAction<X: Extension = NoExt> {
1353 /// `ADD [COLUMN] <col> <type> …` — add a column to the table.
1354 AddColumn {
1355 /// Whether the if not exists form was present in the source.
1356 if_not_exists: bool,
1357 /// Whether the optional `COLUMN` noise word was written (`ADD COLUMN c` vs the
1358 /// bare `ADD c`). Exact-synonym fidelity; the canonical render emits `COLUMN`.
1359 column_keyword: bool,
1360 /// Object targeted by this syntax.
1361 target: Option<AlterColumnTarget>,
1362 /// Column referenced by this syntax.
1363 column: ColumnDef<X>,
1364 /// Source location and node identity.
1365 meta: Meta,
1366 },
1367 /// `DROP [COLUMN] <name>` — remove a column from the table.
1368 DropColumn {
1369 /// Whether the if exists form was present in the source.
1370 if_exists: bool,
1371 /// Whether the optional `COLUMN` noise word was written (`DROP COLUMN c` vs the
1372 /// bare `DROP c`). Exact-synonym fidelity; the canonical render emits `COLUMN`.
1373 column_keyword: bool,
1374 /// Name referenced by this syntax.
1375 name: AlterColumnTarget,
1376 /// Optional behavior for this syntax.
1377 behavior: Option<DropBehavior>,
1378 /// Source location and node identity.
1379 meta: Meta,
1380 },
1381 /// `ALTER [COLUMN] <name> …` — change an existing column's definition.
1382 AlterColumn {
1383 /// Whether the optional `COLUMN` noise word was written (`ALTER COLUMN c` vs the
1384 /// bare `ALTER c`). Exact-synonym fidelity; the canonical render emits `COLUMN`.
1385 column_keyword: bool,
1386 /// Name referenced by this syntax.
1387 name: Ident,
1388 /// The per-column change; see [`AlterColumnAction`].
1389 change: AlterColumnAction<X>,
1390 /// Source location and node identity.
1391 meta: Meta,
1392 },
1393 /// `ADD CONSTRAINT …` — add a table constraint.
1394 AddConstraint {
1395 /// The constraint being added; see [`TableConstraintDef`].
1396 constraint: TableConstraintDef<X>,
1397 /// Source location and node identity.
1398 meta: Meta,
1399 },
1400 /// `DROP CONSTRAINT <name>` — remove a table constraint.
1401 DropConstraint {
1402 /// Whether the if exists form was present in the source.
1403 if_exists: bool,
1404 /// Name referenced by this syntax.
1405 name: Ident,
1406 /// Optional behavior for this syntax.
1407 behavior: Option<DropBehavior>,
1408 /// Source location and node identity.
1409 meta: Meta,
1410 },
1411 /// `RENAME [COLUMN] <name> TO <new_name>`.
1412 ///
1413 /// PostgreSQL parses the rename forms as a distinct `RenameStmt`, not the
1414 /// `AlterTableStmt` the other actions take; the canonical AST keeps one
1415 /// [`AlterTable`] node for every `ALTER TABLE` spelling, so the rename
1416 /// is a further action variant here. The optional `COLUMN` noise word is
1417 /// recorded, mirroring [`AddColumn`](Self::AddColumn).
1418 RenameColumn {
1419 /// Whether the optional `COLUMN` noise word was written (`RENAME COLUMN c` vs
1420 /// the bare `RENAME c`). Exact-synonym fidelity; the canonical render emits
1421 /// `COLUMN`.
1422 column_keyword: bool,
1423 /// Name referenced by this syntax.
1424 name: AlterColumnTarget,
1425 /// The column's new name.
1426 new_name: Ident,
1427 /// Source location and node identity.
1428 meta: Meta,
1429 },
1430 /// `RENAME TO <new_name>` — rename the table.
1431 RenameTable {
1432 /// The table's new name.
1433 new_name: Ident,
1434 /// Source location and node identity.
1435 meta: Meta,
1436 },
1437 /// `ATTACH PARTITION <name> <bound>` — attach an existing table as a partition
1438 /// (PostgreSQL declarative partitioning). A *standalone* action: PostgreSQL parses
1439 /// `ATTACH`/`DETACH PARTITION` as their own `AlterTableStmt` productions, so they never
1440 /// combine with other actions in a comma list (`ALTER TABLE p ADD COLUMN x, ATTACH …` is a
1441 /// syntax error). Gated by
1442 /// [`CreateTableClauseSyntax::declarative_partitioning`](crate::dialect::CreateTableClauseSyntax::declarative_partitioning).
1443 AttachPartition {
1444 /// The partition (child) table name.
1445 partition: ObjectName,
1446 // Boxed as the fat child (matching the `PartitionOf` body): a `PartitionBound` is the
1447 // widest payload here, so a partition attach pays one cold allocation rather than setting
1448 // `AlterTableAction`'s width (ADR-0007, box/inline budget).
1449 /// The partition bound (`FOR VALUES …` / `DEFAULT`); see [`PartitionBound`].
1450 bound: Box<PartitionBound<X>>,
1451 /// Source location and node identity.
1452 meta: Meta,
1453 },
1454 /// `DETACH PARTITION <name> [CONCURRENTLY | FINALIZE]` — detach a partition (PostgreSQL).
1455 /// Like [`AttachPartition`](Self::AttachPartition) a standalone action. The optional
1456 /// [`mode`](Self::DetachPartition::mode) records the `CONCURRENTLY` / `FINALIZE`
1457 /// qualifier; `None` for the bare form.
1458 DetachPartition {
1459 /// The partition (child) table name.
1460 partition: ObjectName,
1461 /// Mode selected by this syntax.
1462 mode: Option<DetachPartitionMode>,
1463 /// Source location and node identity.
1464 meta: Meta,
1465 },
1466}
1467
1468/// The qualifier on a `DETACH PARTITION` action. A `Copy` leaf enum whose span rides the owning
1469/// [`AlterTableAction::DetachPartition`], like [`DropBehavior`].
1470#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1471#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1472#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1473pub enum DetachPartitionMode {
1474 /// `CONCURRENTLY` — detach without a long-held lock (PostgreSQL).
1475 Concurrently,
1476 /// `FINALIZE` — complete an interrupted concurrent detach.
1477 Finalize,
1478}
1479
1480/// A single-column alteration inside `ALTER TABLE ... ALTER COLUMN <name> ...`.
1481///
1482/// The ANSI `SET DATA TYPE` and PostgreSQL `TYPE` spellings map to one canonical
1483/// [`SetDataType`](Self::SetDataType) variant; the optional PostgreSQL
1484/// `USING <expr>` conversion expression rides along when present.
1485#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1486#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1487#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1488pub enum AlterColumnAction<X: Extension = NoExt> {
1489 /// `SET DEFAULT <expr>` — set the column's default value.
1490 SetDefault {
1491 /// Expression evaluated by this syntax.
1492 expr: Box<Expr<X>>,
1493 /// Source location and node identity.
1494 meta: Meta,
1495 },
1496 /// `DROP DEFAULT` — remove the column's default value.
1497 DropDefault {
1498 /// Source location and node identity.
1499 meta: Meta,
1500 },
1501 /// `SET NOT NULL` — add a not-null constraint to the column.
1502 SetNotNull {
1503 /// Source location and node identity.
1504 meta: Meta,
1505 },
1506 /// `DROP NOT NULL` — remove the column's not-null constraint.
1507 DropNotNull {
1508 /// Source location and node identity.
1509 meta: Meta,
1510 },
1511 /// `SET DATA TYPE <t>` / `TYPE <t> [USING …]` — change the column's data type.
1512 SetDataType {
1513 /// Whether the ANSI `SET DATA` prefix was written (`SET DATA TYPE <t>`) versus
1514 /// the bare PostgreSQL `TYPE <t>`. Exact-synonym fidelity; the canonical render
1515 /// emits `SET DATA TYPE`.
1516 set_data: bool,
1517 /// Data type named by this syntax.
1518 data_type: DataType<X>,
1519 /// Optional using for this syntax.
1520 using: Option<Box<Expr<X>>>,
1521 /// Source location and node identity.
1522 meta: Meta,
1523 },
1524}
1525
1526/// `DROP {TABLE | VIEW | INDEX | SCHEMA} [IF EXISTS] <name> [, ...] [CASCADE | RESTRICT]`.
1527///
1528/// Non-generic: a drop names objects and a behaviour only — it carries no
1529/// expressions or extension nodes — so it parallels the other leaf statement
1530/// payloads ([`super::TransactionStatement`], [`super::SessionStatement`]). The `IF
1531/// EXISTS` prefix and the `CASCADE`/`RESTRICT` drop behaviour are each gated by
1532/// `schema_change_syntax` dialect data. Materialized-view and concurrent-index drop
1533/// surfaces are not modelled here.
1534#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1535#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1536#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1537pub struct DropStatement {
1538 /// Which object kind is dropped (`TABLE`/`VIEW`/…); see [`DropObjectKind`].
1539 pub object_kind: DropObjectKind,
1540 /// Whether the if exists form was present in the source.
1541 pub if_exists: bool,
1542 /// Names in source order.
1543 pub names: ThinVec<ObjectName>,
1544 /// Optional behavior for this syntax.
1545 pub behavior: Option<DropBehavior>,
1546 /// Source location and node identity.
1547 pub meta: Meta,
1548}
1549
1550/// The kind of object a `DROP` statement removes.
1551///
1552/// The routine kinds (`FUNCTION`/`PROCEDURE`) take an argument-type signature, so
1553/// they are a separate [`Statement::DropRoutine`]
1554/// statement rather than a variant here;
1555/// this covers the object kinds a plain name list can drop.
1556#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1557#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1558#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1559pub enum DropObjectKind {
1560 /// `DROP TABLE`.
1561 Table,
1562 /// `DROP VIEW`.
1563 View,
1564 /// `DROP MATERIALIZED VIEW`.
1565 MaterializedView,
1566 /// `DROP INDEX`.
1567 Index,
1568 /// `DROP SCHEMA`.
1569 Schema,
1570 /// `DROP TYPE <name> [, …]` — a user-defined type (DuckDB/PostgreSQL), gated by
1571 /// [`StatementDdlGates::create_type`](crate::dialect::StatementDdlGates::create_type) (the same
1572 /// flag that admits [`CreateType`]). The comma list and `CASCADE`/`RESTRICT` behaviour
1573 /// ride the shared [`DropStatement`] grammar; DuckDB parse-accepts a multi-name list and
1574 /// only rejects it at plan time ("can only drop one object at a time"), so the shape is
1575 /// the general one.
1576 Type,
1577 /// `DROP SEQUENCE <name> [, …]` — a sequence generator (DuckDB/PostgreSQL), gated by
1578 /// [`StatementDdlGates::create_sequence`](crate::dialect::StatementDdlGates::create_sequence) (the same
1579 /// flag that admits [`CreateSequence`]). The comma list and `CASCADE`/`RESTRICT` ride the
1580 /// shared [`DropStatement`] grammar. DuckDB's parser accepts `DROP SEQUENCE` (the
1581 /// `IF EXISTS` form binds; the bare/list/`CASCADE` forms parse but bind-reject a missing
1582 /// object — engine-measured), so the modelled shape is the general one.
1583 Sequence,
1584 /// `DROP MACRO <name> [, …]` — a scalar macro (DuckDB), gated by
1585 /// [`StatementDdlGates::create_macro`](crate::dialect::StatementDdlGates::create_macro) (the same
1586 /// flag that admits [`CreateMacro`]). Unlike PostgreSQL's `DROP FUNCTION`, the macro drop
1587 /// takes *no* argument-type signature (`DROP MACRO m(int)` is a DuckDB syntax error), so it
1588 /// rides the shared [`DropStatement`] name-list grammar rather than the signature
1589 /// [`Statement::DropRoutine`] path — the `FUNCTION` spelling
1590 /// (which DuckDB accepts as a synonym) still routes to that routine drop, so the two DROP
1591 /// spellings land on distinct nodes and no [`MacroSpelling`] tag is needed here. The comma
1592 /// list and `CASCADE`/`RESTRICT` ride the shared grammar; DuckDB parse-accepts a multi-name
1593 /// list and only bind-rejects it ("can only drop one object at a time" — engine-measured).
1594 Macro,
1595 /// `DROP MACRO TABLE <name> [, …]` — a *table* macro (DuckDB), the drop counterpart of a
1596 /// `CREATE MACRO … AS TABLE`. Separate from [`Macro`](DropObjectKind::Macro) because DuckDB
1597 /// keeps scalar and table macros in distinct namespaces (`DROP MACRO m` drops a "Macro
1598 /// Function", `DROP MACRO TABLE m` a "Table Macro Function" — engine-measured) and the
1599 /// `TABLE` keyword must round-trip verbatim. Gated by the same
1600 /// [`StatementDdlGates::create_macro`](crate::dialect::StatementDdlGates::create_macro) flag;
1601 /// only `MACRO TABLE` (not `TABLE MACRO` or `FUNCTION TABLE`) is accepted — engine-measured.
1602 MacroTable,
1603 /// `DROP TRIGGER [IF EXISTS] [<schema> .] <name>` — a trigger (MySQL / SQLite), gated by
1604 /// either trigger-modelling flag (SQLite's
1605 /// [`StatementDdlGates::create_trigger`](crate::dialect::StatementDdlGates::create_trigger) or
1606 /// MySQL's
1607 /// [`StatementDdlGates::compound_statements`](crate::dialect::StatementDdlGates::compound_statements)).
1608 /// Both dialects spell the same name-only drop (no `ON <table>`, which is PostgreSQL's
1609 /// separate trigger-drop shape), so it rides the shared [`DropStatement`] name-list grammar.
1610 Trigger,
1611}
1612
1613/// The dependency behaviour of a `DROP` or `ALTER TABLE ... DROP`: whether dependent
1614/// objects are dropped too (`CASCADE`) or block the drop (`RESTRICT`).
1615#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1616#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1617#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1618pub enum DropBehavior {
1619 /// `CASCADE` — also drop objects that depend on the target.
1620 Cascade,
1621 /// `RESTRICT` — refuse the drop if any object depends on the target.
1622 Restrict,
1623}
1624
1625/// A PostgreSQL `DROP TRANSFORM [IF EXISTS] FOR <type> LANGUAGE <lang>
1626/// [CASCADE | RESTRICT]` statement (`DropTransformStmt`, gram.y).
1627///
1628/// Kept apart from [`DropStatement`] because a transform is not named by a plain
1629/// name list: it is identified by the `(type, language)` pair a `CREATE TRANSFORM`
1630/// registers, which is exactly the [`ObjectReference::Transform`] shape the shared
1631/// object-reference axis already models (also reached by `ALTER EXTENSION … ADD|DROP
1632/// TRANSFORM`). Reusing that variant keeps every transform reference on one node, so
1633/// an AST walk finds transforms uniformly regardless of the parent statement, rather
1634/// than re-deriving the `FOR type LANGUAGE lang` shape here (the reason [`DropRoutine`]
1635/// inlines its signature does not apply — a transform has a single fixed shape, not a
1636/// per-kind one).
1637///
1638/// PostgreSQL admits exactly one transform per statement (no comma list — engine-measured)
1639/// and places `IF EXISTS` between the `TRANSFORM` keyword and `FOR` (`DROP TRANSFORM IF
1640/// EXISTS FOR …`; `FOR type IF EXISTS LANGUAGE` is a syntax error), which is why the guard
1641/// rides on this node rather than on the shared reference render.
1642///
1643/// [`DropRoutine`]: super::Statement::DropRoutine
1644#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1645#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1646#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1647pub struct DropTransform<X: Extension = NoExt> {
1648 /// The dropped transform, always an [`ObjectReference::Transform`] (`FOR <type>
1649 /// LANGUAGE <lang>`).
1650 pub object: ObjectReference<X>,
1651 /// Whether the `IF EXISTS` guard was present in the source.
1652 pub if_exists: bool,
1653 /// Optional `CASCADE`/`RESTRICT` behaviour for this syntax.
1654 pub behavior: Option<DropBehavior>,
1655 /// Source location and node identity.
1656 pub meta: Meta,
1657}
1658
1659/// The object kind a [`Statement::CommentOn`] targets.
1660///
1661/// PostgreSQL's `COMMENT ON` object list is large (tables, columns, functions, types,
1662/// operators, extensions, roles, …). This models only the deliberate subset the
1663/// datafusion-parity corpus needs — `TABLE`, `COLUMN`, `DATABASE`, and `PROCEDURE` —
1664/// and is `#[non_exhaustive]` so the remaining object kinds can be added later without
1665/// a breaking change. The object's name rides
1666/// [`Statement::CommentOn::name`](super::Statement::CommentOn); only a procedure adds
1667/// per-target data (its argument-type signature).
1668#[non_exhaustive]
1669#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1670#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1671#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1672pub enum CommentTarget<X: Extension = NoExt> {
1673 /// `COMMENT ON TABLE <name>`.
1674 Table,
1675 /// `COMMENT ON COLUMN <name>`.
1676 Column,
1677 /// `COMMENT ON DATABASE <name>`.
1678 Database,
1679 /// `COMMENT ON PROCEDURE <name>[(<arg types>)]`. The argument-type list
1680 /// disambiguates overloaded procedures; `None` is an unspecified signature
1681 /// (bare `PROCEDURE foo`), `Some` a written list — possibly empty (`foo()`) —
1682 /// mirroring [`RoutineSignature::arg_types`](super::RoutineSignature).
1683 Procedure {
1684 /// Optional arg types for this syntax.
1685 arg_types: Option<ThinVec<DataType<X>>>,
1686 },
1687}
1688
1689/// The payload of a [`Statement::CommentOn`], boxed off the
1690/// statement enum to keep it within its size budget (like [`DropStatement`]).
1691///
1692/// `name` is the object's (possibly qualified) name; `target` records its kind (and, for
1693/// a procedure, its argument-type signature). `comment` is `None` for `IS NULL` — which
1694/// clears the object's comment — and `Some` for a string literal.
1695#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1696#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1697#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1698pub struct CommentOnStatement<X: Extension = NoExt> {
1699 /// Object targeted by this syntax.
1700 pub target: CommentTarget<X>,
1701 /// Name referenced by this syntax.
1702 pub name: ObjectName,
1703 /// Optional comment for this syntax.
1704 pub comment: Option<Literal>,
1705 /// Source location and node identity.
1706 pub meta: Meta,
1707}
1708
1709/// `CREATE SCHEMA [IF NOT EXISTS] [<name>] [AUTHORIZATION <role>] [<schema element> ...]`.
1710///
1711/// At least one of `name` / `authorization` is present — PostgreSQL's `CREATE SCHEMA
1712/// AUTHORIZATION joe` omits the schema name and derives it from the owning role.
1713///
1714/// The SQL-standard form embeds a list of component objects created *inside* the new
1715/// schema (`CREATE SCHEMA s CREATE TABLE t (...)`); [`elements`](Self::elements) holds
1716/// them as children so the whole construct stays ONE statement, mirroring PostgreSQL's
1717/// `CreateSchemaStmt.schemaElts` (which likewise carries the components as raw
1718/// statement parsenodes). Rendering them embedded preserves the statement COUNT that
1719/// downstream consumers observe — the earlier model split them into separate
1720/// `;`-joined statements, a statement-level rewrite no source-fidelity render can undo.
1721///
1722/// The element list is a *closed* admissible set enforced by the parser (measured
1723/// against PostgreSQL: `CREATE TABLE`/`VIEW`/`INDEX`/`SEQUENCE`/`TRIGGER` and `GRANT`
1724/// — and PostgreSQL rejects `CREATE MATERIALIZED VIEW`/`FUNCTION`, a nested `CREATE
1725/// SCHEMA`, `DROP`/`ALTER`/`INSERT`/… as elements). Modelled as a general
1726/// [`Statement`] list rather than a bespoke element enum — the same idiom as
1727/// [`CreateTrigger`]'s body — because the members ARE statements and reusing the
1728/// statement shape keeps the differential/structural oracles element-agnostic; the
1729/// closed set is the recorded acceptance bound, enforced at parse time. Non-empty only
1730/// under PostgreSQL (`schema_elements` gate); every other dialect parses a bare schema
1731/// head. Generic over `X` because the elements carry the extension node.
1732#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1733#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1734#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1735pub struct CreateSchema<X: Extension = NoExt> {
1736 /// Whether the if not exists form was present in the source.
1737 pub if_not_exists: bool,
1738 /// Name referenced by this syntax.
1739 pub name: Option<ObjectName>,
1740 /// Optional authorization for this syntax.
1741 pub authorization: Option<Ident>,
1742 /// The embedded schema-element statements, in source order. Empty for a bare
1743 /// `CREATE SCHEMA` head (the common case) and for every non-PostgreSQL dialect.
1744 pub elements: ThinVec<Statement<X>>,
1745 /// Source location and node identity.
1746 pub meta: Meta,
1747}
1748
1749/// `CREATE [OR REPLACE] [TEMP|TEMPORARY] VIEW` and `CREATE MATERIALIZED VIEW`,
1750/// with the `AS <query>` body that every view shares (a view body reuses [`Query`]
1751/// rather than re-deriving the SELECT grammar).
1752///
1753/// One node covers both spellings, distinguished by `materialized`. The divergent
1754/// tails are mutually exclusive by construction: the parser fills `check_option`
1755/// only for a regular view and `with_data` only for a materialized one. `OR
1756/// REPLACE` and the `MATERIALIZED` keyword (with its `WITH [NO] DATA` populate
1757/// clause) are PostgreSQL extensions gated by `schema_change_syntax` dialect data;
1758/// the ANSI `WITH [CASCADED|LOCAL] CHECK OPTION` is always accepted.
1759///
1760/// `recursive` records the `CREATE [OR REPLACE] [TEMP|TEMPORARY] RECURSIVE VIEW`
1761/// spelling (DuckDB, gated by
1762/// [`StatementDdlGates::recursive_views`](crate::dialect::StatementDdlGates::recursive_views)):
1763/// the keyword
1764/// sits between the `TEMP`/`TEMPORARY` prefix and `VIEW`, never composes with
1765/// `MATERIALIZED` (engine-rejected), and — mirroring the engine, which desugars a
1766/// recursive view to `WITH RECURSIVE` — requires the explicit `columns` list, so
1767/// the parser rejects the bare `RECURSIVE VIEW v AS …` form.
1768#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1769#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1770#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1771pub struct CreateView<X: Extension = NoExt> {
1772 /// Whether the or replace form was present in the source.
1773 pub or_replace: bool,
1774 /// The MySQL `[ALGORITHM = …] [DEFINER = …] [SQL SECURITY …]` definition-option prefix
1775 /// (see [`ViewOptions`]); all-`None` for every non-MySQL view and a bare MySQL view. The
1776 /// options sit between `OR REPLACE` and the `VIEW` keyword.
1777 pub options: ViewOptions,
1778 /// Whether the materialized form was present in the source.
1779 pub materialized: bool,
1780 /// Whether the recursive form was present in the source.
1781 pub recursive: bool,
1782 /// Optional temporary for this syntax.
1783 pub temporary: Option<TemporaryTableKind>,
1784 /// Whether the if not exists form was present in the source.
1785 pub if_not_exists: bool,
1786 /// Name referenced by this syntax.
1787 pub name: ObjectName,
1788 /// Columns in source order.
1789 pub columns: ThinVec<Ident>,
1790 /// Query governed by this node.
1791 pub query: Box<Query<X>>,
1792 /// Optional check option for this syntax.
1793 pub check_option: Option<ViewCheckOption>,
1794 /// Whether the with data form was present in the source.
1795 pub with_data: Option<bool>,
1796 /// Source location and node identity.
1797 pub meta: Meta,
1798}
1799
1800/// `WITH [ CASCADED | LOCAL ] CHECK OPTION` on a regular (non-materialized) view.
1801#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1802#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1803#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1804pub enum ViewCheckOption {
1805 /// Bare `WITH CHECK OPTION`; PostgreSQL treats the unqualified form as `CASCADED`.
1806 Unspecified,
1807 /// `WITH CASCADED CHECK OPTION` — enforce the check on this and all underlying views.
1808 Cascaded,
1809 /// `WITH LOCAL CHECK OPTION` — enforce the check on this view only.
1810 Local,
1811}
1812
1813/// The MySQL `ALGORITHM = { UNDEFINED | MERGE | TEMPTABLE }` view-processing algorithm, the
1814/// first of the [`ViewOptions`] definition-option prefix. A `Copy` tag whose span rides the
1815/// owning [`ViewOptions`], like [`SqlSecurityContext`].
1816#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1817#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1818#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1819pub enum ViewAlgorithm {
1820 /// `ALGORITHM = UNDEFINED` — the server chooses between `MERGE` and `TEMPTABLE` (the default).
1821 Undefined,
1822 /// `ALGORITHM = MERGE` — the view's text is merged into the referencing statement.
1823 Merge,
1824 /// `ALGORITHM = TEMPTABLE` — the view result is materialized into a temporary table.
1825 TempTable,
1826}
1827
1828/// The MySQL view definition-option prefix `[ALGORITHM = …] [DEFINER = …] [SQL SECURITY …]`,
1829/// shared verbatim by [`CreateView`] and [`AlterView`] — the two statements name the same
1830/// options in the same fixed source order, immediately before the `VIEW` keyword.
1831///
1832/// Each option is independently optional; the source order (algorithm, then definer, then SQL
1833/// security) is engine-required — a permutation (`DEFINER` before `ALGORITHM`, `SQL SECURITY`
1834/// before either) is `ER_PARSE_ERROR`. All-`None` is the ordinary view with no prefix (every
1835/// non-MySQL dialect, and a bare MySQL `CREATE/ALTER VIEW`). The [`Definer`] reuses the shared
1836/// routine/event account reference; the [`SqlSecurityContext`] reuses the routine security tag.
1837/// This is a non-spanned bundle — [`Definer`] carries its own span, and the two `Copy` tags
1838/// ride the owning statement's span, so the prefix needs no `meta` of its own.
1839#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
1840#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1841#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1842pub struct ViewOptions {
1843 /// The optional `ALGORITHM = …` processing algorithm; `None` when omitted.
1844 pub algorithm: Option<ViewAlgorithm>,
1845 /// The optional `DEFINER = <user>` account; `None` when omitted. Boxed like the routine
1846 /// definer (a rare fat field paying only a null pointer when absent).
1847 pub definer: Option<Box<Definer>>,
1848 /// The optional `SQL SECURITY { DEFINER | INVOKER }` privilege context; `None` when omitted.
1849 pub sql_security: Option<SqlSecurityContext>,
1850}
1851
1852/// `ALTER [ALGORITHM = …] [DEFINER = …] [SQL SECURITY …] VIEW <name> [(<columns>)] AS <query>
1853/// [WITH [CASCADED | LOCAL] CHECK OPTION]` — the MySQL view redefinition (gated by
1854/// [`StatementDdlGates::view_definition_options`](crate::dialect::StatementDdlGates::view_definition_options)).
1855///
1856/// MySQL's `ALTER VIEW` re-specifies the whole view body; it is the [`CreateView`] grammar
1857/// minus `OR REPLACE`/`IF NOT EXISTS` (server-measured: both `ER_PARSE_ERROR` after `ALTER`)
1858/// and the `TEMP`/`MATERIALIZED`/`RECURSIVE` prefixes MySQL has no views for. The shared
1859/// [`ViewOptions`] prefix and the [`ViewCheckOption`] tail are reused verbatim, and the body
1860/// reuses [`Query`] like every view.
1861#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1862#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1863#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1864pub struct AlterView<X: Extension = NoExt> {
1865 /// The `[ALGORITHM = …] [DEFINER = …] [SQL SECURITY …]` definition-option prefix; all-`None`
1866 /// for a bare `ALTER VIEW`.
1867 pub options: ViewOptions,
1868 /// The view name (`db.view` or bare `view`).
1869 pub name: ObjectName,
1870 /// The optional explicit output-column list; empty when omitted.
1871 pub columns: ThinVec<Ident>,
1872 /// The redefining `AS <query>` body.
1873 pub query: Box<Query<X>>,
1874 /// The optional `WITH [CASCADED | LOCAL] CHECK OPTION` constraint.
1875 pub check_option: Option<ViewCheckOption>,
1876 /// Source location and node identity.
1877 pub meta: Meta,
1878}
1879
1880/// A DuckDB `CREATE [PERSISTENT] SECRET <name> ( <option> <value> [, ...] )`
1881/// secrets-management statement (DuckDB-specific; gated by
1882/// [`StatementDdlGates::create_secret`](crate::dialect::StatementDdlGates::create_secret)).
1883///
1884/// DuckDB stores credentials (S3/HTTP/… access) as named secrets. `persistent`
1885/// records the `PERSISTENT` keyword (a persistent secret survives the session; the
1886/// bare form is session-temporary). The parenthesized [`options`](Self::options) are
1887/// `<name> <value>` pairs — the required `TYPE <provider>` plus provider-specific
1888/// settings (`KEY_ID`, `REGION`, …). Only the bare/`PERSISTENT` name-then-options form
1889/// the corpus exercises is modelled; the explicit `TEMPORARY` keyword, `OR REPLACE`,
1890/// `IF NOT EXISTS`, the anonymous (unnamed) secret, and the `IN <storage>` clause are
1891/// not part of this shape.
1892#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1893#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1894#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1895pub struct CreateSecret<X: Extension = NoExt> {
1896 /// Whether the persistent form was present in the source.
1897 pub persistent: bool,
1898 /// Name referenced by this syntax.
1899 pub name: ObjectName,
1900 /// Options supplied in source order.
1901 pub options: ThinVec<SecretOption<X>>,
1902 /// Source location and node identity.
1903 pub meta: Meta,
1904}
1905
1906/// One `<name> <value>` option inside a [`CreateSecret`] option list (`TYPE S3`,
1907/// `KEY_ID '...'`). The value is modelled as a general [`Expr`] — DuckDB's own option
1908/// value is a bare word, string, or list — so the corpus `TYPE <provider>` form
1909/// round-trips; the wider expression grammar is the recorded acceptance bound.
1910#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1911#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1912#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1913pub struct SecretOption<X: Extension = NoExt> {
1914 /// Name referenced by this syntax.
1915 pub name: Ident,
1916 /// Value supplied by this syntax.
1917 pub value: Expr<X>,
1918 /// Source location and node identity.
1919 pub meta: Meta,
1920}
1921
1922/// A DuckDB `DROP [PERSISTENT | TEMPORARY] SECRET [IF EXISTS] <name> [FROM <storage>]`
1923/// secrets-management statement (DuckDB-specific; gated by
1924/// [`StatementDdlGates::create_secret`](crate::dialect::StatementDdlGates::create_secret) —
1925/// the same whole-statement gate that admits [`CreateSecret`], because `DROP SECRET` is the
1926/// same secrets behaviour surface and rides the one flag rather than a second gate).
1927///
1928/// The drop counterpart of [`CreateSecret`]. In DuckDB's grammar `drop_secret.y` is the
1929/// *only* `DROP` with its own top-level `stmt` production (not a [`DropStatement`] object
1930/// kind), because it carries the `opt_persist` persistence modifier and a `FROM <storage>`
1931/// backend selector — neither of which the shared name-list DROP grammar has.
1932/// [`persistence`](Self::persistence) records which of the three `opt_persist` spellings
1933/// preceded `SECRET` (absent → [`SecretPersistence::Default`]);
1934/// [`storage`](Self::storage) is the optional `FROM <backend>` secret-storage name. The
1935/// dropped secret is a single identifier (grammar `ColId`), never a qualified object name.
1936///
1937/// Non-generic: a secret drop names a secret, an optional storage backend, and two flags —
1938/// it carries no expressions or extension nodes — so it parallels the leaf
1939/// [`DropStatement`]/[`DropTransform`] payloads.
1940#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1941#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1942#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1943pub struct DropSecretStmt {
1944 /// Which `opt_persist` spelling preceded `SECRET`; see [`SecretPersistence`].
1945 pub persistence: SecretPersistence,
1946 /// Whether the `IF EXISTS` existence guard was present in the source.
1947 pub if_exists: bool,
1948 /// Name referenced by this syntax.
1949 pub name: Ident,
1950 /// Optional `FROM <storage>` secret-storage backend selector.
1951 pub storage: Option<Ident>,
1952 /// Source location and node identity.
1953 pub meta: Meta,
1954}
1955
1956/// The `opt_persist` persistence modifier on a DuckDB `DROP SECRET`: which of the three
1957/// storage scopes the statement names. (The wider `CREATE SECRET` grammar shares the same
1958/// `opt_persist` production; [`CreateSecret`] models only its `PERSISTENT`/absent forms as
1959/// a `bool`, so this three-valued modifier lives with the drop that needs all three.)
1960#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1961#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1962#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1963pub enum SecretPersistence {
1964 /// No modifier written — DuckDB's `default` secret scope (`DROP SECRET s`).
1965 Default,
1966 /// `TEMPORARY` — the session-scoped, in-memory secret store.
1967 Temporary,
1968 /// `PERSISTENT` — the on-disk persistent secret store.
1969 Persistent,
1970}
1971
1972/// A SQLite `CREATE VIRTUAL TABLE [IF NOT EXISTS] [<schema> .] <name> USING <module>
1973/// [( <arg> [, <arg>] * )]` statement (SQLite-specific; gated by
1974/// [`StatementDdlGates::create_virtual_table`](crate::dialect::StatementDdlGates::create_virtual_table)).
1975///
1976/// A virtual table delegates its storage and query implementation to a *module*
1977/// (`fts5`, `rtree`, `csv`, …). The module — not SQLite's core grammar — owns the
1978/// argument syntax: `fts5` reads column names and `tokenize = …` options, `rtree`
1979/// reads dimension columns, and a bespoke module reads whatever it likes. SQLite's
1980/// own parser therefore imposes almost no structure on the argument list — it splits
1981/// the parenthesized text on the *top-level* commas (parentheses nest and quoted
1982/// strings are transparent) and hands each raw slice to the module verbatim, even
1983/// tolerating empty members (`USING m(a,,b)`). Module resolution and any argument
1984/// validation happen at execution time, so the parse layer accepts an unknown module
1985/// and any balanced-parenthesis token soup.
1986///
1987/// Each argument is consequently modelled as an OPAQUE verbatim [`ModuleArg`] (the
1988/// interned source text of one top-level slice), never a parsed sub-grammar — imposing
1989/// column/option structure would invent constraints SQLite does not enforce.
1990///
1991/// [`args`](Self::args) is `None` for the bare `USING m` form and `Some` (possibly
1992/// empty) for the parenthesized `USING m (…)` form, so the two round-trip distinctly.
1993/// The name admits at most two parts (`schema.table`); SQLite has no `TEMP` virtual
1994/// table, so no temporary modifier is modelled. Non-generic: the arguments hold no
1995/// expressions or extension nodes.
1996#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1997#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1998#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1999pub struct CreateVirtualTable {
2000 /// Whether the if not exists form was present in the source.
2001 pub if_not_exists: bool,
2002 /// Name referenced by this syntax.
2003 pub name: ObjectName,
2004 /// The virtual-table module name (`USING <module>`).
2005 pub module: Ident,
2006 /// Arguments in source order.
2007 pub args: Option<ThinVec<ModuleArg>>,
2008 /// Source location and node identity.
2009 pub meta: Meta,
2010}
2011
2012/// One opaque, verbatim argument of a [`CreateVirtualTable`] module argument list.
2013///
2014/// [`text`](Self::text) is the interned source text of a single top-level
2015/// comma-delimited slice, preserved exactly (internal spacing and all) because the
2016/// module owns its grammar — the parser never interprets it. An empty slice (from
2017/// `USING m(a,,b)` or a trailing comma) is a legal empty argument.
2018#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2019#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2020#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2021pub struct ModuleArg {
2022 /// The comment text.
2023 pub text: Symbol,
2024 /// Source location and node identity.
2025 pub meta: Meta,
2026}
2027
2028/// `CREATE [UNIQUE] INDEX [CONCURRENTLY] [IF NOT EXISTS] [<name>] ON <table>
2029/// [USING <method>] (<column> [, ...]) [WHERE <predicate>]`.
2030///
2031/// `UNIQUE` is portable; `CONCURRENTLY`, the `USING <method>` access-method clause,
2032/// and the trailing `WHERE` partial-index predicate are PostgreSQL extensions gated
2033/// by `schema_change_syntax.index_extensions` dialect data. The index
2034/// name is optional — PostgreSQL derives one from the table and columns when it is
2035/// omitted (`CREATE INDEX ON t (a)`). COLLATE / operator-class / `INCLUDE` column
2036/// decorations are a deliberate follow-up.
2037#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2038#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2039#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2040pub struct CreateIndex<X: Extension = NoExt> {
2041 /// Whether the unique form was present in the source.
2042 pub unique: bool,
2043 /// Whether the concurrently form was present in the source.
2044 pub concurrently: bool,
2045 /// Whether the if not exists form was present in the source.
2046 pub if_not_exists: bool,
2047 /// Name referenced by this syntax.
2048 pub name: Option<Ident>,
2049 /// Table referenced by this syntax.
2050 pub table: ObjectName,
2051 /// Optional using for this syntax.
2052 pub using: Option<Ident>,
2053 /// Columns in source order.
2054 pub columns: ThinVec<IndexColumn<X>>,
2055 /// Predicate that controls this clause.
2056 pub predicate: Option<Box<Expr<X>>>,
2057 /// Source location and node identity.
2058 pub meta: Meta,
2059}
2060
2061/// One key column of a [`CreateIndex`]: an expression with optional sort modifiers.
2062///
2063/// A bare column is the common case (`expr` is an [`Expr::Column`](super::Expr)); a
2064/// parenthesized expression indexes a computed value. `asc` / `nulls_first` mirror
2065/// [`OrderByExpr`](super::OrderByExpr) and stay `None` when the modifier is
2066/// unwritten, leaving the dialect default to the consumer.
2067#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2068#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2069#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2070pub struct IndexColumn<X: Extension = NoExt> {
2071 /// Expression evaluated by this syntax.
2072 pub expr: Expr<X>,
2073 /// Whether the asc form was present in the source.
2074 pub asc: Option<bool>,
2075 /// Whether the nulls first form was present in the source.
2076 pub nulls_first: Option<bool>,
2077 /// Source location and node identity.
2078 pub meta: Meta,
2079}
2080
2081/// A SQLite `CREATE [TEMP|TEMPORARY] TRIGGER [IF NOT EXISTS] [<schema> .] <name>
2082/// [BEFORE | AFTER | INSTEAD OF] <event> ON <table> [FOR EACH ROW] [WHEN <expr>]
2083/// BEGIN <stmt>; ... END` statement (SQLite-specific; gated by
2084/// [`StatementDdlGates::create_trigger`](crate::dialect::StatementDdlGates::create_trigger)).
2085///
2086/// Unlike the other `CREATE` families — which are always accepted because their body
2087/// grammar is standard — the trigger gate is real: only SQLite's `BEGIN … END`
2088/// SQL-statement-body form is modelled, and PostgreSQL/MySQL spell an incompatible
2089/// body (`EXECUTE FUNCTION f()` / an external routine), which they genuinely reject
2090/// for this form. Gating to SQLite (and Lenient) is therefore behaviour-accurate, not
2091/// only a modelling limitation.
2092///
2093/// The [`body`](Self::body) is a non-empty sequence of `INSERT`/`UPDATE`/`DELETE`/
2094/// `SELECT` statements — a statement list inside a statement (the structurally
2095/// heaviest SQLite shape). It reuses the existing [`Statement`] nodes rather than
2096/// minting trigger-body variants (the node stays generic over `X`); the
2097/// parser gates the body kinds and routes each through the recursion-guarded
2098/// statement dispatcher, and the enclosing [`Statement::CreateTrigger`]
2099/// boxes this payload to keep the enum within its size budget.
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 CreateTrigger<X: Extension = NoExt> {
2104 /// Optional temporary for this syntax.
2105 pub temporary: Option<TemporaryTableKind>,
2106 /// Whether the if not exists form was present in the source.
2107 pub if_not_exists: bool,
2108 /// Name referenced by this syntax.
2109 pub name: ObjectName,
2110 /// The fire time; `None` when unwritten (SQLite defaults it to `BEFORE`, but the
2111 /// absent form is preserved so it round-trips).
2112 pub timing: Option<TriggerTiming>,
2113 /// Which DML event fires the trigger; see [`TriggerEvent`].
2114 pub event: TriggerEvent,
2115 /// Table referenced by this syntax.
2116 pub table: ObjectName,
2117 /// Whether `FOR EACH ROW` was written. SQLite parses (and ignores) it but rejects
2118 /// `FOR EACH STATEMENT`, so only the `ROW` form is modelled as a surface tag.
2119 pub for_each_row: bool,
2120 /// Optional when for this syntax.
2121 pub when: Option<Expr<X>>,
2122 /// The trigger-body statement list, in source order (always non-empty — SQLite
2123 /// rejects an empty `BEGIN END`).
2124 pub body: ThinVec<Statement<X>>,
2125 /// Source location and node identity.
2126 pub meta: Meta,
2127}
2128
2129/// When a [`CreateTrigger`] fires relative to the event.
2130///
2131/// A tag (no `meta`): the timing keyword's span is subsumed by the enclosing
2132/// [`CreateTrigger`], exactly as [`TemporaryTableKind`] rides its parent's span.
2133#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2134#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2135#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2136pub enum TriggerTiming {
2137 /// `BEFORE` — fire before the event is applied.
2138 Before,
2139 /// `AFTER` — fire after the event is applied.
2140 After,
2141 /// `INSTEAD OF` (valid on a view).
2142 InsteadOf,
2143}
2144
2145/// The DML event that fires a [`CreateTrigger`].
2146///
2147/// `UPDATE` alone fires on any column; `UPDATE OF a, b` restricts it to the listed
2148/// columns ([`columns`](Self::Update::columns) empty for the bare `UPDATE`). `INSERT`
2149/// and `DELETE` take no column list.
2150#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2151#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2152#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2153pub enum TriggerEvent {
2154 /// `DELETE` — fire on row deletion.
2155 Delete {
2156 /// Source location and node identity.
2157 meta: Meta,
2158 },
2159 /// `INSERT` — fire on row insertion.
2160 Insert {
2161 /// Source location and node identity.
2162 meta: Meta,
2163 },
2164 /// `UPDATE [OF col, …]` — fire on row update, optionally restricted to columns.
2165 Update {
2166 /// Columns in source order.
2167 columns: ThinVec<Ident>,
2168 /// Source location and node identity.
2169 meta: Meta,
2170 },
2171}
2172
2173/// `CREATE [DEFINER = <user>] TRIGGER [IF NOT EXISTS] <name> {BEFORE | AFTER}
2174/// {INSERT | UPDATE | DELETE} ON <table> FOR EACH ROW [{FOLLOWS | PRECEDES} <other>]
2175/// <sp_proc_stmt>` — the MySQL SQL/PSM trigger (gated by
2176/// [`StatementDdlGates::compound_statements`](crate::dialect::StatementDdlGates::compound_statements),
2177/// the same stored-program gate the routine wrappers ride).
2178///
2179/// A DISTINCT node from the SQLite [`CreateTrigger`], not an extension of it, because the two
2180/// shapes do not genuinely unify — the decisive split is the body: SQLite's is a
2181/// `BEGIN <stmt>; … END` *list* of plain SQL statements ([`CreateTrigger::body`], a
2182/// [`ThinVec`]), MySQL's is a *single* `sp_proc_stmt` — usually a
2183/// [`Statement::Compound`] block, but any one body statement (a `SET`,
2184/// a flow-control construct) — parsed through the shared `parse_body_statement` seam and boxed
2185/// because [`Statement`] is large. Their decorating axes are disjoint too: SQLite carries
2186/// `TEMP` / `WHEN` / `INSTEAD OF` / `UPDATE OF <cols>`, MySQL carries [`DEFINER`](Self::definer),
2187/// mandatory `BEFORE`/`AFTER` timing, mandatory `FOR EACH ROW`, and the
2188/// [`FOLLOWS`/`PRECEDES` ordering](Self::ordering) anchor. Folding both into one node would
2189/// leave half its fields dialect-dead — the [`CreateProcedure`] precedent (a MySQL
2190/// stored-program object gets its own node rather than overloading a cross-dialect one) applies.
2191///
2192/// The [`timing`](Self::timing) and [`event`](Self::event) axes reuse the shared
2193/// [`TriggerTiming`] / [`TriggerEvent`] vocabulary — MySQL simply never emits the SQLite-only
2194/// [`TriggerTiming::InsteadOf`] or a non-empty [`TriggerEvent::Update`] column list (the parser
2195/// enforces the bare forms), so the reuse is safe and keeps one trigger-axis vocabulary.
2196#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2197#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2198#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2199pub struct CreateStoredTrigger<X: Extension = NoExt> {
2200 /// The optional `DEFINER = <user>` account prefix; `None` when omitted. Boxed (the rare
2201 /// fat field pays only a null pointer when absent), like [`CreateProcedure::definer`].
2202 pub definer: Option<Box<Definer>>,
2203 /// Whether the `IF NOT EXISTS` guard was written (MySQL 8.0.29+).
2204 pub if_not_exists: bool,
2205 /// Name referenced by this syntax.
2206 pub name: ObjectName,
2207 /// The fire time (`BEFORE` / `AFTER`) — mandatory in MySQL (no defaulted/absent form, and
2208 /// never [`TriggerTiming::InsteadOf`]).
2209 pub timing: TriggerTiming,
2210 /// The DML event that fires the trigger (`INSERT` / `UPDATE` / `DELETE`) — always the bare
2211 /// form; MySQL has no `UPDATE OF <cols>`, so a [`TriggerEvent::Update`] carries no columns.
2212 pub event: TriggerEvent,
2213 /// Table referenced by this syntax.
2214 pub table: ObjectName,
2215 /// The optional `{FOLLOWS | PRECEDES} <other>` ordering anchor relative to another trigger on
2216 /// the same table and event; `None` for the unordered form.
2217 pub ordering: Option<TriggerOrder>,
2218 /// The trigger body: one `sp_proc_stmt`, boxed. Usually a
2219 /// [`Statement::Compound`] `BEGIN … END` block, but any single
2220 /// body statement is admitted, parsed through the `parse_body_statement` seam.
2221 pub body: Box<Statement<X>>,
2222 /// Source location and node identity.
2223 pub meta: Meta,
2224}
2225
2226/// The MySQL `{FOLLOWS | PRECEDES} <other_trigger>` ordering clause of a
2227/// [`CreateStoredTrigger`] (`trigger_follows_precedes_clause`): where the new trigger fires
2228/// relative to an existing one on the same table and event.
2229///
2230/// The anchor is an [`Ident`] (MySQL's `ident_or_text` — a bare or quoted trigger name) so both
2231/// source spellings round-trip from its span. The keyword direction is the enum variant.
2232#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2233#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2234#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2235pub enum TriggerOrder {
2236 /// `FOLLOWS <other>` — fire immediately after the named trigger.
2237 Follows {
2238 /// The anchor trigger this one follows.
2239 anchor: Ident,
2240 /// Source location and node identity.
2241 meta: Meta,
2242 },
2243 /// `PRECEDES <other>` — fire immediately before the named trigger.
2244 Precedes {
2245 /// The anchor trigger this one precedes.
2246 anchor: Ident,
2247 /// Source location and node identity.
2248 meta: Meta,
2249 },
2250}
2251
2252/// `CREATE DATABASE [IF NOT EXISTS] <name>`.
2253///
2254/// Non-generic, like [`CreateSchema`]: a database is named by identifiers only. The
2255/// trailing `[WITH] <option> ...` list (`OWNER`, `TEMPLATE`, `ENCODING`, …) is a
2256/// deliberate follow-up — real migrations rarely spell it and it carries no
2257/// expressions the shape must compare.
2258#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2259#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2260#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2261pub struct CreateDatabase {
2262 /// The MySQL `IF NOT EXISTS` existence guard (the [`CreateTable`] precedent). Gated
2263 /// for acceptance by
2264 /// [`ExistenceGuards::create_database_if_not_exists`](crate::dialect::ExistenceGuards):
2265 /// PostgreSQL has no `CREATE DATABASE IF NOT EXISTS`, so the flag is off there and
2266 /// the guard surfaces as a clean parse error.
2267 pub if_not_exists: bool,
2268 /// Name referenced by this syntax.
2269 pub name: ObjectName,
2270 /// Source location and node identity.
2271 pub meta: Meta,
2272}
2273
2274/// `CREATE [OR REPLACE] FUNCTION <name> ( [<param> [, ...]] ) [RETURNS <type>]
2275/// [<option> ...]`.
2276///
2277/// The routine body has two disjoint grammatical homes on the [`FunctionBody`] axis. An
2278/// opaque source *string* is introduced by the order-independent `AS` option, spelled either
2279/// single-quoted (`AS 'SELECT 1'`) or dollar-quoted (`AS $$ … $$` / `AS $tag$ … $tag$`) where
2280/// the dialect enables [`dollar_quoted_strings`](crate::dialect::StringLiteralSyntax); the
2281/// delimiter and verbatim body text round-trip from the body [`Literal`]'s span. A *live* SQL
2282/// body — a SQL-standard `RETURN <expr>` (PostgreSQL 14+ / standard `opt_routine_body`) — is
2283/// the trailing [`body`](Self::body) slot, which strictly follows the whole option list (proven
2284/// against the PG oracle: `LANGUAGE sql RETURN 1` accepts, `RETURN 1 LANGUAGE sql` rejects), so
2285/// it is a distinct parse position from `AS`, not another order-independent option. The two
2286/// homes share the [`FunctionBody`] *type* (the axis vocabulary the dollar-body sibling staged)
2287/// but not a grammatical slot.
2288///
2289/// The node is generic over `X` because a parameter or `RETURNS` type can be a host-owned
2290/// [`DataType::Other`] under a custom dialect, and the live
2291/// [`body`](Self::body) carries an [`Expr`]`<X>` (stock builtins pin `X = NoExt`). The M1
2292/// surface is the parameter list, an optional `RETURNS <type>`, the [`FunctionOption`] cluster
2293/// the corpus exercises (`LANGUAGE` / `AS` / null-call behaviour), and the trailing `RETURN`
2294/// body; the fuller volatility, security, parallelism, and `SET` option matrix — and the
2295/// statement-list `BEGIN ATOMIC … END` body that shares the trailing slot — are deliberate
2296/// follow-ups the open option list and the [`FunctionBody`] axis extend without a shape change.
2297#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2298#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2299#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2300pub struct CreateFunction<X: Extension = NoExt> {
2301 /// Whether the or replace form was present in the source.
2302 pub or_replace: bool,
2303 /// The optional MySQL `DEFINER = <user>` clause; `None` when omitted (always `None`
2304 /// for the PostgreSQL string-body routine, which has no definer prefix). Boxed because a
2305 /// [`Definer`] is comparatively large and the clause is rare, so a definer-less routine
2306 /// pays only a null pointer (the [`FunctionParam::default`] precedent).
2307 pub definer: Option<Box<Definer>>,
2308 /// Whether the MySQL `IF NOT EXISTS` guard was written (MySQL 8.0.29+); always `false`
2309 /// for PostgreSQL, which spells the intent `CREATE OR REPLACE`.
2310 pub if_not_exists: bool,
2311 /// Name referenced by this syntax.
2312 pub name: ObjectName,
2313 /// params in source order.
2314 pub params: ThinVec<FunctionParam<X>>,
2315 /// Optional returns for this syntax.
2316 pub returns: Option<DataType<X>>,
2317 /// Options supplied in source order.
2318 pub options: ThinVec<FunctionOption<X>>,
2319 /// The trailing SQL-standard routine body (`opt_routine_body`) — a `RETURN <expr>` live
2320 /// SQL expression that follows the entire option list. Boxed and optional because it is
2321 /// usually absent (an `AS`-string routine leaves it unset, paying one null pointer — the
2322 /// [`FunctionParam::default`] precedent); a [`FunctionBody::Definition`] string body never
2323 /// lands here (it rides the `AS` option instead).
2324 pub body: Option<Box<FunctionBody<X>>>,
2325 /// Source location and node identity.
2326 pub meta: Meta,
2327}
2328
2329/// One routine parameter: an optional argument [`mode`](Self::mode)
2330/// (`IN`/`OUT`/`INOUT`/`VARIADIC`), an optional name, its type (`b INT`), and an
2331/// optional default value (`b INT DEFAULT 0` / `b INT = 0`).
2332///
2333/// The three lead-in facets are independent: a bare type (`f(int)`) leaves both
2334/// `mode` and `name` unset, a mode may precede an unnamed type (`f(OUT int)`), and a
2335/// named parameter may carry a default. The name is PostgreSQL's `param_name` =
2336/// `type_function_name` production (parsed with the [type-name reservation
2337/// set](crate::dialect::FeatureSet::reserved_type_name)): a `type_func_name` keyword
2338/// like `left` is a legal parameter name, a `col_name` keyword like `int` is not — so
2339/// a lone `int` is unambiguously the type, mirroring how PostgreSQL resolves the
2340/// name-vs-type ambiguity. The default is the PostgreSQL `func_arg_with_default`
2341/// tail — boxed because it is usually absent and a [`FunctionParamDefault`] (an
2342/// [`Expr`]) is comparatively large, so a default-less parameter pays only a null
2343/// pointer (the [`ForeignKeyRef`] referential-action precedent). It is *parameter
2344/// metadata*: an ordinary function-**call** argument is a
2345/// [`FunctionArg`](crate::ast::FunctionArg) on the unrelated call path and is
2346/// unaffected.
2347#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2348#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2349#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2350pub struct FunctionParam<X: Extension = NoExt> {
2351 /// Mode selected by this syntax.
2352 pub mode: Option<FunctionParamMode>,
2353 /// Name referenced by this syntax.
2354 pub name: Option<Ident>,
2355 /// Data type named by this syntax.
2356 pub data_type: DataType<X>,
2357 /// Optional default for this syntax.
2358 pub default: Option<Box<FunctionParamDefault<X>>>,
2359 /// Source location and node identity.
2360 pub meta: Meta,
2361}
2362
2363/// A routine parameter's argument mode — PostgreSQL's `arg_class` prefix
2364/// (`func_arg: arg_class param_name func_type | …`). `IN` is the default and may be
2365/// omitted; `OUT`/`INOUT` mark output parameters; `VARIADIC` marks a trailing
2366/// array-spread parameter. A fieldless [`Copy`] tag whose span rides the owning
2367/// [`FunctionParam`] (the [`FunctionParamDefaultSpelling`] precedent): it records only
2368/// the written mode so it round-trips.
2369///
2370/// PostgreSQL admits the mode either before *or* after the name (`IN a int` and
2371/// `a IN int` both parse); this models the documented, canonical mode-first spelling
2372/// (which sqlparser-rs's `ArgMode` also targets) and adds [`Variadic`](Self::Variadic),
2373/// which sqlparser-rs omits but real PostgreSQL requires. The rarer name-first spelling
2374/// is a deliberate parse-surface boundary — no corpus exercises it and PostgreSQL itself
2375/// normalizes the two orders to one AST.
2376#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2377#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2378#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2379pub enum FunctionParamMode {
2380 /// `IN` — an input parameter (the default when omitted).
2381 In,
2382 /// `OUT` — an output parameter.
2383 Out,
2384 /// `INOUT` — an input/output parameter.
2385 InOut,
2386 /// `VARIADIC` — a trailing array-spread parameter.
2387 Variadic,
2388}
2389
2390/// A routine parameter's default-value clause: the [`spelling`](Self::spelling)
2391/// (`DEFAULT` keyword vs `=`) and the value [`Expr`]. PostgreSQL's grammar spells
2392/// one production two ways — `func_arg_with_default: func_arg DEFAULT a_expr |
2393/// func_arg '=' a_expr` — so the tag records which the source used and the value is
2394/// a live SQL expression reusing [`Expr`] rather than a re-derived literal grammar.
2395///
2396/// sqlparser-rs models the same clause as a bare `default_expr: Option<Expr>` and
2397/// always re-renders it as `= <expr>`, normalizing the `DEFAULT` spelling away; this
2398/// AST instead carries the fieldless [`FunctionParamDefaultSpelling`] tag so both
2399/// forms round-trip verbatim (the spelling-fidelity doctrine, as for
2400/// [`EqualsSpelling`](crate::ast::EqualsSpelling)).
2401#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2402#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2403#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2404pub struct FunctionParamDefault<X: Extension = NoExt> {
2405 /// Exact source spelling retained for faithful rendering.
2406 pub spelling: FunctionParamDefaultSpelling,
2407 /// Value supplied by this syntax.
2408 pub value: Expr<X>,
2409 /// Source location and node identity.
2410 pub meta: Meta,
2411}
2412
2413/// Surface spelling for a [`FunctionParamDefault`]: PostgreSQL admits both the
2414/// `DEFAULT` keyword and a bare `=` to introduce a routine parameter's default, two
2415/// spellings of the one `func_arg_with_default` production. A `Copy` tag whose span
2416/// rides the owning [`FunctionParamDefault`], like [`MacroSpelling`]; it records the
2417/// source form only so rendering round-trips exactly (a fidelity tag, not a validity
2418/// one — the parser accepts both spellings wherever defaults are gated on).
2419#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2420#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2421#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2422pub enum FunctionParamDefaultSpelling {
2423 /// The `DEFAULT <expr>` keyword form.
2424 Default,
2425 /// The `= <expr>` operator form.
2426 Equals,
2427}
2428
2429#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2430#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2431#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2432/// The SQL function option forms represented by the AST.
2433pub enum FunctionOption<X: Extension = NoExt> {
2434 /// A `LANGUAGE <name>` clause naming the routine's implementation language. The name is a
2435 /// `NonReservedWord_or_Sconst` ([`LanguageName`]) — a bare word or, on the PostgreSQL
2436 /// surface, an `Sconst` string (`LANGUAGE 'sql'`/`E'sql'`/`$$sql$$`) — shared with the
2437 /// [`DoArg::Language`](crate::ast::DoArg) argument.
2438 Language {
2439 /// Name referenced by this syntax.
2440 name: LanguageName,
2441 /// Source location and node identity.
2442 meta: Meta,
2443 },
2444 /// `AS <body>` — the *string* routine body, carried on the [`FunctionBody`] axis. The `AS`
2445 /// option only ever homes an opaque source string (plain `'…'` or dollar-quoted
2446 /// `$tag$…$tag$`), i.e. [`FunctionBody::Definition`]; the live `RETURN <expr>` body rides
2447 /// the trailing [`CreateFunction::body`] slot, a disjoint grammatical position.
2448 As {
2449 /// The routine's string body; see [`FunctionBody`].
2450 body: FunctionBody<X>,
2451 /// Source location and node identity.
2452 meta: Meta,
2453 },
2454 /// The null-call behaviour (`CALLED ON NULL INPUT` / `RETURNS NULL ON NULL INPUT`
2455 /// / `STRICT`).
2456 NullBehavior {
2457 /// The null-input behaviour; see [`FunctionNullBehavior`].
2458 behavior: FunctionNullBehavior,
2459 /// Source location and node identity.
2460 meta: Meta,
2461 },
2462 /// The MySQL `[NOT] DETERMINISTIC` routine characteristic — whether the routine
2463 /// always produces the same result for the same inputs. The `not` field records the negated
2464 /// spelling so `NOT DETERMINISTIC` round-trips distinctly from a
2465 /// routine that simply omits the characteristic. A stored-routine option (MySQL SQL/PSM),
2466 /// not a PostgreSQL `CREATE FUNCTION` clause.
2467 Deterministic {
2468 /// Whether the `NOT` prefix was written (`NOT DETERMINISTIC`).
2469 not: bool,
2470 /// Source location and node identity.
2471 meta: Meta,
2472 },
2473 /// The MySQL SQL-data-access routine characteristic (`CONTAINS SQL` / `NO SQL` /
2474 /// `READS SQL DATA` / `MODIFIES SQL DATA`) — the advisory declaration of how the
2475 /// routine body touches data.
2476 DataAccess {
2477 /// Which data-access class was written; see [`SqlDataAccess`].
2478 access: SqlDataAccess,
2479 /// Source location and node identity.
2480 meta: Meta,
2481 },
2482 /// The MySQL `SQL SECURITY {DEFINER | INVOKER}` routine characteristic — the privilege
2483 /// context the routine executes under.
2484 SqlSecurity {
2485 /// The security context; see [`SqlSecurityContext`].
2486 context: SqlSecurityContext,
2487 /// Source location and node identity.
2488 meta: Meta,
2489 },
2490 /// The MySQL `COMMENT '<string>'` routine characteristic — a stored descriptive comment.
2491 /// The PostgreSQL comment surface is the separate `COMMENT ON FUNCTION` statement, so
2492 /// this inline option is MySQL-only.
2493 Comment {
2494 /// The comment string literal.
2495 comment: Literal,
2496 /// Source location and node identity.
2497 meta: Meta,
2498 },
2499}
2500
2501/// The MySQL routine SQL-data-access characteristic — how the routine body is declared to
2502/// touch data. Advisory (the server does not enforce it), recorded so it round-trips. A
2503/// `Copy` tag whose span rides the owning [`FunctionOption::DataAccess`], like
2504/// [`FunctionNullBehavior`].
2505#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2506#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2507#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2508pub enum SqlDataAccess {
2509 /// `CONTAINS SQL` — the body contains SQL but neither reads nor writes data (the default).
2510 ContainsSql,
2511 /// `NO SQL` — the body contains no SQL.
2512 NoSql,
2513 /// `READS SQL DATA` — the body reads but does not modify data.
2514 ReadsSqlData,
2515 /// `MODIFIES SQL DATA` — the body may modify data.
2516 ModifiesSqlData,
2517}
2518
2519/// The MySQL `SQL SECURITY` context — whose privileges a routine runs under. A `Copy` tag
2520/// whose span rides the owning [`FunctionOption::SqlSecurity`], like [`SqlDataAccess`].
2521#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2522#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2523#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2524pub enum SqlSecurityContext {
2525 /// `SQL SECURITY DEFINER` — run under the routine definer's privileges (the default).
2526 Definer,
2527 /// `SQL SECURITY INVOKER` — run under the calling user's privileges.
2528 Invoker,
2529}
2530
2531/// The executable body of a [`CreateFunction`] routine — the body-representation **axis**.
2532///
2533/// Two body *kinds* are modelled today. [`Definition`](Self::Definition) is an opaque source
2534/// string introduced by the order-independent `AS` option (`AS 'SELECT 1'`, `AS $$ … $$`,
2535/// `AS $body$ … $body$`). [`Return`](Self::Return) is the SQL-standard *live* body
2536/// `RETURN <expr>` (PostgreSQL 14+ / standard `opt_routine_body`), a real SQL [`Expr`] that
2537/// rides the trailing [`CreateFunction::body`] slot — a *distinct* parse position from `AS`
2538/// (the `RETURN` body strictly follows the whole option list, oracle-proven). This is a
2539/// distinct enum — not a bare [`Literal`] field on [`FunctionOption::As`] — precisely so the
2540/// live body reuses one body vocabulary across both slots rather than reshaping the option.
2541///
2542/// The axis is body *kind* (opaque string vs. live SQL), **not** quote style: a plain `'…'`
2543/// and a dollar-quoted `$tag$…$tag$` body are both
2544/// [`LiteralKind::String`](crate::ast::LiteralKind) and differ only in source spelling. That
2545/// spelling — the delimiter tag and the verbatim body text — round-trips from the
2546/// [`Literal`]'s span; it is recovered from source, never normalized, so a dollar body
2547/// re-renders byte-for-byte.
2548///
2549/// The enum is generic over `X` because [`Return`](Self::Return) carries an [`Expr`]`<X>`; the
2550/// [`Definition`](Self::Definition) string variant is generic-free. A statement-list
2551/// `BEGIN ATOMIC … END` body (PostgreSQL, oracle-accepted) shares the trailing slot and slots
2552/// in here as a further variant carrying a routine-body statement list — deliberately deferred:
2553/// modelling it honestly needs a routine-body statement grammar (its inner `RETURN` is not a
2554/// top-level [`Statement`]), which is a separate surface from this single-`Expr` body.
2555#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2556#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2557#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2558pub enum FunctionBody<X: Extension = NoExt> {
2559 /// `AS <string>` — an opaque routine body in the target language, kept as the source
2560 /// [`Literal`] (plain-quoted or dollar-quoted) rather than re-parsed, because the body's
2561 /// grammar is the target language, not SQL.
2562 Definition {
2563 /// Definition text supplied by this syntax.
2564 definition: Literal,
2565 /// Source location and node identity.
2566 meta: Meta,
2567 },
2568 /// `RETURN <expr>` — the SQL-standard live expression body (`opt_routine_body`'s
2569 /// `ReturnStmt`). The [`Expr`] is boxed (the [`MacroBody`] precedent) so the common `AS`
2570 /// string body pays no [`Expr`] footprint on the shared [`FunctionOption::As`] path.
2571 Return {
2572 /// Expression evaluated by this syntax.
2573 expr: Box<Expr<X>>,
2574 /// Source location and node identity.
2575 meta: Meta,
2576 },
2577 /// A MySQL SQL/PSM routine body — one `routine_body` statement, usually the
2578 /// `BEGIN … END` compound block ([`Statement::Compound`])
2579 /// but any single body statement (a bare `SET`, a flow-control construct, a `RETURN`).
2580 /// This is the live statement-list body the [`FunctionBody`] axis doc anticipated; it
2581 /// rides the trailing [`CreateFunction::body`] slot exactly as [`Return`](Self::Return)
2582 /// does — a distinct grammatical position after the whole characteristic list. Boxed
2583 /// ([`Statement`] is large) so the common `AS`-string path pays no footprint.
2584 Block {
2585 /// The routine body statement (typically a compound block); parsed through the
2586 /// `parse_body_statement` seam so its inner grammar is the MySQL body sub-language.
2587 body: Box<Statement<X>>,
2588 /// Source location and node identity.
2589 meta: Meta,
2590 },
2591}
2592
2593/// How a routine handles a NULL argument. `Strict` is the exact shorthand for
2594/// `ReturnsNullOnNull`, kept distinct so the written spelling round-trips. A `Copy`
2595/// leaf enum whose span lives on the owning [`FunctionOption`], like [`DropBehavior`].
2596#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2597#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2598#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2599pub enum FunctionNullBehavior {
2600 /// `CALLED ON NULL INPUT` — run the routine even when an argument is null.
2601 CalledOnNull,
2602 /// `RETURNS NULL ON NULL INPUT` — return null without running when any argument is null.
2603 ReturnsNullOnNull,
2604 /// `STRICT` — shorthand for `RETURNS NULL ON NULL INPUT`.
2605 Strict,
2606}
2607
2608/// The MySQL `DEFINER = <user>` clause prefixing a routine (and, on other tickets, a
2609/// view/trigger/event) definition — the account the object's `SQL SECURITY DEFINER` context
2610/// resolves to.
2611///
2612/// This is the *measured minimal* account surface the routine family needs: the bare
2613/// `<user>[@<host>]` account name and the `CURRENT_USER [()]` self-reference, the two forms
2614/// the corpus and the oracle exercise. The full MySQL account-name axis — the quoting-nuance
2615/// matrix, role grants, and the `IDENTIFIED BY` authentication tail — is owned by
2616/// `parse-mysql-user-role-ddl`; this node deliberately stops at the account *reference* a
2617/// routine header carries, and that ticket widens it (or lifts it to a shared account node)
2618/// without reshaping the routine statements that hold it.
2619#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2620#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2621#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2622pub enum Definer {
2623 /// `DEFINER = <user>[@<host>]` — a named account. The `user` and optional `host` are
2624 /// each an [`Ident`] so both the bare (`root`) and quoted (`'root'@'localhost'`) source
2625 /// spellings round-trip from their spans.
2626 Account {
2627 /// The account user name.
2628 user: Ident,
2629 /// The optional `@<host>` part; `None` for a bare user with no host.
2630 host: Option<Ident>,
2631 /// Source location and node identity.
2632 meta: Meta,
2633 },
2634 /// `DEFINER = CURRENT_USER [()]` — the current user at definition time.
2635 CurrentUser {
2636 /// Whether the empty `()` call-style parentheses were written.
2637 parens: bool,
2638 /// Source location and node identity.
2639 meta: Meta,
2640 },
2641}
2642
2643/// `CREATE [DEFINER = <user>] PROCEDURE [IF NOT EXISTS] <name> ( [<param> [, …]] )
2644/// [<characteristic> …] <routine_body>` — the MySQL stored-procedure definition (SQL/PSM).
2645///
2646/// A distinct node from [`CreateFunction`]: a procedure has no `RETURNS` type, its body may
2647/// not contain `RETURN` (the routine family enforces `ER_SP_BADRETURN`), and it is invoked by
2648/// `CALL` rather than in an expression. It shares the routine *vocabulary* — the parameter
2649/// list reuses [`FunctionParam`] (with its `IN`/`OUT`/`INOUT` [`FunctionParamMode`]) and the
2650/// characteristic list reuses [`FunctionOption`] (`LANGUAGE`/`COMMENT`/`DETERMINISTIC`/data
2651/// access/`SQL SECURITY`) — but not a statement shape. The [`body`](Self::body) is a single
2652/// MySQL body statement (typically a [`Statement::Compound`]
2653/// block), parsed through the `parse_body_statement` seam and boxed because [`Statement`] is
2654/// large.
2655#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2656#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2657#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2658pub struct CreateProcedure<X: Extension = NoExt> {
2659 /// The optional `DEFINER = <user>` clause; `None` when omitted. Boxed (the rare fat
2660 /// field pays only a null pointer when absent), like [`CreateFunction::definer`].
2661 pub definer: Option<Box<Definer>>,
2662 /// Whether the `IF NOT EXISTS` guard was written (MySQL 8.0.29+).
2663 pub if_not_exists: bool,
2664 /// The procedure name.
2665 pub name: ObjectName,
2666 /// The parameter list, in source order (always parenthesized, possibly empty).
2667 pub params: ThinVec<FunctionParam<X>>,
2668 /// The routine characteristics, in source order (`LANGUAGE`/`COMMENT`/`DETERMINISTIC`/
2669 /// data-access/`SQL SECURITY`), carried on the shared [`FunctionOption`] axis.
2670 pub characteristics: ThinVec<FunctionOption<X>>,
2671 /// The routine body — one MySQL body statement, usually a compound block.
2672 pub body: Box<Statement<X>>,
2673 /// Source location and node identity.
2674 pub meta: Meta,
2675}
2676
2677/// Which routine kind an [`AlterRoutine`] targets — the `PROCEDURE`/`FUNCTION` object keyword.
2678/// A `Copy` surface tag whose span rides the owning [`AlterRoutine`], like
2679/// [`ShowRoutineKind`](crate::ast::ShowRoutineKind).
2680#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2681#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2682#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2683pub enum RoutineKind {
2684 /// `PROCEDURE`.
2685 Procedure,
2686 /// `FUNCTION`.
2687 Function,
2688}
2689
2690/// `ALTER {PROCEDURE | FUNCTION} <name> [<characteristic> …]` — the MySQL routine-characteristics
2691/// alteration (SQL/PSM). Characteristics only: unlike [`CreateProcedure`]/[`CreateFunction`]
2692/// there is no body and no parameter list — `ALTER` re-declares only the mutable characteristic
2693/// subset (`COMMENT` / `LANGUAGE` / data access / `SQL SECURITY`; the server rejects
2694/// `DETERMINISTIC` here, which the parser enforces). One node spans both routine kinds via
2695/// [`kind`](Self::kind); boxed by [`Statement::AlterRoutine`].
2696#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2697#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2698#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2699pub struct AlterRoutine<X: Extension = NoExt> {
2700 /// Whether this alters a `PROCEDURE` or a `FUNCTION`.
2701 pub kind: RoutineKind,
2702 /// The routine name.
2703 pub name: ObjectName,
2704 /// The re-declared characteristics, in source order (the `ALTER`-legal subset).
2705 pub characteristics: ThinVec<FunctionOption<X>>,
2706 /// Source location and node identity.
2707 pub meta: Meta,
2708}
2709
2710/// The `ON SCHEDULE <when>` clause of a MySQL `CREATE`/`ALTER EVENT` — the one-shot
2711/// `AT <timestamp>` form or the recurring `EVERY <interval> [STARTS …] [ENDS …]` form.
2712///
2713/// Both timestamp positions ([`At::at`](Self::At::at), [`Every::starts`](Self::Every::starts),
2714/// [`Every::ends`](Self::Every::ends)) are ordinary [`Expr`]s, so a `NOW() + INTERVAL 1 DAY`
2715/// offset rides the expression grammar with no special schedule handling. The recurring
2716/// interval is `<value> <unit>` where the unit reuses the shared [`IntervalFields`] vocabulary
2717/// (MySQL's `interval` production, rendered in underscore spelling — `DAY_HOUR`, `MINUTE_SECOND`).
2718#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2719#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2720#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2721pub enum EventSchedule<X: Extension = NoExt> {
2722 /// `AT <timestamp>` — a one-shot event fired once at the given time. The [`Expr`] is
2723 /// boxed to keep the schedule (and the enclosing event node) lean.
2724 At {
2725 /// The one-shot execution timestamp (any expression, e.g. `NOW() + INTERVAL 1 DAY`).
2726 at: Box<Expr<X>>,
2727 /// Source location and node identity.
2728 meta: Meta,
2729 },
2730 /// `EVERY <value> <unit> [STARTS <ts>] [ENDS <ts>]` — a recurring event. The `<value>
2731 /// <unit>` interval reuses the shared [`IntervalFields`] vocabulary; `STARTS`/`ENDS` are
2732 /// optional window bounds (each an ordinary expression).
2733 Every {
2734 /// The interval count (an expression, typically an integer literal).
2735 value: Box<Expr<X>>,
2736 /// The interval unit, in the shared [`IntervalFields`] vocabulary (MySQL `interval`).
2737 unit: IntervalFields,
2738 /// The optional `STARTS <ts>` window start.
2739 starts: Option<Box<Expr<X>>>,
2740 /// The optional `ENDS <ts>` window end.
2741 ends: Option<Box<Expr<X>>>,
2742 /// Source location and node identity.
2743 meta: Meta,
2744 },
2745}
2746
2747/// `ON COMPLETION [NOT] PRESERVE` — whether a MySQL event is retained after its last
2748/// execution. A `Copy` surface tag whose span rides the owning event node, like
2749/// [`RoutineKind`].
2750#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2751#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2752#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2753pub enum EventOnCompletion {
2754 /// `ON COMPLETION PRESERVE` — keep the event definition after its final run.
2755 Preserve,
2756 /// `ON COMPLETION NOT PRESERVE` — drop the event after its final run (the default).
2757 NotPreserve,
2758}
2759
2760/// The `ENABLE | DISABLE [ON SLAVE|REPLICA]` activation state of a MySQL event. A `Copy`
2761/// surface tag whose span rides the owning event node.
2762///
2763/// [`DisableOnReplica`](Self::DisableOnReplica) carries a [`ReplicaSpelling`] because MySQL
2764/// 8.4 still admits BOTH the deprecated `DISABLE ON SLAVE` and the current `DISABLE ON
2765/// REPLICA` (server-measured: both accept, `SLAVE` warns) and the source spelling must
2766/// round-trip.
2767#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2768#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2769#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2770pub enum EventStatus {
2771 /// `ENABLE` — the event is active.
2772 Enable,
2773 /// `DISABLE` — the event is inactive on every node.
2774 Disable,
2775 /// `DISABLE ON SLAVE` / `DISABLE ON REPLICA` — the event is active only on the source,
2776 /// disabled on replicas. The [`ReplicaSpelling`] records which keyword was written.
2777 DisableOnReplica(ReplicaSpelling),
2778}
2779
2780/// Which of MySQL's two interchangeable replica keywords a source used — the deprecated
2781/// `SLAVE` or the current `REPLICA`. Both parse on MySQL 8.4 (`SLAVE` with a deprecation
2782/// warning), so the spelling is a round-trip tag, not a semantic difference. A `Copy` leaf.
2783#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2784#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2785#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2786pub enum ReplicaSpelling {
2787 /// The deprecated `SLAVE` keyword (still admitted on MySQL 8.4 with a warning).
2788 Slave,
2789 /// The current `REPLICA` keyword.
2790 Replica,
2791}
2792
2793/// `CREATE [DEFINER = <user>] EVENT [IF NOT EXISTS] <name> ON SCHEDULE <schedule>
2794/// [ON COMPLETION [NOT] PRESERVE] [ENABLE | DISABLE [ON SLAVE|REPLICA]] [COMMENT '…']
2795/// DO <body>` — the MySQL scheduled-event definition (SQL/PSM, gated by
2796/// [`StatementDdlGates::compound_statements`](crate::dialect::StatementDdlGates::compound_statements),
2797/// the stored-program body surface).
2798///
2799/// The clause order after the name is fixed by the grammar (schedule, completion, status,
2800/// comment, `DO`) — server-measured: a comment before the status is a syntax error. The
2801/// [`body`](Self::body) is a single MySQL body statement (usually a [`Statement::Compound`]
2802/// block), parsed through the `parse_body_statement` seam and boxed because [`Statement`] is
2803/// large; an event body carries no return value, so `RETURN` inside it is rejected (as in a
2804/// procedure body — server `ER_SP_BADRETURN`). The [`Definer`] prefix reuses the shared
2805/// routine account reference.
2806#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2807#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2808#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2809pub struct CreateEvent<X: Extension = NoExt> {
2810 /// The optional `DEFINER = <user>` clause; `None` when omitted. Boxed like the routine
2811 /// definer (a rare fat field paying only a null pointer when absent).
2812 pub definer: Option<Box<Definer>>,
2813 /// Whether the `IF NOT EXISTS` guard was written.
2814 pub if_not_exists: bool,
2815 /// The event name (`db.event` or bare `event`).
2816 pub name: ObjectName,
2817 /// The `ON SCHEDULE` clause — the `AT` one-shot or `EVERY` recurring form.
2818 pub schedule: EventSchedule<X>,
2819 /// The optional `ON COMPLETION [NOT] PRESERVE` clause.
2820 pub on_completion: Option<EventOnCompletion>,
2821 /// The optional `ENABLE | DISABLE [ON SLAVE|REPLICA]` activation state.
2822 pub status: Option<EventStatus>,
2823 /// The optional `COMMENT '…'` description.
2824 pub comment: Option<Literal>,
2825 /// The event body — one MySQL body statement (the `DO <stmt>` clause).
2826 pub body: Box<Statement<X>>,
2827 /// Source location and node identity.
2828 pub meta: Meta,
2829}
2830
2831/// `ALTER [DEFINER = <user>] EVENT <name> [ON SCHEDULE <schedule>] [ON COMPLETION [NOT]
2832/// PRESERVE] [RENAME TO <name>] [ENABLE | DISABLE [ON SLAVE|REPLICA]] [COMMENT '…']
2833/// [DO <body>]` — the MySQL event alteration.
2834///
2835/// Every clause is optional, but the grammar requires **at least one** (server-measured: a
2836/// bare `ALTER EVENT e` is a syntax error), which the parser enforces. The clause order is
2837/// the same fixed order as [`CreateEvent`], with `RENAME TO` between the schedule/completion
2838/// and the status. Unlike [`CreateEvent`] the schedule and body are optional, and
2839/// [`on_completion`](Self::on_completion) may appear with or without a schedule.
2840#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2841#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2842#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2843pub struct AlterEvent<X: Extension = NoExt> {
2844 /// The optional `DEFINER = <user>` clause; `None` when omitted.
2845 pub definer: Option<Box<Definer>>,
2846 /// The event name.
2847 pub name: ObjectName,
2848 /// The optional `ON SCHEDULE` clause.
2849 pub schedule: Option<EventSchedule<X>>,
2850 /// The optional `ON COMPLETION [NOT] PRESERVE` clause.
2851 pub on_completion: Option<EventOnCompletion>,
2852 /// The optional `RENAME TO <name>` clause.
2853 pub rename_to: Option<ObjectName>,
2854 /// The optional `ENABLE | DISABLE [ON SLAVE|REPLICA]` activation state.
2855 pub status: Option<EventStatus>,
2856 /// The optional `COMMENT '…'` description.
2857 pub comment: Option<Literal>,
2858 /// The optional `DO <body>` re-definition (one MySQL body statement).
2859 pub body: Option<Box<Statement<X>>>,
2860 /// Source location and node identity.
2861 pub meta: Meta,
2862}
2863
2864/// `DROP EVENT [IF EXISTS] <name>` — the MySQL scheduled-event drop. Kept apart from the
2865/// generic [`Statement::Drop`] because MySQL's event drop names exactly ONE event
2866/// (server-measured: `DROP EVENT a, b` is a syntax error) and takes no `CASCADE`/`RESTRICT`
2867/// behaviour, unlike the shared name-list [`DropStatement`] grammar.
2868#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2869#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2870#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2871pub struct DropEvent {
2872 /// Whether the `IF EXISTS` guard was written.
2873 pub if_exists: bool,
2874 /// The single event name (`db.event` or bare `event`).
2875 pub name: ObjectName,
2876 /// Source location and node identity.
2877 pub meta: Meta,
2878}
2879
2880/// `DROP {DATABASE | SCHEMA} [IF EXISTS] <name>` — MySQL's single-database drop, where
2881/// `DATABASE` and `SCHEMA` are exact synonyms (the lexer folds `SCHEMA` onto `DATABASE`).
2882/// Kept apart from the generic name-list [`Statement::Drop`] because the form names exactly
2883/// ONE unqualified database — server-measured on mysql:8, `DROP DATABASE a, b` is
2884/// `ER_PARSE_ERROR`, `DROP DATABASE db.x` is `ER_PARSE_ERROR`, and it takes no
2885/// `CASCADE`/`RESTRICT` (both syntax errors) — unlike PostgreSQL/DuckDB's `DROP SCHEMA
2886/// <name> [, …] [CASCADE | RESTRICT]`, which keeps riding the shared [`DropStatement`]
2887/// grammar there. Gated by
2888/// [`StatementDdlGates::drop_database`](crate::dialect::StatementDdlGates::drop_database).
2889#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2890#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2891#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2892pub struct DropDatabase {
2893 /// Which of the two synonymous keywords was written; recorded so the source spelling
2894 /// round-trips verbatim (the tag pattern of [`TemporaryTableKind`], not a semantic
2895 /// difference — MySQL treats the two identically).
2896 pub spelling: DatabaseKeyword,
2897 /// Whether the `IF EXISTS` guard was written.
2898 pub if_exists: bool,
2899 /// The single database name — a bare unqualified identifier (`ident`; a dotted
2900 /// `db.x` is a server syntax error).
2901 pub name: Ident,
2902 /// Source location and node identity.
2903 pub meta: Meta,
2904}
2905
2906/// The keyword spelling of a database object — `DATABASE` or its exact synonym `SCHEMA`
2907/// (MySQL folds the two onto one grammar). Recorded on [`DropDatabase`] so the written
2908/// keyword round-trips.
2909#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2910#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2911#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2912pub enum DatabaseKeyword {
2913 /// The `DATABASE` spelling.
2914 Database,
2915 /// The `SCHEMA` spelling (a MySQL synonym for `DATABASE`).
2916 Schema,
2917}
2918
2919/// `DROP INDEX <name> ON <table> [ALGORITHM [=] {DEFAULT | INPLACE | INSTANT | COPY}]
2920/// [LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}]` — MySQL's index drop (`drop_index_stmt`,
2921/// `sql_yacc.yy`). Kept apart from the generic name-list [`Statement::Drop`] because it names
2922/// the owning table with a mandatory `ON <table>` (server-measured: `DROP INDEX i` with no
2923/// `ON` is `ER_PARSE_ERROR` on mysql:8) and carries the online-DDL `ALGORITHM`/`LOCK`
2924/// execution hints, neither of which the shared grammar has. The index name is a bare
2925/// identifier (`ident`; a dotted `i.j` is a syntax error) while the table is a possibly-dotted
2926/// [`ObjectName`] (`db.t` binds). Gated by
2927/// [`IndexAlterSyntax::index_drop_on_table`](crate::dialect::IndexAlterSyntax::index_drop_on_table).
2928#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2929#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2930#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2931pub struct DropIndexOnTable {
2932 /// The dropped index — a bare unqualified identifier.
2933 pub name: Ident,
2934 /// The table the index belongs to (`ON <table>`); may be schema-qualified (`db.t`).
2935 pub table: ObjectName,
2936 /// The trailing `ALGORITHM`/`LOCK` execution hints in source order (`opt_index_lock_and_algorithm`).
2937 /// The grammar admits at most one of each and both orderings (`ALGORITHM … LOCK …` or
2938 /// `LOCK … ALGORITHM …`, server-measured: a repeated `ALGORITHM`/`LOCK` is `ER_PARSE_ERROR`),
2939 /// so this holds zero, one, or two entries and preserves the written order for round-trip.
2940 pub options: ThinVec<IndexLockAlgorithmOption>,
2941 /// Source location and node identity.
2942 pub meta: Meta,
2943}
2944
2945/// One entry of a `DROP INDEX … ON …` (or online `ALTER TABLE`) `opt_index_lock_and_algorithm`
2946/// tail — an `ALGORITHM` or a `LOCK` execution hint. Each records whether the optional `=`
2947/// (`opt_equal`) was written so the surface form round-trips verbatim.
2948#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2949#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2950#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2951pub enum IndexLockAlgorithmOption {
2952 /// `ALGORITHM [=] {DEFAULT | INPLACE | INSTANT | COPY}`.
2953 Algorithm {
2954 /// Whether the optional `=` was written between the keyword and the value.
2955 equals: bool,
2956 /// The chosen algorithm.
2957 value: IndexAlgorithm,
2958 },
2959 /// `LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}`.
2960 Lock {
2961 /// Whether the optional `=` was written between the keyword and the value.
2962 equals: bool,
2963 /// The chosen lock level.
2964 value: IndexLock,
2965 },
2966}
2967
2968/// The online-DDL `ALGORITHM` value (`alter_algorithm_option_value`). `DEFAULT` is a keyword;
2969/// the rest are matched case-insensitively as identifiers, and an unknown value is a *binding*
2970/// reject (`ER_UNKNOWN_ALTER_ALGORITHM` 1800), not a syntax error — so only these four bind.
2971#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2972#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2973#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2974pub enum IndexAlgorithm {
2975 /// `DEFAULT` — let the server choose.
2976 Default,
2977 /// `INPLACE` — rebuild in place where supported.
2978 Inplace,
2979 /// `INSTANT` — metadata-only change where supported.
2980 Instant,
2981 /// `COPY` — rebuild via a table copy.
2982 Copy,
2983}
2984
2985/// The online-DDL `LOCK` value (`alter_lock_option_value`). `DEFAULT` is a keyword; the rest
2986/// are matched case-insensitively as identifiers, and an unknown value is a *binding* reject
2987/// (`ER_UNKNOWN_ALTER_LOCK` 1801), not a syntax error — so only these four bind.
2988#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2989#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2990#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2991pub enum IndexLock {
2992 /// `DEFAULT` — the server's default concurrency.
2993 Default,
2994 /// `NONE` — permit concurrent reads and writes.
2995 None,
2996 /// `SHARED` — permit concurrent reads only.
2997 Shared,
2998 /// `EXCLUSIVE` — permit no concurrent access.
2999 Exclusive,
3000}
3001
3002/// `CREATE [OR REPLACE] [TEMP|TEMPORARY] {MACRO | FUNCTION} [IF NOT EXISTS] <name>
3003/// (<param> [, ...]) AS <body>` — DuckDB's macro DDL (gated by
3004/// [`StatementDdlGates::create_macro`](crate::dialect::StatementDdlGates::create_macro)).
3005///
3006/// This is a distinct node from [`CreateFunction`], not a variant of it: a DuckDB
3007/// macro body is a *live* SQL expression or query (`AS x + 1` / `AS TABLE SELECT …`),
3008/// whereas a PostgreSQL/MySQL `CREATE FUNCTION` body is an opaque source *string* in a
3009/// target language (`AS 'RETURN 1'`). The two share only the `CREATE … (params) AS`
3010/// prefix; their bodies, parameter grammars (a macro parameter is a bare untyped name,
3011/// a routine parameter is `[name] <type>`), and option tails are disjoint.
3012///
3013/// DuckDB spells the same feature with either the `MACRO` keyword or `FUNCTION` as an
3014/// exact synonym; [`spelling`](Self::spelling) records which so the source keyword
3015/// round-trips (the tag pattern of [`TemporaryTableKind`], not a semantic difference).
3016/// Whole-statement gated to DuckDB (and Lenient): every other dialect either has no
3017/// macro grammar (`MACRO` is left unconsumed and surfaces as an unknown statement) or
3018/// spells `CREATE FUNCTION` as the incompatible string-body routine, so the gate is
3019/// behaviour-accurate, not merely a modelling limitation. Boxed by the enclosing
3020/// [`Statement::CreateMacro`] to keep the enum within its size budget.
3021///
3022/// The single-body surface — one `(params) AS body` per statement — is modelled; DuckDB
3023/// additionally accepts a comma-separated *overload* list (`… AS a, (x, y) AS b`), a
3024/// deliberate follow-up (no vendored corpus statement spells it) the shape would extend
3025/// by lifting `params`/`body` into an overload sequence.
3026#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3027#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3028#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3029pub struct CreateMacro<X: Extension = NoExt> {
3030 /// Whether the or replace form was present in the source.
3031 pub or_replace: bool,
3032 /// Optional temporary for this syntax.
3033 pub temporary: Option<TemporaryTableKind>,
3034 /// Exact source spelling retained for faithful rendering.
3035 pub spelling: MacroSpelling,
3036 /// Whether the if not exists form was present in the source.
3037 pub if_not_exists: bool,
3038 /// Name referenced by this syntax.
3039 pub name: ObjectName,
3040 /// params in source order.
3041 pub params: ThinVec<MacroParam<X>>,
3042 /// Statement or query body governed by this node.
3043 pub body: MacroBody<X>,
3044 /// Source location and node identity.
3045 pub meta: Meta,
3046}
3047
3048/// Which keyword introduced a [`CreateMacro`] — DuckDB accepts `MACRO` and `FUNCTION`
3049/// as exact synonyms. A `Copy` tag whose span rides the owning [`CreateMacro`], like
3050/// [`TemporaryTableKind`].
3051#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3052#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3053#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3054pub enum MacroSpelling {
3055 /// Source used the `MACRO` spelling.
3056 Macro,
3057 /// Source used the `FUNCTION` spelling.
3058 Function,
3059}
3060
3061/// One [`CreateMacro`] parameter: a bare untyped name with an optional `:= <expr>`
3062/// default (`CREATE MACRO m(a, b := 10) AS …`). Unlike a [`FunctionParam`], a macro
3063/// parameter carries no type — DuckDB macros are template-substituted, not typed — so
3064/// the name is mandatory and no `DataType` rides the node.
3065#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3066#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3067#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3068pub struct MacroParam<X: Extension = NoExt> {
3069 /// Name referenced by this syntax.
3070 pub name: Ident,
3071 /// Optional default for this syntax.
3072 pub default: Option<Expr<X>>,
3073 /// Source location and node identity.
3074 pub meta: Meta,
3075}
3076
3077/// A [`CreateMacro`] body: a scalar expression (`AS <expr>`) or a table-producing
3078/// query (`AS TABLE <query>`). The `TABLE` keyword is the DuckDB discriminant between a
3079/// scalar macro (returns one value) and a table macro (returns a relation); the body
3080/// grammar reuses [`Expr`] / [`Query`] rather than re-deriving either.
3081#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3082#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3083#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3084pub enum MacroBody<X: Extension = NoExt> {
3085 /// A scalar macro body — an expression returning a single value.
3086 Scalar {
3087 /// Expression evaluated by this syntax.
3088 expr: Box<Expr<X>>,
3089 /// Source location and node identity.
3090 meta: Meta,
3091 },
3092 /// A table macro body — a query returning a relation.
3093 Table {
3094 /// Query governed by this node.
3095 query: Box<Query<X>>,
3096 /// Source location and node identity.
3097 meta: Meta,
3098 },
3099}
3100
3101/// `CREATE [OR REPLACE] [TEMP|TEMPORARY] TYPE [IF NOT EXISTS] <name> AS <definition>` —
3102/// DuckDB's user-defined-type DDL (gated by
3103/// [`StatementDdlGates::create_type`](crate::dialect::StatementDdlGates::create_type)).
3104///
3105/// The name is optionally schema-qualified (`CREATE TYPE s1.mood AS …`). Its
3106/// [`definition`](Self::definition) is either the dedicated `ENUM` production (a label
3107/// list or a label-supplying query) or an alias to any other (possibly composite/nested)
3108/// data type — see [`CreateTypeDefinition`].
3109///
3110/// `OR REPLACE` and `IF NOT EXISTS` are mutually exclusive in DuckDB's grammar (under `OR
3111/// REPLACE` the parser reads `IF` as the type name), so the parser fills `if_not_exists`
3112/// only on the plain form; the two flags are never both set. Whole-statement gated to
3113/// DuckDB (and Lenient): every other dialect leaves `TYPE` unconsumed after `CREATE`,
3114/// where it surfaces as the `TABLE` expectation (an unknown statement). Boxed by the
3115/// enclosing [`Statement::CreateType`] to keep the enum
3116/// within its size budget.
3117#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3118#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3119#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3120pub struct CreateType<X: Extension = NoExt> {
3121 /// Whether the or replace form was present in the source.
3122 pub or_replace: bool,
3123 /// Optional temporary for this syntax.
3124 pub temporary: Option<TemporaryTableKind>,
3125 /// Whether the if not exists form was present in the source.
3126 pub if_not_exists: bool,
3127 /// Name referenced by this syntax.
3128 pub name: ObjectName,
3129 /// The type definition — an `ENUM` label list or a type alias; see [`CreateTypeDefinition`].
3130 pub definition: CreateTypeDefinition<X>,
3131 /// Source location and node identity.
3132 pub meta: Meta,
3133}
3134
3135/// The `AS <definition>` body of a [`CreateType`]. DuckDB's `CREATE TYPE` grammar has a
3136/// dedicated `ENUM` rule distinct from the general data-type grammar — its labels are
3137/// parser-restricted to string constants (`ENUM(1, 2)` is a *parse* error, unlike the
3138/// data-type-position `x::ENUM(...)` cast, which parses any modifier and only bind-rejects
3139/// a non-string) — so an enum definition is its own variant rather than a
3140/// [`DataType::Enum`](crate::ast::DataType). Every non-`ENUM` spelling (`STRUCT`/`MAP`/
3141/// `UNION`, an alias to a named type, arrays, `DECIMAL(p, s)`, …) reuses the shared data
3142/// type via [`Alias`](Self::Alias).
3143#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3144#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3145#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3146pub enum CreateTypeDefinition<X: Extension = NoExt> {
3147 /// `AS ENUM ('a', 'b', …)` — a labelled enumeration. The label list may be empty
3148 /// (`ENUM ()`, which DuckDB accepts) and each label is a string literal.
3149 Enum {
3150 /// labels in source order.
3151 labels: ThinVec<Literal>,
3152 /// Source location and node identity.
3153 meta: Meta,
3154 },
3155 /// `AS ENUM (<query>)` — the enum labels are drawn from a query's single column
3156 /// (`ENUM (SELECT DISTINCT month FROM …)`); the parenthesized query reuses [`Query`].
3157 EnumFromQuery {
3158 /// Query governed by this node.
3159 query: Box<Query<X>>,
3160 /// Source location and node identity.
3161 meta: Meta,
3162 },
3163 /// `AS <type>` — an alias to another type: a scalar (`AS VARCHAR`), a composite/nested
3164 /// constructor (`AS STRUCT(…)` / `AS MAP(…)` / `AS UNION(…)`), an array (`AS my_int[]`),
3165 /// or a named user type. Reuses the shared [`DataType`] grammar.
3166 Alias {
3167 /// Data type named by this syntax.
3168 data_type: DataType<X>,
3169 /// Source location and node identity.
3170 meta: Meta,
3171 },
3172}
3173
3174/// A `CREATE [TEMPORARY] SEQUENCE [IF NOT EXISTS] <name> [<option> ...]` sequence-generator
3175/// statement (SQL:2003 T176; PostgreSQL and DuckDB), gated by
3176/// [`StatementDdlGates::create_sequence`](crate::dialect::StatementDdlGates::create_sequence).
3177///
3178/// The trailing options are the SQL-standard sequence-generator core — `START [WITH]`,
3179/// `INCREMENT [BY]`, `MINVALUE`/`NO MINVALUE`, `MAXVALUE`/`NO MAXVALUE`, `CYCLE`/`NO CYCLE` —
3180/// which both engines' parsers accept in any order, so this single node is gated
3181/// per-dialect by the one flag rather than split into parallel PostgreSQL/DuckDB nodes
3182/// (ADR-0011). They reuse [`IdentityOption`], the identical option vocabulary the
3183/// `GENERATED … AS IDENTITY` column form already carries; the [`Cache`](IdentityOption::Cache)
3184/// variant is never produced here — DuckDB's `CREATE SEQUENCE` grammar rejects `CACHE`
3185/// (engine-measured), so admitting it would over-accept, and it is a PostgreSQL-only
3186/// extension left to a follow-up.
3187///
3188/// PostgreSQL's extended tails (`AS <type>`, `CACHE`, `OWNED BY`, `RESTART`) and DuckDB's
3189/// `OR REPLACE SEQUENCE` are deliberately unmodelled here: each is one engine's extension,
3190/// not the shared standard core, and none is exercised by the DuckDB corpus.
3191#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3192#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3193#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3194pub struct CreateSequence<X: Extension = NoExt> {
3195 /// Optional temporary for this syntax.
3196 pub temporary: Option<TemporaryTableKind>,
3197 /// Whether the if not exists form was present in the source.
3198 pub if_not_exists: bool,
3199 /// Name referenced by this syntax.
3200 pub name: ObjectName,
3201 /// Options supplied in source order.
3202 pub options: ThinVec<IdentityOption<X>>,
3203 /// Source location and node identity.
3204 pub meta: Meta,
3205}
3206
3207/// A `CREATE EXTENSION [IF NOT EXISTS] <name> [WITH] [SCHEMA s] [VERSION v] [CASCADE]`
3208/// statement (PostgreSQL; gated by
3209/// [`StatementDdlGates::extension_ddl`](crate::dialect::StatementDdlGates::extension_ddl)).
3210///
3211/// Non-generic: an extension is named by a bare identifier and its options carry only
3212/// names and version strings — no expressions or extension nodes. The `WITH` keyword is
3213/// optional sugar (PostgreSQL's `opt_with`); [`with`](Self::with) records whether it was
3214/// written so it round-trips. The options are order-independent and repeatable in the
3215/// grammar (`create_extension_opt_list`), kept here in source order.
3216#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3217#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3218#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3219pub struct CreateExtension {
3220 /// Whether the if not exists form was present in the source.
3221 pub if_not_exists: bool,
3222 /// The extension name.
3223 pub name: Ident,
3224 /// Whether the optional `WITH` keyword preceded the option list.
3225 pub with: bool,
3226 /// Options supplied in source order.
3227 pub options: ThinVec<CreateExtensionOption>,
3228 /// Source location and node identity.
3229 pub meta: Meta,
3230}
3231
3232/// One `CREATE EXTENSION` option (`create_extension_opt_item`). PostgreSQL's grammar
3233/// carries a `FROM <old_version>` item whose action is a parse-time
3234/// `FEATURE_NOT_SUPPORTED` reject, so that form is not modelled here.
3235#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3236#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3237#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3238pub enum CreateExtensionOption {
3239 /// `SCHEMA <name>` — the schema the extension's objects are installed into.
3240 Schema {
3241 /// The target schema name.
3242 name: Ident,
3243 /// Source location and node identity.
3244 meta: Meta,
3245 },
3246 /// `VERSION <version>` — the version to install.
3247 Version {
3248 /// Version value supplied by this syntax.
3249 version: ExtensionVersion,
3250 /// Source location and node identity.
3251 meta: Meta,
3252 },
3253 /// `CASCADE` — install missing required extensions too.
3254 Cascade {
3255 /// Source location and node identity.
3256 meta: Meta,
3257 },
3258}
3259
3260/// An extension version value (PostgreSQL's `NonReservedWord_or_Sconst`): a bare word or
3261/// a string constant. PostgreSQL folds both to the same string internally, but the
3262/// surface spelling is kept distinct so it round-trips.
3263#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3264#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3265#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3266pub enum ExtensionVersion {
3267 /// A bare non-reserved word (`VERSION v1`).
3268 Word {
3269 /// Identifier-form version value.
3270 word: Ident,
3271 /// Source location and node identity.
3272 meta: Meta,
3273 },
3274 /// A string constant (`VERSION '1.0'`).
3275 String {
3276 /// Value supplied by this syntax.
3277 value: Literal,
3278 /// Source location and node identity.
3279 meta: Meta,
3280 },
3281}
3282
3283/// An `ALTER EXTENSION <name> ...` statement, gated by
3284/// [`StatementDdlGates::extension_ddl`](crate::dialect::StatementDdlGates::extension_ddl).
3285///
3286/// Unifies PostgreSQL's two `ALTER EXTENSION` productions under one node, since both
3287/// share the `ALTER EXTENSION <name>` head: the `UPDATE [TO <version>]` version bump
3288/// (`AlterExtensionStmt`) and the `ADD`/`DROP <member>` membership change
3289/// (`AlterExtensionContentsStmt`). PostgreSQL's `ALTER EXTENSION <name> SET SCHEMA
3290/// <schema>` is a separate `AlterObjectSchemaStmt` production (shared with every other
3291/// relocatable object), not an `AlterExtensionStmt`, so it is out of this node's scope.
3292#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3293#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3294#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3295pub struct AlterExtension<X: Extension = NoExt> {
3296 /// The extension name.
3297 pub name: Ident,
3298 /// The change applied to the extension.
3299 pub action: AlterExtensionAction<X>,
3300 /// Source location and node identity.
3301 pub meta: Meta,
3302}
3303
3304/// The change an [`AlterExtension`] applies.
3305#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3306#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3307#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3308pub enum AlterExtensionAction<X: Extension = NoExt> {
3309 /// `UPDATE [TO <version>]` — bump the installed version (`AlterExtensionStmt`).
3310 Update {
3311 /// The target version, when a `TO` clause was written.
3312 version: Option<ExtensionVersion>,
3313 /// Source location and node identity.
3314 meta: Meta,
3315 },
3316 /// `ADD <member>` (`add` is `true`) or `DROP <member>` — change extension membership
3317 /// (`AlterExtensionContentsStmt`).
3318 Change {
3319 /// `true` for `ADD`, `false` for `DROP`.
3320 add: bool,
3321 /// The member object added to or dropped from the extension.
3322 member: ObjectReference<X>,
3323 /// Source location and node identity.
3324 meta: Meta,
3325 },
3326}
3327
3328/// An `ALTER DATABASE [IF EXISTS] <name> SET ALIAS TO <alias>` statement (DuckDB), gated by
3329/// [`StatementDdlGates::alter_database`](crate::dialect::StatementDdlGates::alter_database).
3330///
3331/// DuckDB's sole `AlterDatabaseStmt` production re-aliases an attached database
3332/// (`third_party/libpg_query/grammar/statements/alter_database.y` at the pinned v1.5.4
3333/// commit `08e34c447b`). The grammar reduces `SET <ident> TO <name>`, but its action rejects
3334/// any keyword but `ALIAS`, so only `SET ALIAS TO` is a parse-accept (engine-measured: `SET
3335/// FOO TO` is a syntax error, and `ALTER DATABASE d RENAME TO e` is likewise a syntax error —
3336/// DuckDB has no `RENAME` form for a database). The change is held in an
3337/// [`AlterDatabaseAction`] enum so a sibling dialect (MySQL's charset/collation `ALTER
3338/// DATABASE`) can add its own action variants without reshaping this node.
3339#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3340#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3341#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3342pub struct AlterDatabase {
3343 /// Whether the `IF EXISTS` existence guard was present in the source.
3344 pub if_exists: bool,
3345 /// The database name.
3346 pub name: Ident,
3347 /// The change applied to the database.
3348 pub action: AlterDatabaseAction,
3349 /// Source location and node identity.
3350 pub meta: Meta,
3351}
3352
3353/// The change an [`AlterDatabase`] applies.
3354#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3355#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3356#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3357pub enum AlterDatabaseAction {
3358 /// `SET ALIAS TO <alias>` — re-alias the attached database.
3359 SetAlias {
3360 /// The new alias.
3361 new_name: Ident,
3362 /// Source location and node identity.
3363 meta: Meta,
3364 },
3365}
3366
3367/// An `ALTER SEQUENCE [IF EXISTS] <name> <option>...` statement (DuckDB), gated by
3368/// [`StatementDdlGates::alter_sequence`](crate::dialect::StatementDdlGates::alter_sequence).
3369///
3370/// DuckDB's `AlterSeqStmt` production changes a sequence generator's options
3371/// (`third_party/libpg_query/grammar/statements/alter_sequence.y` at the pinned v1.5.4
3372/// commit). Its `SeqOptList` is the same option grammar `CREATE SEQUENCE` reduces, so the
3373/// shared core is carried through the reused [`IdentityOption`] axis
3374/// ([`AlterSequenceOption::Common`]); the ALTER-only leads DuckDB additionally parses
3375/// (`RESTART`, `AS <type>`, `OWNED BY`, `SEQUENCE NAME`) are their own variants. Options
3376/// are kept in source order. `SET SCHEMA` is *not* an option here — it is DuckDB's separate
3377/// [`AlterObjectSchema`] production, dispatched before this one.
3378#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3379#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3380#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3381pub struct AlterSequence<X: Extension = NoExt> {
3382 /// Whether the `IF EXISTS` existence guard was present in the source.
3383 pub if_exists: bool,
3384 /// The sequence name.
3385 pub name: ObjectName,
3386 /// Options supplied in source order.
3387 pub options: ThinVec<AlterSequenceOption<X>>,
3388 /// Source location and node identity.
3389 pub meta: Meta,
3390}
3391
3392/// One `ALTER SEQUENCE` option (DuckDB's `SeqOptElem`).
3393#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3394#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3395#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3396pub enum AlterSequenceOption<X: Extension = NoExt> {
3397 /// A shared sequence-generator option `CREATE SEQUENCE` also accepts (`START [WITH]`,
3398 /// `INCREMENT [BY]`, `MIN`/`MAXVALUE`, `NO MIN`/`MAXVALUE`, `CYCLE`/`NO CYCLE`, `CACHE`),
3399 /// reusing the [`IdentityOption`] axis.
3400 Common {
3401 /// The reused shared option.
3402 option: IdentityOption<X>,
3403 /// Source location and node identity.
3404 meta: Meta,
3405 },
3406 /// `RESTART [[WITH] <n>]` — reset the sequence's current value (bare = restart to its
3407 /// start value).
3408 Restart {
3409 /// The restart value, when a `[WITH] <n>` tail was written.
3410 value: Option<Expr<X>>,
3411 /// Source location and node identity.
3412 meta: Meta,
3413 },
3414 /// `AS <type>` — change the sequence's integer type.
3415 As {
3416 /// The sequence's new integer type.
3417 data_type: DataType<X>,
3418 /// Source location and node identity.
3419 meta: Meta,
3420 },
3421 /// `OWNED BY {<column> | NONE}` — tie the sequence's lifetime to a column, or detach it
3422 /// (`None` renders `NONE`).
3423 OwnedBy {
3424 /// The owning column, or `None` for `OWNED BY NONE`.
3425 owner: Option<ObjectName>,
3426 /// Source location and node identity.
3427 meta: Meta,
3428 },
3429 /// `SEQUENCE NAME <name>` — the internal rename form libpg_query documents as pg_dump-only.
3430 SequenceName {
3431 /// The new internal name.
3432 name: ObjectName,
3433 /// Source location and node identity.
3434 meta: Meta,
3435 },
3436}
3437
3438/// An `ALTER {TABLE | VIEW | SEQUENCE} [IF EXISTS] <name> SET SCHEMA <schema>` statement
3439/// (DuckDB's `AlterObjectSchemaStmt`), gated by
3440/// [`StatementDdlGates::alter_object_set_schema`](crate::dialect::StatementDdlGates::alter_object_set_schema).
3441///
3442/// Relocates a relocatable object to another schema
3443/// (`third_party/libpg_query/grammar/statements/alter_schema.y` at the pinned v1.5.4 commit).
3444/// DuckDB 1.5.4's binder rejects this with `Not implemented: T_AlterObjectSchemaStmt`, but the
3445/// production is parse-reachable (engine-measured via `json_serialize_sql`), and PARSE-level
3446/// parity is the modelled bar — analogous to PostgreSQL's grammar-present, engine-unimplemented
3447/// `CreateAssertionStmt`. The object kind is one of [`SchemaRelocationObject`]'s three.
3448#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3449#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3450#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3451pub struct AlterObjectSchema {
3452 /// Which relocatable object kind was named.
3453 pub object_type: SchemaRelocationObject,
3454 /// Whether the `IF EXISTS` existence guard was present in the source.
3455 pub if_exists: bool,
3456 /// The object's current (schema-qualified) name.
3457 pub name: ObjectName,
3458 /// The destination schema.
3459 pub new_schema: Ident,
3460 /// Source location and node identity.
3461 pub meta: Meta,
3462}
3463
3464/// The relocatable object kind an [`AlterObjectSchema`] moves — DuckDB's `AlterObjectSchemaStmt`
3465/// admits exactly these three object heads.
3466#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3467#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3468#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3469pub enum SchemaRelocationObject {
3470 /// `ALTER TABLE … SET SCHEMA`.
3471 Table,
3472 /// `ALTER VIEW … SET SCHEMA`.
3473 View,
3474 /// `ALTER SEQUENCE … SET SCHEMA`.
3475 Sequence,
3476}
3477
3478/// A reference to a database object by kind and kind-appropriate signature — the shared
3479/// object-reference axis for PostgreSQL object-membership DDL.
3480///
3481/// PostgreSQL's `ALTER EXTENSION … ADD|DROP <member>` grammar
3482/// (`AlterExtensionContentsStmt`) names a member object by a kind keyword plus a
3483/// signature whose shape depends on the kind: a bare or qualified name, a routine
3484/// argument-type signature, an aggregate/operator signature, a cast type pair, a type
3485/// name, or a transform. The sibling object-DDL heads name objects the same way — the
3486/// `parse-pg-alter-object-depends` head (`ALTER … DEPENDS ON EXTENSION`) reuses
3487/// [`Routine`](Self::Routine) and [`Named`](Self::Named) for its `FUNCTION`/`PROCEDURE`/
3488/// `ROUTINE`, `MATERIALIZED VIEW`, and `INDEX` objects, and `parse-pg-drop-transform`
3489/// reuses [`Transform`](Self::Transform) — so the axis lives here once rather than being
3490/// re-derived per head.
3491///
3492/// [`Trigger`](Self::Trigger) is the one shape no `ALTER EXTENSION` member takes: its
3493/// `TRIGGER <name> ON <table>` form is exclusive to the `DEPENDS ON EXTENSION` head
3494/// (`ALTER EXTENSION … ADD TRIGGER` is a reject), so it was added to the axis additively
3495/// when that head's measured grammar demanded it, keeping every object-DDL head on one
3496/// shared reference type.
3497#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3498#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3499#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3500pub enum ObjectReference<X: Extension = NoExt> {
3501 /// A kind keyword and a name. [`kind`](ObjectRefKind) records whether the name may be
3502 /// schema-qualified: PostgreSQL's `object_type_any_name` kinds (`TABLE`, `VIEW`, …)
3503 /// accept a dotted `any_name`, its `object_type_name` kinds (`SCHEMA`, `ROLE`, …)
3504 /// only a single `name`.
3505 Named {
3506 /// The object kind; see [`ObjectRefKind`].
3507 kind: ObjectRefKind,
3508 /// The referenced name.
3509 name: ObjectName,
3510 /// Source location and node identity.
3511 meta: Meta,
3512 },
3513 /// `FUNCTION`/`PROCEDURE`/`ROUTINE <name>[(<argtypes>)]`
3514 /// (PostgreSQL's `function_with_argtypes`; the argument list is optional).
3515 Routine {
3516 /// Which routine kind; see [`RoutineObjectKind`].
3517 kind: RoutineObjectKind,
3518 /// The routine name and optional argument-type signature.
3519 signature: RoutineSignature<X>,
3520 /// Source location and node identity.
3521 meta: Meta,
3522 },
3523 /// `AGGREGATE <name>(<aggr_args>)`.
3524 Aggregate {
3525 /// The aggregate name.
3526 name: ObjectName,
3527 /// The aggregate argument signature.
3528 args: AggregateArgs<X>,
3529 /// Source location and node identity.
3530 meta: Meta,
3531 },
3532 /// `OPERATOR [<schema>.]<sym>(<left>, <right>)` (PostgreSQL's `operator_with_argtypes`).
3533 Operator {
3534 /// The optional schema qualification (empty for an unqualified operator).
3535 schema: ObjectName,
3536 /// The symbolic operator spelling, interned exact-case.
3537 op: Symbol,
3538 /// The operand types.
3539 args: OperatorArgs<X>,
3540 /// Source location and node identity.
3541 meta: Meta,
3542 },
3543 /// `OPERATOR CLASS`/`OPERATOR FAMILY <name> USING <access_method>`.
3544 OperatorClass {
3545 /// `true` for `OPERATOR FAMILY`, `false` for `OPERATOR CLASS`.
3546 family: bool,
3547 /// The operator class/family name.
3548 name: ObjectName,
3549 /// The `USING <access_method>` index access method.
3550 access_method: Ident,
3551 /// Source location and node identity.
3552 meta: Meta,
3553 },
3554 /// `CAST (<from> AS <to>)`.
3555 Cast {
3556 /// The cast's source type.
3557 from: DataType<X>,
3558 /// The cast's target type.
3559 to: DataType<X>,
3560 /// Source location and node identity.
3561 meta: Meta,
3562 },
3563 /// `DOMAIN`/`TYPE <typename>` — a type-named object.
3564 Type {
3565 /// `true` for the `DOMAIN` keyword, `false` for `TYPE` (both name a type via a
3566 /// `Typename`).
3567 domain: bool,
3568 /// The type name (a `Typename`, possibly schema-qualified).
3569 name: DataType<X>,
3570 /// Source location and node identity.
3571 meta: Meta,
3572 },
3573 /// `TRANSFORM FOR <typename> LANGUAGE <language>`.
3574 Transform {
3575 /// The type the transform is `FOR`.
3576 type_name: DataType<X>,
3577 /// The transform's procedural language name.
3578 language: Ident,
3579 /// Source location and node identity.
3580 meta: Meta,
3581 },
3582 /// `TRIGGER <name> ON <table>` — a trigger named by its bare name plus the table it
3583 /// is defined on (PostgreSQL's `ALTER TRIGGER name ON qualified_name`). Unlike the
3584 /// other object kinds, a trigger is not schema-qualifiable on its own name (it is a
3585 /// `name`, a single `ColId`); the `table` carries the qualification.
3586 ///
3587 /// This variant is reached only by the `DEPENDS ON EXTENSION` head — no `ALTER
3588 /// EXTENSION` member is a trigger — so `parse_object_reference` never constructs it.
3589 Trigger {
3590 /// The trigger's bare name.
3591 name: Ident,
3592 /// The table the trigger is defined on (a `qualified_name`).
3593 table: ObjectName,
3594 /// Source location and node identity.
3595 meta: Meta,
3596 },
3597}
3598
3599/// A member-object kind naming an object by a bare or qualified name — the
3600/// name-only shapes of [`ObjectReference::Named`], covering PostgreSQL's
3601/// `object_type_any_name` and `object_type_name` productions.
3602#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3603#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3604#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3605pub enum ObjectRefKind {
3606 /// `TABLE`.
3607 Table,
3608 /// `SEQUENCE`.
3609 Sequence,
3610 /// `VIEW`.
3611 View,
3612 /// `MATERIALIZED VIEW`.
3613 MaterializedView,
3614 /// `INDEX`.
3615 Index,
3616 /// `FOREIGN TABLE`.
3617 ForeignTable,
3618 /// `COLLATION`.
3619 Collation,
3620 /// `CONVERSION`.
3621 Conversion,
3622 /// `STATISTICS`.
3623 Statistics,
3624 /// `TEXT SEARCH PARSER`.
3625 TextSearchParser,
3626 /// `TEXT SEARCH DICTIONARY`.
3627 TextSearchDictionary,
3628 /// `TEXT SEARCH TEMPLATE`.
3629 TextSearchTemplate,
3630 /// `TEXT SEARCH CONFIGURATION`.
3631 TextSearchConfiguration,
3632 /// `ACCESS METHOD`.
3633 AccessMethod,
3634 /// `EVENT TRIGGER`.
3635 EventTrigger,
3636 /// `EXTENSION`.
3637 Extension,
3638 /// `FOREIGN DATA WRAPPER`.
3639 ForeignDataWrapper,
3640 /// `[PROCEDURAL] LANGUAGE`. The optional `PROCEDURAL` keyword is exact-synonym sugar
3641 /// (PostgreSQL's `opt_procedural`); the canonical render emits `LANGUAGE`.
3642 Language,
3643 /// `PUBLICATION`.
3644 Publication,
3645 /// `SCHEMA`.
3646 Schema,
3647 /// `SERVER` (a foreign server).
3648 Server,
3649 /// `DATABASE`.
3650 Database,
3651 /// `ROLE`.
3652 Role,
3653 /// `TABLESPACE`.
3654 Tablespace,
3655}
3656
3657impl ObjectRefKind {
3658 /// Whether this kind names a schema-qualifiable object (PostgreSQL's
3659 /// `object_type_any_name` group), so its name may be a dotted `any_name`. The
3660 /// remaining kinds are `object_type_name`, which take only a single `name`.
3661 pub fn schema_qualifiable(self) -> bool {
3662 matches!(
3663 self,
3664 Self::Table
3665 | Self::Sequence
3666 | Self::View
3667 | Self::MaterializedView
3668 | Self::Index
3669 | Self::ForeignTable
3670 | Self::Collation
3671 | Self::Conversion
3672 | Self::Statistics
3673 | Self::TextSearchParser
3674 | Self::TextSearchDictionary
3675 | Self::TextSearchTemplate
3676 | Self::TextSearchConfiguration
3677 )
3678 }
3679}
3680
3681/// The argument signature of an `AGGREGATE` member (PostgreSQL's `aggr_args`).
3682///
3683/// Argument names and modes are dropped, keeping only the type list — the same
3684/// simplification [`RoutineSignature`] applies to `function_with_argtypes`.
3685#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3686#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3687#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3688pub enum AggregateArgs<X: Extension = NoExt> {
3689 /// `(*)` — the zero-argument wildcard.
3690 Star {
3691 /// Source location and node identity.
3692 meta: Meta,
3693 },
3694 /// A direct argument-type list, optionally followed by an ordered-set `ORDER BY`
3695 /// list: `(<types>)`, `(ORDER BY <types>)` (empty `direct`), or
3696 /// `(<types> ORDER BY <types>)`.
3697 Types {
3698 /// Direct argument types (empty for the leading `ORDER BY` form).
3699 direct: ThinVec<DataType<X>>,
3700 /// Ordered-set `ORDER BY` argument types, when the clause is present.
3701 order_by: Option<ThinVec<DataType<X>>>,
3702 /// Source location and node identity.
3703 meta: Meta,
3704 },
3705}
3706
3707/// The operand types of an `OPERATOR` member (PostgreSQL's `oper_argtypes`): a binary or
3708/// unary operator's left and right operand types, where a `NONE` operand (a unary
3709/// operator's missing side) is `None`. At least one side is always present.
3710#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3711#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3712#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3713pub struct OperatorArgs<X: Extension = NoExt> {
3714 /// The left operand type, or `None` for a left-unary operator's `NONE`.
3715 pub left: Option<DataType<X>>,
3716 /// The right operand type, or `None` for a right-unary operator's `NONE`.
3717 pub right: Option<DataType<X>>,
3718 /// Source location and node identity.
3719 pub meta: Meta,
3720}
3721
3722/// An `ALTER <object> [NO] DEPENDS ON EXTENSION <extension>` statement (PostgreSQL's
3723/// `AlterObjectDependsStmt`), gated by
3724/// [`StatementDdlGates::extension_ddl`](crate::dialect::StatementDdlGates::extension_ddl).
3725///
3726/// Records or clears a dependency of a database object on an extension, so that dropping
3727/// the extension cascades to the object (and `pg_dump` skips it). PostgreSQL admits only
3728/// four object heads here — `FUNCTION`/`PROCEDURE`/`ROUTINE` (a `function_with_argtypes`),
3729/// `TRIGGER <name> ON <table>`, `MATERIALIZED VIEW`, and `INDEX` — captured by the shared
3730/// [`ObjectReference`] axis, so the wider `object_type` keyword set (`TABLE`, `VIEW`,
3731/// `SEQUENCE`, …) that `object` could otherwise spell is out of the parsed grammar.
3732///
3733/// The construct references an extension but operates on a non-extension object; it shares
3734/// the [`extension_ddl`](crate::dialect::StatementDdlGates::extension_ddl) gate rather than
3735/// carrying its own, because it is meaningless without the extension system and its dialect
3736/// boundary (PostgreSQL on, everything else off) is identical to extension DDL's — a
3737/// dialect that has extensions has this, one that does not (DuckDB's `INSTALL`/`LOAD`) has
3738/// neither.
3739#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3740#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3741#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3742pub struct AlterObjectDepends<X: Extension = NoExt> {
3743 /// The object whose extension dependency is being set — one of the four
3744 /// [`ObjectReference`] shapes the `DEPENDS` grammar admits.
3745 pub object: ObjectReference<X>,
3746 /// Whether the `NO DEPENDS` negation form was written (PostgreSQL's `opt_no`), which
3747 /// removes the recorded dependency instead of adding it.
3748 pub no: bool,
3749 /// The extension the object depends on (a bare `name`, not schema-qualifiable).
3750 pub extension: Ident,
3751 /// Source location and node identity.
3752 pub meta: Meta,
3753}
3754
3755/// A MySQL `CREATE SERVER <name> FOREIGN DATA WRAPPER <wrapper> OPTIONS ( <option> [, ...] )`
3756/// federated-server definition (`sql_yacc.yy` `CREATE SERVER_SYM …`), gated by
3757/// [`StatementDdlGates::server_definition`](crate::dialect::StatementDdlGates::server_definition).
3758///
3759/// Registers a remote server for the `FEDERATED` storage engine. The server name and the
3760/// `FOREIGN DATA WRAPPER` name are each an `ident_or_text` (a bare/backtick identifier or a
3761/// quoted string, folded to an [`Ident`] whose quote style round-trips). The `OPTIONS` list is
3762/// the fixed [`ServerOption`] keyword set and is non-empty — an empty `OPTIONS ()` is
3763/// `ER_PARSE_ERROR` on mysql:8.4.10. Shares its gate and the [`ServerOption`] axis with
3764/// [`AlterServer`] (the two differ only in whether a wrapper is named); [`DropServer`] disposes
3765/// of the same object.
3766#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3767#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3768#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3769pub struct CreateServer {
3770 /// The server name (`ident_or_text`).
3771 pub name: Ident,
3772 /// The `FOREIGN DATA WRAPPER` name (`ident_or_text`) — the storage-engine wrapper the
3773 /// server uses.
3774 pub wrapper: Ident,
3775 /// The `OPTIONS` list, in source order; non-empty (`server_options_list`).
3776 pub options: ThinVec<ServerOption>,
3777 /// Source location and node identity.
3778 pub meta: Meta,
3779}
3780
3781/// A MySQL `ALTER SERVER <name> OPTIONS ( <option> [, ...] )` federated-server change
3782/// (`sql_yacc.yy` `ALTER SERVER_SYM …`), gated by
3783/// [`StatementDdlGates::server_definition`](crate::dialect::StatementDdlGates::server_definition).
3784///
3785/// Re-sets connection options on an existing server. Unlike [`CreateServer`] it names no
3786/// `FOREIGN DATA WRAPPER` (the wrapper is fixed at creation), but the `OPTIONS` list is the
3787/// same non-empty [`ServerOption`] grammar.
3788#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3789#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3790#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3791pub struct AlterServer {
3792 /// The server name (`ident_or_text`).
3793 pub name: Ident,
3794 /// The `OPTIONS` list, in source order; non-empty (`server_options_list`).
3795 pub options: ThinVec<ServerOption>,
3796 /// Source location and node identity.
3797 pub meta: Meta,
3798}
3799
3800/// A MySQL `DROP SERVER [IF EXISTS] <name>` federated-server drop (`sql_yacc.yy`
3801/// `drop_server_stmt`), gated by
3802/// [`StatementDdlGates::server_definition`](crate::dialect::StatementDdlGates::server_definition).
3803///
3804/// Removes a single server; no comma list (`DROP SERVER a, b` is `ER_PARSE_ERROR` on
3805/// mysql:8.4.10). The name is an `ident_or_text`.
3806#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3807#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3808#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3809pub struct DropServer {
3810 /// Whether the `IF EXISTS` guard was written (`drop_server_stmt`'s `if_exists`).
3811 pub if_exists: bool,
3812 /// The server name (`ident_or_text`).
3813 pub name: Ident,
3814 /// Source location and node identity.
3815 pub meta: Meta,
3816}
3817
3818/// One `CREATE`/`ALTER SERVER` option — a fixed keyword ([`ServerOptionKind`]) and its value
3819/// (`sql_yacc.yy` `server_option`). Every option carries exactly one value: the string-valued
3820/// keywords (`HOST`/`DATABASE`/`USER`/`PASSWORD`/`SOCKET`/`OWNER`) take a `TEXT_STRING_sys`
3821/// string literal, and `PORT` takes a `ulong_num` unsigned-integer literal — the parser holds
3822/// each to its measured value type (`HOST 123` and `PORT '3306'` are both `ER_PARSE_ERROR` on
3823/// mysql:8.4.10). The value is a [`Literal`] whose [`kind`](Literal::kind) records which.
3824#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3825#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3826#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3827pub struct ServerOption {
3828 /// Which fixed option keyword this is.
3829 pub kind: ServerOptionKind,
3830 /// The option value — a string literal for every kind but [`ServerOptionKind::Port`],
3831 /// which carries an unsigned-integer literal.
3832 pub value: Literal,
3833 /// Source location and node identity.
3834 pub meta: Meta,
3835}
3836
3837/// The fixed keyword of a [`ServerOption`] (`sql_yacc.yy` `server_option`). A surface tag (no
3838/// `meta` — its span rides the owning [`ServerOption`]). Only these seven keywords are
3839/// options; any other (`FOO 'bar'`) is `ER_PARSE_ERROR` on mysql:8.4.10.
3840#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3841#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3842#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3843pub enum ServerOptionKind {
3844 /// `HOST '<host>'` — the remote host name.
3845 Host,
3846 /// `DATABASE '<db>'` — the remote default database.
3847 Database,
3848 /// `USER '<user>'` — the connection user.
3849 User,
3850 /// `PASSWORD '<password>'` — the connection password.
3851 Password,
3852 /// `SOCKET '<socket>'` — the remote unix socket path.
3853 Socket,
3854 /// `OWNER '<owner>'` — the server owner.
3855 Owner,
3856 /// `PORT <n>` — the remote TCP port (an unsigned-integer value, not a string).
3857 Port,
3858}
3859
3860/// A MySQL `ALTER INSTANCE <action>` server-instance administration statement (`sql_yacc.yy`
3861/// `alter_instance_stmt`), gated by
3862/// [`StatementDdlGates::alter_instance`](crate::dialect::StatementDdlGates::alter_instance).
3863///
3864/// A single instance-wide maintenance action (rotate an encryption master key, reload TLS
3865/// material or the keyring, toggle the InnoDB redo log) — see [`AlterInstanceAction`]. Unlike
3866/// the server and database DDL this names no object.
3867#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3868#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3869#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3870pub struct AlterInstance {
3871 /// The instance action performed.
3872 pub action: AlterInstanceAction,
3873 /// Source location and node identity.
3874 pub meta: Meta,
3875}
3876
3877/// The action of an [`AlterInstance`] (`sql_yacc.yy` `alter_instance_action`). The `INNODB`,
3878/// `BINLOG`, and `REDO_LOG` words are matched as identifiers by the server (`is_identifier`),
3879/// so a wrong word is `ER_PARSE_ERROR` (`ROTATE FOO MASTER KEY`, `ENABLE INNODB FOO`); this
3880/// models only the measured accepted set.
3881#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3882#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3883#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3884pub enum AlterInstanceAction {
3885 /// `ROTATE INNODB MASTER KEY` — rotate the InnoDB tablespace-encryption master key.
3886 RotateInnodbMasterKey {
3887 /// Source location and node identity.
3888 meta: Meta,
3889 },
3890 /// `ROTATE BINLOG MASTER KEY` — rotate the binary-log encryption master key.
3891 RotateBinlogMasterKey {
3892 /// Source location and node identity.
3893 meta: Meta,
3894 },
3895 /// `RELOAD TLS [FOR CHANNEL <channel>] [NO ROLLBACK ON ERROR]` — reconfigure the TLS
3896 /// context. The optional `FOR CHANNEL` names a bare `ident` (a quoted `'ch'` is
3897 /// `ER_PARSE_ERROR` on mysql:8.4.10, unlike `FLUSH … FOR CHANNEL`'s string); the default
3898 /// is to roll back the active context on error, and `NO ROLLBACK ON ERROR` suppresses it.
3899 ReloadTls {
3900 /// The `FOR CHANNEL <channel>` name (a bare `ident`), or `None`.
3901 channel: Option<Ident>,
3902 /// Whether `NO ROLLBACK ON ERROR` was written.
3903 no_rollback_on_error: bool,
3904 /// Source location and node identity.
3905 meta: Meta,
3906 },
3907 /// `RELOAD KEYRING` — reload keyring component options from the manifest.
3908 ReloadKeyring {
3909 /// Source location and node identity.
3910 meta: Meta,
3911 },
3912 /// `ENABLE INNODB REDO_LOG` — re-enable redo logging.
3913 EnableInnodbRedoLog {
3914 /// Source location and node identity.
3915 meta: Meta,
3916 },
3917 /// `DISABLE INNODB REDO_LOG` — disable redo logging (for bulk load).
3918 DisableInnodbRedoLog {
3919 /// Source location and node identity.
3920 meta: Meta,
3921 },
3922}
3923
3924/// A MySQL `CREATE [OR REPLACE] SPATIAL REFERENCE SYSTEM [IF NOT EXISTS] <srid> <attributes>`
3925/// spatial-reference-system definition (`sql_yacc.yy` `create_srs_stmt`), gated by
3926/// [`StatementDdlGates::spatial_reference_system`](crate::dialect::StatementDdlGates::spatial_reference_system).
3927///
3928/// `OR REPLACE` and `IF NOT EXISTS` are mutually exclusive: the grammar has one branch for each
3929/// (`CREATE OR REPLACE …` and `CREATE … IF NOT EXISTS …`), so `CREATE OR REPLACE … IF NOT EXISTS`
3930/// is `ER_PARSE_ERROR` on mysql:8.4.10. The `<srid>` is a `real_ulonglong_num` unsigned integer
3931/// (decimal or `0x` hex; a negative or fractional value is `ER_PARSE_ERROR`, an out-of-`u64`-range
3932/// value is the post-parse `ER_DATA_OUT_OF_RANGE`). Every attribute is optional and the list is
3933/// order-free at the grammar level — a missing NAME/DEFINITION is the post-parse semantic reject
3934/// `ER_SRS_MISSING_MANDATORY_ATTRIBUTE`, and a repeated attribute is
3935/// `ER_SRS_MULTIPLE_ATTRIBUTE_DEFINITIONS`, neither a syntax error — so [`attributes`](Self::attributes)
3936/// keeps the source order and admits repeats, matching the recognized grammar rather than the
3937/// narrower semantic rule.
3938#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3939#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3940#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3941pub struct CreateSpatialReferenceSystem {
3942 /// Whether the `OR REPLACE` branch was written (mutually exclusive with
3943 /// [`if_not_exists`](Self::if_not_exists)).
3944 pub or_replace: bool,
3945 /// Whether the `IF NOT EXISTS` guard was written (mutually exclusive with
3946 /// [`or_replace`](Self::or_replace)).
3947 pub if_not_exists: bool,
3948 /// The SRID — a `real_ulonglong_num` unsigned-integer literal (decimal or `0x` hex).
3949 pub srid: Literal,
3950 /// The attribute list, in source order (order-free grammar; repeats admitted).
3951 pub attributes: ThinVec<SrsAttribute>,
3952 /// Source location and node identity.
3953 pub meta: Meta,
3954}
3955
3956/// One `CREATE SPATIAL REFERENCE SYSTEM` attribute (`sql_yacc.yy` `srs_attributes`). The
3957/// attributes are order-free and each independently optional; a repeat of the same attribute is
3958/// a post-parse semantic reject (`ER_SRS_MULTIPLE_ATTRIBUTE_DEFINITIONS`), not a syntax error,
3959/// so the parser accepts the recognized grammar and preserves whatever the source wrote.
3960#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3961#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3962#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3963pub enum SrsAttribute {
3964 /// `NAME '<name>'` — the SRS name (a `TEXT_STRING_sys_nonewline` string literal).
3965 Name {
3966 /// The name string.
3967 value: Literal,
3968 /// Source location and node identity.
3969 meta: Meta,
3970 },
3971 /// `DEFINITION '<wkt>'` — the WKT coordinate-system definition string.
3972 Definition {
3973 /// The definition string.
3974 value: Literal,
3975 /// Source location and node identity.
3976 meta: Meta,
3977 },
3978 /// `ORGANIZATION '<org>' IDENTIFIED BY <id>` — the authoring organization and its numeric
3979 /// coordinate-system id. `IDENTIFIED BY` is mandatory (`ORGANIZATION 'o'` alone is
3980 /// `ER_PARSE_ERROR`) and the id is a `real_ulonglong_num` integer (`IDENTIFIED BY 'x'` is
3981 /// `ER_PARSE_ERROR`).
3982 Organization {
3983 /// The organization name string.
3984 organization: Literal,
3985 /// The `IDENTIFIED BY` numeric coordinate-system id (unsigned-integer literal).
3986 identifier: Literal,
3987 /// Source location and node identity.
3988 meta: Meta,
3989 },
3990 /// `DESCRIPTION '<text>'` — the free-text description string.
3991 Description {
3992 /// The description string.
3993 value: Literal,
3994 /// Source location and node identity.
3995 meta: Meta,
3996 },
3997}
3998
3999/// A MySQL `DROP SPATIAL REFERENCE SYSTEM [IF EXISTS] <srid>` spatial-reference-system drop
4000/// (`sql_yacc.yy` `drop_srs_stmt`), gated by
4001/// [`StatementDdlGates::spatial_reference_system`](crate::dialect::StatementDdlGates::spatial_reference_system).
4002///
4003/// Removes a single SRS by id; no comma list (`DROP SPATIAL REFERENCE SYSTEM 1, 2` is
4004/// `ER_PARSE_ERROR` on mysql:8.4.10). The `<srid>` is a `real_ulonglong_num` unsigned integer.
4005#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4006#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4007#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4008pub struct DropSpatialReferenceSystem {
4009 /// Whether the `IF EXISTS` guard was written (`drop_srs_stmt`'s `if_exists`).
4010 pub if_exists: bool,
4011 /// The SRID — a `real_ulonglong_num` unsigned-integer literal.
4012 pub srid: Literal,
4013 /// Source location and node identity.
4014 pub meta: Meta,
4015}
4016
4017/// A MySQL `CREATE RESOURCE GROUP <name> TYPE [=] {SYSTEM | USER} [VCPU …] [THREAD_PRIORITY …]
4018/// [ENABLE | DISABLE]` resource-group definition (`sql_yacc.yy` `create_resource_group_stmt`),
4019/// gated by [`StatementDdlGates::resource_group`](crate::dialect::StatementDdlGates::resource_group).
4020///
4021/// `TYPE` is mandatory (a bare `CREATE RESOURCE GROUP g` is `ER_PARSE_ERROR` on mysql:8.4.10) and
4022/// the option train is fixed-order — `VCPU`, then `THREAD_PRIORITY`, then `ENABLE`/`DISABLE`; any
4023/// permutation (`… ENABLE VCPU …`) is `ER_PARSE_ERROR`. `CREATE` admits no `FORCE` (`ENABLE FORCE`
4024/// is `ER_PARSE_ERROR`), unlike [`AlterResourceGroup`] and [`DropResourceGroup`], which share the
4025/// same [`resource_group`](crate::dialect::StatementDdlGates::resource_group) gate.
4026#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4027#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4028#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4029pub struct CreateResourceGroup {
4030 /// The resource-group name (`ident`).
4031 pub name: Ident,
4032 /// Whether the `TYPE =` spelling (vs bare `TYPE`) was written (`opt_equal`).
4033 pub type_equals: bool,
4034 /// The group type (`SYSTEM`/`USER`), mandatory.
4035 pub group_type: ResourceGroupType,
4036 /// The optional `VCPU [=] <ranges>` CPU-affinity clause.
4037 pub vcpu: Option<ResourceGroupVcpu>,
4038 /// The optional `THREAD_PRIORITY [=] <n>` clause.
4039 pub thread_priority: Option<ResourceGroupThreadPriority>,
4040 /// The optional `ENABLE`/`DISABLE` state clause.
4041 pub state: Option<ResourceGroupState>,
4042 /// Source location and node identity.
4043 pub meta: Meta,
4044}
4045
4046/// A MySQL `ALTER RESOURCE GROUP <name> [VCPU …] [THREAD_PRIORITY …] [ENABLE | DISABLE] [FORCE]`
4047/// resource-group change (`sql_yacc.yy` `alter_resource_group_stmt`), gated by
4048/// [`StatementDdlGates::resource_group`](crate::dialect::StatementDdlGates::resource_group).
4049///
4050/// Every clause is optional — a bare `ALTER RESOURCE GROUP g` grammar-accepts on mysql:8.4.10.
4051/// The trailing `FORCE` is an *independent* optional (`opt_force`), not a suffix of
4052/// `ENABLE`/`DISABLE`: `ALTER RESOURCE GROUP g FORCE`, with neither state keyword, grammar-accepts
4053/// — so [`force`](Self::force) is a peer of [`state`](Self::state), not nested inside it. Shares
4054/// the [`ResourceGroupVcpu`]/[`ResourceGroupThreadPriority`]/[`ResourceGroupState`] axes with
4055/// [`CreateResourceGroup`]; `CREATE` differs only in requiring `TYPE` and forbidding `FORCE`.
4056#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4057#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4058#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4059pub struct AlterResourceGroup {
4060 /// The resource-group name (`ident`).
4061 pub name: Ident,
4062 /// The optional `VCPU [=] <ranges>` CPU-affinity clause.
4063 pub vcpu: Option<ResourceGroupVcpu>,
4064 /// The optional `THREAD_PRIORITY [=] <n>` clause.
4065 pub thread_priority: Option<ResourceGroupThreadPriority>,
4066 /// The optional `ENABLE`/`DISABLE` state clause.
4067 pub state: Option<ResourceGroupState>,
4068 /// Whether the trailing `FORCE` was written (`opt_force`, independent of
4069 /// [`state`](Self::state)).
4070 pub force: bool,
4071 /// Source location and node identity.
4072 pub meta: Meta,
4073}
4074
4075/// A MySQL `DROP RESOURCE GROUP <name> [FORCE]` resource-group drop (`sql_yacc.yy`
4076/// `drop_resource_group_stmt`), gated by
4077/// [`StatementDdlGates::resource_group`](crate::dialect::StatementDdlGates::resource_group).
4078///
4079/// Removes a single group by name; the optional trailing `FORCE` (`opt_force`) evicts running
4080/// threads.
4081#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4082#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4083#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4084pub struct DropResourceGroup {
4085 /// The resource-group name (`ident`).
4086 pub name: Ident,
4087 /// Whether the trailing `FORCE` was written (`opt_force`).
4088 pub force: bool,
4089 /// Source location and node identity.
4090 pub meta: Meta,
4091}
4092
4093/// The `TYPE` of a [`CreateResourceGroup`] (`sql_yacc.yy` `resource_group_types`): the fixed
4094/// `SYSTEM`/`USER` keyword set. A surface tag (no `meta` — its span rides the owning node); any
4095/// other word is `ER_PARSE_ERROR`.
4096#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4097#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4098#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4099pub enum ResourceGroupType {
4100 /// `SYSTEM` — a system resource group.
4101 System,
4102 /// `USER` — a user resource group.
4103 User,
4104}
4105
4106/// The `ENABLE`/`DISABLE` state clause of a resource-group `CREATE`/`ALTER`
4107/// (`opt_resource_group_enable_disable`). A surface tag (no `meta` — its span rides the owning
4108/// node).
4109#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4110#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4111#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4112pub enum ResourceGroupState {
4113 /// `ENABLE` — enable the group.
4114 Enable,
4115 /// `DISABLE` — disable the group.
4116 Disable,
4117}
4118
4119/// The `VCPU [=] <range> [, <range> …]` CPU-affinity clause of a resource-group `CREATE`/`ALTER`
4120/// (`sql_yacc.yy` `VCPU_SYM opt_equal vcpu_range_spec_list`). The `[=]` presence
4121/// ([`equals`](Self::equals)) and the non-empty [`ranges`](Self::ranges) list round-trip. The
4122/// separator between ranges is `opt_comma` (a comma *or* whitespace both parse), so the parse is
4123/// separator-insensitive and the list is rendered canonically comma-separated.
4124#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4125#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4126#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4127pub struct ResourceGroupVcpu {
4128 /// Whether the `VCPU =` spelling (vs bare `VCPU`) was written (`opt_equal`).
4129 pub equals: bool,
4130 /// The CPU-id range list, in source order; non-empty (`vcpu_range_spec_list`).
4131 pub ranges: ThinVec<VcpuRange>,
4132 /// Source location and node identity.
4133 pub meta: Meta,
4134}
4135
4136/// One `vcpu_num_or_range`: a single CPU id ([`end`](Self::end) `None`) or an inclusive
4137/// `start-end` range. Each bound is a `NUM` unsigned-integer literal; a value beyond the host CPU
4138/// count is a post-parse semantic reject (`ER_RESOURCE_GROUP_…`), not a syntax error, so the
4139/// parser accepts any integer bounds.
4140#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4141#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4142#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4143pub struct VcpuRange {
4144 /// The single id, or the inclusive-range start.
4145 pub start: Literal,
4146 /// The inclusive-range end (`start-end`), or `None` for a single id.
4147 pub end: Option<Literal>,
4148 /// Source location and node identity.
4149 pub meta: Meta,
4150}
4151
4152/// The `THREAD_PRIORITY [=] <n>` clause of a resource-group `CREATE`/`ALTER`
4153/// (`opt_resource_group_priority`). The value is a `signed_num` — an optionally negative integer
4154/// (`THREAD_PRIORITY = -5` grammar-accepts on mysql:8.4.10) — so the sign is carried apart from
4155/// the magnitude literal (`-` is a separate token). The `[=]` presence and the sign round-trip.
4156#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4157#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4158#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4159pub struct ResourceGroupThreadPriority {
4160 /// Whether the `THREAD_PRIORITY =` spelling (vs bare `THREAD_PRIORITY`) was written
4161 /// (`opt_equal`).
4162 pub equals: bool,
4163 /// Whether a leading `-` was written (`signed_num`'s negative branch).
4164 pub negative: bool,
4165 /// The magnitude — a `NUM` unsigned-integer literal.
4166 pub value: Literal,
4167 /// Source location and node identity.
4168 pub meta: Meta,
4169}
4170
4171/// A MySQL `ALTER {DATABASE | SCHEMA} [<name>] <option> …` schema-option change (`sql_yacc.yy`
4172/// `alter_database_stmt`), gated by
4173/// [`StatementDdlGates::alter_database_options`](crate::dialect::StatementDdlGates::alter_database_options).
4174///
4175/// A distinct node — and gate — from DuckDB's [`AlterDatabase`] `SET ALIAS` relocation: the two
4176/// share only the `ALTER DATABASE` head, and their grammars are disjoint (DuckDB requires a name
4177/// and a single `SET ALIAS TO`; MySQL's name is optional — `ALTER DATABASE CHARACTER SET utf8`
4178/// binds the default schema — and it takes a non-empty, repeatable list of charset/collation/
4179/// encryption/read-only [`AlterDatabaseOption`]s). Modelling them apart keeps each behaviour's
4180/// invariants in the type rather than as XOR-populated fields on one node. The `DATABASE`/
4181/// `SCHEMA` [`spelling`](Self::spelling) is an exact synonym recorded for round-trip.
4182#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4183#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4184#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4185pub struct AlterDatabaseOptions {
4186 /// Which of the synonymous `DATABASE`/`SCHEMA` keywords was written.
4187 pub spelling: DatabaseKeyword,
4188 /// The database name, or `None` for the unqualified `ALTER DATABASE <option> …` form that
4189 /// targets the session's default schema (`ident_or_empty`; a dotted `d.x` is
4190 /// `ER_PARSE_ERROR`, so a bare [`Ident`]).
4191 pub name: Option<Ident>,
4192 /// The option list, in source order; non-empty (`alter_database_options`).
4193 pub options: ThinVec<AlterDatabaseOption>,
4194 /// Source location and node identity.
4195 pub meta: Meta,
4196}
4197
4198/// One `ALTER {DATABASE | SCHEMA}` option (`sql_yacc.yy` `alter_database_option`). The `[=]`
4199/// ([`opt_equal`]) and, where admitted, the leading `[DEFAULT]` are recorded so the surface
4200/// form round-trips verbatim. The value grammars are fixed; a value outside the set is a parse
4201/// or bind reject on mysql:8.4.10 (`READ ONLY 2` is `ER_PARSE_ERROR`; `ENCRYPTION 'X'` binds
4202/// then rejects `ER_WRONG_VALUE`, so any string parses).
4203///
4204/// [`opt_equal`]: https://dev.mysql.com/doc/refman/8.4/en/alter-database.html
4205#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4206#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4207#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4208pub enum AlterDatabaseOption {
4209 /// `[DEFAULT] {CHARACTER SET | CHARSET} [=] <charset>` — the default character set. The
4210 /// charset name is `charset_name` (`ident_or_text`, or the reserved `BINARY`), folded to
4211 /// an [`Ident`].
4212 CharacterSet {
4213 /// Whether the leading `DEFAULT` was written.
4214 default: bool,
4215 /// Which of the `CHARACTER SET`/`CHARSET` synonyms was written.
4216 keyword: CharsetKeyword,
4217 /// Whether the optional `=` was written.
4218 equals: bool,
4219 /// The character-set name.
4220 charset: Ident,
4221 /// Source location and node identity.
4222 meta: Meta,
4223 },
4224 /// `[DEFAULT] COLLATE [=] <collation>` — the default collation. The name is
4225 /// `collation_name` (`ident_or_text`, or the reserved `BINARY`), folded to an [`Ident`].
4226 Collate {
4227 /// Whether the leading `DEFAULT` was written.
4228 default: bool,
4229 /// Whether the optional `=` was written.
4230 equals: bool,
4231 /// The collation name.
4232 collation: Ident,
4233 /// Source location and node identity.
4234 meta: Meta,
4235 },
4236 /// `[DEFAULT] ENCRYPTION [=] '<Y|N>'` — the default tablespace encryption. Any string
4237 /// literal parses; the `Y`/`N` restriction is a bind-time check (`ER_WRONG_VALUE`), so the
4238 /// value is held verbatim as a [`Literal`].
4239 Encryption {
4240 /// Whether the leading `DEFAULT` was written.
4241 default: bool,
4242 /// Whether the optional `=` was written.
4243 equals: bool,
4244 /// The encryption value (a string literal).
4245 value: Literal,
4246 /// Source location and node identity.
4247 meta: Meta,
4248 },
4249 /// `READ ONLY [=] {DEFAULT | 0 | 1}` — the schema read-only flag. Takes no leading
4250 /// `DEFAULT` prefix (unlike the charset/collation/encryption options — `DEFAULT READ ONLY`
4251 /// is `ER_PARSE_ERROR`); the value is a [`ReadOnlyValue`] `ternary_option`, and any other
4252 /// number (`READ ONLY 2`) is `ER_PARSE_ERROR`.
4253 ReadOnly {
4254 /// Whether the optional `=` was written.
4255 equals: bool,
4256 /// The read-only value.
4257 value: ReadOnlyValue,
4258 /// Source location and node identity.
4259 meta: Meta,
4260 },
4261}
4262
4263/// Which of the synonymous `CHARACTER SET`/`CHARSET` keywords introduced an
4264/// [`AlterDatabaseOption::CharacterSet`]. A surface tag (no `meta`) recording the spelling for
4265/// round-trip; the two are semantically identical.
4266#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4267#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4268#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4269pub enum CharsetKeyword {
4270 /// The two-word `CHARACTER SET` spelling.
4271 CharacterSet,
4272 /// The one-word `CHARSET` synonym.
4273 Charset,
4274}
4275
4276/// The value of an [`AlterDatabaseOption::ReadOnly`] — MySQL's `ternary_option`. A surface tag
4277/// (no `meta`); `0`/`1` are the boolean settings and `DEFAULT` inherits the global default.
4278/// Any other number is `ER_PARSE_ERROR` on mysql:8.4.10.
4279#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4280#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4281#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4282pub enum ReadOnlyValue {
4283 /// `DEFAULT` — inherit the global read-only default.
4284 Default,
4285 /// `0` — read-write.
4286 Off,
4287 /// `1` — read-only.
4288 On,
4289}
4290
4291/// The binary unit suffix on a MySQL storage-DDL size literal (`size_number`'s `IDENT_sys`
4292/// form): `K`, `M`, or `G`. MySQL folds `<digits><suffix>` to a byte count by shifting the
4293/// number left 10/20/30 bits, so the tag names the multiplier a downstream converter would
4294/// otherwise re-decode from the spelling. Case is *not* carried here — the exact source
4295/// letter (`16m` vs `16M`) round-trips from the enclosing [`SizeLiteral`]'s span.
4296#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4297#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4298#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4299pub enum SizeUnit {
4300 /// `K` — the value is shifted left 10 bits (× 1024).
4301 Kilo,
4302 /// `M` — the value is shifted left 20 bits (× 1024²).
4303 Mega,
4304 /// `G` — the value is shifted left 30 bits (× 1024³).
4305 Giga,
4306}
4307
4308/// A MySQL storage-DDL size literal (`size_number`): a byte count written either as a plain
4309/// integer (`134217728`) or as an integer with a binary unit suffix (`128M`, `2G`, `16k`).
4310///
4311/// MySQL lexes the suffixed form as a *single* `IDENT_sys` token (an unquoted identifier may
4312/// begin with digits), so the digits and the suffix letter must abut with no space — `16 M`
4313/// is a syntax error. This crate's tokenizer instead splits `16M` into a number token and an
4314/// adjacent word; the parser rejoins them by span adjacency, so [`meta`](Self::meta) spans the
4315/// whole literal and renders verbatim. [`unit`](Self::unit) records the multiplier as
4316/// structured metadata (`None` for a bare byte count); like a [`Literal`], the numeric value
4317/// and the exact spelling round-trip from the span, so the value carries only the surface tag
4318/// a consumer cannot otherwise recover.
4319#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4320#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4321#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4322pub struct SizeLiteral {
4323 /// The binary unit suffix, or `None` for a bare integer byte count; see [`SizeUnit`].
4324 pub unit: Option<SizeUnit>,
4325 /// Source location and node identity. Spans the whole literal (digits + any suffix),
4326 /// which is what round-trips the exact spelling.
4327 pub meta: Meta,
4328}
4329
4330/// Which storage-DDL option keyword carries a [`SizeLiteral`] value — the `size_number`-valued
4331/// members of MySQL's shared `ts_option_*` family, grouped here because they share the exact
4332/// `<KEYWORD> [=] <size>` shape and differ only in the keyword.
4333#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4334#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4335#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4336pub enum TablespaceSizeOption {
4337 /// `INITIAL_SIZE` — the file's initial size.
4338 InitialSize,
4339 /// `AUTOEXTEND_SIZE` — the auto-extension increment.
4340 AutoextendSize,
4341 /// `MAX_SIZE` — the file's maximum size.
4342 MaxSize,
4343 /// `EXTENT_SIZE` — the extent size.
4344 ExtentSize,
4345 /// `UNDO_BUFFER_SIZE` — the UNDO buffer size (logfile group).
4346 UndoBufferSize,
4347 /// `REDO_BUFFER_SIZE` — the REDO buffer size (logfile group).
4348 RedoBufferSize,
4349 /// `FILE_BLOCK_SIZE` — the file block size (tablespace).
4350 FileBlockSize,
4351}
4352
4353/// One option in a MySQL tablespace / logfile-group statement — a member of the server's shared
4354/// `ts_option_*` family (every one of these DDL statements builds one
4355/// `PT_alter_tablespace_option` list). The full universe is modelled here as one axis; each
4356/// statement context accepts only its own subset (e.g. `UNDO TABLESPACE` takes `ENGINE` alone,
4357/// `ALTER TABLESPACE` excludes `FILE_BLOCK_SIZE`), enforced by the parser rather than the type,
4358/// so an out-of-context option ends the list and surfaces as a clean parse error — matching the
4359/// live server's per-context grammar.
4360///
4361/// Non-generic: every value is a size literal, an integer/string [`Literal`], an [`Ident`], or a
4362/// flag — no expressions or extension nodes. Each option records whether its optional `=`
4363/// (`opt_equal`) was written so the surface form round-trips.
4364#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4365#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4366#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4367pub enum TablespaceOption {
4368 /// A `size_number`-valued option (`INITIAL_SIZE [=] 128M`, …); see [`TablespaceSizeOption`].
4369 Size {
4370 /// Which size option keyword this is.
4371 kind: TablespaceSizeOption,
4372 /// Whether the optional `=` was written between the keyword and the value.
4373 equals: bool,
4374 /// The size value.
4375 size: SizeLiteral,
4376 /// Source location and node identity.
4377 meta: Meta,
4378 },
4379 /// `NODEGROUP [=] <n>` — a plain integer node-group id (`real_ulong_num`, never suffixed).
4380 Nodegroup {
4381 /// Whether the optional `=` was written between the keyword and the value.
4382 equals: bool,
4383 /// The node-group id.
4384 value: Literal,
4385 /// Source location and node identity.
4386 meta: Meta,
4387 },
4388 /// `[STORAGE] ENGINE [=] <name>` — the storage engine (`ident_or_text`: a bare identifier or
4389 /// a quoted string, folded to an [`Ident`] whose quote style round-trips).
4390 Engine {
4391 /// Whether the optional leading `STORAGE` noise keyword was written (`opt_storage`).
4392 storage: bool,
4393 /// Whether the optional `=` was written between `ENGINE` and the value.
4394 equals: bool,
4395 /// The engine name.
4396 name: Ident,
4397 /// Source location and node identity.
4398 meta: Meta,
4399 },
4400 /// `WAIT` or `NO_WAIT` — the NDB completion-wait flag (`ts_option_wait`).
4401 Wait {
4402 /// `true` for `NO_WAIT`, `false` for `WAIT`.
4403 negated: bool,
4404 /// Source location and node identity.
4405 meta: Meta,
4406 },
4407 /// `COMMENT [=] '<text>'` — a free-text comment string.
4408 Comment {
4409 /// Whether the optional `=` was written between the keyword and the value.
4410 equals: bool,
4411 /// The comment string.
4412 value: Literal,
4413 /// Source location and node identity.
4414 meta: Meta,
4415 },
4416 /// `ENCRYPTION [=] '<y_or_n>'` — the tablespace encryption flag string (`'Y'`/`'N'`).
4417 Encryption {
4418 /// Whether the optional `=` was written between the keyword and the value.
4419 equals: bool,
4420 /// The encryption flag string.
4421 value: Literal,
4422 /// Source location and node identity.
4423 meta: Meta,
4424 },
4425 /// `ENGINE_ATTRIBUTE [=] '<json>'` — the engine-specific JSON attribute string.
4426 EngineAttribute {
4427 /// Whether the optional `=` was written between the keyword and the value.
4428 equals: bool,
4429 /// The JSON attribute string.
4430 value: Literal,
4431 /// Source location and node identity.
4432 meta: Meta,
4433 },
4434}
4435
4436/// A MySQL `CREATE [UNDO] TABLESPACE <name> [ADD DATAFILE '<f>'] [USE LOGFILE GROUP <lg>]
4437/// [<option>...]` statement (gated by
4438/// [`StatementDdlGates::tablespace_ddl`](crate::dialect::StatementDdlGates::tablespace_ddl)).
4439///
4440/// One node for both the InnoDB/NDB tablespace (`Sql_cmd_create_tablespace`) and the
4441/// `UNDO`-tablespace variant (`Sql_cmd_create_undo_tablespace`), distinguished by
4442/// [`undo`](Self::undo). The two differ only in the leading `UNDO` keyword, whether the datafile
4443/// is mandatory (it is for `UNDO`, optional otherwise), and the accepted option set (`UNDO`
4444/// admits `ENGINE` alone) — all enforced by the parser. The `USE LOGFILE GROUP` clause is
4445/// NDB-only and never appears on the `UNDO` form.
4446#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4447#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4448#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4449pub struct CreateTablespace {
4450 /// Whether this is the `CREATE UNDO TABLESPACE` variant.
4451 pub undo: bool,
4452 /// The tablespace name.
4453 pub name: Ident,
4454 /// `ADD DATAFILE '<file>'`. Mandatory (`Some`) for the `UNDO` form; optional otherwise.
4455 pub datafile: Option<Literal>,
4456 /// `USE LOGFILE GROUP <name>` (NDB, regular tablespace only).
4457 pub use_logfile_group: Option<Ident>,
4458 /// Options supplied in source order.
4459 pub options: ThinVec<TablespaceOption>,
4460 /// Source location and node identity.
4461 pub meta: Meta,
4462}
4463
4464/// The undo-tablespace access state set by `ALTER UNDO TABLESPACE <name> SET {ACTIVE | INACTIVE}`
4465/// (`undo_tablespace_state`).
4466#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4467#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4468#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4469pub enum UndoTablespaceState {
4470 /// `SET ACTIVE`.
4471 Active,
4472 /// `SET INACTIVE`.
4473 Inactive,
4474}
4475
4476/// A MySQL `ALTER [UNDO] TABLESPACE <name> <action>` statement (gated by
4477/// [`StatementDdlGates::tablespace_ddl`](crate::dialect::StatementDdlGates::tablespace_ddl)).
4478///
4479/// The four regular productions (`ADD`/`DROP DATAFILE`, `RENAME TO`, a bare option list) and the
4480/// `UNDO` production (`SET {ACTIVE | INACTIVE}`) share the `ALTER [UNDO] TABLESPACE <name>` head
4481/// and are unified under [`AlterTablespaceAction`]. The action itself distinguishes the `UNDO`
4482/// form (only it carries [`SetState`](AlterTablespaceAction::SetState)), so no separate `undo`
4483/// flag is needed.
4484#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4485#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4486#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4487pub struct AlterTablespace {
4488 /// The tablespace name.
4489 pub name: Ident,
4490 /// The change applied to the tablespace.
4491 pub action: AlterTablespaceAction,
4492 /// Source location and node identity.
4493 pub meta: Meta,
4494}
4495
4496/// The change an [`AlterTablespace`] applies.
4497#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4498#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4499#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4500pub enum AlterTablespaceAction {
4501 /// `ADD DATAFILE '<f>' [<option>...]`.
4502 AddDatafile {
4503 /// The datafile path.
4504 datafile: Literal,
4505 /// The trailing alter-tablespace options in source order.
4506 options: ThinVec<TablespaceOption>,
4507 /// Source location and node identity.
4508 meta: Meta,
4509 },
4510 /// `DROP DATAFILE '<f>' [<option>...]`.
4511 DropDatafile {
4512 /// The datafile path.
4513 datafile: Literal,
4514 /// The trailing alter-tablespace options in source order.
4515 options: ThinVec<TablespaceOption>,
4516 /// Source location and node identity.
4517 meta: Meta,
4518 },
4519 /// `RENAME TO <new_name>` — takes no options (a trailing option is a parse error).
4520 Rename {
4521 /// The new tablespace name.
4522 new_name: Ident,
4523 /// Source location and node identity.
4524 meta: Meta,
4525 },
4526 /// A bare (non-empty) option list — `ALTER TABLESPACE <name> <option> [, <option>]...`
4527 /// with no `ADD`/`DROP`/`RENAME` lead.
4528 Options {
4529 /// The options in source order (at least one).
4530 options: ThinVec<TablespaceOption>,
4531 /// Source location and node identity.
4532 meta: Meta,
4533 },
4534 /// `SET {ACTIVE | INACTIVE} [<option>...]` — the `ALTER UNDO TABLESPACE` form (the trailing
4535 /// options are the `UNDO` subset, `ENGINE` alone).
4536 SetState {
4537 /// The new access state.
4538 state: UndoTablespaceState,
4539 /// The trailing undo-tablespace options in source order.
4540 options: ThinVec<TablespaceOption>,
4541 /// Source location and node identity.
4542 meta: Meta,
4543 },
4544}
4545
4546/// A MySQL `DROP [UNDO] TABLESPACE <name> [<option>...]` statement (gated by
4547/// [`StatementDdlGates::tablespace_ddl`](crate::dialect::StatementDdlGates::tablespace_ddl)).
4548///
4549/// One node for `DROP TABLESPACE` (`Sql_cmd_drop_tablespace`) and `DROP UNDO TABLESPACE`
4550/// (`Sql_cmd_drop_undo_tablespace`), distinguished by [`undo`](Self::undo): the regular form
4551/// accepts `ENGINE`/`WAIT` options, the `UNDO` form `ENGINE` alone (parser-enforced).
4552#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4553#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4554#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4555pub struct DropTablespace {
4556 /// Whether this is the `DROP UNDO TABLESPACE` variant.
4557 pub undo: bool,
4558 /// The tablespace name.
4559 pub name: Ident,
4560 /// Options supplied in source order.
4561 pub options: ThinVec<TablespaceOption>,
4562 /// Source location and node identity.
4563 pub meta: Meta,
4564}
4565
4566/// A MySQL `CREATE LOGFILE GROUP <name> ADD UNDOFILE '<f>' [<option>...]` statement (NDB; gated
4567/// by
4568/// [`StatementDdlGates::logfile_group_ddl`](crate::dialect::StatementDdlGates::logfile_group_ddl)).
4569///
4570/// The `ADD UNDOFILE` clause is mandatory (a bare `CREATE LOGFILE GROUP <name>` is a syntax
4571/// error). The option set is the logfile-group subset of the shared `ts_option_*` family
4572/// (`INITIAL_SIZE`, `UNDO_BUFFER_SIZE`, `REDO_BUFFER_SIZE`, `NODEGROUP`, `ENGINE`, `WAIT`,
4573/// `COMMENT`), parser-enforced.
4574#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4575#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4576#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4577pub struct CreateLogfileGroup {
4578 /// The logfile-group name.
4579 pub name: Ident,
4580 /// The mandatory `ADD UNDOFILE '<file>'` path.
4581 pub undofile: Literal,
4582 /// Options supplied in source order.
4583 pub options: ThinVec<TablespaceOption>,
4584 /// Source location and node identity.
4585 pub meta: Meta,
4586}
4587
4588/// A MySQL `ALTER LOGFILE GROUP <name> ADD UNDOFILE '<f>' [<option>...]` statement (NDB; gated by
4589/// [`StatementDdlGates::logfile_group_ddl`](crate::dialect::StatementDdlGates::logfile_group_ddl)).
4590///
4591/// Like [`CreateLogfileGroup`] the `ADD UNDOFILE` clause is mandatory; the accepted option set is
4592/// the narrower alter subset (`INITIAL_SIZE`, `ENGINE`, `WAIT`), parser-enforced.
4593#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4594#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4595#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4596pub struct AlterLogfileGroup {
4597 /// The logfile-group name.
4598 pub name: Ident,
4599 /// The mandatory `ADD UNDOFILE '<file>'` path.
4600 pub undofile: Literal,
4601 /// Options supplied in source order.
4602 pub options: ThinVec<TablespaceOption>,
4603 /// Source location and node identity.
4604 pub meta: Meta,
4605}
4606
4607/// A MySQL `DROP LOGFILE GROUP <name> [<option>...]` statement (NDB; gated by
4608/// [`StatementDdlGates::logfile_group_ddl`](crate::dialect::StatementDdlGates::logfile_group_ddl)).
4609///
4610/// The option set is `ENGINE`/`WAIT` (`opt_drop_ts_options`, shared with `DROP TABLESPACE`).
4611#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4612#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4613#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4614pub struct DropLogfileGroup {
4615 /// The logfile-group name.
4616 pub name: Ident,
4617 /// Options supplied in source order.
4618 pub options: ThinVec<TablespaceOption>,
4619 /// Source location and node identity.
4620 pub meta: Meta,
4621}