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