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 [`StatementDdlGates::create_or_replace_table`](crate::dialect::StatementDdlGates::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 /// `COLOCATE WITH <anchor> ON (<columns>)`.
1144 ColocateWith {
1145 /// Anchor table.
1146 table: ObjectName,
1147 /// This table's colocation-key columns.
1148 columns: ThinVec<Ident>,
1149 /// Source location and node identity.
1150 meta: Meta,
1151 },
1152 /// `IN COLOCATION GROUP <group> [ON (<columns>)]`.
1153 InColocationGroup {
1154 /// Group name.
1155 group: Ident,
1156 /// Optional member-key columns; omission is valid with explicit table storage options.
1157 columns: ThinVec<Ident>,
1158 /// Source location and node identity.
1159 meta: Meta,
1160 },
1161 /// A `WITH (<param> = <value>, …)` storage-parameter clause.
1162 With {
1163 /// params in source order.
1164 params: ThinVec<TableStorageParameter<X>>,
1165 /// Source location and node identity.
1166 meta: Meta,
1167 },
1168 /// An `ON COMMIT {PRESERVE ROWS | DELETE ROWS | DROP}` temporary-table clause.
1169 OnCommit {
1170 /// The on-commit action; see [`OnCommitAction`].
1171 action: OnCommitAction,
1172 /// Source location and node identity.
1173 meta: Meta,
1174 },
1175 /// A `TABLESPACE <name>` clause.
1176 Tablespace {
1177 /// The tablespace name.
1178 tablespace: Ident,
1179 /// Source location and node identity.
1180 meta: Meta,
1181 },
1182 /// A MySQL trailing table option written as an open `<name> [=] <value>` pair —
1183 /// `ENGINE = InnoDB`, `AUTO_INCREMENT = 100`, `DEFAULT CHARSET = utf8mb4`,
1184 /// `COMMENT = '...'`, `ROW_FORMAT = DYNAMIC`, `COLLATE = utf8mb4_general_ci`.
1185 ///
1186 /// Modelled as one canonical name/value pair rather than a variant (or a
1187 /// `CreateTable` field) per option keyword: the MySQL option
1188 /// vocabulary is large and server-version-dependent, so a single open shape
1189 /// round-trips an arbitrary option list — the same reasoning the [`CopyOption`]
1190 /// list follows. The optional `DEFAULT` noise word MySQL accepts before
1191 /// `CHARSET`/`COLLATE` is normalized away by the parser, and the `=` is optional.
1192 /// Gated by `create_table_clause_syntax.table_options`.
1193 ///
1194 /// [`CopyOption`]: super::CopyOption
1195 KeyValue {
1196 // The whole `<name> = <value>` payload is boxed (ADR-0007): a `TableOption`
1197 // (name + a 40 B value + meta) is the only fat payload among these variants, so
1198 // inlining it would set the enum's width and tax the lean `With`/`OnCommit`/
1199 // `Tablespace` variants — and, transitively, every `CreateTableOption` an
1200 // ANSI/PostgreSQL parse stores in its options `ThinVec`. Table options are a
1201 // cold DDL path, so the lone allocation per MySQL option is cheap, the same
1202 // box call `ColumnOption::Identity` makes for its `IdentityColumn`.
1203 /// The `<name> = <value>` option pair; see [`TableOption`].
1204 option: Box<TableOption>,
1205 /// Source location and node identity.
1206 meta: Meta,
1207 },
1208 /// The SQLite `WITHOUT ROWID` trailing table option: the table is stored as a
1209 /// clustered index on its primary key rather than the default implicit `rowid`.
1210 /// A bare keyword-style option (no `= value`), so a variant of its own rather than
1211 /// the MySQL [`KeyValue`](Self::KeyValue) name/value catch-all. SQLite
1212 /// comma-separates these trailing options (`... STRICT, WITHOUT ROWID`). Gated for
1213 /// acceptance by
1214 /// [`CreateTableClauseSyntax::without_rowid_table_option`](crate::dialect::CreateTableClauseSyntax::without_rowid_table_option).
1215 WithoutRowid {
1216 /// Source location and node identity.
1217 meta: Meta,
1218 },
1219 /// The SQLite `STRICT` trailing table option: the table enforces its declared
1220 /// column types instead of SQLite's default flexible typing. Like
1221 /// [`WithoutRowid`](Self::WithoutRowid) a bare keyword-style option, gated for
1222 /// acceptance by
1223 /// [`CreateTableClauseSyntax::strict_table_option`](crate::dialect::CreateTableClauseSyntax::strict_table_option).
1224 Strict {
1225 /// Source location and node identity.
1226 meta: Meta,
1227 },
1228 /// The legacy PostgreSQL `WITHOUT OIDS` trailing option: historically suppressed the
1229 /// system `oid` column. Modern PostgreSQL keeps it in the grammar as an accepted no-op,
1230 /// while the affirmative `WITH OIDS` has no production and *rejects* (both
1231 /// engine-measured). A bare keyword-style option, so a
1232 /// variant of its own rather than the MySQL [`KeyValue`](Self::KeyValue) catch-all. It
1233 /// occupies PostgreSQL's `OptWith` slot — mutually exclusive with a `WITH (…)` storage-
1234 /// parameter list, and it precedes `ON COMMIT` / `TABLESPACE`. Gated for acceptance by
1235 /// [`CreateTableClauseSyntax::without_oids`](crate::dialect::CreateTableClauseSyntax::without_oids).
1236 WithoutOids {
1237 /// Source location and node identity.
1238 meta: Meta,
1239 },
1240}
1241
1242/// One MySQL trailing table option: a `<name> = <value>` pair.
1243///
1244/// The canonical "options as data" shape: `name` is the option keyword as
1245/// written (`ENGINE`, `AUTO_INCREMENT`, `CHARSET`, ...) and `value` is its argument.
1246/// Boxed inside [`CreateTableOptionKind::KeyValue`] to keep that enum lean; mirrors
1247/// the [`CopyOption`](super::CopyOption) list element.
1248#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1249#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1250#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1251pub struct TableOption {
1252 /// Name referenced by this syntax.
1253 pub name: Ident,
1254 /// Value supplied by this syntax.
1255 pub value: TableOptionValue,
1256 /// Source location and node identity.
1257 pub meta: Meta,
1258}
1259
1260/// The value of a [`TableOption`].
1261///
1262/// Three surface forms a downstream converter cannot otherwise recover:
1263/// a bareword/keyword (`InnoDB`, `DYNAMIC`), a string (`COMMENT = '...'`), or a
1264/// number (`AUTO_INCREMENT = 100`). String and numeric values ride their
1265/// [`Literal`]'s `meta.span` and materialise lazily; a bareword keeps its
1266/// source spelling on the [`Ident`]. Mirrors [`CopyOptionValue`](super::CopyOptionValue),
1267/// which omits the numeric form COPY options never take.
1268#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1269#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1270#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1271pub enum TableOptionValue {
1272 /// A bareword/keyword value: `ENGINE = InnoDB`, `ROW_FORMAT = DYNAMIC`.
1273 Word {
1274 /// Identifier-form value.
1275 word: Ident,
1276 /// Source location and node identity.
1277 meta: Meta,
1278 },
1279 /// A string value: `COMMENT = 'text'`.
1280 String {
1281 /// Value supplied by this syntax.
1282 value: Literal,
1283 /// Source location and node identity.
1284 meta: Meta,
1285 },
1286 /// A numeric value: `AUTO_INCREMENT = 100`, `KEY_BLOCK_SIZE = 8`.
1287 Number {
1288 /// Value supplied by this syntax.
1289 value: Literal,
1290 /// Source location and node identity.
1291 meta: Meta,
1292 },
1293}
1294
1295#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1296#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1297#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1298/// The SQL on commit action forms represented by the AST.
1299pub enum OnCommitAction {
1300 /// `ON COMMIT PRESERVE ROWS` — keep the temp table's rows after commit.
1301 PreserveRows,
1302 /// `ON COMMIT DELETE ROWS` — empty the temp table on each commit.
1303 DeleteRows,
1304 /// `ON COMMIT DROP` — drop the temp table at the end of the transaction.
1305 Drop,
1306}
1307
1308#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1309#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1310#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1311/// An SQL table storage parameter.
1312pub struct TableStorageParameter<X: Extension = NoExt> {
1313 /// Name referenced by this syntax.
1314 pub name: ObjectName,
1315 /// Value supplied by this syntax.
1316 pub value: Option<Expr<X>>,
1317 /// Source location and node identity.
1318 pub meta: Meta,
1319}
1320
1321/// `ALTER TABLE [IF EXISTS] <name> <action> [, <action>]...`.
1322///
1323/// The schema-evolution counterpart of [`CreateTable`]. A single statement carries
1324/// one or more comma-separated [`AlterTableAction`]s, matching PostgreSQL's
1325/// multi-action form (`ALTER TABLE t ADD COLUMN a INT, DROP COLUMN b`). The `IF
1326/// EXISTS` prefix is gated by `existence_guards.if_exists` dialect data:
1327/// off under ANSI, on under PostgreSQL. The PostgreSQL-only `ONLY`
1328/// inheritance qualifier is a deliberate follow-up, tracked separately.
1329#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1330#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1331#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1332pub struct AlterTable<X: Extension = NoExt> {
1333 /// Whether the if exists form was present in the source.
1334 pub if_exists: bool,
1335 /// Name referenced by this syntax.
1336 pub name: ObjectName,
1337 /// actions in source order.
1338 pub actions: ThinVec<AlterTableAction<X>>,
1339 /// Source location and node identity.
1340 pub meta: Meta,
1341}
1342
1343/// A column target inside an `ALTER TABLE` action.
1344///
1345/// Most dialects name a top-level column (`c`), while DuckDB also admits dotted paths into
1346/// nested STRUCT fields for selected actions (`s.s2.k`). Kept separate from [`ObjectName`]
1347/// so ALTER-specific nested paths do not imply that table columns generally use relation-name
1348/// semantics.
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 struct AlterColumnTarget {
1353 /// parts in source order.
1354 pub parts: ThinVec<Ident>,
1355 /// Source location and node identity.
1356 pub meta: Meta,
1357}
1358
1359/// One action in an `ALTER TABLE` statement.
1360///
1361/// `AddColumn`/`AddConstraint` reuse the `CREATE TABLE` element nodes ([`ColumnDef`]
1362/// and [`TableConstraintDef`]) rather than inventing alter-specific copies, so the
1363/// `Other(X)` extension seams already on [`ColumnOption`]/[`TableConstraint`] cover
1364/// custom DDL attached through an alter too. The optional `COLUMN` noise
1365/// word is not represented: rendering normalizes to the canonical `ADD COLUMN`
1366/// spelling.
1367#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1368#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1369#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1370pub enum AlterTableAction<X: Extension = NoExt> {
1371 /// `SET COLOCATION GROUP <group>`.
1372 SetColocationGroup {
1373 /// Destination group.
1374 group: Ident,
1375 /// Source location and node identity.
1376 meta: Meta,
1377 },
1378 /// `DROP COLOCATION GROUP`.
1379 DropColocationGroup {
1380 /// Source location and node identity.
1381 meta: Meta,
1382 },
1383 /// `ADD [COLUMN] <col> <type> …` — add a column to the table.
1384 AddColumn {
1385 /// Whether the if not exists form was present in the source.
1386 if_not_exists: bool,
1387 /// Whether the optional `COLUMN` noise word was written (`ADD COLUMN c` vs the
1388 /// bare `ADD c`). Exact-synonym fidelity; the canonical render emits `COLUMN`.
1389 column_keyword: bool,
1390 /// Object targeted by this syntax.
1391 target: Option<AlterColumnTarget>,
1392 /// Column referenced by this syntax.
1393 column: ColumnDef<X>,
1394 /// Source location and node identity.
1395 meta: Meta,
1396 },
1397 /// `DROP [COLUMN] <name>` — remove a column from the table.
1398 DropColumn {
1399 /// Whether the if exists form was present in the source.
1400 if_exists: bool,
1401 /// Whether the optional `COLUMN` noise word was written (`DROP COLUMN c` vs the
1402 /// bare `DROP c`). Exact-synonym fidelity; the canonical render emits `COLUMN`.
1403 column_keyword: bool,
1404 /// Name referenced by this syntax.
1405 name: AlterColumnTarget,
1406 /// Optional behavior for this syntax.
1407 behavior: Option<DropBehavior>,
1408 /// Source location and node identity.
1409 meta: Meta,
1410 },
1411 /// `ALTER [COLUMN] <name> …` — change an existing column's definition.
1412 AlterColumn {
1413 /// Whether the optional `COLUMN` noise word was written (`ALTER COLUMN c` vs the
1414 /// bare `ALTER c`). Exact-synonym fidelity; the canonical render emits `COLUMN`.
1415 column_keyword: bool,
1416 /// Name referenced by this syntax.
1417 name: Ident,
1418 /// The per-column change; see [`AlterColumnAction`].
1419 change: AlterColumnAction<X>,
1420 /// Source location and node identity.
1421 meta: Meta,
1422 },
1423 /// `ADD CONSTRAINT …` — add a table constraint.
1424 AddConstraint {
1425 /// The constraint being added; see [`TableConstraintDef`].
1426 constraint: TableConstraintDef<X>,
1427 /// Source location and node identity.
1428 meta: Meta,
1429 },
1430 /// `DROP CONSTRAINT <name>` — remove a table constraint.
1431 DropConstraint {
1432 /// Whether the if exists form was present in the source.
1433 if_exists: bool,
1434 /// Name referenced by this syntax.
1435 name: Ident,
1436 /// Optional behavior for this syntax.
1437 behavior: Option<DropBehavior>,
1438 /// Source location and node identity.
1439 meta: Meta,
1440 },
1441 /// `DROP PRIMARY KEY` — remove the table's unnamed primary key.
1442 DropPrimaryKey {
1443 /// Optional drop behavior.
1444 behavior: Option<DropBehavior>,
1445 /// Source location and node identity.
1446 meta: Meta,
1447 },
1448 /// `SET (<name> = <value>, …)` — replace table storage/property options.
1449 SetOptions {
1450 /// Options in source order.
1451 params: ThinVec<TableStorageParameter<X>>,
1452 /// Source location and node identity.
1453 meta: Meta,
1454 },
1455 /// `RENAME [COLUMN] <name> TO <new_name>`.
1456 ///
1457 /// PostgreSQL parses the rename forms as a distinct `RenameStmt`, not the
1458 /// `AlterTableStmt` the other actions take; the canonical AST keeps one
1459 /// [`AlterTable`] node for every `ALTER TABLE` spelling, so the rename
1460 /// is a further action variant here. The optional `COLUMN` noise word is
1461 /// recorded, mirroring [`AddColumn`](Self::AddColumn).
1462 RenameColumn {
1463 /// Whether the optional `COLUMN` noise word was written (`RENAME COLUMN c` vs
1464 /// the bare `RENAME c`). Exact-synonym fidelity; the canonical render emits
1465 /// `COLUMN`.
1466 column_keyword: bool,
1467 /// Name referenced by this syntax.
1468 name: AlterColumnTarget,
1469 /// The column's new name.
1470 new_name: Ident,
1471 /// Source location and node identity.
1472 meta: Meta,
1473 },
1474 /// `RENAME CONSTRAINT <name> TO <new_name>`.
1475 RenameConstraint {
1476 /// Existing constraint name.
1477 name: Ident,
1478 /// Replacement constraint name.
1479 new_name: Ident,
1480 /// Source location and node identity.
1481 meta: Meta,
1482 },
1483 /// `RENAME TO <new_name>` — rename the table.
1484 RenameTable {
1485 /// The table's new name.
1486 new_name: Ident,
1487 /// Source location and node identity.
1488 meta: Meta,
1489 },
1490 /// `ATTACH PARTITION <name> <bound>` — attach an existing table as a partition
1491 /// (PostgreSQL declarative partitioning). A *standalone* action: PostgreSQL parses
1492 /// `ATTACH`/`DETACH PARTITION` as their own `AlterTableStmt` productions, so they never
1493 /// combine with other actions in a comma list (`ALTER TABLE p ADD COLUMN x, ATTACH …` is a
1494 /// syntax error). Gated by
1495 /// [`CreateTableClauseSyntax::declarative_partitioning`](crate::dialect::CreateTableClauseSyntax::declarative_partitioning).
1496 AttachPartition {
1497 /// The partition (child) table name.
1498 partition: ObjectName,
1499 // Boxed as the fat child (matching the `PartitionOf` body): a `PartitionBound` is the
1500 // widest payload here, so a partition attach pays one cold allocation rather than setting
1501 // `AlterTableAction`'s width (ADR-0007, box/inline budget).
1502 /// The partition bound (`FOR VALUES …` / `DEFAULT`); see [`PartitionBound`].
1503 bound: Box<PartitionBound<X>>,
1504 /// Source location and node identity.
1505 meta: Meta,
1506 },
1507 /// `DETACH PARTITION <name> [CONCURRENTLY | FINALIZE]` — detach a partition (PostgreSQL).
1508 /// Like [`AttachPartition`](Self::AttachPartition) a standalone action. The optional
1509 /// [`mode`](Self::DetachPartition::mode) records the `CONCURRENTLY` / `FINALIZE`
1510 /// qualifier; `None` for the bare form.
1511 DetachPartition {
1512 /// The partition (child) table name.
1513 partition: ObjectName,
1514 /// Mode selected by this syntax.
1515 mode: Option<DetachPartitionMode>,
1516 /// Source location and node identity.
1517 meta: Meta,
1518 },
1519}
1520
1521/// The qualifier on a `DETACH PARTITION` action. A `Copy` leaf enum whose span rides the owning
1522/// [`AlterTableAction::DetachPartition`], like [`DropBehavior`].
1523#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1524#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1525#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1526pub enum DetachPartitionMode {
1527 /// `CONCURRENTLY` — detach without a long-held lock (PostgreSQL).
1528 Concurrently,
1529 /// `FINALIZE` — complete an interrupted concurrent detach.
1530 Finalize,
1531}
1532
1533/// A single-column alteration inside `ALTER TABLE ... ALTER COLUMN <name> ...`.
1534///
1535/// The ANSI `SET DATA TYPE` and PostgreSQL `TYPE` spellings map to one canonical
1536/// [`SetDataType`](Self::SetDataType) variant; the optional PostgreSQL
1537/// `USING <expr>` conversion expression rides along when present.
1538#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1539#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1540#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1541pub enum AlterColumnAction<X: Extension = NoExt> {
1542 /// `SET DEFAULT <expr>` — set the column's default value.
1543 SetDefault {
1544 /// Expression evaluated by this syntax.
1545 expr: Box<Expr<X>>,
1546 /// Source location and node identity.
1547 meta: Meta,
1548 },
1549 /// `DROP DEFAULT` — remove the column's default value.
1550 DropDefault {
1551 /// Source location and node identity.
1552 meta: Meta,
1553 },
1554 /// `SET NOT NULL` — add a not-null constraint to the column.
1555 SetNotNull {
1556 /// Source location and node identity.
1557 meta: Meta,
1558 },
1559 /// `DROP NOT NULL` — remove the column's not-null constraint.
1560 DropNotNull {
1561 /// Source location and node identity.
1562 meta: Meta,
1563 },
1564 /// `ADD GENERATED {ALWAYS | BY DEFAULT} AS IDENTITY [(…)]` — add identity generation
1565 /// to an existing column.
1566 AddIdentity {
1567 /// Identity generation mode and sequence options.
1568 identity: Box<IdentityColumn<X>>,
1569 /// Source location and node identity.
1570 meta: Meta,
1571 },
1572 /// `SET DATA TYPE <t>` / `TYPE <t> [USING …]` — change the column's data type.
1573 SetDataType {
1574 /// Whether the ANSI `SET DATA` prefix was written (`SET DATA TYPE <t>`) versus
1575 /// the bare PostgreSQL `TYPE <t>`. Exact-synonym fidelity; the canonical render
1576 /// emits `SET DATA TYPE`.
1577 set_data: bool,
1578 /// Data type named by this syntax.
1579 data_type: DataType<X>,
1580 /// Optional using for this syntax.
1581 using: Option<Box<Expr<X>>>,
1582 /// Source location and node identity.
1583 meta: Meta,
1584 },
1585}
1586
1587/// `DROP {TABLE | VIEW | INDEX | SCHEMA} [IF EXISTS] <name> [, ...] [CASCADE | RESTRICT]`.
1588///
1589/// Non-generic: a drop names objects and a behaviour only — it carries no
1590/// expressions or extension nodes — so it parallels the other leaf statement
1591/// payloads ([`super::TransactionStatement`], [`super::SessionStatement`]). The `IF
1592/// EXISTS` prefix and the `CASCADE`/`RESTRICT` drop behaviour are each gated by
1593/// `schema_change_syntax` dialect data. Materialized-view and concurrent-index drop
1594/// surfaces are not modelled here.
1595#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1596#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1597#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1598pub struct DropStatement {
1599 /// Which object kind is dropped (`TABLE`/`VIEW`/…); see [`DropObjectKind`].
1600 pub object_kind: DropObjectKind,
1601 /// Whether the if exists form was present in the source.
1602 pub if_exists: bool,
1603 /// Names in source order.
1604 pub names: ThinVec<ObjectName>,
1605 /// Optional behavior for this syntax.
1606 pub behavior: Option<DropBehavior>,
1607 /// Source location and node identity.
1608 pub meta: Meta,
1609}
1610
1611/// The kind of object a `DROP` statement removes.
1612///
1613/// The routine kinds (`FUNCTION`/`PROCEDURE`) take an argument-type signature, so
1614/// they are a separate [`Statement::DropRoutine`]
1615/// statement rather than a variant here;
1616/// this covers the object kinds a plain name list can drop.
1617#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1618#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1619#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1620pub enum DropObjectKind {
1621 /// `DROP TABLE`.
1622 Table,
1623 /// `DROP VIEW`.
1624 View,
1625 /// `DROP MATERIALIZED VIEW`.
1626 MaterializedView,
1627 /// `DROP INDEX`.
1628 Index,
1629 /// `DROP SCHEMA`.
1630 Schema,
1631 /// `DROP TYPE <name> [, …]` — a user-defined type (DuckDB/PostgreSQL), gated by
1632 /// [`StatementDdlGates::create_type`](crate::dialect::StatementDdlGates::create_type) (the same
1633 /// flag that admits [`CreateType`]). The comma list and `CASCADE`/`RESTRICT` behaviour
1634 /// ride the shared [`DropStatement`] grammar; DuckDB parse-accepts a multi-name list and
1635 /// only rejects it at plan time ("can only drop one object at a time"), so the shape is
1636 /// the general one.
1637 Type,
1638 /// `DROP SEQUENCE <name> [, …]` — a sequence generator (DuckDB/PostgreSQL), gated by
1639 /// [`StatementDdlGates::create_sequence`](crate::dialect::StatementDdlGates::create_sequence) (the same
1640 /// flag that admits [`CreateSequence`]). The comma list and `CASCADE`/`RESTRICT` ride the
1641 /// shared [`DropStatement`] grammar. DuckDB's parser accepts `DROP SEQUENCE` (the
1642 /// `IF EXISTS` form binds; the bare/list/`CASCADE` forms parse but bind-reject a missing
1643 /// object — engine-measured), so the modelled shape is the general one.
1644 Sequence,
1645 /// `DROP MACRO <name> [, …]` — a scalar macro (DuckDB), gated by
1646 /// [`StatementDdlGates::create_macro`](crate::dialect::StatementDdlGates::create_macro) (the same
1647 /// flag that admits [`CreateMacro`]). Unlike PostgreSQL's `DROP FUNCTION`, the macro drop
1648 /// takes *no* argument-type signature (`DROP MACRO m(int)` is a DuckDB syntax error), so it
1649 /// rides the shared [`DropStatement`] name-list grammar rather than the signature
1650 /// [`Statement::DropRoutine`] path — the `FUNCTION` spelling
1651 /// (which DuckDB accepts as a synonym) still routes to that routine drop, so the two DROP
1652 /// spellings land on distinct nodes and no [`MacroSpelling`] tag is needed here. The comma
1653 /// list and `CASCADE`/`RESTRICT` ride the shared grammar; DuckDB parse-accepts a multi-name
1654 /// list and only bind-rejects it ("can only drop one object at a time" — engine-measured).
1655 Macro,
1656 /// `DROP MACRO TABLE <name> [, …]` — a *table* macro (DuckDB), the drop counterpart of a
1657 /// `CREATE MACRO … AS TABLE`. Separate from [`Macro`](DropObjectKind::Macro) because DuckDB
1658 /// keeps scalar and table macros in distinct namespaces (`DROP MACRO m` drops a "Macro
1659 /// Function", `DROP MACRO TABLE m` a "Table Macro Function" — engine-measured) and the
1660 /// `TABLE` keyword must round-trip verbatim. Gated by the same
1661 /// [`StatementDdlGates::create_macro`](crate::dialect::StatementDdlGates::create_macro) flag;
1662 /// only `MACRO TABLE` (not `TABLE MACRO` or `FUNCTION TABLE`) is accepted — engine-measured.
1663 MacroTable,
1664 /// `DROP TRIGGER [IF EXISTS] [<schema> .] <name>` — a trigger (MySQL / SQLite), gated by
1665 /// either trigger-modelling flag (SQLite's
1666 /// [`StatementDdlGates::create_trigger`](crate::dialect::StatementDdlGates::create_trigger) or
1667 /// MySQL's
1668 /// [`StatementDdlGates::compound_statements`](crate::dialect::StatementDdlGates::compound_statements)).
1669 /// Both dialects spell the same name-only drop (no `ON <table>`, which is PostgreSQL's
1670 /// separate trigger-drop shape), so it rides the shared [`DropStatement`] name-list grammar.
1671 Trigger,
1672}
1673
1674/// The dependency behaviour of a `DROP` or `ALTER TABLE ... DROP`: whether dependent
1675/// objects are dropped too (`CASCADE`) or block the drop (`RESTRICT`).
1676#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1677#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1678#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1679pub enum DropBehavior {
1680 /// `CASCADE` — also drop objects that depend on the target.
1681 Cascade,
1682 /// `RESTRICT` — refuse the drop if any object depends on the target.
1683 Restrict,
1684}
1685
1686/// A PostgreSQL `DROP TRANSFORM [IF EXISTS] FOR <type> LANGUAGE <lang>
1687/// [CASCADE | RESTRICT]` statement (`DropTransformStmt`, gram.y).
1688///
1689/// Kept apart from [`DropStatement`] because a transform is not named by a plain
1690/// name list: it is identified by the `(type, language)` pair a `CREATE TRANSFORM`
1691/// registers, which is exactly the [`ObjectReference::Transform`] shape the shared
1692/// object-reference axis already models (also reached by `ALTER EXTENSION … ADD|DROP
1693/// TRANSFORM`). Reusing that variant keeps every transform reference on one node, so
1694/// an AST walk finds transforms uniformly regardless of the parent statement, rather
1695/// than re-deriving the `FOR type LANGUAGE lang` shape here (the reason [`DropRoutine`]
1696/// inlines its signature does not apply — a transform has a single fixed shape, not a
1697/// per-kind one).
1698///
1699/// PostgreSQL admits exactly one transform per statement (no comma list — engine-measured)
1700/// and places `IF EXISTS` between the `TRANSFORM` keyword and `FOR` (`DROP TRANSFORM IF
1701/// EXISTS FOR …`; `FOR type IF EXISTS LANGUAGE` is a syntax error), which is why the guard
1702/// rides on this node rather than on the shared reference render.
1703///
1704/// [`DropRoutine`]: super::Statement::DropRoutine
1705#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1706#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1707#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1708pub struct DropTransform<X: Extension = NoExt> {
1709 /// The dropped transform, always an [`ObjectReference::Transform`] (`FOR <type>
1710 /// LANGUAGE <lang>`).
1711 pub object: ObjectReference<X>,
1712 /// Whether the `IF EXISTS` guard was present in the source.
1713 pub if_exists: bool,
1714 /// Optional `CASCADE`/`RESTRICT` behaviour for this syntax.
1715 pub behavior: Option<DropBehavior>,
1716 /// Source location and node identity.
1717 pub meta: Meta,
1718}
1719
1720/// The object kind a [`Statement::CommentOn`] targets.
1721///
1722/// The object's name rides [`Statement::CommentOn::name`](super::Statement::CommentOn).
1723/// Targets with additional grammar carry that data in their variant.
1724#[non_exhaustive]
1725#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1726#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1727#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1728pub enum CommentTarget<X: Extension = NoExt> {
1729 /// `COMMENT ON TABLE <name>`.
1730 Table,
1731 /// `COMMENT ON COLUMN <name>`.
1732 Column,
1733 /// `COMMENT ON DATABASE <name>`.
1734 Database,
1735 /// `COMMENT ON VIEW <name>`.
1736 View,
1737 /// `COMMENT ON MATERIALIZED VIEW <name>`.
1738 MaterializedView,
1739 /// `COMMENT ON INDEX <name>`.
1740 Index,
1741 /// `COMMENT ON CONSTRAINT <name> ON <table>`.
1742 Constraint,
1743 /// `COMMENT ON PROCEDURE <name>[(<arg types>)]`. The argument-type list
1744 /// disambiguates overloaded procedures; `None` is an unspecified signature
1745 /// (bare `PROCEDURE foo`), `Some` a written list — possibly empty (`foo()`) —
1746 /// mirroring [`RoutineSignature::arg_types`](super::RoutineSignature).
1747 Procedure {
1748 /// Optional arg types for this syntax.
1749 arg_types: Option<ThinVec<DataType<X>>>,
1750 },
1751}
1752
1753/// The payload of a [`Statement::CommentOn`], boxed off the
1754/// statement enum to keep it within its size budget (like [`DropStatement`]).
1755///
1756/// `name` is the object's (possibly qualified) name; `target` records its kind (and, for
1757/// a procedure, its argument-type signature). `comment` is `None` for `IS NULL` — which
1758/// clears the object's comment — and `Some` for a string literal.
1759#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1760#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1761#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1762pub struct CommentOnStatement<X: Extension = NoExt> {
1763 /// Whether the front-position `IF EXISTS` guard was present.
1764 pub if_exists: bool,
1765 /// Object targeted by this syntax.
1766 pub target: CommentTarget<X>,
1767 /// Name referenced by this syntax.
1768 pub name: ObjectName,
1769 /// Relation owning a `CONSTRAINT` target; absent for every other target.
1770 pub constraint_table: Option<ObjectName>,
1771 /// Optional comment for this syntax.
1772 pub comment: Option<Literal>,
1773 /// Source location and node identity.
1774 pub meta: Meta,
1775}
1776
1777/// `CREATE SCHEMA [IF NOT EXISTS] [<name>] [AUTHORIZATION <role>] [<schema element> ...]`.
1778///
1779/// At least one of `name` / `authorization` is present — PostgreSQL's `CREATE SCHEMA
1780/// AUTHORIZATION joe` omits the schema name and derives it from the owning role.
1781///
1782/// The SQL-standard form embeds a list of component objects created *inside* the new
1783/// schema (`CREATE SCHEMA s CREATE TABLE t (...)`); [`elements`](Self::elements) holds
1784/// them as children so the whole construct stays ONE statement, mirroring PostgreSQL's
1785/// `CreateSchemaStmt.schemaElts` (which likewise carries the components as raw
1786/// statement parsenodes). Rendering them embedded preserves the statement COUNT that
1787/// downstream consumers observe — the earlier model split them into separate
1788/// `;`-joined statements, a statement-level rewrite no source-fidelity render can undo.
1789///
1790/// The element list is a *closed* admissible set enforced by the parser (measured
1791/// against PostgreSQL: `CREATE TABLE`/`VIEW`/`INDEX`/`SEQUENCE`/`TRIGGER` and `GRANT`
1792/// — and PostgreSQL rejects `CREATE MATERIALIZED VIEW`/`FUNCTION`, a nested `CREATE
1793/// SCHEMA`, `DROP`/`ALTER`/`INSERT`/… as elements). Modelled as a general
1794/// [`Statement`] list rather than a bespoke element enum — the same idiom as
1795/// [`CreateTrigger`]'s body — because the members ARE statements and reusing the
1796/// statement shape keeps the differential/structural oracles element-agnostic; the
1797/// closed set is the recorded acceptance bound, enforced at parse time. Non-empty only
1798/// under PostgreSQL (`schema_elements` gate); every other dialect parses a bare schema
1799/// head. Generic over `X` because the elements carry the extension node.
1800#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1801#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1802#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1803pub struct CreateSchema<X: Extension = NoExt> {
1804 /// Whether the if not exists form was present in the source.
1805 pub if_not_exists: bool,
1806 /// Name referenced by this syntax.
1807 pub name: Option<ObjectName>,
1808 /// Optional authorization for this syntax.
1809 pub authorization: Option<Ident>,
1810 /// The embedded schema-element statements, in source order. Empty for a bare
1811 /// `CREATE SCHEMA` head (the common case) and for every non-PostgreSQL dialect.
1812 pub elements: ThinVec<Statement<X>>,
1813 /// Source location and node identity.
1814 pub meta: Meta,
1815}
1816
1817/// `CREATE [OR REPLACE] [TEMP|TEMPORARY] VIEW` and `CREATE MATERIALIZED VIEW`,
1818/// with the `AS <query>` body that every view shares (a view body reuses [`Query`]
1819/// rather than re-deriving the SELECT grammar).
1820///
1821/// One node covers both spellings, distinguished by `materialized`. The divergent
1822/// tails are mutually exclusive by construction: the parser fills `check_option`
1823/// only for a regular view and `with_data` only for a materialized one. `OR
1824/// REPLACE` and the `MATERIALIZED` keyword (with its `WITH [NO] DATA` populate
1825/// clause) are PostgreSQL extensions gated by `schema_change_syntax` dialect data;
1826/// the ANSI `WITH [CASCADED|LOCAL] CHECK OPTION` is always accepted.
1827///
1828/// `recursive` records the `CREATE [OR REPLACE] [TEMP|TEMPORARY] RECURSIVE VIEW`
1829/// spelling (DuckDB, gated by
1830/// [`ViewSequenceClauseSyntax::recursive_views`](crate::dialect::ViewSequenceClauseSyntax::recursive_views)):
1831/// the keyword
1832/// sits between the `TEMP`/`TEMPORARY` prefix and `VIEW`, never composes with
1833/// `MATERIALIZED` (engine-rejected), and — mirroring the engine, which desugars a
1834/// recursive view to `WITH RECURSIVE` — requires the explicit `columns` list, so
1835/// the parser rejects the bare `RECURSIVE VIEW v AS …` form.
1836#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1837#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1838#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1839pub struct CreateView<X: Extension = NoExt> {
1840 /// Whether the or replace form was present in the source.
1841 pub or_replace: bool,
1842 /// The MySQL `[ALGORITHM = …] [DEFINER = …] [SQL SECURITY …]` definition-option prefix
1843 /// (see [`ViewOptions`]); all-`None` for every non-MySQL view and a bare MySQL view. The
1844 /// options sit between `OR REPLACE` and the `VIEW` keyword.
1845 pub options: ViewOptions,
1846 /// Whether the materialized form was present in the source.
1847 pub materialized: bool,
1848 /// Whether the recursive form was present in the source.
1849 pub recursive: bool,
1850 /// Optional temporary for this syntax.
1851 pub temporary: Option<TemporaryTableKind>,
1852 /// Whether the if not exists form was present in the source.
1853 pub if_not_exists: bool,
1854 /// Name referenced by this syntax.
1855 pub name: ObjectName,
1856 /// Columns in source order.
1857 pub columns: ThinVec<Ident>,
1858 /// Optional `TO <target>` storage relation for dialects that expose one.
1859 pub to: Option<ObjectName>,
1860 /// Query governed by this node.
1861 pub query: Box<Query<X>>,
1862 /// Optional check option for this syntax.
1863 pub check_option: Option<ViewCheckOption>,
1864 /// Whether the with data form was present in the source.
1865 pub with_data: Option<bool>,
1866 /// Source location and node identity.
1867 pub meta: Meta,
1868}
1869
1870/// `REFRESH MATERIALIZED VIEW [CONCURRENTLY] <name> [WITH [NO] DATA]`.
1871///
1872/// The modifier fields are retained even when a downstream engine chooses to reject them
1873/// semantically; parsing them prevents an accepted spelling from being silently discarded.
1874#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1875#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1876#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1877pub struct RefreshMaterializedView {
1878 /// Whether `CONCURRENTLY` was written.
1879 pub concurrently: bool,
1880 /// Materialized view being refreshed.
1881 pub name: ObjectName,
1882 /// `Some(true)` for `WITH DATA`, `Some(false)` for `WITH NO DATA`, and `None` when omitted.
1883 pub with_data: Option<bool>,
1884 /// Source location and node identity.
1885 pub meta: Meta,
1886}
1887
1888/// Partition function declared by a colocation group.
1889#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1890#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1891#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1892pub enum ColocationPartitionKind {
1893 /// Hash partitioning.
1894 Hash,
1895 /// Range partitioning.
1896 Range,
1897}
1898
1899/// `CREATE COLOCATION GROUP [IF NOT EXISTS] ...`.
1900#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1901#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1902#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1903pub struct CreateColocationGroup {
1904 /// Whether `IF NOT EXISTS` was written.
1905 pub if_not_exists: bool,
1906 /// Group name.
1907 pub name: Ident,
1908 /// Partition function.
1909 pub partition: ColocationPartitionKind,
1910 /// Partition-key columns.
1911 pub columns: ThinVec<Ident>,
1912 /// Shard count, preserved as the parsed integer literal for downstream range validation.
1913 pub shards: Literal,
1914 /// Source location and node identity.
1915 pub meta: Meta,
1916}
1917
1918/// `DROP COLOCATION GROUP [IF EXISTS] <name>`.
1919#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1920#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1921#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1922pub struct DropColocationGroup {
1923 /// Whether `IF EXISTS` was written.
1924 pub if_exists: bool,
1925 /// Group name.
1926 pub name: Ident,
1927 /// Source location and node identity.
1928 pub meta: Meta,
1929}
1930
1931/// `WITH [ CASCADED | LOCAL ] CHECK OPTION` on a regular (non-materialized) view.
1932#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1933#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1934#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1935pub enum ViewCheckOption {
1936 /// Bare `WITH CHECK OPTION`; PostgreSQL treats the unqualified form as `CASCADED`.
1937 Unspecified,
1938 /// `WITH CASCADED CHECK OPTION` — enforce the check on this and all underlying views.
1939 Cascaded,
1940 /// `WITH LOCAL CHECK OPTION` — enforce the check on this view only.
1941 Local,
1942}
1943
1944/// The MySQL `ALGORITHM = { UNDEFINED | MERGE | TEMPTABLE }` view-processing algorithm, the
1945/// first of the [`ViewOptions`] definition-option prefix. A `Copy` tag whose span rides the
1946/// owning [`ViewOptions`], like [`SqlSecurityContext`].
1947#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1948#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1949#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1950pub enum ViewAlgorithm {
1951 /// `ALGORITHM = UNDEFINED` — the server chooses between `MERGE` and `TEMPTABLE` (the default).
1952 Undefined,
1953 /// `ALGORITHM = MERGE` — the view's text is merged into the referencing statement.
1954 Merge,
1955 /// `ALGORITHM = TEMPTABLE` — the view result is materialized into a temporary table.
1956 TempTable,
1957}
1958
1959/// The MySQL view definition-option prefix `[ALGORITHM = …] [DEFINER = …] [SQL SECURITY …]`,
1960/// shared verbatim by [`CreateView`] and [`AlterView`] — the two statements name the same
1961/// options in the same fixed source order, immediately before the `VIEW` keyword.
1962///
1963/// Each option is independently optional; the source order (algorithm, then definer, then SQL
1964/// security) is engine-required — a permutation (`DEFINER` before `ALGORITHM`, `SQL SECURITY`
1965/// before either) is `ER_PARSE_ERROR`. All-`None` is the ordinary view with no prefix (every
1966/// non-MySQL dialect, and a bare MySQL `CREATE/ALTER VIEW`). The [`Definer`] reuses the shared
1967/// routine/event account reference; the [`SqlSecurityContext`] reuses the routine security tag.
1968/// This is a non-spanned bundle — [`Definer`] carries its own span, and the two `Copy` tags
1969/// ride the owning statement's span, so the prefix needs no `meta` of its own.
1970#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
1971#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1972#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1973pub struct ViewOptions {
1974 /// The optional `ALGORITHM = …` processing algorithm; `None` when omitted.
1975 pub algorithm: Option<ViewAlgorithm>,
1976 /// The optional `DEFINER = <user>` account; `None` when omitted. Boxed like the routine
1977 /// definer (a rare fat field paying only a null pointer when absent).
1978 pub definer: Option<Box<Definer>>,
1979 /// The optional `SQL SECURITY { DEFINER | INVOKER }` privilege context; `None` when omitted.
1980 pub sql_security: Option<SqlSecurityContext>,
1981}
1982
1983/// `ALTER [ALGORITHM = …] [DEFINER = …] [SQL SECURITY …] VIEW <name> [(<columns>)] AS <query>
1984/// [WITH [CASCADED | LOCAL] CHECK OPTION]` — the MySQL view redefinition (gated by
1985/// [`ViewSequenceClauseSyntax::view_definition_options`](crate::dialect::ViewSequenceClauseSyntax::view_definition_options)).
1986///
1987/// MySQL's `ALTER VIEW` re-specifies the whole view body; it is the [`CreateView`] grammar
1988/// minus `OR REPLACE`/`IF NOT EXISTS` (server-measured: both `ER_PARSE_ERROR` after `ALTER`)
1989/// and the `TEMP`/`MATERIALIZED`/`RECURSIVE` prefixes MySQL has no views for. The shared
1990/// [`ViewOptions`] prefix and the [`ViewCheckOption`] tail are reused verbatim, and the body
1991/// reuses [`Query`] like every view.
1992#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1993#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
1994#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
1995pub struct AlterView<X: Extension = NoExt> {
1996 /// The `[ALGORITHM = …] [DEFINER = …] [SQL SECURITY …]` definition-option prefix; all-`None`
1997 /// for a bare `ALTER VIEW`.
1998 pub options: ViewOptions,
1999 /// The view name (`db.view` or bare `view`).
2000 pub name: ObjectName,
2001 /// The optional explicit output-column list; empty when omitted.
2002 pub columns: ThinVec<Ident>,
2003 /// The redefining `AS <query>` body.
2004 pub query: Box<Query<X>>,
2005 /// The optional `WITH [CASCADED | LOCAL] CHECK OPTION` constraint.
2006 pub check_option: Option<ViewCheckOption>,
2007 /// Source location and node identity.
2008 pub meta: Meta,
2009}
2010
2011/// A DuckDB `CREATE [PERSISTENT] SECRET <name> ( <option> <value> [, ...] )`
2012/// secrets-management statement (DuckDB-specific; gated by
2013/// [`StatementDdlGates::create_secret`](crate::dialect::StatementDdlGates::create_secret)).
2014///
2015/// DuckDB stores credentials (S3/HTTP/… access) as named secrets. `persistent`
2016/// records the `PERSISTENT` keyword (a persistent secret survives the session; the
2017/// bare form is session-temporary). The parenthesized [`options`](Self::options) are
2018/// `<name> <value>` pairs — the required `TYPE <provider>` plus provider-specific
2019/// settings (`KEY_ID`, `REGION`, …). Only the bare/`PERSISTENT` name-then-options form
2020/// the corpus exercises is modelled; the explicit `TEMPORARY` keyword, `OR REPLACE`,
2021/// `IF NOT EXISTS`, the anonymous (unnamed) secret, and the `IN <storage>` clause are
2022/// not part of this shape.
2023#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2024#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2025#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2026pub struct CreateSecret<X: Extension = NoExt> {
2027 /// Whether the persistent form was present in the source.
2028 pub persistent: bool,
2029 /// Name referenced by this syntax.
2030 pub name: ObjectName,
2031 /// Options supplied in source order.
2032 pub options: ThinVec<SecretOption<X>>,
2033 /// Source location and node identity.
2034 pub meta: Meta,
2035}
2036
2037/// One `<name> <value>` option inside a [`CreateSecret`] option list (`TYPE S3`,
2038/// `KEY_ID '...'`). The value is modelled as a general [`Expr`] — DuckDB's own option
2039/// value is a bare word, string, or list — so the corpus `TYPE <provider>` form
2040/// round-trips; the wider expression grammar is the recorded acceptance bound.
2041#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2042#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2043#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2044pub struct SecretOption<X: Extension = NoExt> {
2045 /// Name referenced by this syntax.
2046 pub name: Ident,
2047 /// Value supplied by this syntax.
2048 pub value: Expr<X>,
2049 /// Source location and node identity.
2050 pub meta: Meta,
2051}
2052
2053/// A DuckDB `DROP [PERSISTENT | TEMPORARY] SECRET [IF EXISTS] <name> [FROM <storage>]`
2054/// secrets-management statement (DuckDB-specific; gated by
2055/// [`StatementDdlGates::create_secret`](crate::dialect::StatementDdlGates::create_secret) —
2056/// the same whole-statement gate that admits [`CreateSecret`], because `DROP SECRET` is the
2057/// same secrets behaviour surface and rides the one flag rather than a second gate).
2058///
2059/// The drop counterpart of [`CreateSecret`]. In DuckDB's grammar `drop_secret.y` is the
2060/// *only* `DROP` with its own top-level `stmt` production (not a [`DropStatement`] object
2061/// kind), because it carries the `opt_persist` persistence modifier and a `FROM <storage>`
2062/// backend selector — neither of which the shared name-list DROP grammar has.
2063/// [`persistence`](Self::persistence) records which of the three `opt_persist` spellings
2064/// preceded `SECRET` (absent → [`SecretPersistence::Default`]);
2065/// [`storage`](Self::storage) is the optional `FROM <backend>` secret-storage name. The
2066/// dropped secret is a single identifier (grammar `ColId`), never a qualified object name.
2067///
2068/// Non-generic: a secret drop names a secret, an optional storage backend, and two flags —
2069/// it carries no expressions or extension nodes — so it parallels the leaf
2070/// [`DropStatement`]/[`DropTransform`] payloads.
2071#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2072#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2073#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2074pub struct DropSecretStmt {
2075 /// Which `opt_persist` spelling preceded `SECRET`; see [`SecretPersistence`].
2076 pub persistence: SecretPersistence,
2077 /// Whether the `IF EXISTS` existence guard was present in the source.
2078 pub if_exists: bool,
2079 /// Name referenced by this syntax.
2080 pub name: Ident,
2081 /// Optional `FROM <storage>` secret-storage backend selector.
2082 pub storage: Option<Ident>,
2083 /// Source location and node identity.
2084 pub meta: Meta,
2085}
2086
2087/// The `opt_persist` persistence modifier on a DuckDB `DROP SECRET`: which of the three
2088/// storage scopes the statement names. (The wider `CREATE SECRET` grammar shares the same
2089/// `opt_persist` production; [`CreateSecret`] models only its `PERSISTENT`/absent forms as
2090/// a `bool`, so this three-valued modifier lives with the drop that needs all three.)
2091#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2092#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2093#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2094pub enum SecretPersistence {
2095 /// No modifier written — DuckDB's `default` secret scope (`DROP SECRET s`).
2096 Default,
2097 /// `TEMPORARY` — the session-scoped, in-memory secret store.
2098 Temporary,
2099 /// `PERSISTENT` — the on-disk persistent secret store.
2100 Persistent,
2101}
2102
2103/// A SQLite `CREATE VIRTUAL TABLE [IF NOT EXISTS] [<schema> .] <name> USING <module>
2104/// [( <arg> [, <arg>] * )]` statement (SQLite-specific; gated by
2105/// [`StatementDdlGates::create_virtual_table`](crate::dialect::StatementDdlGates::create_virtual_table)).
2106///
2107/// A virtual table delegates its storage and query implementation to a *module*
2108/// (`fts5`, `rtree`, `csv`, …). The module — not SQLite's core grammar — owns the
2109/// argument syntax: `fts5` reads column names and `tokenize = …` options, `rtree`
2110/// reads dimension columns, and a bespoke module reads whatever it likes. SQLite's
2111/// own parser therefore imposes almost no structure on the argument list — it splits
2112/// the parenthesized text on the *top-level* commas (parentheses nest and quoted
2113/// strings are transparent) and hands each raw slice to the module verbatim, even
2114/// tolerating empty members (`USING m(a,,b)`). Module resolution and any argument
2115/// validation happen at execution time, so the parse layer accepts an unknown module
2116/// and any balanced-parenthesis token soup.
2117///
2118/// Each argument is consequently modelled as an OPAQUE verbatim [`ModuleArg`] (the
2119/// interned source text of one top-level slice), never a parsed sub-grammar — imposing
2120/// column/option structure would invent constraints SQLite does not enforce.
2121///
2122/// [`args`](Self::args) is `None` for the bare `USING m` form and `Some` (possibly
2123/// empty) for the parenthesized `USING m (…)` form, so the two round-trip distinctly.
2124/// The name admits at most two parts (`schema.table`); SQLite has no `TEMP` virtual
2125/// table, so no temporary modifier is modelled. Non-generic: the arguments hold no
2126/// expressions or extension nodes.
2127#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2128#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2129#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2130pub struct CreateVirtualTable {
2131 /// Whether the if not exists form was present in the source.
2132 pub if_not_exists: bool,
2133 /// Name referenced by this syntax.
2134 pub name: ObjectName,
2135 /// The virtual-table module name (`USING <module>`).
2136 pub module: Ident,
2137 /// Arguments in source order.
2138 pub args: Option<ThinVec<ModuleArg>>,
2139 /// Source location and node identity.
2140 pub meta: Meta,
2141}
2142
2143/// One opaque, verbatim argument of a [`CreateVirtualTable`] module argument list.
2144///
2145/// [`text`](Self::text) is the interned source text of a single top-level
2146/// comma-delimited slice, preserved exactly (internal spacing and all) because the
2147/// module owns its grammar — the parser never interprets it. An empty slice (from
2148/// `USING m(a,,b)` or a trailing comma) is a legal empty argument.
2149#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2150#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2151#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2152pub struct ModuleArg {
2153 /// The comment text.
2154 pub text: Symbol,
2155 /// Source location and node identity.
2156 pub meta: Meta,
2157}
2158
2159/// `CREATE [UNIQUE] INDEX [CONCURRENTLY] [IF NOT EXISTS] [<name>] ON <table>
2160/// [USING <method>] (<column> [, ...]) [WHERE <predicate>]`.
2161///
2162/// `UNIQUE` is portable; `CONCURRENTLY`, the `USING <method>` access-method clause,
2163/// and the trailing `WHERE` partial-index predicate are PostgreSQL extensions gated
2164/// by `schema_change_syntax.index_extensions` dialect data. The index
2165/// name is optional — PostgreSQL derives one from the table and columns when it is
2166/// omitted (`CREATE INDEX ON t (a)`). COLLATE / operator-class / `INCLUDE` column
2167/// decorations are a deliberate follow-up.
2168#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2169#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2170#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2171pub struct CreateIndex<X: Extension = NoExt> {
2172 /// Whether the unique form was present in the source.
2173 pub unique: bool,
2174 /// Whether the concurrently form was present in the source.
2175 pub concurrently: bool,
2176 /// Whether the if not exists form was present in the source.
2177 pub if_not_exists: bool,
2178 /// Name referenced by this syntax.
2179 pub name: Option<Ident>,
2180 /// Table referenced by this syntax.
2181 pub table: ObjectName,
2182 /// Optional using for this syntax.
2183 pub using: Option<Ident>,
2184 /// Columns in source order.
2185 pub columns: ThinVec<IndexColumn<X>>,
2186 /// `WITH (<name> = <value>, …)` index storage parameters.
2187 pub with_params: ThinVec<TableStorageParameter<X>>,
2188 /// Predicate that controls this clause.
2189 pub predicate: Option<Box<Expr<X>>>,
2190 /// Source location and node identity.
2191 pub meta: Meta,
2192}
2193
2194/// One key column of a [`CreateIndex`]: an expression with optional sort modifiers.
2195///
2196/// A bare column is the common case (`expr` is an [`Expr::Column`](super::Expr)); a
2197/// parenthesized expression indexes a computed value. `asc` / `nulls_first` mirror
2198/// [`OrderByExpr`](super::OrderByExpr) and stay `None` when the modifier is
2199/// unwritten, leaving the dialect default to the consumer.
2200#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2201#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2202#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2203pub struct IndexColumn<X: Extension = NoExt> {
2204 /// Expression evaluated by this syntax.
2205 pub expr: Expr<X>,
2206 /// Whether the asc form was present in the source.
2207 pub asc: Option<bool>,
2208 /// Whether the nulls first form was present in the source.
2209 pub nulls_first: Option<bool>,
2210 /// Source location and node identity.
2211 pub meta: Meta,
2212}
2213
2214/// A SQLite `CREATE [TEMP|TEMPORARY] TRIGGER [IF NOT EXISTS] [<schema> .] <name>
2215/// [BEFORE | AFTER | INSTEAD OF] <event> ON <table> [FOR EACH ROW] [WHEN <expr>]
2216/// BEGIN <stmt>; ... END` statement (SQLite-specific; gated by
2217/// [`StatementDdlGates::create_trigger`](crate::dialect::StatementDdlGates::create_trigger)).
2218///
2219/// Unlike the other `CREATE` families — which are always accepted because their body
2220/// grammar is standard — the trigger gate is real: only SQLite's `BEGIN … END`
2221/// SQL-statement-body form is modelled, and PostgreSQL/MySQL spell an incompatible
2222/// body (`EXECUTE FUNCTION f()` / an external routine), which they genuinely reject
2223/// for this form. Gating to SQLite (and Lenient) is therefore behaviour-accurate, not
2224/// only a modelling limitation.
2225///
2226/// The [`body`](Self::body) is a non-empty sequence of `INSERT`/`UPDATE`/`DELETE`/
2227/// `SELECT` statements — a statement list inside a statement (the structurally
2228/// heaviest SQLite shape). It reuses the existing [`Statement`] nodes rather than
2229/// minting trigger-body variants (the node stays generic over `X`); the
2230/// parser gates the body kinds and routes each through the recursion-guarded
2231/// statement dispatcher, and the enclosing [`Statement::CreateTrigger`]
2232/// boxes this payload to keep the enum within its size budget.
2233#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2234#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2235#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2236pub struct CreateTrigger<X: Extension = NoExt> {
2237 /// Optional temporary for this syntax.
2238 pub temporary: Option<TemporaryTableKind>,
2239 /// Whether the if not exists form was present in the source.
2240 pub if_not_exists: bool,
2241 /// Name referenced by this syntax.
2242 pub name: ObjectName,
2243 /// The fire time; `None` when unwritten (SQLite defaults it to `BEFORE`, but the
2244 /// absent form is preserved so it round-trips).
2245 pub timing: Option<TriggerTiming>,
2246 /// Which DML event fires the trigger; see [`TriggerEvent`].
2247 pub event: TriggerEvent,
2248 /// Table referenced by this syntax.
2249 pub table: ObjectName,
2250 /// Whether `FOR EACH ROW` was written. SQLite parses (and ignores) it but rejects
2251 /// `FOR EACH STATEMENT`, so only the `ROW` form is modelled as a surface tag.
2252 pub for_each_row: bool,
2253 /// Optional when for this syntax.
2254 pub when: Option<Expr<X>>,
2255 /// The trigger-body statement list, in source order (always non-empty — SQLite
2256 /// rejects an empty `BEGIN END`).
2257 pub body: ThinVec<Statement<X>>,
2258 /// Source location and node identity.
2259 pub meta: Meta,
2260}
2261
2262/// When a [`CreateTrigger`] fires relative to the event.
2263///
2264/// A tag (no `meta`): the timing keyword's span is subsumed by the enclosing
2265/// [`CreateTrigger`], exactly as [`TemporaryTableKind`] rides its parent's span.
2266#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2267#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2268#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2269pub enum TriggerTiming {
2270 /// `BEFORE` — fire before the event is applied.
2271 Before,
2272 /// `AFTER` — fire after the event is applied.
2273 After,
2274 /// `INSTEAD OF` (valid on a view).
2275 InsteadOf,
2276}
2277
2278/// The DML event that fires a [`CreateTrigger`].
2279///
2280/// `UPDATE` alone fires on any column; `UPDATE OF a, b` restricts it to the listed
2281/// columns ([`columns`](Self::Update::columns) empty for the bare `UPDATE`). `INSERT`
2282/// and `DELETE` take no column list.
2283#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2284#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2285#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2286pub enum TriggerEvent {
2287 /// `DELETE` — fire on row deletion.
2288 Delete {
2289 /// Source location and node identity.
2290 meta: Meta,
2291 },
2292 /// `INSERT` — fire on row insertion.
2293 Insert {
2294 /// Source location and node identity.
2295 meta: Meta,
2296 },
2297 /// `UPDATE [OF col, …]` — fire on row update, optionally restricted to columns.
2298 Update {
2299 /// Columns in source order.
2300 columns: ThinVec<Ident>,
2301 /// Source location and node identity.
2302 meta: Meta,
2303 },
2304}
2305
2306/// `CREATE [DEFINER = <user>] TRIGGER [IF NOT EXISTS] <name> {BEFORE | AFTER}
2307/// {INSERT | UPDATE | DELETE} ON <table> FOR EACH ROW [{FOLLOWS | PRECEDES} <other>]
2308/// <sp_proc_stmt>` — the MySQL SQL/PSM trigger (gated by
2309/// [`StatementDdlGates::compound_statements`](crate::dialect::StatementDdlGates::compound_statements),
2310/// the same stored-program gate the routine wrappers ride).
2311///
2312/// A DISTINCT node from the SQLite [`CreateTrigger`], not an extension of it, because the two
2313/// shapes do not genuinely unify — the decisive split is the body: SQLite's is a
2314/// `BEGIN <stmt>; … END` *list* of plain SQL statements ([`CreateTrigger::body`], a
2315/// [`ThinVec`]), MySQL's is a *single* `sp_proc_stmt` — usually a
2316/// [`Statement::Compound`] block, but any one body statement (a `SET`,
2317/// a flow-control construct) — parsed through the shared `parse_body_statement` seam and boxed
2318/// because [`Statement`] is large. Their decorating axes are disjoint too: SQLite carries
2319/// `TEMP` / `WHEN` / `INSTEAD OF` / `UPDATE OF <cols>`, MySQL carries [`DEFINER`](Self::definer),
2320/// mandatory `BEFORE`/`AFTER` timing, mandatory `FOR EACH ROW`, and the
2321/// [`FOLLOWS`/`PRECEDES` ordering](Self::ordering) anchor. Folding both into one node would
2322/// leave half its fields dialect-dead — the [`CreateProcedure`] precedent (a MySQL
2323/// stored-program object gets its own node rather than overloading a cross-dialect one) applies.
2324///
2325/// The [`timing`](Self::timing) and [`event`](Self::event) axes reuse the shared
2326/// [`TriggerTiming`] / [`TriggerEvent`] vocabulary — MySQL simply never emits the SQLite-only
2327/// [`TriggerTiming::InsteadOf`] or a non-empty [`TriggerEvent::Update`] column list (the parser
2328/// enforces the bare forms), so the reuse is safe and keeps one trigger-axis vocabulary.
2329#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2330#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2331#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2332pub struct CreateStoredTrigger<X: Extension = NoExt> {
2333 /// The optional `DEFINER = <user>` account prefix; `None` when omitted. Boxed (the rare
2334 /// fat field pays only a null pointer when absent), like [`CreateProcedure::definer`].
2335 pub definer: Option<Box<Definer>>,
2336 /// Whether the `IF NOT EXISTS` guard was written (MySQL 8.0.29+).
2337 pub if_not_exists: bool,
2338 /// Name referenced by this syntax.
2339 pub name: ObjectName,
2340 /// The fire time (`BEFORE` / `AFTER`) — mandatory in MySQL (no defaulted/absent form, and
2341 /// never [`TriggerTiming::InsteadOf`]).
2342 pub timing: TriggerTiming,
2343 /// The DML event that fires the trigger (`INSERT` / `UPDATE` / `DELETE`) — always the bare
2344 /// form; MySQL has no `UPDATE OF <cols>`, so a [`TriggerEvent::Update`] carries no columns.
2345 pub event: TriggerEvent,
2346 /// Table referenced by this syntax.
2347 pub table: ObjectName,
2348 /// The optional `{FOLLOWS | PRECEDES} <other>` ordering anchor relative to another trigger on
2349 /// the same table and event; `None` for the unordered form.
2350 pub ordering: Option<TriggerOrder>,
2351 /// The trigger body: one `sp_proc_stmt`, boxed. Usually a
2352 /// [`Statement::Compound`] `BEGIN … END` block, but any single
2353 /// body statement is admitted, parsed through the `parse_body_statement` seam.
2354 pub body: Box<Statement<X>>,
2355 /// Source location and node identity.
2356 pub meta: Meta,
2357}
2358
2359/// The MySQL `{FOLLOWS | PRECEDES} <other_trigger>` ordering clause of a
2360/// [`CreateStoredTrigger`] (`trigger_follows_precedes_clause`): where the new trigger fires
2361/// relative to an existing one on the same table and event.
2362///
2363/// The anchor is an [`Ident`] (MySQL's `ident_or_text` — a bare or quoted trigger name) so both
2364/// source spellings round-trip from its span. The keyword direction is the enum variant.
2365#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2366#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2367#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2368pub enum TriggerOrder {
2369 /// `FOLLOWS <other>` — fire immediately after the named trigger.
2370 Follows {
2371 /// The anchor trigger this one follows.
2372 anchor: Ident,
2373 /// Source location and node identity.
2374 meta: Meta,
2375 },
2376 /// `PRECEDES <other>` — fire immediately before the named trigger.
2377 Precedes {
2378 /// The anchor trigger this one precedes.
2379 anchor: Ident,
2380 /// Source location and node identity.
2381 meta: Meta,
2382 },
2383}
2384
2385/// `CREATE DATABASE [IF NOT EXISTS] <name>`.
2386///
2387/// Non-generic, like [`CreateSchema`]: a database is named by identifiers only. The
2388/// trailing `[WITH] <option> ...` list (`OWNER`, `TEMPLATE`, `ENCODING`, …) is a
2389/// deliberate follow-up — real migrations rarely spell it and it carries no
2390/// expressions the shape must compare.
2391#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2392#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2393#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2394pub struct CreateDatabase {
2395 /// The MySQL `IF NOT EXISTS` existence guard (the [`CreateTable`] precedent). Gated
2396 /// for acceptance by
2397 /// [`ExistenceGuards::create_database_if_not_exists`](crate::dialect::ExistenceGuards):
2398 /// PostgreSQL has no `CREATE DATABASE IF NOT EXISTS`, so the flag is off there and
2399 /// the guard surfaces as a clean parse error.
2400 pub if_not_exists: bool,
2401 /// Name referenced by this syntax.
2402 pub name: ObjectName,
2403 /// Source location and node identity.
2404 pub meta: Meta,
2405}
2406
2407/// `CREATE [OR REPLACE] FUNCTION <name> ( [<param> [, ...]] ) [RETURNS <type>]
2408/// [<option> ...]`.
2409///
2410/// The routine body has two disjoint grammatical homes on the [`FunctionBody`] axis. An
2411/// opaque source *string* is introduced by the order-independent `AS` option, spelled either
2412/// single-quoted (`AS 'SELECT 1'`) or dollar-quoted (`AS $$ … $$` / `AS $tag$ … $tag$`) where
2413/// the dialect enables [`dollar_quoted_strings`](crate::dialect::StringLiteralSyntax); the
2414/// delimiter and verbatim body text round-trip from the body [`Literal`]'s span. A *live* SQL
2415/// body — a SQL-standard `RETURN <expr>` (PostgreSQL 14+ / standard `opt_routine_body`) — is
2416/// the trailing [`body`](Self::body) slot, which strictly follows the whole option list (proven
2417/// against the PG oracle: `LANGUAGE sql RETURN 1` accepts, `RETURN 1 LANGUAGE sql` rejects), so
2418/// it is a distinct parse position from `AS`, not another order-independent option. The two
2419/// homes share the [`FunctionBody`] *type* (the axis vocabulary the dollar-body sibling staged)
2420/// but not a grammatical slot.
2421///
2422/// The node is generic over `X` because a parameter or `RETURNS` type can be a host-owned
2423/// [`DataType::Other`] under a custom dialect, and the live
2424/// [`body`](Self::body) carries an [`Expr`]`<X>` (stock builtins pin `X = NoExt`). The M1
2425/// surface is the parameter list, an optional `RETURNS <type>`, the [`FunctionOption`] cluster
2426/// the corpus exercises (`LANGUAGE` / `AS` / null-call behaviour), and the trailing `RETURN`
2427/// body; the fuller volatility, security, parallelism, and `SET` option matrix — and the
2428/// statement-list `BEGIN ATOMIC … END` body that shares the trailing slot — are deliberate
2429/// follow-ups the open option list and the [`FunctionBody`] axis extend without a shape change.
2430#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2431#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2432#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2433pub struct CreateFunction<X: Extension = NoExt> {
2434 /// Whether the or replace form was present in the source.
2435 pub or_replace: bool,
2436 /// The optional MySQL `DEFINER = <user>` clause; `None` when omitted (always `None`
2437 /// for the PostgreSQL string-body routine, which has no definer prefix). Boxed because a
2438 /// [`Definer`] is comparatively large and the clause is rare, so a definer-less routine
2439 /// pays only a null pointer (the [`FunctionParam::default`] precedent).
2440 pub definer: Option<Box<Definer>>,
2441 /// Whether the MySQL `IF NOT EXISTS` guard was written (MySQL 8.0.29+); always `false`
2442 /// for PostgreSQL, which spells the intent `CREATE OR REPLACE`.
2443 pub if_not_exists: bool,
2444 /// Name referenced by this syntax.
2445 pub name: ObjectName,
2446 /// params in source order.
2447 pub params: ThinVec<FunctionParam<X>>,
2448 /// Optional returns for this syntax.
2449 pub returns: Option<DataType<X>>,
2450 /// Options supplied in source order.
2451 pub options: ThinVec<FunctionOption<X>>,
2452 /// The trailing SQL-standard routine body (`opt_routine_body`) — a `RETURN <expr>` live
2453 /// SQL expression that follows the entire option list. Boxed and optional because it is
2454 /// usually absent (an `AS`-string routine leaves it unset, paying one null pointer — the
2455 /// [`FunctionParam::default`] precedent); a [`FunctionBody::Definition`] string body never
2456 /// lands here (it rides the `AS` option instead).
2457 pub body: Option<Box<FunctionBody<X>>>,
2458 /// Source location and node identity.
2459 pub meta: Meta,
2460}
2461
2462/// One routine parameter: an optional argument [`mode`](Self::mode)
2463/// (`IN`/`OUT`/`INOUT`/`VARIADIC`), an optional name, its type (`b INT`), and an
2464/// optional default value (`b INT DEFAULT 0` / `b INT = 0`).
2465///
2466/// The three lead-in facets are independent: a bare type (`f(int)`) leaves both
2467/// `mode` and `name` unset, a mode may precede an unnamed type (`f(OUT int)`), and a
2468/// named parameter may carry a default. The name is PostgreSQL's `param_name` =
2469/// `type_function_name` production (parsed with the [type-name reservation
2470/// set](crate::dialect::FeatureSet::reserved_type_name)): a `type_func_name` keyword
2471/// like `left` is a legal parameter name, a `col_name` keyword like `int` is not — so
2472/// a lone `int` is unambiguously the type, mirroring how PostgreSQL resolves the
2473/// name-vs-type ambiguity. The default is the PostgreSQL `func_arg_with_default`
2474/// tail — boxed because it is usually absent and a [`FunctionParamDefault`] (an
2475/// [`Expr`]) is comparatively large, so a default-less parameter pays only a null
2476/// pointer (the [`ForeignKeyRef`] referential-action precedent). It is *parameter
2477/// metadata*: an ordinary function-**call** argument is a
2478/// [`FunctionArg`](crate::ast::FunctionArg) on the unrelated call path and is
2479/// unaffected.
2480#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2481#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2482#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2483pub struct FunctionParam<X: Extension = NoExt> {
2484 /// Mode selected by this syntax.
2485 pub mode: Option<FunctionParamMode>,
2486 /// Name referenced by this syntax.
2487 pub name: Option<Ident>,
2488 /// Data type named by this syntax.
2489 pub data_type: DataType<X>,
2490 /// Optional default for this syntax.
2491 pub default: Option<Box<FunctionParamDefault<X>>>,
2492 /// Source location and node identity.
2493 pub meta: Meta,
2494}
2495
2496/// A routine parameter's argument mode — PostgreSQL's `arg_class` prefix
2497/// (`func_arg: arg_class param_name func_type | …`). `IN` is the default and may be
2498/// omitted; `OUT`/`INOUT` mark output parameters; `VARIADIC` marks a trailing
2499/// array-spread parameter. A fieldless [`Copy`] tag whose span rides the owning
2500/// [`FunctionParam`] (the [`FunctionParamDefaultSpelling`] precedent): it records only
2501/// the written mode so it round-trips.
2502///
2503/// PostgreSQL admits the mode either before *or* after the name (`IN a int` and
2504/// `a IN int` both parse); this models the documented, canonical mode-first spelling
2505/// (which sqlparser-rs's `ArgMode` also targets) and adds [`Variadic`](Self::Variadic),
2506/// which sqlparser-rs omits but real PostgreSQL requires. The rarer name-first spelling
2507/// is a deliberate parse-surface boundary — no corpus exercises it and PostgreSQL itself
2508/// normalizes the two orders to one AST.
2509#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2510#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2511#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2512pub enum FunctionParamMode {
2513 /// `IN` — an input parameter (the default when omitted).
2514 In,
2515 /// `OUT` — an output parameter.
2516 Out,
2517 /// `INOUT` — an input/output parameter.
2518 InOut,
2519 /// `VARIADIC` — a trailing array-spread parameter.
2520 Variadic,
2521}
2522
2523/// A routine parameter's default-value clause: the [`spelling`](Self::spelling)
2524/// (`DEFAULT` keyword vs `=`) and the value [`Expr`]. PostgreSQL's grammar spells
2525/// one production two ways — `func_arg_with_default: func_arg DEFAULT a_expr |
2526/// func_arg '=' a_expr` — so the tag records which the source used and the value is
2527/// a live SQL expression reusing [`Expr`] rather than a re-derived literal grammar.
2528///
2529/// sqlparser-rs models the same clause as a bare `default_expr: Option<Expr>` and
2530/// always re-renders it as `= <expr>`, normalizing the `DEFAULT` spelling away; this
2531/// AST instead carries the fieldless [`FunctionParamDefaultSpelling`] tag so both
2532/// forms round-trip verbatim (the spelling-fidelity doctrine, as for
2533/// [`EqualsSpelling`](crate::ast::EqualsSpelling)).
2534#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2535#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2536#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2537pub struct FunctionParamDefault<X: Extension = NoExt> {
2538 /// Exact source spelling retained for faithful rendering.
2539 pub spelling: FunctionParamDefaultSpelling,
2540 /// Value supplied by this syntax.
2541 pub value: Expr<X>,
2542 /// Source location and node identity.
2543 pub meta: Meta,
2544}
2545
2546/// Surface spelling for a [`FunctionParamDefault`]: PostgreSQL admits both the
2547/// `DEFAULT` keyword and a bare `=` to introduce a routine parameter's default, two
2548/// spellings of the one `func_arg_with_default` production. A `Copy` tag whose span
2549/// rides the owning [`FunctionParamDefault`], like [`MacroSpelling`]; it records the
2550/// source form only so rendering round-trips exactly (a fidelity tag, not a validity
2551/// one — the parser accepts both spellings wherever defaults are gated on).
2552#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2553#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2554#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2555pub enum FunctionParamDefaultSpelling {
2556 /// The `DEFAULT <expr>` keyword form.
2557 Default,
2558 /// The `= <expr>` operator form.
2559 Equals,
2560}
2561
2562#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2563#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2564#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2565/// The SQL function option forms represented by the AST.
2566pub enum FunctionOption<X: Extension = NoExt> {
2567 /// A `LANGUAGE <name>` clause naming the routine's implementation language. The name is a
2568 /// `NonReservedWord_or_Sconst` ([`LanguageName`]) — a bare word or, on the PostgreSQL
2569 /// surface, an `Sconst` string (`LANGUAGE 'sql'`/`E'sql'`/`$$sql$$`) — shared with the
2570 /// [`DoArg::Language`](crate::ast::DoArg) argument.
2571 Language {
2572 /// Name referenced by this syntax.
2573 name: LanguageName,
2574 /// Source location and node identity.
2575 meta: Meta,
2576 },
2577 /// `AS <body>` — the *string* routine body, carried on the [`FunctionBody`] axis. The `AS`
2578 /// option only ever homes an opaque source string (plain `'…'` or dollar-quoted
2579 /// `$tag$…$tag$`), i.e. [`FunctionBody::Definition`]; the live `RETURN <expr>` body rides
2580 /// the trailing [`CreateFunction::body`] slot, a disjoint grammatical position.
2581 As {
2582 /// The routine's string body; see [`FunctionBody`].
2583 body: FunctionBody<X>,
2584 /// Source location and node identity.
2585 meta: Meta,
2586 },
2587 /// The null-call behaviour (`CALLED ON NULL INPUT` / `RETURNS NULL ON NULL INPUT`
2588 /// / `STRICT`).
2589 NullBehavior {
2590 /// The null-input behaviour; see [`FunctionNullBehavior`].
2591 behavior: FunctionNullBehavior,
2592 /// Source location and node identity.
2593 meta: Meta,
2594 },
2595 /// The MySQL `[NOT] DETERMINISTIC` routine characteristic — whether the routine
2596 /// always produces the same result for the same inputs. The `not` field records the negated
2597 /// spelling so `NOT DETERMINISTIC` round-trips distinctly from a
2598 /// routine that simply omits the characteristic. A stored-routine option (MySQL SQL/PSM),
2599 /// not a PostgreSQL `CREATE FUNCTION` clause.
2600 Deterministic {
2601 /// Whether the `NOT` prefix was written (`NOT DETERMINISTIC`).
2602 not: bool,
2603 /// Source location and node identity.
2604 meta: Meta,
2605 },
2606 /// The MySQL SQL-data-access routine characteristic (`CONTAINS SQL` / `NO SQL` /
2607 /// `READS SQL DATA` / `MODIFIES SQL DATA`) — the advisory declaration of how the
2608 /// routine body touches data.
2609 DataAccess {
2610 /// Which data-access class was written; see [`SqlDataAccess`].
2611 access: SqlDataAccess,
2612 /// Source location and node identity.
2613 meta: Meta,
2614 },
2615 /// The MySQL `SQL SECURITY {DEFINER | INVOKER}` routine characteristic — the privilege
2616 /// context the routine executes under.
2617 SqlSecurity {
2618 /// The security context; see [`SqlSecurityContext`].
2619 context: SqlSecurityContext,
2620 /// Source location and node identity.
2621 meta: Meta,
2622 },
2623 /// The MySQL `COMMENT '<string>'` routine characteristic — a stored descriptive comment.
2624 /// The PostgreSQL comment surface is the separate `COMMENT ON FUNCTION` statement, so
2625 /// this inline option is MySQL-only.
2626 Comment {
2627 /// The comment string literal.
2628 comment: Literal,
2629 /// Source location and node identity.
2630 meta: Meta,
2631 },
2632}
2633
2634/// The MySQL routine SQL-data-access characteristic — how the routine body is declared to
2635/// touch data. Advisory (the server does not enforce it), recorded so it round-trips. A
2636/// `Copy` tag whose span rides the owning [`FunctionOption::DataAccess`], like
2637/// [`FunctionNullBehavior`].
2638#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2639#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2640#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2641pub enum SqlDataAccess {
2642 /// `CONTAINS SQL` — the body contains SQL but neither reads nor writes data (the default).
2643 ContainsSql,
2644 /// `NO SQL` — the body contains no SQL.
2645 NoSql,
2646 /// `READS SQL DATA` — the body reads but does not modify data.
2647 ReadsSqlData,
2648 /// `MODIFIES SQL DATA` — the body may modify data.
2649 ModifiesSqlData,
2650}
2651
2652/// The MySQL `SQL SECURITY` context — whose privileges a routine runs under. A `Copy` tag
2653/// whose span rides the owning [`FunctionOption::SqlSecurity`], like [`SqlDataAccess`].
2654#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2655#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2656#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2657pub enum SqlSecurityContext {
2658 /// `SQL SECURITY DEFINER` — run under the routine definer's privileges (the default).
2659 Definer,
2660 /// `SQL SECURITY INVOKER` — run under the calling user's privileges.
2661 Invoker,
2662}
2663
2664/// The executable body of a [`CreateFunction`] routine — the body-representation **axis**.
2665///
2666/// Two body *kinds* are modelled today. [`Definition`](Self::Definition) is an opaque source
2667/// string introduced by the order-independent `AS` option (`AS 'SELECT 1'`, `AS $$ … $$`,
2668/// `AS $body$ … $body$`). [`Return`](Self::Return) is the SQL-standard *live* body
2669/// `RETURN <expr>` (PostgreSQL 14+ / standard `opt_routine_body`), a real SQL [`Expr`] that
2670/// rides the trailing [`CreateFunction::body`] slot — a *distinct* parse position from `AS`
2671/// (the `RETURN` body strictly follows the whole option list, oracle-proven). This is a
2672/// distinct enum — not a bare [`Literal`] field on [`FunctionOption::As`] — precisely so the
2673/// live body reuses one body vocabulary across both slots rather than reshaping the option.
2674///
2675/// The axis is body *kind* (opaque string vs. live SQL), **not** quote style: a plain `'…'`
2676/// and a dollar-quoted `$tag$…$tag$` body are both
2677/// [`LiteralKind::String`](crate::ast::LiteralKind) and differ only in source spelling. That
2678/// spelling — the delimiter tag and the verbatim body text — round-trips from the
2679/// [`Literal`]'s span; it is recovered from source, never normalized, so a dollar body
2680/// re-renders byte-for-byte.
2681///
2682/// The enum is generic over `X` because [`Return`](Self::Return) carries an [`Expr`]`<X>`; the
2683/// [`Definition`](Self::Definition) string variant is generic-free. A statement-list
2684/// `BEGIN ATOMIC … END` body (PostgreSQL, oracle-accepted) shares the trailing slot and slots
2685/// in here as a further variant carrying a routine-body statement list — deliberately deferred:
2686/// modelling it honestly needs a routine-body statement grammar (its inner `RETURN` is not a
2687/// top-level [`Statement`]), which is a separate surface from this single-`Expr` body.
2688#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2689#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2690#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2691pub enum FunctionBody<X: Extension = NoExt> {
2692 /// `AS <string>` — an opaque routine body in the target language, kept as the source
2693 /// [`Literal`] (plain-quoted or dollar-quoted) rather than re-parsed, because the body's
2694 /// grammar is the target language, not SQL.
2695 Definition {
2696 /// Definition text supplied by this syntax.
2697 definition: Literal,
2698 /// Source location and node identity.
2699 meta: Meta,
2700 },
2701 /// `RETURN <expr>` — the SQL-standard live expression body (`opt_routine_body`'s
2702 /// `ReturnStmt`). The [`Expr`] is boxed (the [`MacroBody`] precedent) so the common `AS`
2703 /// string body pays no [`Expr`] footprint on the shared [`FunctionOption::As`] path.
2704 Return {
2705 /// Expression evaluated by this syntax.
2706 expr: Box<Expr<X>>,
2707 /// Source location and node identity.
2708 meta: Meta,
2709 },
2710 /// A MySQL SQL/PSM routine body — one `routine_body` statement, usually the
2711 /// `BEGIN … END` compound block ([`Statement::Compound`])
2712 /// but any single body statement (a bare `SET`, a flow-control construct, a `RETURN`).
2713 /// This is the live statement-list body the [`FunctionBody`] axis doc anticipated; it
2714 /// rides the trailing [`CreateFunction::body`] slot exactly as [`Return`](Self::Return)
2715 /// does — a distinct grammatical position after the whole characteristic list. Boxed
2716 /// ([`Statement`] is large) so the common `AS`-string path pays no footprint.
2717 Block {
2718 /// The routine body statement (typically a compound block); parsed through the
2719 /// `parse_body_statement` seam so its inner grammar is the MySQL body sub-language.
2720 body: Box<Statement<X>>,
2721 /// Source location and node identity.
2722 meta: Meta,
2723 },
2724}
2725
2726/// How a routine handles a NULL argument. `Strict` is the exact shorthand for
2727/// `ReturnsNullOnNull`, kept distinct so the written spelling round-trips. A `Copy`
2728/// leaf enum whose span lives on the owning [`FunctionOption`], like [`DropBehavior`].
2729#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2730#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2731#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2732pub enum FunctionNullBehavior {
2733 /// `CALLED ON NULL INPUT` — run the routine even when an argument is null.
2734 CalledOnNull,
2735 /// `RETURNS NULL ON NULL INPUT` — return null without running when any argument is null.
2736 ReturnsNullOnNull,
2737 /// `STRICT` — shorthand for `RETURNS NULL ON NULL INPUT`.
2738 Strict,
2739}
2740
2741/// The MySQL `DEFINER = <user>` clause prefixing a routine (and, on other tickets, a
2742/// view/trigger/event) definition — the account the object's `SQL SECURITY DEFINER` context
2743/// resolves to.
2744///
2745/// This is the *measured minimal* account surface the routine family needs: the bare
2746/// `<user>[@<host>]` account name and the `CURRENT_USER [()]` self-reference, the two forms
2747/// the corpus and the oracle exercise. The full MySQL account-name axis — the quoting-nuance
2748/// matrix, role grants, and the `IDENTIFIED BY` authentication tail — is owned by
2749/// `parse-mysql-user-role-ddl`; this node deliberately stops at the account *reference* a
2750/// routine header carries, and that ticket widens it (or lifts it to a shared account node)
2751/// without reshaping the routine statements that hold it.
2752#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2753#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2754#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2755pub enum Definer {
2756 /// `DEFINER = <user>[@<host>]` — a named account. The `user` and optional `host` are
2757 /// each an [`Ident`] so both the bare (`root`) and quoted (`'root'@'localhost'`) source
2758 /// spellings round-trip from their spans.
2759 Account {
2760 /// The account user name.
2761 user: Ident,
2762 /// The optional `@<host>` part; `None` for a bare user with no host.
2763 host: Option<Ident>,
2764 /// Source location and node identity.
2765 meta: Meta,
2766 },
2767 /// `DEFINER = CURRENT_USER [()]` — the current user at definition time.
2768 CurrentUser {
2769 /// Whether the empty `()` call-style parentheses were written.
2770 parens: bool,
2771 /// Source location and node identity.
2772 meta: Meta,
2773 },
2774}
2775
2776/// `CREATE [DEFINER = <user>] PROCEDURE [IF NOT EXISTS] <name> ( [<param> [, …]] )
2777/// [<characteristic> …] <routine_body>` — the MySQL stored-procedure definition (SQL/PSM).
2778///
2779/// A distinct node from [`CreateFunction`]: a procedure has no `RETURNS` type, its body may
2780/// not contain `RETURN` (the routine family enforces `ER_SP_BADRETURN`), and it is invoked by
2781/// `CALL` rather than in an expression. It shares the routine *vocabulary* — the parameter
2782/// list reuses [`FunctionParam`] (with its `IN`/`OUT`/`INOUT` [`FunctionParamMode`]) and the
2783/// characteristic list reuses [`FunctionOption`] (`LANGUAGE`/`COMMENT`/`DETERMINISTIC`/data
2784/// access/`SQL SECURITY`) — but not a statement shape. The [`body`](Self::body) is a single
2785/// MySQL body statement (typically a [`Statement::Compound`]
2786/// block), parsed through the `parse_body_statement` seam and boxed because [`Statement`] is
2787/// large.
2788#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2789#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2790#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2791pub struct CreateProcedure<X: Extension = NoExt> {
2792 /// The optional `DEFINER = <user>` clause; `None` when omitted. Boxed (the rare fat
2793 /// field pays only a null pointer when absent), like [`CreateFunction::definer`].
2794 pub definer: Option<Box<Definer>>,
2795 /// Whether the `IF NOT EXISTS` guard was written (MySQL 8.0.29+).
2796 pub if_not_exists: bool,
2797 /// The procedure name.
2798 pub name: ObjectName,
2799 /// The parameter list, in source order (always parenthesized, possibly empty).
2800 pub params: ThinVec<FunctionParam<X>>,
2801 /// The routine characteristics, in source order (`LANGUAGE`/`COMMENT`/`DETERMINISTIC`/
2802 /// data-access/`SQL SECURITY`), carried on the shared [`FunctionOption`] axis.
2803 pub characteristics: ThinVec<FunctionOption<X>>,
2804 /// The routine body — one MySQL body statement, usually a compound block.
2805 pub body: Box<Statement<X>>,
2806 /// Source location and node identity.
2807 pub meta: Meta,
2808}
2809
2810/// Which routine kind an [`AlterRoutine`] targets — the `PROCEDURE`/`FUNCTION` object keyword.
2811/// A `Copy` surface tag whose span rides the owning [`AlterRoutine`], like
2812/// [`ShowRoutineKind`](crate::ast::ShowRoutineKind).
2813#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2814#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2815#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2816pub enum RoutineKind {
2817 /// `PROCEDURE`.
2818 Procedure,
2819 /// `FUNCTION`.
2820 Function,
2821}
2822
2823/// `ALTER {PROCEDURE | FUNCTION} <name> [<characteristic> …]` — the MySQL routine-characteristics
2824/// alteration (SQL/PSM). Characteristics only: unlike [`CreateProcedure`]/[`CreateFunction`]
2825/// there is no body and no parameter list — `ALTER` re-declares only the mutable characteristic
2826/// subset (`COMMENT` / `LANGUAGE` / data access / `SQL SECURITY`; the server rejects
2827/// `DETERMINISTIC` here, which the parser enforces). One node spans both routine kinds via
2828/// [`kind`](Self::kind); boxed by [`Statement::AlterRoutine`].
2829#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2830#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2831#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2832pub struct AlterRoutine<X: Extension = NoExt> {
2833 /// Whether this alters a `PROCEDURE` or a `FUNCTION`.
2834 pub kind: RoutineKind,
2835 /// The routine name.
2836 pub name: ObjectName,
2837 /// The re-declared characteristics, in source order (the `ALTER`-legal subset).
2838 pub characteristics: ThinVec<FunctionOption<X>>,
2839 /// Source location and node identity.
2840 pub meta: Meta,
2841}
2842
2843/// The `ON SCHEDULE <when>` clause of a MySQL `CREATE`/`ALTER EVENT` — the one-shot
2844/// `AT <timestamp>` form or the recurring `EVERY <interval> [STARTS …] [ENDS …]` form.
2845///
2846/// Both timestamp positions ([`At::at`](Self::At::at), [`Every::starts`](Self::Every::starts),
2847/// [`Every::ends`](Self::Every::ends)) are ordinary [`Expr`]s, so a `NOW() + INTERVAL 1 DAY`
2848/// offset rides the expression grammar with no special schedule handling. The recurring
2849/// interval is `<value> <unit>` where the unit reuses the shared [`IntervalFields`] vocabulary
2850/// (MySQL's `interval` production, rendered in underscore spelling — `DAY_HOUR`, `MINUTE_SECOND`).
2851#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2852#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2853#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2854pub enum EventSchedule<X: Extension = NoExt> {
2855 /// `AT <timestamp>` — a one-shot event fired once at the given time. The [`Expr`] is
2856 /// boxed to keep the schedule (and the enclosing event node) lean.
2857 At {
2858 /// The one-shot execution timestamp (any expression, e.g. `NOW() + INTERVAL 1 DAY`).
2859 at: Box<Expr<X>>,
2860 /// Source location and node identity.
2861 meta: Meta,
2862 },
2863 /// `EVERY <value> <unit> [STARTS <ts>] [ENDS <ts>]` — a recurring event. The `<value>
2864 /// <unit>` interval reuses the shared [`IntervalFields`] vocabulary; `STARTS`/`ENDS` are
2865 /// optional window bounds (each an ordinary expression).
2866 Every {
2867 /// The interval count (an expression, typically an integer literal).
2868 value: Box<Expr<X>>,
2869 /// The interval unit, in the shared [`IntervalFields`] vocabulary (MySQL `interval`).
2870 unit: IntervalFields,
2871 /// The optional `STARTS <ts>` window start.
2872 starts: Option<Box<Expr<X>>>,
2873 /// The optional `ENDS <ts>` window end.
2874 ends: Option<Box<Expr<X>>>,
2875 /// Source location and node identity.
2876 meta: Meta,
2877 },
2878}
2879
2880/// `ON COMPLETION [NOT] PRESERVE` — whether a MySQL event is retained after its last
2881/// execution. A `Copy` surface tag whose span rides the owning event node, like
2882/// [`RoutineKind`].
2883#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2884#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2885#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2886pub enum EventOnCompletion {
2887 /// `ON COMPLETION PRESERVE` — keep the event definition after its final run.
2888 Preserve,
2889 /// `ON COMPLETION NOT PRESERVE` — drop the event after its final run (the default).
2890 NotPreserve,
2891}
2892
2893/// The `ENABLE | DISABLE [ON SLAVE|REPLICA]` activation state of a MySQL event. A `Copy`
2894/// surface tag whose span rides the owning event node.
2895///
2896/// [`DisableOnReplica`](Self::DisableOnReplica) carries a [`ReplicaSpelling`] because MySQL
2897/// 8.4 still admits BOTH the deprecated `DISABLE ON SLAVE` and the current `DISABLE ON
2898/// REPLICA` (server-measured: both accept, `SLAVE` warns) and the source spelling must
2899/// round-trip.
2900#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2901#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2902#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2903pub enum EventStatus {
2904 /// `ENABLE` — the event is active.
2905 Enable,
2906 /// `DISABLE` — the event is inactive on every node.
2907 Disable,
2908 /// `DISABLE ON SLAVE` / `DISABLE ON REPLICA` — the event is active only on the source,
2909 /// disabled on replicas. The [`ReplicaSpelling`] records which keyword was written.
2910 DisableOnReplica(ReplicaSpelling),
2911}
2912
2913/// Which of MySQL's two interchangeable replica keywords a source used — the deprecated
2914/// `SLAVE` or the current `REPLICA`. Both parse on MySQL 8.4 (`SLAVE` with a deprecation
2915/// warning), so the spelling is a round-trip tag, not a semantic difference. A `Copy` leaf.
2916#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2917#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2918#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2919pub enum ReplicaSpelling {
2920 /// The deprecated `SLAVE` keyword (still admitted on MySQL 8.4 with a warning).
2921 Slave,
2922 /// The current `REPLICA` keyword.
2923 Replica,
2924}
2925
2926/// `CREATE [DEFINER = <user>] EVENT [IF NOT EXISTS] <name> ON SCHEDULE <schedule>
2927/// [ON COMPLETION [NOT] PRESERVE] [ENABLE | DISABLE [ON SLAVE|REPLICA]] [COMMENT '…']
2928/// DO <body>` — the MySQL scheduled-event definition (SQL/PSM, gated by
2929/// [`StatementDdlGates::compound_statements`](crate::dialect::StatementDdlGates::compound_statements),
2930/// the stored-program body surface).
2931///
2932/// The clause order after the name is fixed by the grammar (schedule, completion, status,
2933/// comment, `DO`) — server-measured: a comment before the status is a syntax error. The
2934/// [`body`](Self::body) is a single MySQL body statement (usually a [`Statement::Compound`]
2935/// block), parsed through the `parse_body_statement` seam and boxed because [`Statement`] is
2936/// large; an event body carries no return value, so `RETURN` inside it is rejected (as in a
2937/// procedure body — server `ER_SP_BADRETURN`). The [`Definer`] prefix reuses the shared
2938/// routine account reference.
2939#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2940#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2941#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2942pub struct CreateEvent<X: Extension = NoExt> {
2943 /// The optional `DEFINER = <user>` clause; `None` when omitted. Boxed like the routine
2944 /// definer (a rare fat field paying only a null pointer when absent).
2945 pub definer: Option<Box<Definer>>,
2946 /// Whether the `IF NOT EXISTS` guard was written.
2947 pub if_not_exists: bool,
2948 /// The event name (`db.event` or bare `event`).
2949 pub name: ObjectName,
2950 /// The `ON SCHEDULE` clause — the `AT` one-shot or `EVERY` recurring form.
2951 pub schedule: EventSchedule<X>,
2952 /// The optional `ON COMPLETION [NOT] PRESERVE` clause.
2953 pub on_completion: Option<EventOnCompletion>,
2954 /// The optional `ENABLE | DISABLE [ON SLAVE|REPLICA]` activation state.
2955 pub status: Option<EventStatus>,
2956 /// The optional `COMMENT '…'` description.
2957 pub comment: Option<Literal>,
2958 /// The event body — one MySQL body statement (the `DO <stmt>` clause).
2959 pub body: Box<Statement<X>>,
2960 /// Source location and node identity.
2961 pub meta: Meta,
2962}
2963
2964/// `ALTER [DEFINER = <user>] EVENT <name> [ON SCHEDULE <schedule>] [ON COMPLETION [NOT]
2965/// PRESERVE] [RENAME TO <name>] [ENABLE | DISABLE [ON SLAVE|REPLICA]] [COMMENT '…']
2966/// [DO <body>]` — the MySQL event alteration.
2967///
2968/// Every clause is optional, but the grammar requires **at least one** (server-measured: a
2969/// bare `ALTER EVENT e` is a syntax error), which the parser enforces. The clause order is
2970/// the same fixed order as [`CreateEvent`], with `RENAME TO` between the schedule/completion
2971/// and the status. Unlike [`CreateEvent`] the schedule and body are optional, and
2972/// [`on_completion`](Self::on_completion) may appear with or without a schedule.
2973#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2974#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
2975#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
2976pub struct AlterEvent<X: Extension = NoExt> {
2977 /// The optional `DEFINER = <user>` clause; `None` when omitted.
2978 pub definer: Option<Box<Definer>>,
2979 /// The event name.
2980 pub name: ObjectName,
2981 /// The optional `ON SCHEDULE` clause.
2982 pub schedule: Option<EventSchedule<X>>,
2983 /// The optional `ON COMPLETION [NOT] PRESERVE` clause.
2984 pub on_completion: Option<EventOnCompletion>,
2985 /// The optional `RENAME TO <name>` clause.
2986 pub rename_to: Option<ObjectName>,
2987 /// The optional `ENABLE | DISABLE [ON SLAVE|REPLICA]` activation state.
2988 pub status: Option<EventStatus>,
2989 /// The optional `COMMENT '…'` description.
2990 pub comment: Option<Literal>,
2991 /// The optional `DO <body>` re-definition (one MySQL body statement).
2992 pub body: Option<Box<Statement<X>>>,
2993 /// Source location and node identity.
2994 pub meta: Meta,
2995}
2996
2997/// `DROP EVENT [IF EXISTS] <name>` — the MySQL scheduled-event drop. Kept apart from the
2998/// generic [`Statement::Drop`] because MySQL's event drop names exactly ONE event
2999/// (server-measured: `DROP EVENT a, b` is a syntax error) and takes no `CASCADE`/`RESTRICT`
3000/// behaviour, unlike the shared name-list [`DropStatement`] grammar.
3001#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3002#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3003#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3004pub struct DropEvent {
3005 /// Whether the `IF EXISTS` guard was written.
3006 pub if_exists: bool,
3007 /// The single event name (`db.event` or bare `event`).
3008 pub name: ObjectName,
3009 /// Source location and node identity.
3010 pub meta: Meta,
3011}
3012
3013/// `DROP {DATABASE | SCHEMA} [IF EXISTS] <name>` — MySQL's single-database drop, where
3014/// `DATABASE` and `SCHEMA` are exact synonyms (the lexer folds `SCHEMA` onto `DATABASE`).
3015/// Kept apart from the generic name-list [`Statement::Drop`] because the form names exactly
3016/// ONE unqualified database — server-measured on mysql:8, `DROP DATABASE a, b` is
3017/// `ER_PARSE_ERROR`, `DROP DATABASE db.x` is `ER_PARSE_ERROR`, and it takes no
3018/// `CASCADE`/`RESTRICT` (both syntax errors) — unlike PostgreSQL/DuckDB's `DROP SCHEMA
3019/// <name> [, …] [CASCADE | RESTRICT]`, which keeps riding the shared [`DropStatement`]
3020/// grammar there. Gated by
3021/// [`StatementDdlGates::drop_database`](crate::dialect::StatementDdlGates::drop_database).
3022#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3023#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3024#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3025pub struct DropDatabase {
3026 /// Which of the two synonymous keywords was written; recorded so the source spelling
3027 /// round-trips verbatim (the tag pattern of [`TemporaryTableKind`], not a semantic
3028 /// difference — MySQL treats the two identically).
3029 pub spelling: DatabaseKeyword,
3030 /// Whether the `IF EXISTS` guard was written.
3031 pub if_exists: bool,
3032 /// The single database name — a bare unqualified identifier (`ident`; a dotted
3033 /// `db.x` is a server syntax error).
3034 pub name: Ident,
3035 /// Source location and node identity.
3036 pub meta: Meta,
3037}
3038
3039/// The keyword spelling of a database object — `DATABASE` or its exact synonym `SCHEMA`
3040/// (MySQL folds the two onto one grammar). Recorded on [`DropDatabase`] so the written
3041/// keyword round-trips.
3042#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3043#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3044#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3045pub enum DatabaseKeyword {
3046 /// The `DATABASE` spelling.
3047 Database,
3048 /// The `SCHEMA` spelling (a MySQL synonym for `DATABASE`).
3049 Schema,
3050}
3051
3052/// `DROP INDEX <name> ON <table> [ALGORITHM [=] {DEFAULT | INPLACE | INSTANT | COPY}]
3053/// [LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}]` — MySQL's index drop (`drop_index_stmt`,
3054/// `sql_yacc.yy`). Kept apart from the generic name-list [`Statement::Drop`] because it names
3055/// the owning table with a mandatory `ON <table>` (server-measured: `DROP INDEX i` with no
3056/// `ON` is `ER_PARSE_ERROR` on mysql:8) and carries the online-DDL `ALGORITHM`/`LOCK`
3057/// execution hints, neither of which the shared grammar has. The index name is a bare
3058/// identifier (`ident`; a dotted `i.j` is a syntax error) while the table is a possibly-dotted
3059/// [`ObjectName`] (`db.t` binds). Gated by
3060/// [`IndexAlterSyntax::index_drop_on_table`](crate::dialect::IndexAlterSyntax::index_drop_on_table).
3061#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3062#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3063#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3064pub struct DropIndexOnTable {
3065 /// The dropped index — a bare unqualified identifier.
3066 pub name: Ident,
3067 /// The table the index belongs to (`ON <table>`); may be schema-qualified (`db.t`).
3068 pub table: ObjectName,
3069 /// The trailing `ALGORITHM`/`LOCK` execution hints in source order (`opt_index_lock_and_algorithm`).
3070 /// The grammar admits at most one of each and both orderings (`ALGORITHM … LOCK …` or
3071 /// `LOCK … ALGORITHM …`, server-measured: a repeated `ALGORITHM`/`LOCK` is `ER_PARSE_ERROR`),
3072 /// so this holds zero, one, or two entries and preserves the written order for round-trip.
3073 pub options: ThinVec<IndexLockAlgorithmOption>,
3074 /// Source location and node identity.
3075 pub meta: Meta,
3076}
3077
3078/// One entry of a `DROP INDEX … ON …` (or online `ALTER TABLE`) `opt_index_lock_and_algorithm`
3079/// tail — an `ALGORITHM` or a `LOCK` execution hint. Each records whether the optional `=`
3080/// (`opt_equal`) was written so the surface form round-trips verbatim.
3081#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3082#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3083#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3084pub enum IndexLockAlgorithmOption {
3085 /// `ALGORITHM [=] {DEFAULT | INPLACE | INSTANT | COPY}`.
3086 Algorithm {
3087 /// Whether the optional `=` was written between the keyword and the value.
3088 equals: bool,
3089 /// The chosen algorithm.
3090 value: IndexAlgorithm,
3091 },
3092 /// `LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}`.
3093 Lock {
3094 /// Whether the optional `=` was written between the keyword and the value.
3095 equals: bool,
3096 /// The chosen lock level.
3097 value: IndexLock,
3098 },
3099}
3100
3101/// The online-DDL `ALGORITHM` value (`alter_algorithm_option_value`). `DEFAULT` is a keyword;
3102/// the rest are matched case-insensitively as identifiers, and an unknown value is a *binding*
3103/// reject (`ER_UNKNOWN_ALTER_ALGORITHM` 1800), not a syntax error — so only these four bind.
3104#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3105#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3106#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3107pub enum IndexAlgorithm {
3108 /// `DEFAULT` — let the server choose.
3109 Default,
3110 /// `INPLACE` — rebuild in place where supported.
3111 Inplace,
3112 /// `INSTANT` — metadata-only change where supported.
3113 Instant,
3114 /// `COPY` — rebuild via a table copy.
3115 Copy,
3116}
3117
3118/// The online-DDL `LOCK` value (`alter_lock_option_value`). `DEFAULT` is a keyword; the rest
3119/// are matched case-insensitively as identifiers, and an unknown value is a *binding* reject
3120/// (`ER_UNKNOWN_ALTER_LOCK` 1801), not a syntax error — so only these four bind.
3121#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3122#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3123#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3124pub enum IndexLock {
3125 /// `DEFAULT` — the server's default concurrency.
3126 Default,
3127 /// `NONE` — permit concurrent reads and writes.
3128 None,
3129 /// `SHARED` — permit concurrent reads only.
3130 Shared,
3131 /// `EXCLUSIVE` — permit no concurrent access.
3132 Exclusive,
3133}
3134
3135/// `CREATE [OR REPLACE] [TEMP|TEMPORARY] {MACRO | FUNCTION} [IF NOT EXISTS] <name>
3136/// (<param> [, ...]) AS <body>` — DuckDB's macro DDL (gated by
3137/// [`StatementDdlGates::create_macro`](crate::dialect::StatementDdlGates::create_macro)).
3138///
3139/// This is a distinct node from [`CreateFunction`], not a variant of it: a DuckDB
3140/// macro body is a *live* SQL expression or query (`AS x + 1` / `AS TABLE SELECT …`),
3141/// whereas a PostgreSQL/MySQL `CREATE FUNCTION` body is an opaque source *string* in a
3142/// target language (`AS 'RETURN 1'`). The two share only the `CREATE … (params) AS`
3143/// prefix; their bodies, parameter grammars (a macro parameter is a bare untyped name,
3144/// a routine parameter is `[name] <type>`), and option tails are disjoint.
3145///
3146/// DuckDB spells the same feature with either the `MACRO` keyword or `FUNCTION` as an
3147/// exact synonym; [`spelling`](Self::spelling) records which so the source keyword
3148/// round-trips (the tag pattern of [`TemporaryTableKind`], not a semantic difference).
3149/// Whole-statement gated to DuckDB (and Lenient): every other dialect either has no
3150/// macro grammar (`MACRO` is left unconsumed and surfaces as an unknown statement) or
3151/// spells `CREATE FUNCTION` as the incompatible string-body routine, so the gate is
3152/// behaviour-accurate, not merely a modelling limitation. Boxed by the enclosing
3153/// [`Statement::CreateMacro`] to keep the enum within its size budget.
3154///
3155/// The single-body surface — one `(params) AS body` per statement — is modelled; DuckDB
3156/// additionally accepts a comma-separated *overload* list (`… AS a, (x, y) AS b`), a
3157/// deliberate follow-up (no vendored corpus statement spells it) the shape would extend
3158/// by lifting `params`/`body` into an overload sequence.
3159#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3160#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3161#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3162pub struct CreateMacro<X: Extension = NoExt> {
3163 /// Whether the or replace form was present in the source.
3164 pub or_replace: bool,
3165 /// Optional temporary for this syntax.
3166 pub temporary: Option<TemporaryTableKind>,
3167 /// Exact source spelling retained for faithful rendering.
3168 pub spelling: MacroSpelling,
3169 /// Whether the if not exists form was present in the source.
3170 pub if_not_exists: bool,
3171 /// Name referenced by this syntax.
3172 pub name: ObjectName,
3173 /// params in source order.
3174 pub params: ThinVec<MacroParam<X>>,
3175 /// Statement or query body governed by this node.
3176 pub body: MacroBody<X>,
3177 /// Source location and node identity.
3178 pub meta: Meta,
3179}
3180
3181/// Which keyword introduced a [`CreateMacro`] — DuckDB accepts `MACRO` and `FUNCTION`
3182/// as exact synonyms. A `Copy` tag whose span rides the owning [`CreateMacro`], like
3183/// [`TemporaryTableKind`].
3184#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3185#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3186#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3187pub enum MacroSpelling {
3188 /// Source used the `MACRO` spelling.
3189 Macro,
3190 /// Source used the `FUNCTION` spelling.
3191 Function,
3192}
3193
3194/// One [`CreateMacro`] parameter: a bare untyped name with an optional `:= <expr>`
3195/// default (`CREATE MACRO m(a, b := 10) AS …`). Unlike a [`FunctionParam`], a macro
3196/// parameter carries no type — DuckDB macros are template-substituted, not typed — so
3197/// the name is mandatory and no `DataType` rides the node.
3198#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3199#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3200#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3201pub struct MacroParam<X: Extension = NoExt> {
3202 /// Name referenced by this syntax.
3203 pub name: Ident,
3204 /// Optional default for this syntax.
3205 pub default: Option<Expr<X>>,
3206 /// Source location and node identity.
3207 pub meta: Meta,
3208}
3209
3210/// A [`CreateMacro`] body: a scalar expression (`AS <expr>`) or a table-producing
3211/// query (`AS TABLE <query>`). The `TABLE` keyword is the DuckDB discriminant between a
3212/// scalar macro (returns one value) and a table macro (returns a relation); the body
3213/// grammar reuses [`Expr`] / [`Query`] rather than re-deriving either.
3214#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3215#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3216#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3217pub enum MacroBody<X: Extension = NoExt> {
3218 /// A scalar macro body — an expression returning a single value.
3219 Scalar {
3220 /// Expression evaluated by this syntax.
3221 expr: Box<Expr<X>>,
3222 /// Source location and node identity.
3223 meta: Meta,
3224 },
3225 /// A table macro body — a query returning a relation.
3226 Table {
3227 /// Query governed by this node.
3228 query: Box<Query<X>>,
3229 /// Source location and node identity.
3230 meta: Meta,
3231 },
3232}
3233
3234/// `CREATE [OR REPLACE] [TEMP|TEMPORARY] TYPE [IF NOT EXISTS] <name> AS <definition>` —
3235/// DuckDB's user-defined-type DDL (gated by
3236/// [`StatementDdlGates::create_type`](crate::dialect::StatementDdlGates::create_type)).
3237///
3238/// The name is optionally schema-qualified (`CREATE TYPE s1.mood AS …`). Its
3239/// [`definition`](Self::definition) is either the dedicated `ENUM` production (a label
3240/// list or a label-supplying query) or an alias to any other (possibly composite/nested)
3241/// data type — see [`CreateTypeDefinition`].
3242///
3243/// `OR REPLACE` and `IF NOT EXISTS` are mutually exclusive in DuckDB's grammar (under `OR
3244/// REPLACE` the parser reads `IF` as the type name), so the parser fills `if_not_exists`
3245/// only on the plain form; the two flags are never both set. Whole-statement gated to
3246/// DuckDB (and Lenient): every other dialect leaves `TYPE` unconsumed after `CREATE`,
3247/// where it surfaces as the `TABLE` expectation (an unknown statement). Boxed by the
3248/// enclosing [`Statement::CreateType`] to keep the enum
3249/// within its size budget.
3250#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3251#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3252#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3253pub struct CreateType<X: Extension = NoExt> {
3254 /// Whether the or replace form was present in the source.
3255 pub or_replace: bool,
3256 /// Optional temporary for this syntax.
3257 pub temporary: Option<TemporaryTableKind>,
3258 /// Whether the if not exists form was present in the source.
3259 pub if_not_exists: bool,
3260 /// Name referenced by this syntax.
3261 pub name: ObjectName,
3262 /// The type definition — an `ENUM` label list or a type alias; see [`CreateTypeDefinition`].
3263 pub definition: CreateTypeDefinition<X>,
3264 /// Source location and node identity.
3265 pub meta: Meta,
3266}
3267
3268/// The `AS <definition>` body of a [`CreateType`]. DuckDB's `CREATE TYPE` grammar has a
3269/// dedicated `ENUM` rule distinct from the general data-type grammar — its labels are
3270/// parser-restricted to string constants (`ENUM(1, 2)` is a *parse* error, unlike the
3271/// data-type-position `x::ENUM(...)` cast, which parses any modifier and only bind-rejects
3272/// a non-string) — so an enum definition is its own variant rather than a
3273/// [`DataType::Enum`](crate::ast::DataType). Every non-`ENUM` spelling (`STRUCT`/`MAP`/
3274/// `UNION`, an alias to a named type, arrays, `DECIMAL(p, s)`, …) reuses the shared data
3275/// type via [`Alias`](Self::Alias).
3276#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3277#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3278#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3279pub enum CreateTypeDefinition<X: Extension = NoExt> {
3280 /// `AS ENUM ('a', 'b', …)` — a labelled enumeration. The label list may be empty
3281 /// (`ENUM ()`, which DuckDB accepts) and each label is a string literal.
3282 Enum {
3283 /// labels in source order.
3284 labels: ThinVec<Literal>,
3285 /// Source location and node identity.
3286 meta: Meta,
3287 },
3288 /// `AS ENUM (<query>)` — the enum labels are drawn from a query's single column
3289 /// (`ENUM (SELECT DISTINCT month FROM …)`); the parenthesized query reuses [`Query`].
3290 EnumFromQuery {
3291 /// Query governed by this node.
3292 query: Box<Query<X>>,
3293 /// Source location and node identity.
3294 meta: Meta,
3295 },
3296 /// `AS <type>` — an alias to another type: a scalar (`AS VARCHAR`), a composite/nested
3297 /// constructor (`AS STRUCT(…)` / `AS MAP(…)` / `AS UNION(…)`), an array (`AS my_int[]`),
3298 /// or a named user type. Reuses the shared [`DataType`] grammar.
3299 Alias {
3300 /// Data type named by this syntax.
3301 data_type: DataType<X>,
3302 /// Source location and node identity.
3303 meta: Meta,
3304 },
3305}
3306
3307/// A `CREATE [TEMPORARY] SEQUENCE [IF NOT EXISTS] <name> [<option> ...]` sequence-generator
3308/// statement (SQL:2003 T176; PostgreSQL and DuckDB), gated by
3309/// [`StatementDdlGates::create_sequence`](crate::dialect::StatementDdlGates::create_sequence).
3310///
3311/// The trailing options are the SQL-standard sequence-generator core — `START [WITH]`,
3312/// `INCREMENT [BY]`, `MINVALUE`/`NO MINVALUE`, `MAXVALUE`/`NO MAXVALUE`, `CYCLE`/`NO CYCLE` —
3313/// which both engines' parsers accept in any order, so this single node is gated
3314/// per-dialect by the one flag rather than split into parallel PostgreSQL/DuckDB nodes
3315/// (ADR-0011). They reuse [`IdentityOption`], the identical option vocabulary the
3316/// `GENERATED … AS IDENTITY` column form already carries; the [`Cache`](IdentityOption::Cache)
3317/// variant is never produced here — DuckDB's `CREATE SEQUENCE` grammar rejects `CACHE`
3318/// (engine-measured), so admitting it would over-accept, and it is a PostgreSQL-only
3319/// extension left to a follow-up.
3320///
3321/// PostgreSQL's extended tails (`AS <type>`, `CACHE`, `OWNED BY`, `RESTART`) and DuckDB's
3322/// `OR REPLACE SEQUENCE` are deliberately unmodelled here: each is one engine's extension,
3323/// not the shared standard core, and none is exercised by the DuckDB corpus.
3324#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3325#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3326#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3327pub struct CreateSequence<X: Extension = NoExt> {
3328 /// Optional temporary for this syntax.
3329 pub temporary: Option<TemporaryTableKind>,
3330 /// Whether the if not exists form was present in the source.
3331 pub if_not_exists: bool,
3332 /// Name referenced by this syntax.
3333 pub name: ObjectName,
3334 /// Options supplied in source order.
3335 pub options: ThinVec<IdentityOption<X>>,
3336 /// Source location and node identity.
3337 pub meta: Meta,
3338}
3339
3340/// A `CREATE EXTENSION [IF NOT EXISTS] <name> [WITH] [SCHEMA s] [VERSION v] [CASCADE]`
3341/// statement (PostgreSQL; gated by
3342/// [`StatementDdlGates::extension_ddl`](crate::dialect::StatementDdlGates::extension_ddl)).
3343///
3344/// Non-generic: an extension is named by a bare identifier and its options carry only
3345/// names and version strings — no expressions or extension nodes. The `WITH` keyword is
3346/// optional sugar (PostgreSQL's `opt_with`); [`with`](Self::with) records whether it was
3347/// written so it round-trips. The options are order-independent and repeatable in the
3348/// grammar (`create_extension_opt_list`), kept here in source order.
3349#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3350#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3351#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3352pub struct CreateExtension {
3353 /// Whether the if not exists form was present in the source.
3354 pub if_not_exists: bool,
3355 /// The extension name.
3356 pub name: Ident,
3357 /// Whether the optional `WITH` keyword preceded the option list.
3358 pub with: bool,
3359 /// Options supplied in source order.
3360 pub options: ThinVec<CreateExtensionOption>,
3361 /// Source location and node identity.
3362 pub meta: Meta,
3363}
3364
3365/// One `CREATE EXTENSION` option (`create_extension_opt_item`). PostgreSQL's grammar
3366/// carries a `FROM <old_version>` item whose action is a parse-time
3367/// `FEATURE_NOT_SUPPORTED` reject, so that form is not modelled here.
3368#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3369#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3370#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3371pub enum CreateExtensionOption {
3372 /// `SCHEMA <name>` — the schema the extension's objects are installed into.
3373 Schema {
3374 /// The target schema name.
3375 name: Ident,
3376 /// Source location and node identity.
3377 meta: Meta,
3378 },
3379 /// `VERSION <version>` — the version to install.
3380 Version {
3381 /// Version value supplied by this syntax.
3382 version: ExtensionVersion,
3383 /// Source location and node identity.
3384 meta: Meta,
3385 },
3386 /// `CASCADE` — install missing required extensions too.
3387 Cascade {
3388 /// Source location and node identity.
3389 meta: Meta,
3390 },
3391}
3392
3393/// An extension version value (PostgreSQL's `NonReservedWord_or_Sconst`): a bare word or
3394/// a string constant. PostgreSQL folds both to the same string internally, but the
3395/// surface spelling is kept distinct so it round-trips.
3396#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3397#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3398#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3399pub enum ExtensionVersion {
3400 /// A bare non-reserved word (`VERSION v1`).
3401 Word {
3402 /// Identifier-form version value.
3403 word: Ident,
3404 /// Source location and node identity.
3405 meta: Meta,
3406 },
3407 /// A string constant (`VERSION '1.0'`).
3408 String {
3409 /// Value supplied by this syntax.
3410 value: Literal,
3411 /// Source location and node identity.
3412 meta: Meta,
3413 },
3414}
3415
3416/// An `ALTER EXTENSION <name> ...` statement, gated by
3417/// [`StatementDdlGates::extension_ddl`](crate::dialect::StatementDdlGates::extension_ddl).
3418///
3419/// Unifies PostgreSQL's two `ALTER EXTENSION` productions under one node, since both
3420/// share the `ALTER EXTENSION <name>` head: the `UPDATE [TO <version>]` version bump
3421/// (`AlterExtensionStmt`) and the `ADD`/`DROP <member>` membership change
3422/// (`AlterExtensionContentsStmt`). PostgreSQL's `ALTER EXTENSION <name> SET SCHEMA
3423/// <schema>` is a separate `AlterObjectSchemaStmt` production (shared with every other
3424/// relocatable object), not an `AlterExtensionStmt`, so it is out of this node's scope.
3425#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3426#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3427#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3428pub struct AlterExtension<X: Extension = NoExt> {
3429 /// The extension name.
3430 pub name: Ident,
3431 /// The change applied to the extension.
3432 pub action: AlterExtensionAction<X>,
3433 /// Source location and node identity.
3434 pub meta: Meta,
3435}
3436
3437/// The change an [`AlterExtension`] applies.
3438#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3439#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3440#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3441pub enum AlterExtensionAction<X: Extension = NoExt> {
3442 /// `UPDATE [TO <version>]` — bump the installed version (`AlterExtensionStmt`).
3443 Update {
3444 /// The target version, when a `TO` clause was written.
3445 version: Option<ExtensionVersion>,
3446 /// Source location and node identity.
3447 meta: Meta,
3448 },
3449 /// `ADD <member>` (`add` is `true`) or `DROP <member>` — change extension membership
3450 /// (`AlterExtensionContentsStmt`).
3451 Change {
3452 /// `true` for `ADD`, `false` for `DROP`.
3453 add: bool,
3454 /// The member object added to or dropped from the extension.
3455 member: ObjectReference<X>,
3456 /// Source location and node identity.
3457 meta: Meta,
3458 },
3459}
3460
3461/// An `ALTER DATABASE [IF EXISTS] <name> SET ALIAS TO <alias>` statement (DuckDB), gated by
3462/// [`StatementDdlGates::alter_database`](crate::dialect::StatementDdlGates::alter_database).
3463///
3464/// DuckDB's sole `AlterDatabaseStmt` production re-aliases an attached database
3465/// (`third_party/libpg_query/grammar/statements/alter_database.y` at the pinned v1.5.4
3466/// commit `08e34c447b`). The grammar reduces `SET <ident> TO <name>`, but its action rejects
3467/// any keyword but `ALIAS`, so only `SET ALIAS TO` is a parse-accept (engine-measured: `SET
3468/// FOO TO` is a syntax error, and `ALTER DATABASE d RENAME TO e` is likewise a syntax error —
3469/// DuckDB has no `RENAME` form for a database). The change is held in an
3470/// [`AlterDatabaseAction`] enum so a sibling dialect (MySQL's charset/collation `ALTER
3471/// DATABASE`) can add its own action variants without reshaping this node.
3472#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3473#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3474#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3475pub struct AlterDatabase {
3476 /// Whether the `IF EXISTS` existence guard was present in the source.
3477 pub if_exists: bool,
3478 /// The database name.
3479 pub name: Ident,
3480 /// The change applied to the database.
3481 pub action: AlterDatabaseAction,
3482 /// Source location and node identity.
3483 pub meta: Meta,
3484}
3485
3486/// The change an [`AlterDatabase`] applies.
3487#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3488#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3489#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3490pub enum AlterDatabaseAction {
3491 /// `SET ALIAS TO <alias>` — re-alias the attached database.
3492 SetAlias {
3493 /// The new alias.
3494 new_name: Ident,
3495 /// Source location and node identity.
3496 meta: Meta,
3497 },
3498}
3499
3500/// An `ALTER SEQUENCE [IF EXISTS] <name> <option>...` statement (DuckDB), gated by
3501/// [`StatementDdlGates::alter_sequence`](crate::dialect::StatementDdlGates::alter_sequence).
3502///
3503/// DuckDB's `AlterSeqStmt` production changes a sequence generator's options
3504/// (`third_party/libpg_query/grammar/statements/alter_sequence.y` at the pinned v1.5.4
3505/// commit). Its `SeqOptList` is the same option grammar `CREATE SEQUENCE` reduces, so the
3506/// shared core is carried through the reused [`IdentityOption`] axis
3507/// ([`AlterSequenceOption::Common`]); the ALTER-only leads DuckDB additionally parses
3508/// (`RESTART`, `AS <type>`, `OWNED BY`, `SEQUENCE NAME`) are their own variants. Options
3509/// are kept in source order. `SET SCHEMA` is *not* an option here — it is DuckDB's separate
3510/// [`AlterObjectSchema`] production, dispatched before this one.
3511#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3512#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3513#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3514pub struct AlterSequence<X: Extension = NoExt> {
3515 /// Whether the `IF EXISTS` existence guard was present in the source.
3516 pub if_exists: bool,
3517 /// The sequence name.
3518 pub name: ObjectName,
3519 /// Options supplied in source order.
3520 pub options: ThinVec<AlterSequenceOption<X>>,
3521 /// Source location and node identity.
3522 pub meta: Meta,
3523}
3524
3525/// One `ALTER SEQUENCE` option (DuckDB's `SeqOptElem`).
3526#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3527#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3528#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3529pub enum AlterSequenceOption<X: Extension = NoExt> {
3530 /// A shared sequence-generator option `CREATE SEQUENCE` also accepts (`START [WITH]`,
3531 /// `INCREMENT [BY]`, `MIN`/`MAXVALUE`, `NO MIN`/`MAXVALUE`, `CYCLE`/`NO CYCLE`, `CACHE`),
3532 /// reusing the [`IdentityOption`] axis.
3533 Common {
3534 /// The reused shared option.
3535 option: IdentityOption<X>,
3536 /// Source location and node identity.
3537 meta: Meta,
3538 },
3539 /// `RESTART [[WITH] <n>]` — reset the sequence's current value (bare = restart to its
3540 /// start value).
3541 Restart {
3542 /// The restart value, when a `[WITH] <n>` tail was written.
3543 value: Option<Expr<X>>,
3544 /// Source location and node identity.
3545 meta: Meta,
3546 },
3547 /// `AS <type>` — change the sequence's integer type.
3548 As {
3549 /// The sequence's new integer type.
3550 data_type: DataType<X>,
3551 /// Source location and node identity.
3552 meta: Meta,
3553 },
3554 /// `OWNED BY {<column> | NONE}` — tie the sequence's lifetime to a column, or detach it
3555 /// (`None` renders `NONE`).
3556 OwnedBy {
3557 /// The owning column, or `None` for `OWNED BY NONE`.
3558 owner: Option<ObjectName>,
3559 /// Source location and node identity.
3560 meta: Meta,
3561 },
3562 /// `SEQUENCE NAME <name>` — the internal rename form libpg_query documents as pg_dump-only.
3563 SequenceName {
3564 /// The new internal name.
3565 name: ObjectName,
3566 /// Source location and node identity.
3567 meta: Meta,
3568 },
3569}
3570
3571/// An `ALTER {TABLE | VIEW | SEQUENCE} [IF EXISTS] <name> SET SCHEMA <schema>` statement
3572/// (DuckDB's `AlterObjectSchemaStmt`), gated by
3573/// [`StatementDdlGates::alter_object_set_schema`](crate::dialect::StatementDdlGates::alter_object_set_schema).
3574///
3575/// Relocates a relocatable object to another schema
3576/// (`third_party/libpg_query/grammar/statements/alter_schema.y` at the pinned v1.5.4 commit).
3577/// DuckDB 1.5.4's binder rejects this with `Not implemented: T_AlterObjectSchemaStmt`, but the
3578/// production is parse-reachable (engine-measured via `json_serialize_sql`), and PARSE-level
3579/// parity is the modelled bar — analogous to PostgreSQL's grammar-present, engine-unimplemented
3580/// `CreateAssertionStmt`. The object kind is one of [`SchemaRelocationObject`]'s three.
3581#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3582#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3583#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3584pub struct AlterObjectSchema {
3585 /// Which relocatable object kind was named.
3586 pub object_type: SchemaRelocationObject,
3587 /// Whether the `IF EXISTS` existence guard was present in the source.
3588 pub if_exists: bool,
3589 /// The object's current (schema-qualified) name.
3590 pub name: ObjectName,
3591 /// The destination schema.
3592 pub new_schema: Ident,
3593 /// Source location and node identity.
3594 pub meta: Meta,
3595}
3596
3597/// The relocatable object kind an [`AlterObjectSchema`] moves — DuckDB's `AlterObjectSchemaStmt`
3598/// admits exactly these three object heads.
3599#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3600#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3601#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3602pub enum SchemaRelocationObject {
3603 /// `ALTER TABLE … SET SCHEMA`.
3604 Table,
3605 /// `ALTER VIEW … SET SCHEMA`.
3606 View,
3607 /// `ALTER SEQUENCE … SET SCHEMA`.
3608 Sequence,
3609}
3610
3611/// A reference to a database object by kind and kind-appropriate signature — the shared
3612/// object-reference axis for PostgreSQL object-membership DDL.
3613///
3614/// PostgreSQL's `ALTER EXTENSION … ADD|DROP <member>` grammar
3615/// (`AlterExtensionContentsStmt`) names a member object by a kind keyword plus a
3616/// signature whose shape depends on the kind: a bare or qualified name, a routine
3617/// argument-type signature, an aggregate/operator signature, a cast type pair, a type
3618/// name, or a transform. The sibling object-DDL heads name objects the same way — the
3619/// `parse-pg-alter-object-depends` head (`ALTER … DEPENDS ON EXTENSION`) reuses
3620/// [`Routine`](Self::Routine) and [`Named`](Self::Named) for its `FUNCTION`/`PROCEDURE`/
3621/// `ROUTINE`, `MATERIALIZED VIEW`, and `INDEX` objects, and `parse-pg-drop-transform`
3622/// reuses [`Transform`](Self::Transform) — so the axis lives here once rather than being
3623/// re-derived per head.
3624///
3625/// [`Trigger`](Self::Trigger) is the one shape no `ALTER EXTENSION` member takes: its
3626/// `TRIGGER <name> ON <table>` form is exclusive to the `DEPENDS ON EXTENSION` head
3627/// (`ALTER EXTENSION … ADD TRIGGER` is a reject), so it was added to the axis additively
3628/// when that head's measured grammar demanded it, keeping every object-DDL head on one
3629/// shared reference type.
3630#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3631#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3632#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3633pub enum ObjectReference<X: Extension = NoExt> {
3634 /// A kind keyword and a name. [`kind`](ObjectRefKind) records whether the name may be
3635 /// schema-qualified: PostgreSQL's `object_type_any_name` kinds (`TABLE`, `VIEW`, …)
3636 /// accept a dotted `any_name`, its `object_type_name` kinds (`SCHEMA`, `ROLE`, …)
3637 /// only a single `name`.
3638 Named {
3639 /// The object kind; see [`ObjectRefKind`].
3640 kind: ObjectRefKind,
3641 /// The referenced name.
3642 name: ObjectName,
3643 /// Source location and node identity.
3644 meta: Meta,
3645 },
3646 /// `FUNCTION`/`PROCEDURE`/`ROUTINE <name>[(<argtypes>)]`
3647 /// (PostgreSQL's `function_with_argtypes`; the argument list is optional).
3648 Routine {
3649 /// Which routine kind; see [`RoutineObjectKind`].
3650 kind: RoutineObjectKind,
3651 /// The routine name and optional argument-type signature.
3652 signature: RoutineSignature<X>,
3653 /// Source location and node identity.
3654 meta: Meta,
3655 },
3656 /// `AGGREGATE <name>(<aggr_args>)`.
3657 Aggregate {
3658 /// The aggregate name.
3659 name: ObjectName,
3660 /// The aggregate argument signature.
3661 args: AggregateArgs<X>,
3662 /// Source location and node identity.
3663 meta: Meta,
3664 },
3665 /// `OPERATOR [<schema>.]<sym>(<left>, <right>)` (PostgreSQL's `operator_with_argtypes`).
3666 Operator {
3667 /// The optional schema qualification (empty for an unqualified operator).
3668 schema: ObjectName,
3669 /// The symbolic operator spelling, interned exact-case.
3670 op: Symbol,
3671 /// The operand types.
3672 args: OperatorArgs<X>,
3673 /// Source location and node identity.
3674 meta: Meta,
3675 },
3676 /// `OPERATOR CLASS`/`OPERATOR FAMILY <name> USING <access_method>`.
3677 OperatorClass {
3678 /// `true` for `OPERATOR FAMILY`, `false` for `OPERATOR CLASS`.
3679 family: bool,
3680 /// The operator class/family name.
3681 name: ObjectName,
3682 /// The `USING <access_method>` index access method.
3683 access_method: Ident,
3684 /// Source location and node identity.
3685 meta: Meta,
3686 },
3687 /// `CAST (<from> AS <to>)`.
3688 Cast {
3689 /// The cast's source type.
3690 from: DataType<X>,
3691 /// The cast's target type.
3692 to: DataType<X>,
3693 /// Source location and node identity.
3694 meta: Meta,
3695 },
3696 /// `DOMAIN`/`TYPE <typename>` — a type-named object.
3697 Type {
3698 /// `true` for the `DOMAIN` keyword, `false` for `TYPE` (both name a type via a
3699 /// `Typename`).
3700 domain: bool,
3701 /// The type name (a `Typename`, possibly schema-qualified).
3702 name: DataType<X>,
3703 /// Source location and node identity.
3704 meta: Meta,
3705 },
3706 /// `TRANSFORM FOR <typename> LANGUAGE <language>`.
3707 Transform {
3708 /// The type the transform is `FOR`.
3709 type_name: DataType<X>,
3710 /// The transform's procedural language name.
3711 language: Ident,
3712 /// Source location and node identity.
3713 meta: Meta,
3714 },
3715 /// `TRIGGER <name> ON <table>` — a trigger named by its bare name plus the table it
3716 /// is defined on (PostgreSQL's `ALTER TRIGGER name ON qualified_name`). Unlike the
3717 /// other object kinds, a trigger is not schema-qualifiable on its own name (it is a
3718 /// `name`, a single `ColId`); the `table` carries the qualification.
3719 ///
3720 /// This variant is reached only by the `DEPENDS ON EXTENSION` head — no `ALTER
3721 /// EXTENSION` member is a trigger — so `parse_object_reference` never constructs it.
3722 Trigger {
3723 /// The trigger's bare name.
3724 name: Ident,
3725 /// The table the trigger is defined on (a `qualified_name`).
3726 table: ObjectName,
3727 /// Source location and node identity.
3728 meta: Meta,
3729 },
3730}
3731
3732/// A member-object kind naming an object by a bare or qualified name — the
3733/// name-only shapes of [`ObjectReference::Named`], covering PostgreSQL's
3734/// `object_type_any_name` and `object_type_name` productions.
3735#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3736#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3737#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3738pub enum ObjectRefKind {
3739 /// `TABLE`.
3740 Table,
3741 /// `SEQUENCE`.
3742 Sequence,
3743 /// `VIEW`.
3744 View,
3745 /// `MATERIALIZED VIEW`.
3746 MaterializedView,
3747 /// `INDEX`.
3748 Index,
3749 /// `FOREIGN TABLE`.
3750 ForeignTable,
3751 /// `COLLATION`.
3752 Collation,
3753 /// `CONVERSION`.
3754 Conversion,
3755 /// `STATISTICS`.
3756 Statistics,
3757 /// `TEXT SEARCH PARSER`.
3758 TextSearchParser,
3759 /// `TEXT SEARCH DICTIONARY`.
3760 TextSearchDictionary,
3761 /// `TEXT SEARCH TEMPLATE`.
3762 TextSearchTemplate,
3763 /// `TEXT SEARCH CONFIGURATION`.
3764 TextSearchConfiguration,
3765 /// `ACCESS METHOD`.
3766 AccessMethod,
3767 /// `EVENT TRIGGER`.
3768 EventTrigger,
3769 /// `EXTENSION`.
3770 Extension,
3771 /// `FOREIGN DATA WRAPPER`.
3772 ForeignDataWrapper,
3773 /// `[PROCEDURAL] LANGUAGE`. The optional `PROCEDURAL` keyword is exact-synonym sugar
3774 /// (PostgreSQL's `opt_procedural`); the canonical render emits `LANGUAGE`.
3775 Language,
3776 /// `PUBLICATION`.
3777 Publication,
3778 /// `SCHEMA`.
3779 Schema,
3780 /// `SERVER` (a foreign server).
3781 Server,
3782 /// `DATABASE`.
3783 Database,
3784 /// `ROLE`.
3785 Role,
3786 /// `TABLESPACE`.
3787 Tablespace,
3788}
3789
3790impl ObjectRefKind {
3791 /// Whether this kind names a schema-qualifiable object (PostgreSQL's
3792 /// `object_type_any_name` group), so its name may be a dotted `any_name`. The
3793 /// remaining kinds are `object_type_name`, which take only a single `name`.
3794 pub fn schema_qualifiable(self) -> bool {
3795 matches!(
3796 self,
3797 Self::Table
3798 | Self::Sequence
3799 | Self::View
3800 | Self::MaterializedView
3801 | Self::Index
3802 | Self::ForeignTable
3803 | Self::Collation
3804 | Self::Conversion
3805 | Self::Statistics
3806 | Self::TextSearchParser
3807 | Self::TextSearchDictionary
3808 | Self::TextSearchTemplate
3809 | Self::TextSearchConfiguration
3810 )
3811 }
3812}
3813
3814/// The argument signature of an `AGGREGATE` member (PostgreSQL's `aggr_args`).
3815///
3816/// Argument names and modes are dropped, keeping only the type list — the same
3817/// simplification [`RoutineSignature`] applies to `function_with_argtypes`.
3818#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3819#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3820#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3821pub enum AggregateArgs<X: Extension = NoExt> {
3822 /// `(*)` — the zero-argument wildcard.
3823 Star {
3824 /// Source location and node identity.
3825 meta: Meta,
3826 },
3827 /// A direct argument-type list, optionally followed by an ordered-set `ORDER BY`
3828 /// list: `(<types>)`, `(ORDER BY <types>)` (empty `direct`), or
3829 /// `(<types> ORDER BY <types>)`.
3830 Types {
3831 /// Direct argument types (empty for the leading `ORDER BY` form).
3832 direct: ThinVec<DataType<X>>,
3833 /// Ordered-set `ORDER BY` argument types, when the clause is present.
3834 order_by: Option<ThinVec<DataType<X>>>,
3835 /// Source location and node identity.
3836 meta: Meta,
3837 },
3838}
3839
3840/// The operand types of an `OPERATOR` member (PostgreSQL's `oper_argtypes`): a binary or
3841/// unary operator's left and right operand types, where a `NONE` operand (a unary
3842/// operator's missing side) is `None`. At least one side is always present.
3843#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3844#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3845#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3846pub struct OperatorArgs<X: Extension = NoExt> {
3847 /// The left operand type, or `None` for a left-unary operator's `NONE`.
3848 pub left: Option<DataType<X>>,
3849 /// The right operand type, or `None` for a right-unary operator's `NONE`.
3850 pub right: Option<DataType<X>>,
3851 /// Source location and node identity.
3852 pub meta: Meta,
3853}
3854
3855/// An `ALTER <object> [NO] DEPENDS ON EXTENSION <extension>` statement (PostgreSQL's
3856/// `AlterObjectDependsStmt`), gated by
3857/// [`StatementDdlGates::extension_ddl`](crate::dialect::StatementDdlGates::extension_ddl).
3858///
3859/// Records or clears a dependency of a database object on an extension, so that dropping
3860/// the extension cascades to the object (and `pg_dump` skips it). PostgreSQL admits only
3861/// four object heads here — `FUNCTION`/`PROCEDURE`/`ROUTINE` (a `function_with_argtypes`),
3862/// `TRIGGER <name> ON <table>`, `MATERIALIZED VIEW`, and `INDEX` — captured by the shared
3863/// [`ObjectReference`] axis, so the wider `object_type` keyword set (`TABLE`, `VIEW`,
3864/// `SEQUENCE`, …) that `object` could otherwise spell is out of the parsed grammar.
3865///
3866/// The construct references an extension but operates on a non-extension object; it shares
3867/// the [`extension_ddl`](crate::dialect::StatementDdlGates::extension_ddl) gate rather than
3868/// carrying its own, because it is meaningless without the extension system and its dialect
3869/// boundary (PostgreSQL on, everything else off) is identical to extension DDL's — a
3870/// dialect that has extensions has this, one that does not (DuckDB's `INSTALL`/`LOAD`) has
3871/// neither.
3872#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3873#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3874#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3875pub struct AlterObjectDepends<X: Extension = NoExt> {
3876 /// The object whose extension dependency is being set — one of the four
3877 /// [`ObjectReference`] shapes the `DEPENDS` grammar admits.
3878 pub object: ObjectReference<X>,
3879 /// Whether the `NO DEPENDS` negation form was written (PostgreSQL's `opt_no`), which
3880 /// removes the recorded dependency instead of adding it.
3881 pub no: bool,
3882 /// The extension the object depends on (a bare `name`, not schema-qualifiable).
3883 pub extension: Ident,
3884 /// Source location and node identity.
3885 pub meta: Meta,
3886}
3887
3888/// A MySQL `CREATE SERVER <name> FOREIGN DATA WRAPPER <wrapper> OPTIONS ( <option> [, ...] )`
3889/// federated-server definition (`sql_yacc.yy` `CREATE SERVER_SYM …`), gated by
3890/// [`StatementDdlGates::server_definition`](crate::dialect::StatementDdlGates::server_definition).
3891///
3892/// Registers a remote server for the `FEDERATED` storage engine. The server name and the
3893/// `FOREIGN DATA WRAPPER` name are each an `ident_or_text` (a bare/backtick identifier or a
3894/// quoted string, folded to an [`Ident`] whose quote style round-trips). The `OPTIONS` list is
3895/// the fixed [`ServerOption`] keyword set and is non-empty — an empty `OPTIONS ()` is
3896/// `ER_PARSE_ERROR` on mysql:8.4.10. Shares its gate and the [`ServerOption`] axis with
3897/// [`AlterServer`] (the two differ only in whether a wrapper is named); [`DropServer`] disposes
3898/// of the same object.
3899#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3900#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3901#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3902pub struct CreateServer {
3903 /// The server name (`ident_or_text`).
3904 pub name: Ident,
3905 /// The `FOREIGN DATA WRAPPER` name (`ident_or_text`) — the storage-engine wrapper the
3906 /// server uses.
3907 pub wrapper: Ident,
3908 /// The `OPTIONS` list, in source order; non-empty (`server_options_list`).
3909 pub options: ThinVec<ServerOption>,
3910 /// Source location and node identity.
3911 pub meta: Meta,
3912}
3913
3914/// A MySQL `ALTER SERVER <name> OPTIONS ( <option> [, ...] )` federated-server change
3915/// (`sql_yacc.yy` `ALTER SERVER_SYM …`), gated by
3916/// [`StatementDdlGates::server_definition`](crate::dialect::StatementDdlGates::server_definition).
3917///
3918/// Re-sets connection options on an existing server. Unlike [`CreateServer`] it names no
3919/// `FOREIGN DATA WRAPPER` (the wrapper is fixed at creation), but the `OPTIONS` list is the
3920/// same non-empty [`ServerOption`] grammar.
3921#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3922#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3923#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3924pub struct AlterServer {
3925 /// The server name (`ident_or_text`).
3926 pub name: Ident,
3927 /// The `OPTIONS` list, in source order; non-empty (`server_options_list`).
3928 pub options: ThinVec<ServerOption>,
3929 /// Source location and node identity.
3930 pub meta: Meta,
3931}
3932
3933/// A MySQL `DROP SERVER [IF EXISTS] <name>` federated-server drop (`sql_yacc.yy`
3934/// `drop_server_stmt`), gated by
3935/// [`StatementDdlGates::server_definition`](crate::dialect::StatementDdlGates::server_definition).
3936///
3937/// Removes a single server; no comma list (`DROP SERVER a, b` is `ER_PARSE_ERROR` on
3938/// mysql:8.4.10). The name is an `ident_or_text`.
3939#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3940#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3941#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3942pub struct DropServer {
3943 /// Whether the `IF EXISTS` guard was written (`drop_server_stmt`'s `if_exists`).
3944 pub if_exists: bool,
3945 /// The server name (`ident_or_text`).
3946 pub name: Ident,
3947 /// Source location and node identity.
3948 pub meta: Meta,
3949}
3950
3951/// One `CREATE`/`ALTER SERVER` option — a fixed keyword ([`ServerOptionKind`]) and its value
3952/// (`sql_yacc.yy` `server_option`). Every option carries exactly one value: the string-valued
3953/// keywords (`HOST`/`DATABASE`/`USER`/`PASSWORD`/`SOCKET`/`OWNER`) take a `TEXT_STRING_sys`
3954/// string literal, and `PORT` takes a `ulong_num` unsigned-integer literal — the parser holds
3955/// each to its measured value type (`HOST 123` and `PORT '3306'` are both `ER_PARSE_ERROR` on
3956/// mysql:8.4.10). The value is a [`Literal`] whose [`kind`](Literal::kind) records which.
3957#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3958#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3959#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3960pub struct ServerOption {
3961 /// Which fixed option keyword this is.
3962 pub kind: ServerOptionKind,
3963 /// The option value — a string literal for every kind but [`ServerOptionKind::Port`],
3964 /// which carries an unsigned-integer literal.
3965 pub value: Literal,
3966 /// Source location and node identity.
3967 pub meta: Meta,
3968}
3969
3970/// The fixed keyword of a [`ServerOption`] (`sql_yacc.yy` `server_option`). A surface tag (no
3971/// `meta` — its span rides the owning [`ServerOption`]). Only these seven keywords are
3972/// options; any other (`FOO 'bar'`) is `ER_PARSE_ERROR` on mysql:8.4.10.
3973#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3974#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
3975#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
3976pub enum ServerOptionKind {
3977 /// `HOST '<host>'` — the remote host name.
3978 Host,
3979 /// `DATABASE '<db>'` — the remote default database.
3980 Database,
3981 /// `USER '<user>'` — the connection user.
3982 User,
3983 /// `PASSWORD '<password>'` — the connection password.
3984 Password,
3985 /// `SOCKET '<socket>'` — the remote unix socket path.
3986 Socket,
3987 /// `OWNER '<owner>'` — the server owner.
3988 Owner,
3989 /// `PORT <n>` — the remote TCP port (an unsigned-integer value, not a string).
3990 Port,
3991}
3992
3993/// A MySQL `ALTER INSTANCE <action>` server-instance administration statement (`sql_yacc.yy`
3994/// `alter_instance_stmt`), gated by
3995/// [`StatementDdlGates::alter_instance`](crate::dialect::StatementDdlGates::alter_instance).
3996///
3997/// A single instance-wide maintenance action (rotate an encryption master key, reload TLS
3998/// material or the keyring, toggle the InnoDB redo log) — see [`AlterInstanceAction`]. Unlike
3999/// the server and database DDL this names no object.
4000#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4001#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4002#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4003pub struct AlterInstance {
4004 /// The instance action performed.
4005 pub action: AlterInstanceAction,
4006 /// Source location and node identity.
4007 pub meta: Meta,
4008}
4009
4010/// The action of an [`AlterInstance`] (`sql_yacc.yy` `alter_instance_action`). The `INNODB`,
4011/// `BINLOG`, and `REDO_LOG` words are matched as identifiers by the server (`is_identifier`),
4012/// so a wrong word is `ER_PARSE_ERROR` (`ROTATE FOO MASTER KEY`, `ENABLE INNODB FOO`); this
4013/// models only the measured accepted set.
4014#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4015#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4016#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4017pub enum AlterInstanceAction {
4018 /// `ROTATE INNODB MASTER KEY` — rotate the InnoDB tablespace-encryption master key.
4019 RotateInnodbMasterKey {
4020 /// Source location and node identity.
4021 meta: Meta,
4022 },
4023 /// `ROTATE BINLOG MASTER KEY` — rotate the binary-log encryption master key.
4024 RotateBinlogMasterKey {
4025 /// Source location and node identity.
4026 meta: Meta,
4027 },
4028 /// `RELOAD TLS [FOR CHANNEL <channel>] [NO ROLLBACK ON ERROR]` — reconfigure the TLS
4029 /// context. The optional `FOR CHANNEL` names a bare `ident` (a quoted `'ch'` is
4030 /// `ER_PARSE_ERROR` on mysql:8.4.10, unlike `FLUSH … FOR CHANNEL`'s string); the default
4031 /// is to roll back the active context on error, and `NO ROLLBACK ON ERROR` suppresses it.
4032 ReloadTls {
4033 /// The `FOR CHANNEL <channel>` name (a bare `ident`), or `None`.
4034 channel: Option<Ident>,
4035 /// Whether `NO ROLLBACK ON ERROR` was written.
4036 no_rollback_on_error: bool,
4037 /// Source location and node identity.
4038 meta: Meta,
4039 },
4040 /// `RELOAD KEYRING` — reload keyring component options from the manifest.
4041 ReloadKeyring {
4042 /// Source location and node identity.
4043 meta: Meta,
4044 },
4045 /// `ENABLE INNODB REDO_LOG` — re-enable redo logging.
4046 EnableInnodbRedoLog {
4047 /// Source location and node identity.
4048 meta: Meta,
4049 },
4050 /// `DISABLE INNODB REDO_LOG` — disable redo logging (for bulk load).
4051 DisableInnodbRedoLog {
4052 /// Source location and node identity.
4053 meta: Meta,
4054 },
4055}
4056
4057/// A MySQL `CREATE [OR REPLACE] SPATIAL REFERENCE SYSTEM [IF NOT EXISTS] <srid> <attributes>`
4058/// spatial-reference-system definition (`sql_yacc.yy` `create_srs_stmt`), gated by
4059/// [`StatementDdlGates::spatial_reference_system`](crate::dialect::StatementDdlGates::spatial_reference_system).
4060///
4061/// `OR REPLACE` and `IF NOT EXISTS` are mutually exclusive: the grammar has one branch for each
4062/// (`CREATE OR REPLACE …` and `CREATE … IF NOT EXISTS …`), so `CREATE OR REPLACE … IF NOT EXISTS`
4063/// is `ER_PARSE_ERROR` on mysql:8.4.10. The `<srid>` is a `real_ulonglong_num` unsigned integer
4064/// (decimal or `0x` hex; a negative or fractional value is `ER_PARSE_ERROR`, an out-of-`u64`-range
4065/// value is the post-parse `ER_DATA_OUT_OF_RANGE`). Every attribute is optional and the list is
4066/// order-free at the grammar level — a missing NAME/DEFINITION is the post-parse semantic reject
4067/// `ER_SRS_MISSING_MANDATORY_ATTRIBUTE`, and a repeated attribute is
4068/// `ER_SRS_MULTIPLE_ATTRIBUTE_DEFINITIONS`, neither a syntax error — so [`attributes`](Self::attributes)
4069/// keeps the source order and admits repeats, matching the recognized grammar rather than the
4070/// narrower semantic rule.
4071#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4072#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4073#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4074pub struct CreateSpatialReferenceSystem {
4075 /// Whether the `OR REPLACE` branch was written (mutually exclusive with
4076 /// [`if_not_exists`](Self::if_not_exists)).
4077 pub or_replace: bool,
4078 /// Whether the `IF NOT EXISTS` guard was written (mutually exclusive with
4079 /// [`or_replace`](Self::or_replace)).
4080 pub if_not_exists: bool,
4081 /// The SRID — a `real_ulonglong_num` unsigned-integer literal (decimal or `0x` hex).
4082 pub srid: Literal,
4083 /// The attribute list, in source order (order-free grammar; repeats admitted).
4084 pub attributes: ThinVec<SrsAttribute>,
4085 /// Source location and node identity.
4086 pub meta: Meta,
4087}
4088
4089/// One `CREATE SPATIAL REFERENCE SYSTEM` attribute (`sql_yacc.yy` `srs_attributes`). The
4090/// attributes are order-free and each independently optional; a repeat of the same attribute is
4091/// a post-parse semantic reject (`ER_SRS_MULTIPLE_ATTRIBUTE_DEFINITIONS`), not a syntax error,
4092/// so the parser accepts the recognized grammar and preserves whatever the source wrote.
4093#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4094#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4095#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4096pub enum SrsAttribute {
4097 /// `NAME '<name>'` — the SRS name (a `TEXT_STRING_sys_nonewline` string literal).
4098 Name {
4099 /// The name string.
4100 value: Literal,
4101 /// Source location and node identity.
4102 meta: Meta,
4103 },
4104 /// `DEFINITION '<wkt>'` — the WKT coordinate-system definition string.
4105 Definition {
4106 /// The definition string.
4107 value: Literal,
4108 /// Source location and node identity.
4109 meta: Meta,
4110 },
4111 /// `ORGANIZATION '<org>' IDENTIFIED BY <id>` — the authoring organization and its numeric
4112 /// coordinate-system id. `IDENTIFIED BY` is mandatory (`ORGANIZATION 'o'` alone is
4113 /// `ER_PARSE_ERROR`) and the id is a `real_ulonglong_num` integer (`IDENTIFIED BY 'x'` is
4114 /// `ER_PARSE_ERROR`).
4115 Organization {
4116 /// The organization name string.
4117 organization: Literal,
4118 /// The `IDENTIFIED BY` numeric coordinate-system id (unsigned-integer literal).
4119 identifier: Literal,
4120 /// Source location and node identity.
4121 meta: Meta,
4122 },
4123 /// `DESCRIPTION '<text>'` — the free-text description string.
4124 Description {
4125 /// The description string.
4126 value: Literal,
4127 /// Source location and node identity.
4128 meta: Meta,
4129 },
4130}
4131
4132/// A MySQL `DROP SPATIAL REFERENCE SYSTEM [IF EXISTS] <srid>` spatial-reference-system drop
4133/// (`sql_yacc.yy` `drop_srs_stmt`), gated by
4134/// [`StatementDdlGates::spatial_reference_system`](crate::dialect::StatementDdlGates::spatial_reference_system).
4135///
4136/// Removes a single SRS by id; no comma list (`DROP SPATIAL REFERENCE SYSTEM 1, 2` is
4137/// `ER_PARSE_ERROR` on mysql:8.4.10). The `<srid>` is a `real_ulonglong_num` unsigned integer.
4138#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4139#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4140#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4141pub struct DropSpatialReferenceSystem {
4142 /// Whether the `IF EXISTS` guard was written (`drop_srs_stmt`'s `if_exists`).
4143 pub if_exists: bool,
4144 /// The SRID — a `real_ulonglong_num` unsigned-integer literal.
4145 pub srid: Literal,
4146 /// Source location and node identity.
4147 pub meta: Meta,
4148}
4149
4150/// A MySQL `CREATE RESOURCE GROUP <name> TYPE [=] {SYSTEM | USER} [VCPU …] [THREAD_PRIORITY …]
4151/// [ENABLE | DISABLE]` resource-group definition (`sql_yacc.yy` `create_resource_group_stmt`),
4152/// gated by [`StatementDdlGates::resource_group`](crate::dialect::StatementDdlGates::resource_group).
4153///
4154/// `TYPE` is mandatory (a bare `CREATE RESOURCE GROUP g` is `ER_PARSE_ERROR` on mysql:8.4.10) and
4155/// the option train is fixed-order — `VCPU`, then `THREAD_PRIORITY`, then `ENABLE`/`DISABLE`; any
4156/// permutation (`… ENABLE VCPU …`) is `ER_PARSE_ERROR`. `CREATE` admits no `FORCE` (`ENABLE FORCE`
4157/// is `ER_PARSE_ERROR`), unlike [`AlterResourceGroup`] and [`DropResourceGroup`], which share the
4158/// same [`resource_group`](crate::dialect::StatementDdlGates::resource_group) gate.
4159#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4160#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4161#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4162pub struct CreateResourceGroup {
4163 /// The resource-group name (`ident`).
4164 pub name: Ident,
4165 /// Whether the `TYPE =` spelling (vs bare `TYPE`) was written (`opt_equal`).
4166 pub type_equals: bool,
4167 /// The group type (`SYSTEM`/`USER`), mandatory.
4168 pub group_type: ResourceGroupType,
4169 /// The optional `VCPU [=] <ranges>` CPU-affinity clause.
4170 pub vcpu: Option<ResourceGroupVcpu>,
4171 /// The optional `THREAD_PRIORITY [=] <n>` clause.
4172 pub thread_priority: Option<ResourceGroupThreadPriority>,
4173 /// The optional `ENABLE`/`DISABLE` state clause.
4174 pub state: Option<ResourceGroupState>,
4175 /// Source location and node identity.
4176 pub meta: Meta,
4177}
4178
4179/// A MySQL `ALTER RESOURCE GROUP <name> [VCPU …] [THREAD_PRIORITY …] [ENABLE | DISABLE] [FORCE]`
4180/// resource-group change (`sql_yacc.yy` `alter_resource_group_stmt`), gated by
4181/// [`StatementDdlGates::resource_group`](crate::dialect::StatementDdlGates::resource_group).
4182///
4183/// Every clause is optional — a bare `ALTER RESOURCE GROUP g` grammar-accepts on mysql:8.4.10.
4184/// The trailing `FORCE` is an *independent* optional (`opt_force`), not a suffix of
4185/// `ENABLE`/`DISABLE`: `ALTER RESOURCE GROUP g FORCE`, with neither state keyword, grammar-accepts
4186/// — so [`force`](Self::force) is a peer of [`state`](Self::state), not nested inside it. Shares
4187/// the [`ResourceGroupVcpu`]/[`ResourceGroupThreadPriority`]/[`ResourceGroupState`] axes with
4188/// [`CreateResourceGroup`]; `CREATE` differs only in requiring `TYPE` and forbidding `FORCE`.
4189#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4190#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4191#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4192pub struct AlterResourceGroup {
4193 /// The resource-group name (`ident`).
4194 pub name: Ident,
4195 /// The optional `VCPU [=] <ranges>` CPU-affinity clause.
4196 pub vcpu: Option<ResourceGroupVcpu>,
4197 /// The optional `THREAD_PRIORITY [=] <n>` clause.
4198 pub thread_priority: Option<ResourceGroupThreadPriority>,
4199 /// The optional `ENABLE`/`DISABLE` state clause.
4200 pub state: Option<ResourceGroupState>,
4201 /// Whether the trailing `FORCE` was written (`opt_force`, independent of
4202 /// [`state`](Self::state)).
4203 pub force: bool,
4204 /// Source location and node identity.
4205 pub meta: Meta,
4206}
4207
4208/// A MySQL `DROP RESOURCE GROUP <name> [FORCE]` resource-group drop (`sql_yacc.yy`
4209/// `drop_resource_group_stmt`), gated by
4210/// [`StatementDdlGates::resource_group`](crate::dialect::StatementDdlGates::resource_group).
4211///
4212/// Removes a single group by name; the optional trailing `FORCE` (`opt_force`) evicts running
4213/// threads.
4214#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4215#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4216#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4217pub struct DropResourceGroup {
4218 /// The resource-group name (`ident`).
4219 pub name: Ident,
4220 /// Whether the trailing `FORCE` was written (`opt_force`).
4221 pub force: bool,
4222 /// Source location and node identity.
4223 pub meta: Meta,
4224}
4225
4226/// The `TYPE` of a [`CreateResourceGroup`] (`sql_yacc.yy` `resource_group_types`): the fixed
4227/// `SYSTEM`/`USER` keyword set. A surface tag (no `meta` — its span rides the owning node); any
4228/// other word is `ER_PARSE_ERROR`.
4229#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4230#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4231#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4232pub enum ResourceGroupType {
4233 /// `SYSTEM` — a system resource group.
4234 System,
4235 /// `USER` — a user resource group.
4236 User,
4237}
4238
4239/// The `ENABLE`/`DISABLE` state clause of a resource-group `CREATE`/`ALTER`
4240/// (`opt_resource_group_enable_disable`). A surface tag (no `meta` — its span rides the owning
4241/// node).
4242#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4243#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4244#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4245pub enum ResourceGroupState {
4246 /// `ENABLE` — enable the group.
4247 Enable,
4248 /// `DISABLE` — disable the group.
4249 Disable,
4250}
4251
4252/// The `VCPU [=] <range> [, <range> …]` CPU-affinity clause of a resource-group `CREATE`/`ALTER`
4253/// (`sql_yacc.yy` `VCPU_SYM opt_equal vcpu_range_spec_list`). The `[=]` presence
4254/// ([`equals`](Self::equals)) and the non-empty [`ranges`](Self::ranges) list round-trip. The
4255/// separator between ranges is `opt_comma` (a comma *or* whitespace both parse), so the parse is
4256/// separator-insensitive and the list is rendered canonically comma-separated.
4257#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4258#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4259#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4260pub struct ResourceGroupVcpu {
4261 /// Whether the `VCPU =` spelling (vs bare `VCPU`) was written (`opt_equal`).
4262 pub equals: bool,
4263 /// The CPU-id range list, in source order; non-empty (`vcpu_range_spec_list`).
4264 pub ranges: ThinVec<VcpuRange>,
4265 /// Source location and node identity.
4266 pub meta: Meta,
4267}
4268
4269/// One `vcpu_num_or_range`: a single CPU id ([`end`](Self::end) `None`) or an inclusive
4270/// `start-end` range. Each bound is a `NUM` unsigned-integer literal; a value beyond the host CPU
4271/// count is a post-parse semantic reject (`ER_RESOURCE_GROUP_…`), not a syntax error, so the
4272/// parser accepts any integer bounds.
4273#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4274#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4275#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4276pub struct VcpuRange {
4277 /// The single id, or the inclusive-range start.
4278 pub start: Literal,
4279 /// The inclusive-range end (`start-end`), or `None` for a single id.
4280 pub end: Option<Literal>,
4281 /// Source location and node identity.
4282 pub meta: Meta,
4283}
4284
4285/// The `THREAD_PRIORITY [=] <n>` clause of a resource-group `CREATE`/`ALTER`
4286/// (`opt_resource_group_priority`). The value is a `signed_num` — an optionally negative integer
4287/// (`THREAD_PRIORITY = -5` grammar-accepts on mysql:8.4.10) — so the sign is carried apart from
4288/// the magnitude literal (`-` is a separate token). The `[=]` presence and the sign round-trip.
4289#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4290#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4291#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4292pub struct ResourceGroupThreadPriority {
4293 /// Whether the `THREAD_PRIORITY =` spelling (vs bare `THREAD_PRIORITY`) was written
4294 /// (`opt_equal`).
4295 pub equals: bool,
4296 /// Whether a leading `-` was written (`signed_num`'s negative branch).
4297 pub negative: bool,
4298 /// The magnitude — a `NUM` unsigned-integer literal.
4299 pub value: Literal,
4300 /// Source location and node identity.
4301 pub meta: Meta,
4302}
4303
4304/// A MySQL `ALTER {DATABASE | SCHEMA} [<name>] <option> …` schema-option change (`sql_yacc.yy`
4305/// `alter_database_stmt`), gated by
4306/// [`StatementDdlGates::alter_database_options`](crate::dialect::StatementDdlGates::alter_database_options).
4307///
4308/// A distinct node — and gate — from DuckDB's [`AlterDatabase`] `SET ALIAS` relocation: the two
4309/// share only the `ALTER DATABASE` head, and their grammars are disjoint (DuckDB requires a name
4310/// and a single `SET ALIAS TO`; MySQL's name is optional — `ALTER DATABASE CHARACTER SET utf8`
4311/// binds the default schema — and it takes a non-empty, repeatable list of charset/collation/
4312/// encryption/read-only [`AlterDatabaseOption`]s). Modelling them apart keeps each behaviour's
4313/// invariants in the type rather than as XOR-populated fields on one node. The `DATABASE`/
4314/// `SCHEMA` [`spelling`](Self::spelling) is an exact synonym recorded for round-trip.
4315#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4316#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4317#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4318pub struct AlterDatabaseOptions {
4319 /// Which of the synonymous `DATABASE`/`SCHEMA` keywords was written.
4320 pub spelling: DatabaseKeyword,
4321 /// The database name, or `None` for the unqualified `ALTER DATABASE <option> …` form that
4322 /// targets the session's default schema (`ident_or_empty`; a dotted `d.x` is
4323 /// `ER_PARSE_ERROR`, so a bare [`Ident`]).
4324 pub name: Option<Ident>,
4325 /// The option list, in source order; non-empty (`alter_database_options`).
4326 pub options: ThinVec<AlterDatabaseOption>,
4327 /// Source location and node identity.
4328 pub meta: Meta,
4329}
4330
4331/// One `ALTER {DATABASE | SCHEMA}` option (`sql_yacc.yy` `alter_database_option`). The `[=]`
4332/// ([`opt_equal`]) and, where admitted, the leading `[DEFAULT]` are recorded so the surface
4333/// form round-trips verbatim. The value grammars are fixed; a value outside the set is a parse
4334/// or bind reject on mysql:8.4.10 (`READ ONLY 2` is `ER_PARSE_ERROR`; `ENCRYPTION 'X'` binds
4335/// then rejects `ER_WRONG_VALUE`, so any string parses).
4336///
4337/// [`opt_equal`]: https://dev.mysql.com/doc/refman/8.4/en/alter-database.html
4338#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4339#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4340#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4341pub enum AlterDatabaseOption {
4342 /// `[DEFAULT] {CHARACTER SET | CHARSET} [=] <charset>` — the default character set. The
4343 /// charset name is `charset_name` (`ident_or_text`, or the reserved `BINARY`), folded to
4344 /// an [`Ident`].
4345 CharacterSet {
4346 /// Whether the leading `DEFAULT` was written.
4347 default: bool,
4348 /// Which of the `CHARACTER SET`/`CHARSET` synonyms was written.
4349 keyword: CharsetKeyword,
4350 /// Whether the optional `=` was written.
4351 equals: bool,
4352 /// The character-set name.
4353 charset: Ident,
4354 /// Source location and node identity.
4355 meta: Meta,
4356 },
4357 /// `[DEFAULT] COLLATE [=] <collation>` — the default collation. The name is
4358 /// `collation_name` (`ident_or_text`, or the reserved `BINARY`), folded to an [`Ident`].
4359 Collate {
4360 /// Whether the leading `DEFAULT` was written.
4361 default: bool,
4362 /// Whether the optional `=` was written.
4363 equals: bool,
4364 /// The collation name.
4365 collation: Ident,
4366 /// Source location and node identity.
4367 meta: Meta,
4368 },
4369 /// `[DEFAULT] ENCRYPTION [=] '<Y|N>'` — the default tablespace encryption. Any string
4370 /// literal parses; the `Y`/`N` restriction is a bind-time check (`ER_WRONG_VALUE`), so the
4371 /// value is held verbatim as a [`Literal`].
4372 Encryption {
4373 /// Whether the leading `DEFAULT` was written.
4374 default: bool,
4375 /// Whether the optional `=` was written.
4376 equals: bool,
4377 /// The encryption value (a string literal).
4378 value: Literal,
4379 /// Source location and node identity.
4380 meta: Meta,
4381 },
4382 /// `READ ONLY [=] {DEFAULT | 0 | 1}` — the schema read-only flag. Takes no leading
4383 /// `DEFAULT` prefix (unlike the charset/collation/encryption options — `DEFAULT READ ONLY`
4384 /// is `ER_PARSE_ERROR`); the value is a [`ReadOnlyValue`] `ternary_option`, and any other
4385 /// number (`READ ONLY 2`) is `ER_PARSE_ERROR`.
4386 ReadOnly {
4387 /// Whether the optional `=` was written.
4388 equals: bool,
4389 /// The read-only value.
4390 value: ReadOnlyValue,
4391 /// Source location and node identity.
4392 meta: Meta,
4393 },
4394}
4395
4396/// Which of the synonymous `CHARACTER SET`/`CHARSET` keywords introduced an
4397/// [`AlterDatabaseOption::CharacterSet`]. A surface tag (no `meta`) recording the spelling for
4398/// round-trip; the two are semantically identical.
4399#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4400#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4401#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4402pub enum CharsetKeyword {
4403 /// The two-word `CHARACTER SET` spelling.
4404 CharacterSet,
4405 /// The one-word `CHARSET` synonym.
4406 Charset,
4407}
4408
4409/// The value of an [`AlterDatabaseOption::ReadOnly`] — MySQL's `ternary_option`. A surface tag
4410/// (no `meta`); `0`/`1` are the boolean settings and `DEFAULT` inherits the global default.
4411/// Any other number is `ER_PARSE_ERROR` on mysql:8.4.10.
4412#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4413#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4414#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4415pub enum ReadOnlyValue {
4416 /// `DEFAULT` — inherit the global read-only default.
4417 Default,
4418 /// `0` — read-write.
4419 Off,
4420 /// `1` — read-only.
4421 On,
4422}
4423
4424/// The binary unit suffix on a MySQL storage-DDL size literal (`size_number`'s `IDENT_sys`
4425/// form): `K`, `M`, or `G`. MySQL folds `<digits><suffix>` to a byte count by shifting the
4426/// number left 10/20/30 bits, so the tag names the multiplier a downstream converter would
4427/// otherwise re-decode from the spelling. Case is *not* carried here — the exact source
4428/// letter (`16m` vs `16M`) round-trips from the enclosing [`SizeLiteral`]'s span.
4429#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4430#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4431#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4432pub enum SizeUnit {
4433 /// `K` — the value is shifted left 10 bits (× 1024).
4434 Kilo,
4435 /// `M` — the value is shifted left 20 bits (× 1024²).
4436 Mega,
4437 /// `G` — the value is shifted left 30 bits (× 1024³).
4438 Giga,
4439}
4440
4441/// A MySQL storage-DDL size literal (`size_number`): a byte count written either as a plain
4442/// integer (`134217728`) or as an integer with a binary unit suffix (`128M`, `2G`, `16k`).
4443///
4444/// MySQL lexes the suffixed form as a *single* `IDENT_sys` token (an unquoted identifier may
4445/// begin with digits), so the digits and the suffix letter must abut with no space — `16 M`
4446/// is a syntax error. This crate's tokenizer instead splits `16M` into a number token and an
4447/// adjacent word; the parser rejoins them by span adjacency, so [`meta`](Self::meta) spans the
4448/// whole literal and renders verbatim. [`unit`](Self::unit) records the multiplier as
4449/// structured metadata (`None` for a bare byte count); like a [`Literal`], the numeric value
4450/// and the exact spelling round-trip from the span, so the value carries only the surface tag
4451/// a consumer cannot otherwise recover.
4452#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4453#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4454#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4455pub struct SizeLiteral {
4456 /// The binary unit suffix, or `None` for a bare integer byte count; see [`SizeUnit`].
4457 pub unit: Option<SizeUnit>,
4458 /// Source location and node identity. Spans the whole literal (digits + any suffix),
4459 /// which is what round-trips the exact spelling.
4460 pub meta: Meta,
4461}
4462
4463/// Which storage-DDL option keyword carries a [`SizeLiteral`] value — the `size_number`-valued
4464/// members of MySQL's shared `ts_option_*` family, grouped here because they share the exact
4465/// `<KEYWORD> [=] <size>` shape and differ only in the keyword.
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 TablespaceSizeOption {
4470 /// `INITIAL_SIZE` — the file's initial size.
4471 InitialSize,
4472 /// `AUTOEXTEND_SIZE` — the auto-extension increment.
4473 AutoextendSize,
4474 /// `MAX_SIZE` — the file's maximum size.
4475 MaxSize,
4476 /// `EXTENT_SIZE` — the extent size.
4477 ExtentSize,
4478 /// `UNDO_BUFFER_SIZE` — the UNDO buffer size (logfile group).
4479 UndoBufferSize,
4480 /// `REDO_BUFFER_SIZE` — the REDO buffer size (logfile group).
4481 RedoBufferSize,
4482 /// `FILE_BLOCK_SIZE` — the file block size (tablespace).
4483 FileBlockSize,
4484}
4485
4486/// One option in a MySQL tablespace / logfile-group statement — a member of the server's shared
4487/// `ts_option_*` family (every one of these DDL statements builds one
4488/// `PT_alter_tablespace_option` list). The full universe is modelled here as one axis; each
4489/// statement context accepts only its own subset (e.g. `UNDO TABLESPACE` takes `ENGINE` alone,
4490/// `ALTER TABLESPACE` excludes `FILE_BLOCK_SIZE`), enforced by the parser rather than the type,
4491/// so an out-of-context option ends the list and surfaces as a clean parse error — matching the
4492/// live server's per-context grammar.
4493///
4494/// Non-generic: every value is a size literal, an integer/string [`Literal`], an [`Ident`], or a
4495/// flag — no expressions or extension nodes. Each option records whether its optional `=`
4496/// (`opt_equal`) was written so the surface form round-trips.
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 TablespaceOption {
4501 /// A `size_number`-valued option (`INITIAL_SIZE [=] 128M`, …); see [`TablespaceSizeOption`].
4502 Size {
4503 /// Which size option keyword this is.
4504 kind: TablespaceSizeOption,
4505 /// Whether the optional `=` was written between the keyword and the value.
4506 equals: bool,
4507 /// The size value.
4508 size: SizeLiteral,
4509 /// Source location and node identity.
4510 meta: Meta,
4511 },
4512 /// `NODEGROUP [=] <n>` — a plain integer node-group id (`real_ulong_num`, never suffixed).
4513 Nodegroup {
4514 /// Whether the optional `=` was written between the keyword and the value.
4515 equals: bool,
4516 /// The node-group id.
4517 value: Literal,
4518 /// Source location and node identity.
4519 meta: Meta,
4520 },
4521 /// `[STORAGE] ENGINE [=] <name>` — the storage engine (`ident_or_text`: a bare identifier or
4522 /// a quoted string, folded to an [`Ident`] whose quote style round-trips).
4523 Engine {
4524 /// Whether the optional leading `STORAGE` noise keyword was written (`opt_storage`).
4525 storage: bool,
4526 /// Whether the optional `=` was written between `ENGINE` and the value.
4527 equals: bool,
4528 /// The engine name.
4529 name: Ident,
4530 /// Source location and node identity.
4531 meta: Meta,
4532 },
4533 /// `WAIT` or `NO_WAIT` — the NDB completion-wait flag (`ts_option_wait`).
4534 Wait {
4535 /// `true` for `NO_WAIT`, `false` for `WAIT`.
4536 negated: bool,
4537 /// Source location and node identity.
4538 meta: Meta,
4539 },
4540 /// `COMMENT [=] '<text>'` — a free-text comment string.
4541 Comment {
4542 /// Whether the optional `=` was written between the keyword and the value.
4543 equals: bool,
4544 /// The comment string.
4545 value: Literal,
4546 /// Source location and node identity.
4547 meta: Meta,
4548 },
4549 /// `ENCRYPTION [=] '<y_or_n>'` — the tablespace encryption flag string (`'Y'`/`'N'`).
4550 Encryption {
4551 /// Whether the optional `=` was written between the keyword and the value.
4552 equals: bool,
4553 /// The encryption flag string.
4554 value: Literal,
4555 /// Source location and node identity.
4556 meta: Meta,
4557 },
4558 /// `ENGINE_ATTRIBUTE [=] '<json>'` — the engine-specific JSON attribute string.
4559 EngineAttribute {
4560 /// Whether the optional `=` was written between the keyword and the value.
4561 equals: bool,
4562 /// The JSON attribute string.
4563 value: Literal,
4564 /// Source location and node identity.
4565 meta: Meta,
4566 },
4567}
4568
4569/// A MySQL `CREATE [UNDO] TABLESPACE <name> [ADD DATAFILE '<f>'] [USE LOGFILE GROUP <lg>]
4570/// [<option>...]` statement (gated by
4571/// [`StatementDdlGates::tablespace_ddl`](crate::dialect::StatementDdlGates::tablespace_ddl)).
4572///
4573/// One node for both the InnoDB/NDB tablespace (`Sql_cmd_create_tablespace`) and the
4574/// `UNDO`-tablespace variant (`Sql_cmd_create_undo_tablespace`), distinguished by
4575/// [`undo`](Self::undo). The two differ only in the leading `UNDO` keyword, whether the datafile
4576/// is mandatory (it is for `UNDO`, optional otherwise), and the accepted option set (`UNDO`
4577/// admits `ENGINE` alone) — all enforced by the parser. The `USE LOGFILE GROUP` clause is
4578/// NDB-only and never appears on the `UNDO` form.
4579#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4580#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4581#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4582pub struct CreateTablespace {
4583 /// Whether this is the `CREATE UNDO TABLESPACE` variant.
4584 pub undo: bool,
4585 /// The tablespace name.
4586 pub name: Ident,
4587 /// `ADD DATAFILE '<file>'`. Mandatory (`Some`) for the `UNDO` form; optional otherwise.
4588 pub datafile: Option<Literal>,
4589 /// `USE LOGFILE GROUP <name>` (NDB, regular tablespace only).
4590 pub use_logfile_group: Option<Ident>,
4591 /// Options supplied in source order.
4592 pub options: ThinVec<TablespaceOption>,
4593 /// Source location and node identity.
4594 pub meta: Meta,
4595}
4596
4597/// The undo-tablespace access state set by `ALTER UNDO TABLESPACE <name> SET {ACTIVE | INACTIVE}`
4598/// (`undo_tablespace_state`).
4599#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4600#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4601#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4602pub enum UndoTablespaceState {
4603 /// `SET ACTIVE`.
4604 Active,
4605 /// `SET INACTIVE`.
4606 Inactive,
4607}
4608
4609/// A MySQL `ALTER [UNDO] TABLESPACE <name> <action>` statement (gated by
4610/// [`StatementDdlGates::tablespace_ddl`](crate::dialect::StatementDdlGates::tablespace_ddl)).
4611///
4612/// The four regular productions (`ADD`/`DROP DATAFILE`, `RENAME TO`, a bare option list) and the
4613/// `UNDO` production (`SET {ACTIVE | INACTIVE}`) share the `ALTER [UNDO] TABLESPACE <name>` head
4614/// and are unified under [`AlterTablespaceAction`]. The action itself distinguishes the `UNDO`
4615/// form (only it carries [`SetState`](AlterTablespaceAction::SetState)), so no separate `undo`
4616/// flag is needed.
4617#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4618#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4619#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4620pub struct AlterTablespace {
4621 /// The tablespace name.
4622 pub name: Ident,
4623 /// The change applied to the tablespace.
4624 pub action: AlterTablespaceAction,
4625 /// Source location and node identity.
4626 pub meta: Meta,
4627}
4628
4629/// The change an [`AlterTablespace`] applies.
4630#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4631#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4632#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4633pub enum AlterTablespaceAction {
4634 /// `ADD DATAFILE '<f>' [<option>...]`.
4635 AddDatafile {
4636 /// The datafile path.
4637 datafile: Literal,
4638 /// The trailing alter-tablespace options in source order.
4639 options: ThinVec<TablespaceOption>,
4640 /// Source location and node identity.
4641 meta: Meta,
4642 },
4643 /// `DROP DATAFILE '<f>' [<option>...]`.
4644 DropDatafile {
4645 /// The datafile path.
4646 datafile: Literal,
4647 /// The trailing alter-tablespace options in source order.
4648 options: ThinVec<TablespaceOption>,
4649 /// Source location and node identity.
4650 meta: Meta,
4651 },
4652 /// `RENAME TO <new_name>` — takes no options (a trailing option is a parse error).
4653 Rename {
4654 /// The new tablespace name.
4655 new_name: Ident,
4656 /// Source location and node identity.
4657 meta: Meta,
4658 },
4659 /// A bare (non-empty) option list — `ALTER TABLESPACE <name> <option> [, <option>]...`
4660 /// with no `ADD`/`DROP`/`RENAME` lead.
4661 Options {
4662 /// The options in source order (at least one).
4663 options: ThinVec<TablespaceOption>,
4664 /// Source location and node identity.
4665 meta: Meta,
4666 },
4667 /// `SET {ACTIVE | INACTIVE} [<option>...]` — the `ALTER UNDO TABLESPACE` form (the trailing
4668 /// options are the `UNDO` subset, `ENGINE` alone).
4669 SetState {
4670 /// The new access state.
4671 state: UndoTablespaceState,
4672 /// The trailing undo-tablespace options in source order.
4673 options: ThinVec<TablespaceOption>,
4674 /// Source location and node identity.
4675 meta: Meta,
4676 },
4677}
4678
4679/// A MySQL `DROP [UNDO] TABLESPACE <name> [<option>...]` statement (gated by
4680/// [`StatementDdlGates::tablespace_ddl`](crate::dialect::StatementDdlGates::tablespace_ddl)).
4681///
4682/// One node for `DROP TABLESPACE` (`Sql_cmd_drop_tablespace`) and `DROP UNDO TABLESPACE`
4683/// (`Sql_cmd_drop_undo_tablespace`), distinguished by [`undo`](Self::undo): the regular form
4684/// accepts `ENGINE`/`WAIT` options, the `UNDO` form `ENGINE` alone (parser-enforced).
4685#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4686#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4687#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4688pub struct DropTablespace {
4689 /// Whether this is the `DROP UNDO TABLESPACE` variant.
4690 pub undo: bool,
4691 /// The tablespace name.
4692 pub name: Ident,
4693 /// Options supplied in source order.
4694 pub options: ThinVec<TablespaceOption>,
4695 /// Source location and node identity.
4696 pub meta: Meta,
4697}
4698
4699/// A MySQL `CREATE LOGFILE GROUP <name> ADD UNDOFILE '<f>' [<option>...]` statement (NDB; gated
4700/// by
4701/// [`StatementDdlGates::logfile_group_ddl`](crate::dialect::StatementDdlGates::logfile_group_ddl)).
4702///
4703/// The `ADD UNDOFILE` clause is mandatory (a bare `CREATE LOGFILE GROUP <name>` is a syntax
4704/// error). The option set is the logfile-group subset of the shared `ts_option_*` family
4705/// (`INITIAL_SIZE`, `UNDO_BUFFER_SIZE`, `REDO_BUFFER_SIZE`, `NODEGROUP`, `ENGINE`, `WAIT`,
4706/// `COMMENT`), parser-enforced.
4707#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4708#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4709#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4710pub struct CreateLogfileGroup {
4711 /// The logfile-group name.
4712 pub name: Ident,
4713 /// The mandatory `ADD UNDOFILE '<file>'` path.
4714 pub undofile: Literal,
4715 /// Options supplied in source order.
4716 pub options: ThinVec<TablespaceOption>,
4717 /// Source location and node identity.
4718 pub meta: Meta,
4719}
4720
4721/// A MySQL `ALTER LOGFILE GROUP <name> ADD UNDOFILE '<f>' [<option>...]` statement (NDB; gated by
4722/// [`StatementDdlGates::logfile_group_ddl`](crate::dialect::StatementDdlGates::logfile_group_ddl)).
4723///
4724/// Like [`CreateLogfileGroup`] the `ADD UNDOFILE` clause is mandatory; the accepted option set is
4725/// the narrower alter subset (`INITIAL_SIZE`, `ENGINE`, `WAIT`), parser-enforced.
4726#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4727#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4728#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4729pub struct AlterLogfileGroup {
4730 /// The logfile-group name.
4731 pub name: Ident,
4732 /// The mandatory `ADD UNDOFILE '<file>'` path.
4733 pub undofile: Literal,
4734 /// Options supplied in source order.
4735 pub options: ThinVec<TablespaceOption>,
4736 /// Source location and node identity.
4737 pub meta: Meta,
4738}
4739
4740/// A MySQL `DROP LOGFILE GROUP <name> [<option>...]` statement (NDB; gated by
4741/// [`StatementDdlGates::logfile_group_ddl`](crate::dialect::StatementDdlGates::logfile_group_ddl)).
4742///
4743/// The option set is `ENGINE`/`WAIT` (`opt_drop_ts_options`, shared with `DROP TABLESPACE`).
4744#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4745#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
4746#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
4747pub struct DropLogfileGroup {
4748 /// The logfile-group name.
4749 pub name: Ident,
4750 /// Options supplied in source order.
4751 pub options: ThinVec<TablespaceOption>,
4752 /// Source location and node identity.
4753 pub meta: Meta,
4754}