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