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