Skip to main content

uqa_sql/
ast.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Internal SQL AST. Lifts the relevant subset of the `libpg_query`
8//! protobuf tree into a Rust enum the compiler walks. Statements not
9//! yet supported parse cleanly but compile to
10//! [`crate::SQLError::Unsupported`].
11
12use serde::{Deserialize, Serialize};
13
14mod constraints;
15mod cte;
16mod domains;
17mod events;
18mod expressions;
19mod from;
20mod function_binding;
21mod indexes;
22mod interval;
23mod locking;
24mod namespaces;
25mod ranges;
26mod relation_hierarchy;
27mod relation_lifecycle;
28mod routine_security;
29mod routines;
30mod sequence;
31mod types;
32
33pub use constraints::*;
34pub use cte::*;
35pub use domains::*;
36pub use events::*;
37pub use expressions::*;
38pub use from::*;
39pub use function_binding::*;
40pub use indexes::*;
41pub use interval::*;
42pub use locking::*;
43pub use namespaces::*;
44pub use ranges::*;
45pub use relation_hierarchy::*;
46pub use relation_lifecycle::*;
47pub use routine_security::*;
48pub use routines::*;
49pub use sequence::*;
50pub use types::*;
51
52const fn default_include_descendants() -> bool {
53    true
54}
55
56const fn default_true() -> bool {
57    true
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
61pub enum GeneratedColumnKind {
62    Virtual,
63    Stored,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct GeneratedColumn {
68    pub kind: GeneratedColumnKind,
69    pub expression: Box<Expr>,
70    #[serde(default, skip_serializing_if = "Vec::is_empty")]
71    pub function_dependencies: Vec<GeneratedFunctionDependency>,
72}
73
74#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
75pub struct IndexColumnOrder {
76    pub descending: bool,
77    pub nulls_first: bool,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct CreateIndex {
82    #[serde(default)]
83    pub included_columns: Vec<String>,
84    #[serde(default)]
85    pub column_order: Vec<IndexColumnOrder>,
86    #[serde(default)]
87    pub predicate: Option<Box<Expr>>,
88    pub name: Option<String>,
89    pub table: String,
90    /// `gin`, `btree`, `ivf`, `hnsw`, `rtree`, ...
91    pub access_method: String,
92    pub columns: Vec<IndexKey>,
93    #[serde(default)]
94    pub unique: bool,
95    #[serde(default)]
96    pub nulls_not_distinct: bool,
97    /// `CREATE INDEX IF NOT EXISTS`.
98    pub if_not_exists: bool,
99    /// Storage parameters from `WITH (k = v, ...)`. Stored verbatim;
100    /// known keys (`analyzer`, `lists`, `probes`, ...)
101    /// are interpreted by the engine.
102    pub options: Vec<(String, String)>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct DropStmt {
107    pub kind: DropKind,
108    pub names: Vec<String>,
109    pub if_exists: bool,
110    pub cascade: bool,
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
114pub enum DropKind {
115    Table,
116    ForeignTable,
117    Index,
118    View,
119    MaterializedView,
120    Schema,
121    Sequence,
122    Domain,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct AlterTableStmt {
127    pub table: String,
128    /// Local SQL relation identifier used while binding new or replaced generation expressions.
129    pub qualifier: String,
130    pub if_exists: bool,
131    /// Whether the target omitted `ONLY` and therefore allows recursive ALTER behavior.
132    #[serde(default = "default_true")]
133    pub recurse: bool,
134    pub actions: Vec<AlterTableAction>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
138#[expect(
139    clippy::large_enum_variant,
140    reason = "preserves the stable AST serde shape"
141)]
142pub enum AlterTableAction {
143    AddInheritance {
144        parent: String,
145    },
146    DropInheritance {
147        parent: String,
148    },
149    AttachPartition {
150        partition: String,
151        bound: PartitionBound,
152    },
153    DetachPartition {
154        partition: String,
155        concurrently: bool,
156        finalize: bool,
157    },
158    AddColumn {
159        column: ColumnDef,
160        if_not_exists: bool,
161    },
162    AddKeyConstraint {
163        constraint: TableKeyConstraint,
164    },
165    AddCheckConstraint {
166        constraint: TableCheck,
167    },
168    AddForeignKeyConstraint {
169        constraint: ForeignKey,
170    },
171    AddNotNullConstraint {
172        name: Option<String>,
173        column: String,
174        validated: bool,
175        no_inherit: bool,
176    },
177    ValidateConstraint {
178        name: String,
179    },
180    AlterConstraint {
181        name: String,
182        enforceability: Option<bool>,
183        deferrability: Option<(bool, bool)>,
184        no_inherit: Option<bool>,
185    },
186    DropConstraint {
187        name: String,
188        if_exists: bool,
189        cascade: bool,
190    },
191    DropColumn {
192        name: String,
193        if_exists: bool,
194        cascade: bool,
195    },
196    RenameColumn {
197        from: String,
198        to: String,
199    },
200    RenameTable {
201        to: String,
202    },
203    RenameTrigger {
204        from: String,
205        to: String,
206    },
207    RenameConstraint {
208        from: String,
209        to: String,
210    },
211    RenameRule {
212        from: String,
213        to: String,
214    },
215    SetPersistence {
216        persistence: RelationPersistence,
217    },
218    ChangeOwner {
219        owner: String,
220    },
221    SetSchema {
222        schema: String,
223    },
224    SetTriggerEnableMode {
225        name: Option<String>,
226        user_only: bool,
227        mode: EventEnableMode,
228    },
229    SetRuleEnableMode {
230        name: String,
231        mode: EventEnableMode,
232    },
233    SetDefault {
234        name: String,
235        default: Expr,
236    },
237    DropDefault {
238        name: String,
239    },
240    SetExpression {
241        name: String,
242        expression: Expr,
243    },
244    DropExpression {
245        name: String,
246    },
247    SetNotNull {
248        name: String,
249    },
250    DropNotNull {
251        name: String,
252    },
253    AlterColumnType {
254        name: String,
255        ty: ColumnType,
256        #[serde(default, skip_serializing_if = "Option::is_none")]
257        using: Option<Expr>,
258    },
259}
260
261#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
262pub struct InsertStmt {
263    pub table: String,
264    /// Whether `table` is a stored catalog identity rather than a name to resolve in the executing session.
265    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
266    pub target_relation_bound: bool,
267    /// SQL-visible target relation name: explicit alias, otherwise the local relation name.
268    pub target_qualifier: String,
269    #[serde(default = "default_include_descendants")]
270    pub include_descendants: bool,
271    pub columns: Vec<String>,
272    /// Common table expressions defined with `WITH [RECURSIVE] ...`.
273    pub with: Vec<CTE>,
274    /// Inline `VALUES (...) (...)` rows. `DEFAULT VALUES` is represented by one empty row; the vector itself is empty only for `INSERT ... SELECT`, whose query is in `select_source`.
275    pub rows: Vec<Vec<ValueExpr>>,
276    /// Populated when the statement is `INSERT INTO t (...) SELECT ...`.
277    /// The engine materialises the inner select first and then writes
278    /// each row through the standard INSERT path.
279    pub select_source: Option<Box<SelectStmt>>,
280    /// `ON CONFLICT (...) DO ...` clause. `None` for plain
281    /// `INSERT INTO ... VALUES ...` without conflict handling.
282    pub on_conflict: Option<OnConflict>,
283    /// `RETURNING ...` projection list. Empty when absent.
284    pub returning: Vec<Projection>,
285    /// `PostgreSQL` 18 names for the old and new row images visible to
286    /// `RETURNING`. The defaults are `old` and `new`.
287    pub returning_aliases: ReturningAliases,
288}
289
290#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
291pub struct ReturningAliases {
292    pub old: String,
293    pub new: String,
294    #[serde(default)]
295    pub old_explicit: bool,
296    #[serde(default)]
297    pub new_explicit: bool,
298}
299
300impl Default for ReturningAliases {
301    fn default() -> Self {
302        Self {
303            old: "old".into(),
304            new: "new".into(),
305            old_explicit: false,
306            new_explicit: false,
307        }
308    }
309}
310
311#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
312pub struct OnConflict {
313    #[serde(default)]
314    pub predicate: Option<Box<Expr>>,
315    #[serde(default)]
316    pub constraint: Option<String>,
317    /// Conflict target columns parsed from the `ON CONFLICT (col, ...)`
318    /// list. Empty when the clause uses `ON CONFLICT DO NOTHING` with
319    /// no target.
320    pub conflict_columns: Vec<String>,
321    #[serde(default)]
322    pub expressions: Vec<Expr>,
323    pub action: OnConflictAction,
324}
325
326#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
327pub enum OnConflictAction {
328    /// `DO NOTHING` -- skip conflicting rows silently.
329    Nothing,
330    /// `DO UPDATE SET col = expr [, ...] [WHERE pred]` -- apply the
331    /// listed assignments to the existing row when the conflict
332    /// target matches.
333    Update {
334        assignments: Vec<(String, Expr)>,
335        r#where: Option<Box<Expr>>,
336    },
337}
338
339#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
340pub struct SelectStmt {
341    pub projections: Vec<Projection>,
342    /// Rows owned by a `VALUES` query body. `PostgreSQL` represents `VALUES`
343    /// through the same query node used for `SELECT`, so nested query bodies
344    /// such as CTEs and set-operation branches must retain them here.
345    #[serde(default, skip_serializing_if = "Vec::is_empty")]
346    pub values: Vec<Vec<Expr>>,
347    pub from: Option<FromClause>,
348    pub r#where: Option<Expr>,
349    pub group_by: Vec<Expr>,
350    /// Expanded GROUPING SETS / ROLLUP / CUBE specification. When
351    /// non-empty the executor produces one row per grouping set;
352    /// `group_by` is treated as a single grouping set in that case.
353    /// Each inner Vec lists the grouping-key expressions for that
354    /// set (an empty inner Vec means the global grand-total bucket).
355    pub grouping_sets: Vec<Vec<Expr>>,
356    /// `GROUP BY DISTINCT` -- remove duplicate grouping sets after grouping expressions have been resolved against their input types.
357    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
358    pub group_distinct: bool,
359    /// `HAVING <expr>`. Evaluated against each aggregated row and
360    /// filters out groups whose predicate is falsy. Mirrors PG's
361    /// `havingClause`.
362    pub having: Option<Expr>,
363    pub order_by: Vec<OrderBy>,
364    /// `LIMIT <expr>`. Stored as an expression so `LIMIT $1` and any
365    /// other constant-folding integer expression resolves at execute
366    /// time. `None` means no LIMIT clause was supplied.
367    pub limit: Option<Expr>,
368    /// `FETCH ... WITH TIES`. The row-count expression remains in [`Self::limit`]; this flag extends the boundary through every row whose complete `ORDER BY` key equals the last requested row.
369    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
370    pub with_ties: bool,
371    /// `OFFSET <expr>`. Same shape as [`SelectStmt::limit`].
372    pub offset: Option<Expr>,
373    /// Common table expressions defined with `WITH [RECURSIVE] ...`.
374    pub with: Vec<CTE>,
375    /// Optional set operation: `Some` for UNION / INTERSECT / EXCEPT.
376    /// Parsed statements carry both operands in [`SetOp`]; `left` remains
377    /// optional only for backward-compatible deserialization.
378    pub set_op: Option<Box<SetOp>>,
379    /// `SELECT DISTINCT` -- de-duplicate the final result rows. Set by
380    /// the compiler whenever the parsed `distinct_clause` is non-empty.
381    pub distinct: bool,
382    /// `SELECT DISTINCT ON (<expr>, ...)` keys. Empty for plain
383    /// `SELECT DISTINCT`.
384    pub distinct_on: Vec<Expr>,
385    /// `FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE }` row-locking clauses, in source order. Empty when the query does not lock rows.
386    #[serde(default, skip_serializing_if = "Vec::is_empty")]
387    pub locking: Vec<LockingClause>,
388}
389
390#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
391pub struct SetOp {
392    pub kind: SetOpKind,
393    pub all: bool,
394    /// Explicit left-hand subtree. Parsed set operations are left-associative,
395    /// so a chain such as `a UNION b UNION c` carries `(a UNION b)` here
396    /// instead of flattening it back to only `a`.
397    #[serde(default, skip_serializing_if = "Option::is_none")]
398    pub left: Option<Box<SelectStmt>>,
399    pub right: SelectStmt,
400    /// `ORDER BY` applied to the combined `lhs <op> rhs` result.
401    /// Distinct from the LHS / RHS branches' own `ORDER BY`.
402    pub combined_order_by: Vec<OrderBy>,
403    /// `LIMIT` applied to the combined result. `None` means no
404    /// outer LIMIT clause was supplied.
405    pub combined_limit: Option<Expr>,
406    /// Whether the combined set-operation limit is `FETCH ... WITH TIES`.
407    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
408    pub combined_with_ties: bool,
409    /// `OFFSET` applied to the combined result.
410    pub combined_offset: Option<Expr>,
411}
412
413#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
414pub enum SetOpKind {
415    Union,
416    Intersect,
417    Except,
418}
419
420/// `DISCARD` target. Mirrors `PostgreSQL`'s `DiscardMode`.
421#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
422pub enum DiscardTarget {
423    All,
424    Plans,
425    Sequences,
426    Temp,
427}
428
429#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
430pub struct UpdateStmt {
431    pub table: String,
432    /// Whether `table` is a stored catalog identity rather than a name to resolve in the executing session.
433    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
434    pub target_relation_bound: bool,
435    pub target_qualifier: String,
436    #[serde(default = "default_include_descendants")]
437    pub include_descendants: bool,
438    pub assignments: Vec<(String, Expr)>,
439    pub r#where: Option<Expr>,
440    /// Common table expressions defined with `WITH [RECURSIVE] ...`.
441    pub with: Vec<CTE>,
442    /// `UPDATE t SET ... FROM other [JOIN ...]` -- the engine joins
443    /// the target with this clause before applying the assignments.
444    pub from: Option<FromClause>,
445    /// `RETURNING ...` projection list. Empty when absent.
446    pub returning: Vec<Projection>,
447    pub returning_aliases: ReturningAliases,
448}
449
450#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
451pub struct DeleteStmt {
452    pub table: String,
453    /// Whether `table` is a stored catalog identity rather than a name to resolve in the executing session.
454    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
455    pub target_relation_bound: bool,
456    pub target_qualifier: String,
457    #[serde(default = "default_include_descendants")]
458    pub include_descendants: bool,
459    pub r#where: Option<Expr>,
460    /// Common table expressions defined with `WITH [RECURSIVE] ...`.
461    pub with: Vec<CTE>,
462    /// `DELETE FROM t USING other [JOIN ...]` -- the engine joins
463    /// the target with this clause and deletes target rows whose
464    /// joined image satisfies WHERE.
465    pub using: Option<FromClause>,
466    /// `RETURNING ...` projection list. Empty when absent.
467    pub returning: Vec<Projection>,
468    pub returning_aliases: ReturningAliases,
469}
470
471#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
472pub struct SetConstraintName {
473    pub catalog: Option<String>,
474    pub schema: Option<String>,
475    pub name: String,
476}
477
478/// One parser-normalized `VACUUM` option. Keeping the parsed value in the SQL AST lets execution enforce `PostgreSQL`'s transaction-block error before validating command options.
479#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
480pub struct VacuumOption {
481    pub name: String,
482    pub value: Option<VacuumOptionValue>,
483}
484
485#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
486pub enum VacuumOptionValue {
487    Boolean(bool),
488    Integer(i32),
489    String(String),
490}
491
492/// One relation (and optional ANALYZE column list) named by `VACUUM`.
493#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
494pub struct VacuumTarget {
495    pub catalog: Option<String>,
496    pub table: String,
497    #[serde(default = "default_include_descendants")]
498    pub include_descendants: bool,
499    #[serde(default, skip_serializing_if = "Vec::is_empty")]
500    pub columns: Vec<String>,
501}
502
503#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
504pub struct VacuumStmt {
505    #[serde(default, skip_serializing_if = "Vec::is_empty")]
506    pub options: Vec<VacuumOption>,
507    #[serde(default, skip_serializing_if = "Vec::is_empty")]
508    pub targets: Vec<VacuumTarget>,
509}
510
511#[derive(Debug, Clone, Serialize, Deserialize)]
512pub enum Statement {
513    CreateDomain(CreateDomain),
514    CreateTable(CreateTable),
515    CreateTableIfNotExists(DeferredCreateTable),
516    CreateIndex(CreateIndex),
517    Insert(InsertStmt),
518    /// `SelectStmt` is the largest variant by far (CTEs + set-ops + n-ary
519    /// expression trees), so we box it to keep the enum's stack footprint
520    /// proportional to the smaller variants.
521    Select(Box<SelectStmt>),
522    Update(UpdateStmt),
523    Delete(DeleteStmt),
524    Drop(DropStmt),
525    AlterTable(AlterTableStmt),
526    AlterForeignTable(AlterForeignTableStmt),
527    AlterView(AlterViewStmt),
528    /// `CREATE [OR REPLACE] VIEW name [(column_name, ...)] AS SELECT ...`. The body is the underlying `SelectStmt`; views are materialised lazily on every reference (no row caching).
529    CreateView {
530        name: String,
531        #[serde(default)]
532        column_names: Vec<String>,
533        body: Box<SelectStmt>,
534        or_replace: bool,
535        #[serde(default)]
536        persistence: RelationPersistence,
537        /// Validated `PostgreSQL` view reloptions in declaration order.
538        #[serde(default, skip_serializing_if = "Vec::is_empty")]
539        options: Vec<(String, String)>,
540    },
541    /// `CREATE MATERIALIZED VIEW ... AS SELECT ... [WITH [NO] DATA]`.
542    CreateMaterializedView {
543        name: String,
544        #[serde(default)]
545        column_names: Vec<String>,
546        #[serde(default)]
547        if_not_exists: bool,
548        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
549        with_no_data: bool,
550        #[serde(default, skip_serializing_if = "Vec::is_empty")]
551        options: Vec<(String, String)>,
552        body: Box<SelectStmt>,
553    },
554    /// `REFRESH MATERIALIZED VIEW [CONCURRENTLY] name [WITH [NO] DATA]`.
555    RefreshMaterializedView {
556        name: String,
557        concurrently: bool,
558        with_no_data: bool,
559    },
560    /// `CREATE SCHEMA [IF NOT EXISTS] [name] [AUTHORIZATION role]`; an omitted name binds to the resolved owner.
561    CreateSchema {
562        name: Option<String>,
563        if_not_exists: bool,
564        #[serde(default, skip_serializing_if = "Option::is_none")]
565        authorization: Option<SchemaAuthorization>,
566    },
567    AlterSchemaOwner {
568        name: String,
569        new_owner: String,
570    },
571    /// `NOTIFY channel [, 'payload']` queues one asynchronous notification for delivery when the outer transaction commits.
572    Notify {
573        channel: String,
574        payload: String,
575    },
576    /// `LISTEN channel` transactionally subscribes the current SQL session.
577    Listen {
578        channel: String,
579    },
580    /// `UNLISTEN channel | *` transactionally removes one or every subscription. `None` represents `*`.
581    Unlisten {
582        channel: Option<String>,
583    },
584    /// `SET <name> [TO|=] <value>` - runtime parameter assignment.
585    /// The engine gives `search_path` resolution semantics and stores other
586    /// parameters in the logical session for subsequent `SHOW` statements.
587    SetVariable {
588        name: String,
589        value: String,
590        #[serde(default)]
591        local: bool,
592        #[serde(default)]
593        is_default: bool,
594    },
595    /// `RESET <name>` restores one runtime parameter to its session default.
596    ResetVariable {
597        name: String,
598    },
599    /// `RESET ALL` restores every resettable runtime parameter.
600    ResetAllVariables,
601    /// `SET CONSTRAINTS { ALL | name [, ...] } { DEFERRED | IMMEDIATE }`. An empty constraint list represents `ALL`; qualified names retain their SQL spelling so execution can apply schema-search semantics.
602    SetConstraints {
603        constraints: Vec<SetConstraintName>,
604        deferred: bool,
605    },
606    /// `SHOW <variable>` - return the runtime parameter as one
607    /// `(name -> value)` row.
608    ShowVariable {
609        name: String,
610    },
611    /// `DISCARD [ALL|PLANS|SEQUENCES|TEMP|TEMPORARY]` - clear session state.
612    /// The engine resets session variables, prepared statements, sequence state, and the current session's temporary relations as requested.
613    Discard {
614        target: DiscardTarget,
615    },
616    /// `LOAD 'library'` - load a shared library into the session. The
617    /// engine embeds its extension surface, so libraries it provides
618    /// natively (Apache AGE) load as no-ops and unknown libraries fail
619    /// like a missing `$libdir` file.
620    Load {
621        library: String,
622    },
623    /// `EXPLAIN ...`. Carries the inner statement so the engine can
624    /// emit the planner output.
625    Explain {
626        analyze: bool,
627        verbose: bool,
628        format: Option<String>,
629        body: Box<Statement>,
630    },
631    /// `ANALYZE [table]`. The engine refreshes per-column statistics
632    /// for cardinality estimation; the AST simply records the target.
633    Analyze {
634        table: Option<String>,
635    },
636    /// `VACUUM [options] [relations]`. Execution enforces `PostgreSQL`'s transaction-block restriction before validating options and dispatching storage maintenance.
637    Vacuum(VacuumStmt),
638    /// `TRUNCATE TABLE t1, t2 ...`. Wipes the listed table hierarchies unless
639    /// a target uses `ONLY`.
640    Truncate {
641        tables: Vec<TruncateTarget>,
642        cascade: bool,
643        #[serde(default)]
644        restart_identity: bool,
645    },
646    /// `BEGIN` / `COMMIT` / `ROLLBACK` / `SAVEPOINT name`.
647    Transaction(TransactionStmt),
648    /// `DECLARE name [BINARY] [SCROLL] CURSOR [WITH HOLD] FOR query`.
649    DeclareCursor(DeclareCursorStmt),
650    /// `FETCH` or `MOVE` over a named SQL cursor.
651    FetchCursor(FetchCursorStmt),
652    /// `CLOSE name` or `CLOSE ALL`. `None` represents `ALL`.
653    CloseCursor {
654        name: Option<String>,
655    },
656    /// `CREATE SEQUENCE name [START n] [INCREMENT n]`.
657    CreateSequence(CreateSequence),
658    /// `ALTER SEQUENCE name [RESTART [WITH n]] [INCREMENT [BY] n]
659    /// [START [WITH] n]`.
660    AlterSequence(AlterSequence),
661    /// `CREATE TABLE name AS SELECT ...`.
662    CreateTableAs {
663        name: String,
664        if_not_exists: bool,
665        #[serde(default, skip_serializing_if = "Vec::is_empty")]
666        column_names: Vec<String>,
667        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
668        with_no_data: bool,
669        #[serde(default)]
670        persistence: RelationPersistence,
671        #[serde(default)]
672        on_commit: OnCommitAction,
673        body: Box<SelectStmt>,
674    },
675    /// `PREPARE name AS <inner>`.
676    Prepare {
677        name: String,
678        #[serde(default)]
679        parameter_types: Vec<ColumnType>,
680        body: Box<Statement>,
681    },
682    /// `EXECUTE name (param1, param2, ...)`.
683    Execute {
684        name: String,
685        params: Vec<Expr>,
686    },
687    /// `DEALLOCATE name | DEALLOCATE ALL`. `None` means ALL.
688    Deallocate {
689        name: Option<String>,
690    },
691    /// `SELECT * FROM (VALUES ...) [AS alias]` -- a standalone VALUES
692    /// statement (also reachable from a SET-OP body).
693    Values {
694        rows: Vec<Vec<Expr>>,
695    },
696    /// `CREATE SERVER name FOREIGN DATA WRAPPER type OPTIONS (...)`.
697    CreateForeignServer(CreateForeignServer),
698    /// `CREATE FOREIGN TABLE name (...) SERVER server OPTIONS (...)`.
699    CreateForeignTable(CreateForeignTable),
700    /// `CREATE FOREIGN TABLE IF NOT EXISTS` retains its raw-parser declaration until execution can check the shared relation namespace.
701    CreateForeignTableIfNotExists(DeferredCreateForeignTable),
702    /// `MERGE INTO target USING source ON cond WHEN MATCHED THEN ...
703    /// WHEN NOT MATCHED THEN ...`. SQL:2003 conditional UPSERT.
704    Merge(MergeStmt),
705    /// `CREATE [OR REPLACE] FUNCTION | PROCEDURE ...`. Boxed: the
706    /// definition (parameters + body source) dwarfs other variants.
707    CreateFunction(Box<CreateFunction>),
708    /// `DROP FUNCTION | PROCEDURE [IF EXISTS] name[(args)] [, ...]`.
709    DropFunction(DropFunctionStmt),
710    /// `ALTER FUNCTION | PROCEDURE | ROUTINE name[(input_types)]` volatility and null-input attributes.
711    AlterRoutine(AlterRoutineStmt),
712    AlterRoutineOwner(AlterRoutineOwnerStmt),
713    RenameRoutine(RenameRoutineStmt),
714    GrantRoutine(GrantRoutineStmt),
715    GrantTable(GrantTableStmt),
716    GrantSequence(GrantSequenceStmt),
717    GrantDatabase(GrantDatabaseStmt),
718    GrantSchema(GrantSchemaStmt),
719    GrantRole(GrantRoleStmt),
720    CreateRole(CreateRoleStmt),
721    AlterRole(AlterRoleStmt),
722    DropRole(DropRoleStmt),
723    /// `CREATE [OR REPLACE] TRIGGER ... ON relation`.
724    CreateTrigger(CreateTrigger),
725    /// `DROP TRIGGER [IF EXISTS] name ON relation`.
726    DropTrigger(DropTrigger),
727    /// `CREATE [OR REPLACE] RULE ... ON relation`.
728    CreateRule(CreateRule),
729    /// `DROP RULE [IF EXISTS] name ON relation`.
730    DropRule(DropRule),
731    /// `DO [LANGUAGE lang] $$ ... $$` - anonymous code block.
732    DoBlock {
733        language: String,
734        body: String,
735    },
736    /// `CALL proc(args)` - procedure invocation. `OUT` / `INOUT`
737    /// parameters shape the result row.
738    Call {
739        name: String,
740        args: Vec<Expr>,
741    },
742}
743
744#[derive(Debug, Clone, Serialize, Deserialize)]
745pub struct TruncateTarget {
746    pub table: String,
747    #[serde(default = "default_include_descendants")]
748    pub include_descendants: bool,
749}
750
751#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
752pub struct MergeTargetColumnBinding {
753    pub object_id: [u8; 16],
754    /// Domain identities used by non-DEFAULT assignment coercions, including after target deletion.
755    #[serde(default, skip_serializing_if = "std::collections::BTreeSet::is_empty")]
756    pub domain_dependencies: std::collections::BTreeSet<u32>,
757}
758
759#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
760pub struct MergeStmt {
761    #[serde(default)]
762    pub with: Vec<CTE>,
763    pub target: String,
764    pub target_qualifier: String,
765    pub target_alias: Option<String>,
766    /// Creation-bound write targets in a stored body. Removed identities retain their expressions and dependencies but receive no writes.
767    #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
768    pub target_column_bindings: std::collections::BTreeMap<String, MergeTargetColumnBinding>,
769    #[serde(default = "default_include_descendants")]
770    pub include_descendants: bool,
771    pub source: FromClause,
772    pub join_condition: Expr,
773    pub when_clauses: Vec<MergeWhen>,
774    /// `MERGE ... RETURNING ...` projection list. Empty when absent.
775    pub returning: Vec<Projection>,
776    pub returning_aliases: ReturningAliases,
777}
778
779#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
780pub enum MergeWhen {
781    /// `WHEN MATCHED [AND <cond>] THEN UPDATE SET ...`.
782    UpdateMatched {
783        condition: Option<Expr>,
784        assignments: Vec<(String, Expr)>,
785    },
786    /// `WHEN MATCHED [AND <cond>] THEN DELETE`.
787    DeleteMatched { condition: Option<Expr> },
788    /// `WHEN NOT MATCHED BY SOURCE [AND <cond>] THEN UPDATE SET ...`.
789    UpdateNotMatchedBySource {
790        condition: Option<Expr>,
791        assignments: Vec<(String, Expr)>,
792    },
793    /// `WHEN NOT MATCHED BY SOURCE [AND <cond>] THEN DELETE`.
794    DeleteNotMatchedBySource { condition: Option<Expr> },
795    /// `WHEN NOT MATCHED [AND <cond>] THEN INSERT (cols) VALUES (vals)`.
796    InsertNotMatched {
797        condition: Option<Expr>,
798        columns: Vec<String>,
799        values: Vec<Expr>,
800    },
801    /// `WHEN MATCHED [AND <cond>] THEN DO NOTHING`.
802    NothingMatched { condition: Option<Expr> },
803    /// `WHEN NOT MATCHED [AND <cond>] THEN DO NOTHING`.
804    NothingNotMatched { condition: Option<Expr> },
805    /// `WHEN NOT MATCHED BY SOURCE [AND <cond>] THEN DO NOTHING`.
806    NothingNotMatchedBySource { condition: Option<Expr> },
807}
808
809#[derive(Debug, Clone, Serialize, Deserialize)]
810pub struct CreateForeignServer {
811    pub name: String,
812    pub fdw_type: String,
813    pub options: Vec<(String, String)>,
814    pub if_not_exists: bool,
815}
816
817#[derive(Debug, Clone, Serialize, Deserialize)]
818pub struct CreateForeignTable {
819    pub name: String,
820    pub server_name: String,
821    pub columns: Vec<ColumnDef>,
822    #[serde(default)]
823    pub checks: Vec<TableCheck>,
824    pub options: Vec<(String, String)>,
825    pub if_not_exists: bool,
826}
827
828/// A syntactically valid `CREATE FOREIGN TABLE IF NOT EXISTS` whose definition must be analyzed only when its target name is free.
829#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
830pub struct DeferredCreateForeignTable {
831    pub name: String,
832    pub server_name: String,
833    pub definition_sql: String,
834}
835
836#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
837pub enum TransactionIsolationLevel {
838    ReadUncommitted,
839    ReadCommitted,
840    RepeatableRead,
841    Serializable,
842}
843
844impl TransactionIsolationLevel {
845    #[must_use]
846    pub const fn as_str(self) -> &'static str {
847        match self {
848            Self::ReadUncommitted => "read uncommitted",
849            Self::ReadCommitted => "read committed",
850            Self::RepeatableRead => "repeatable read",
851            Self::Serializable => "serializable",
852        }
853    }
854}
855
856#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
857pub struct TransactionCharacteristics {
858    pub isolation: Option<TransactionIsolationLevel>,
859    pub read_only: Option<bool>,
860    pub deferrable: Option<bool>,
861}
862
863#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
864pub enum TransactionStmt {
865    Begin,
866    BeginWithCharacteristics(TransactionCharacteristics),
867    Commit,
868    CommitAndChain,
869    Rollback,
870    RollbackAndChain,
871    SetCharacteristics(TransactionCharacteristics),
872    SetSessionCharacteristics(TransactionCharacteristics),
873    SetSnapshot(String),
874    Savepoint(String),
875    ReleaseSavepoint(String),
876    RollbackToSavepoint(String),
877}
878
879#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
880pub enum CursorDirection {
881    Forward,
882    Backward,
883    Absolute,
884    Relative,
885}
886
887#[derive(Debug, Clone, Serialize, Deserialize)]
888pub struct DeclareCursorStmt {
889    pub name: String,
890    pub binary: bool,
891    /// `None` lets the query determine scrollability, while `Some(true)` and `Some(false)` represent explicit `SCROLL` and `NO SCROLL`.
892    pub scroll: Option<bool>,
893    pub hold: bool,
894    pub query: Box<SelectStmt>,
895}
896
897#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
898pub struct FetchCursorStmt {
899    pub name: String,
900    pub direction: CursorDirection,
901    /// `PostgreSQL` uses `i64::MAX` for `ALL`; negative counts reverse `FORWARD` and `BACKWARD`.
902    pub count: i64,
903    pub move_only: bool,
904}
905
906#[cfg(test)]
907mod tests;