Skip to main content

uqa_sql/plan/
model.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Serializable relational, command, source, and scalar plan data model.
8
9use super::{NullsOrder, ScalarExpr, SetOpKind};
10use crate::catalog::roles::RoleReference;
11
12const fn default_include_descendants() -> bool {
13    true
14}
15
16/// One fully lowered SQL statement.
17///
18/// There is deliberately no `Legacy`, `Opaque`, or raw-`Statement` variant:
19/// adding a SQL statement kind must update the exhaustive lowerer and the
20/// physical driver.
21#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
22pub enum UnifiedPlan {
23    Query(Box<QueryPlan>),
24    Command(Box<CommandPlan>),
25}
26
27/// A relational query with its CTE scope and one relational root.
28#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
29pub struct QueryPlan {
30    /// Whether relation references in this tree are stored catalog identities rather than names that must be resolved in the executing session.
31    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
32    pub relations_bound: bool,
33    pub ctes: Vec<CtePlan>,
34    pub root: RelationalPlan,
35}
36
37/// A named query child owned by a [`QueryPlan`].
38#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
39pub struct CtePlan {
40    pub name: String,
41    pub columns: Vec<String>,
42    pub recursive: bool,
43    #[serde(default)]
44    pub materialization: crate::ast::CteMaterialization,
45    #[serde(default, skip_serializing_if = "Option::is_none")]
46    pub search: Option<CteSearchPlan>,
47    #[serde(default, skip_serializing_if = "Option::is_none")]
48    pub cycle: Option<CteCyclePlan>,
49    #[serde(flatten)]
50    pub body: CtePlanBody,
51}
52
53/// A relational CTE or a command whose RETURNING relation feeds its consumers.
54#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
55pub enum CtePlanBody {
56    #[serde(rename = "query")]
57    Query(Box<QueryPlan>),
58    #[serde(rename = "command")]
59    Command(Box<CommandPlan>),
60}
61
62impl CtePlanBody {
63    pub fn query(&self) -> Option<&QueryPlan> {
64        match self {
65            Self::Query(query) => Some(query),
66            Self::Command(_) => None,
67        }
68    }
69
70    pub fn query_mut(&mut self) -> Option<&mut QueryPlan> {
71        match self {
72            Self::Query(query) => Some(query),
73            Self::Command(_) => None,
74        }
75    }
76
77    pub const fn modifies_data(&self) -> bool {
78        matches!(self, Self::Command(_))
79    }
80
81    /// Whether this WITH definition exposes a relation to its consumers.
82    pub fn returns_rows(&self) -> bool {
83        match self {
84            Self::Query(_) => true,
85            Self::Command(command) => command
86                .returning()
87                .is_some_and(|returning| !returning.is_empty()),
88        }
89    }
90
91    pub fn into_plan(self) -> UnifiedPlan {
92        match self {
93            Self::Query(query) => UnifiedPlan::Query(query),
94            Self::Command(command) => UnifiedPlan::Command(command),
95        }
96    }
97}
98
99impl From<UnifiedPlan> for CtePlanBody {
100    fn from(plan: UnifiedPlan) -> Self {
101        match plan {
102            UnifiedPlan::Query(query) => Self::Query(query),
103            UnifiedPlan::Command(command) => Self::Command(command),
104        }
105    }
106}
107
108/// Generated traversal-order column for a recursive CTE.
109#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
110pub struct CteSearchPlan {
111    pub columns: Vec<String>,
112    pub breadth_first: bool,
113    pub sequence_column: String,
114}
115
116/// Generated cycle mark and path columns for a recursive CTE.
117#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
118pub struct CteCyclePlan {
119    pub columns: Vec<String>,
120    pub mark_column: String,
121    pub mark_value: ScalarExpr,
122    pub mark_default: ScalarExpr,
123    pub path_column: String,
124}
125
126/// Relational nodes common to ordinary SQL, retrieval SQL, and table/graph
127/// functions.
128#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
129pub enum RelationalPlan {
130    /// A single SELECT query block. Its source is a separate plan tree and its
131    /// compute phase is classified as projection, aggregation, or windowing.
132    QueryBlock(Box<QueryBlockPlan>),
133    /// SQL set operations own both input plans; combined ordering and slicing
134    /// are properties of the set node rather than either branch.
135    SetOp {
136        kind: SetOpKind,
137        all: bool,
138        left: Box<QueryPlan>,
139        right: Box<QueryPlan>,
140        order_by: Vec<OrderPlan>,
141        limit: Option<Box<ScalarExpr>>,
142        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
143        with_ties: bool,
144        offset: Option<Box<ScalarExpr>>,
145        subqueries: Vec<QueryPlan>,
146    },
147    /// Standalone `VALUES`, used both as a statement and as a relational
148    /// source. Each cell remains an expression so parameters/functions bind at
149    /// execution time.
150    Values {
151        rows: Vec<Vec<ScalarExpr>>,
152        subqueries: Vec<QueryPlan>,
153    },
154}
155
156/// One SELECT block after `WITH` and set-operation structure has been pulled
157/// into explicit parent/child nodes.
158#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
159pub struct QueryBlockPlan {
160    /// The canonical owners of window inputs; calls keep resolved execution copies.
161    #[serde(default, skip_serializing_if = "Vec::is_empty")]
162    pub windows: Vec<crate::ast::WindowDefinition<crate::ir::ScalarWindowSpec>>,
163    pub projections: Vec<ProjectionPlan>,
164    /// Analyzed column reads retained when unused outputs are removed. These names authorize access but never evaluate the discarded values.
165    #[serde(default, skip_serializing_if = "std::collections::BTreeSet::is_empty")]
166    pub privilege_columns: std::collections::BTreeSet<crate::ColumnIdentity>,
167    pub from: Option<SourcePlan>,
168    pub r#where: Option<ScalarExpr>,
169    pub compute: ComputePlan,
170    pub group_by: Vec<ScalarExpr>,
171    pub grouping_sets: Vec<Vec<ScalarExpr>>,
172    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
173    pub group_distinct: bool,
174    pub having: Option<ScalarExpr>,
175    pub order_by: Vec<OrderPlan>,
176    pub limit: Option<ScalarExpr>,
177    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
178    pub with_ties: bool,
179    pub offset: Option<ScalarExpr>,
180    pub distinct: bool,
181    pub distinct_on: Vec<ScalarExpr>,
182    pub subqueries: Vec<QueryPlan>,
183    pub access: AccessPathPlan,
184    /// `FOR UPDATE` / `FOR SHARE` clauses belonging to this query block.
185    #[serde(default, skip_serializing_if = "Vec::is_empty")]
186    pub locking: Vec<crate::ast::LockingClause>,
187}
188
189/// Cross-paradigm access decision made after the relational and scalar
190/// portions of a query block have both been lowered.
191#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
192pub enum AccessPathPlan {
193    /// Ordinary row-source execution.
194    Row,
195    /// Use the shared document-support/operator algebra for the block predicate.
196    OperatorTree {
197        /// The relational ORDER BY/OFFSET/LIMIT can be pushed into the
198        /// retrieval function before row materialization.
199        score_limit_pushdown: bool,
200    },
201    /// Split a mixed predicate into posting-list candidates followed by
202    /// row-level residual evaluation.
203    Hybrid,
204}
205
206/// Physical strategy selected for a relational join.
207///
208/// `Auto` is used for an unreordered SQL join and lets physical lowering pick
209/// hash execution for a splittable equality predicate or nested-loop execution
210/// otherwise. `Hash` is an optimizer commitment produced by `DPccp` and must be
211/// executable; physical lowering reports an internal planning error if that
212/// invariant is violated.
213#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
214pub enum JoinExecutionStrategy {
215    #[default]
216    Auto,
217    Hash,
218}
219
220/// One independently resolved and bound function inside a range-function group.
221#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
222pub struct TableFunctionPlan {
223    pub name: String,
224    #[serde(default, skip_serializing_if = "Option::is_none")]
225    pub binding: Option<crate::ast::FunctionBinding>,
226    #[serde(default)]
227    pub output_name: String,
228    #[serde(default, skip_serializing_if = "Option::is_none")]
229    pub relations: Option<crate::ast::OperatorJoinRelations>,
230    pub args: Vec<ScalarExpr>,
231    pub column_aliases: Vec<String>,
232    pub column_types: Vec<String>,
233}
234
235/// The row-producing source below a query block.
236#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
237pub enum SourcePlan {
238    Table {
239        name: String,
240        #[serde(default)]
241        qualifier: String,
242        alias: Option<String>,
243        #[serde(default, skip_serializing_if = "Vec::is_empty")]
244        column_aliases: Vec<String>,
245        #[serde(default, skip_serializing_if = "Option::is_none")]
246        bound_columns: Option<Vec<String>>,
247        #[serde(default = "default_include_descendants")]
248        include_descendants: bool,
249    },
250    Join {
251        left: Box<SourcePlan>,
252        right: Box<SourcePlan>,
253        kind: crate::ast::JoinKind,
254        on: Option<ScalarExpr>,
255        #[serde(default, skip_serializing_if = "Option::is_none")]
256        using: Option<crate::ast::JoinUsing>,
257        #[serde(default)]
258        natural: bool,
259        #[serde(default, skip_serializing_if = "Option::is_none")]
260        alias: Option<String>,
261        #[serde(default, skip_serializing_if = "Vec::is_empty")]
262        column_aliases: Vec<String>,
263        lateral: bool,
264        #[serde(default)]
265        strategy: JoinExecutionStrategy,
266    },
267    Values {
268        rows: Vec<Vec<ScalarExpr>>,
269        alias: Option<String>,
270        column_aliases: Vec<String>,
271        #[serde(default, skip_serializing_if = "Option::is_none")]
272        internal_relation: Option<crate::ast::InternalRelationId>,
273        #[serde(default, skip_serializing_if = "Vec::is_empty")]
274        internal_column_types: Vec<Option<crate::ast::ColumnType>>,
275        #[serde(default, skip_serializing_if = "Vec::is_empty")]
276        internal_column_collations: Vec<Option<u32>>,
277    },
278    Function {
279        name: String,
280        #[serde(default, skip_serializing_if = "Option::is_none")]
281        binding: Option<crate::ast::FunctionBinding>,
282        #[serde(default)]
283        output_name: String,
284        #[serde(default, skip_serializing_if = "Option::is_none")]
285        relations: Option<crate::ast::OperatorJoinRelations>,
286        args: Vec<ScalarExpr>,
287        alias: Option<String>,
288        column_aliases: Vec<String>,
289        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
290        ordinality: bool,
291        column_types: Vec<String>,
292    },
293    FunctionGroup {
294        functions: Vec<TableFunctionPlan>,
295        alias: Option<String>,
296        column_aliases: Vec<String>,
297        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
298        ordinality: bool,
299    },
300    Subquery {
301        body: Box<QueryPlan>,
302        alias: Option<String>,
303        column_aliases: Vec<String>,
304    },
305}
306
307/// The SELECT-list phase chosen during lowering.
308#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
309pub enum ComputePlan {
310    Project,
311    Aggregate,
312    Window,
313}
314
315#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
316pub struct ProjectionPlan {
317    pub expr: ScalarExpr,
318    pub alias: Option<String>,
319}
320
321#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
322pub struct OrderPlan {
323    pub expr: ScalarExpr,
324    pub descending: bool,
325    pub nulls: Option<NullsOrder>,
326}
327
328/// Executable scalar IR plus every query-valued descendant it owns.
329#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
330pub struct ExpressionPlan {
331    pub scalar: ScalarExpr,
332    pub subqueries: Vec<QueryPlan>,
333}
334
335#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
336pub struct AssignmentPlan {
337    #[serde(rename = "column")]
338    pub target: crate::ast::AssignmentTargets<ScalarExpr>,
339    pub value: ScalarExpr,
340}
341
342impl AssignmentPlan {
343    pub fn expressions(&self) -> impl Iterator<Item = &ScalarExpr> {
344        self.target
345            .expressions()
346            .chain(std::iter::once(&self.value))
347    }
348
349    pub fn expressions_mut(&mut self) -> impl Iterator<Item = &mut ScalarExpr> {
350        self.target
351            .expressions_mut()
352            .chain(std::iter::once(&mut self.value))
353    }
354}
355
356#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
357pub struct ViewCheckPlan {
358    pub view: String,
359    pub predicate: ScalarExpr,
360}
361
362#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
363pub struct ViewRuleReturningPlan {
364    pub relation: String,
365    pub target_qualifier: String,
366    pub returning: Vec<ProjectionPlan>,
367    pub aliases: crate::ast::ReturningAliases,
368    pub subqueries: Vec<QueryPlan>,
369}
370
371#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
372pub struct ViewRuleInsertPlan {
373    pub relation: String,
374    pub supplied_columns: Vec<String>,
375    pub input_columns: Vec<String>,
376}
377
378#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
379pub struct ViewRuleUpdatePlan {
380    pub relation: String,
381    pub assigned_columns: Vec<String>,
382    pub input_columns: Vec<String>,
383}
384
385#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
386pub struct InsertPlan {
387    pub table: String,
388    pub target_relation_bound: bool,
389    /// Whether non-target relation references are stored catalog identities rather than names that must be resolved in the executing session.
390    pub relations_bound: bool,
391    /// Effective role used for non-target privilege checks in an internally rewritten statement.
392    pub statement_privilege_subject: Option<RoleReference>,
393    /// Effective role used only for privilege checks on an internally rewritten target relation.
394    pub target_privilege_subject: Option<RoleReference>,
395    pub target_qualifier: String,
396    #[serde(default)]
397    pub target_alias: Option<String>,
398    pub include_descendants: bool,
399    pub columns: Vec<crate::ast::AssignmentTarget<ScalarExpr>>,
400    #[serde(default, skip_serializing_if = "Option::is_none")]
401    pub overriding: Option<crate::ast::OverridingKind>,
402    pub ctes: Vec<CtePlan>,
403    pub rows: Vec<Vec<ScalarExpr>>,
404    pub source: Option<Box<QueryPlan>>,
405    pub on_conflict: Option<ConflictPlan>,
406    pub returning: Vec<ProjectionPlan>,
407    pub returning_aliases: crate::ast::ReturningAliases,
408    pub subqueries: Vec<QueryPlan>,
409    pub view_checks: Vec<ViewCheckPlan>,
410    pub view_rule_relations: Vec<String>,
411    pub view_rule_insert_plans: Vec<ViewRuleInsertPlan>,
412    pub view_rule_returning: Option<ViewRuleReturningPlan>,
413}
414
415#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
416pub struct ConflictPlan {
417    pub predicate: Option<Box<ScalarExpr>>,
418    pub constraint: Option<String>,
419    pub conflict_columns: Vec<String>,
420    pub expressions: Vec<ScalarExpr>,
421    pub action: ConflictActionPlan,
422}
423
424#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
425pub enum ConflictActionPlan {
426    Nothing,
427    Update {
428        assignments: Vec<AssignmentPlan>,
429        predicate: Option<Box<ScalarExpr>>,
430    },
431}
432
433#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
434pub struct UpdatePlan {
435    pub table: String,
436    pub target_relation_bound: bool,
437    /// Whether non-target relation references are stored catalog identities rather than names that must be resolved in the executing session.
438    pub relations_bound: bool,
439    /// Effective role used for non-target privilege checks in an internally rewritten statement.
440    pub statement_privilege_subject: Option<RoleReference>,
441    /// Effective role used only for privilege checks on an internally rewritten target relation.
442    pub target_privilege_subject: Option<RoleReference>,
443    pub target_qualifier: String,
444    #[serde(default)]
445    pub target_alias: Option<String>,
446    pub include_descendants: bool,
447    pub assignments: Vec<AssignmentPlan>,
448    pub predicate: Option<ScalarExpr>,
449    pub ctes: Vec<CtePlan>,
450    pub source: Option<Box<SourcePlan>>,
451    pub returning: Vec<ProjectionPlan>,
452    pub returning_aliases: crate::ast::ReturningAliases,
453    pub subqueries: Vec<QueryPlan>,
454    pub view_checks: Vec<ViewCheckPlan>,
455    pub view_rule_relations: Vec<String>,
456    pub view_rule_update_plans: Vec<ViewRuleUpdatePlan>,
457    pub view_rule_returning: Option<ViewRuleReturningPlan>,
458}
459
460#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
461pub struct DeletePlan {
462    pub table: String,
463    pub target_relation_bound: bool,
464    /// Whether non-target relation references are stored catalog identities rather than names that must be resolved in the executing session.
465    pub relations_bound: bool,
466    /// Effective role used for non-target privilege checks in an internally rewritten statement.
467    pub statement_privilege_subject: Option<RoleReference>,
468    /// Effective role used only for privilege checks on an internally rewritten target relation.
469    pub target_privilege_subject: Option<RoleReference>,
470    pub target_qualifier: String,
471    #[serde(default)]
472    pub target_alias: Option<String>,
473    pub include_descendants: bool,
474    pub predicate: Option<ScalarExpr>,
475    pub ctes: Vec<CtePlan>,
476    pub source: Option<Box<SourcePlan>>,
477    pub returning: Vec<ProjectionPlan>,
478    pub returning_aliases: crate::ast::ReturningAliases,
479    pub subqueries: Vec<QueryPlan>,
480    pub view_rule_relations: Vec<String>,
481    pub view_rule_returning: Option<ViewRuleReturningPlan>,
482}
483
484#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
485pub struct MergePlan {
486    #[serde(default)]
487    pub ctes: Vec<CtePlan>,
488    pub target: String,
489    /// Effective role used for non-target privilege checks in an internally rewritten statement.
490    pub statement_privilege_subject: Option<RoleReference>,
491    /// Effective role used only for privilege checks on an internally rewritten target relation.
492    pub target_privilege_subject: Option<RoleReference>,
493    pub target_qualifier: String,
494    pub target_alias: Option<String>,
495    pub include_descendants: bool,
496    pub target_predicate: Option<ScalarExpr>,
497    pub source: Box<SourcePlan>,
498    pub join_condition: ScalarExpr,
499    pub when_clauses: Vec<MergeWhenPlan>,
500    pub returning: Vec<ProjectionPlan>,
501    pub returning_aliases: crate::ast::ReturningAliases,
502    pub subqueries: Vec<QueryPlan>,
503    pub view_checks: Vec<ViewCheckPlan>,
504}
505
506#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
507pub enum MergeWhenPlan {
508    UpdateMatched {
509        condition: Option<ScalarExpr>,
510        assignments: Vec<AssignmentPlan>,
511    },
512    DeleteMatched {
513        condition: Option<ScalarExpr>,
514    },
515    UpdateNotMatchedBySource {
516        condition: Option<ScalarExpr>,
517        assignments: Vec<AssignmentPlan>,
518    },
519    DeleteNotMatchedBySource {
520        condition: Option<ScalarExpr>,
521    },
522    InsertNotMatched {
523        condition: Option<ScalarExpr>,
524        columns: Vec<crate::ast::AssignmentTarget<ScalarExpr>>,
525        #[serde(default, skip_serializing_if = "Option::is_none")]
526        overriding: Option<crate::ast::OverridingKind>,
527        values: Vec<ScalarExpr>,
528    },
529    NothingMatched {
530        condition: Option<ScalarExpr>,
531    },
532    NothingNotMatched {
533        condition: Option<ScalarExpr>,
534    },
535    NothingNotMatchedBySource {
536        condition: Option<ScalarExpr>,
537    },
538}
539
540/// Non-query statement plans. Mutations own physical sources and scalar IR;
541/// query-bearing catalog commands own explicit query children. Typed DDL and
542/// procedural payloads contain catalog data, never a second SQL dispatcher.
543#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
544pub enum CommandPlan {
545    CreateTable(Box<crate::ast::CreateTable>),
546    CreateTableIfNotExists(crate::ast::DeferredCreateTable),
547    CreateIndex(crate::ast::CreateIndex),
548    RenameIndex(crate::ast::RenameIndexStmt),
549    Insert(Box<InsertPlan>),
550    Update(Box<UpdatePlan>),
551    Delete(Box<DeletePlan>),
552    Drop(crate::ast::DropStmt),
553    AlterTable(Box<crate::ast::AlterTableStmt>),
554    AlterView(crate::ast::AlterViewStmt),
555    CreateView {
556        name: String,
557        column_names: Vec<String>,
558        query: Box<QueryPlan>,
559        or_replace: bool,
560        persistence: crate::ast::RelationPersistence,
561        options: Vec<(String, String)>,
562    },
563    CreateMaterializedView {
564        name: String,
565        column_names: Vec<String>,
566        if_not_exists: bool,
567        with_no_data: bool,
568        options: Vec<(String, String)>,
569        query: Box<QueryPlan>,
570    },
571    RefreshMaterializedView {
572        name: String,
573        concurrently: bool,
574        with_no_data: bool,
575    },
576    CreateSchema {
577        name: Option<String>,
578        if_not_exists: bool,
579        #[serde(default, skip_serializing_if = "Option::is_none")]
580        authorization: Option<crate::ast::SchemaAuthorization>,
581    },
582    AlterSchemaOwner {
583        name: String,
584        new_owner: crate::ast::RoleSpecification,
585    },
586    RenameSchema {
587        name: String,
588        new_name: String,
589    },
590    Notify {
591        channel: String,
592        payload: String,
593    },
594    Listen {
595        channel: String,
596    },
597    Unlisten {
598        channel: Option<String>,
599    },
600    SetVariable {
601        name: String,
602        value: String,
603        #[serde(default)]
604        local: bool,
605        #[serde(default)]
606        is_default: bool,
607    },
608    ResetVariable {
609        name: String,
610    },
611    ResetAllVariables,
612    SetConstraints {
613        constraints: Vec<crate::ast::SetConstraintName>,
614        deferred: bool,
615    },
616    ShowVariable {
617        name: String,
618    },
619    Discard {
620        target: crate::ast::DiscardTarget,
621    },
622    Load {
623        library: String,
624    },
625    Explain {
626        analyze: bool,
627        verbose: bool,
628        format: Option<String>,
629        body: Box<UnifiedPlan>,
630    },
631    Analyze {
632        table: Option<String>,
633    },
634    Vacuum(crate::ast::VacuumStmt),
635    LockTable(crate::ast::LockTableStmt),
636    Truncate {
637        tables: Vec<crate::ast::TruncateTarget>,
638        cascade: bool,
639        restart_identity: bool,
640    },
641    Transaction(crate::ast::TransactionStmt),
642    DeclareCursor {
643        name: String,
644        binary: bool,
645        scroll: Option<bool>,
646        hold: bool,
647        query: Box<QueryPlan>,
648    },
649    FetchCursor(crate::ast::FetchCursorStmt),
650    CloseCursor {
651        name: Option<String>,
652    },
653    CreateSequence(crate::ast::CreateSequence),
654    CreateDomain(crate::ast::CreateDomain),
655    AlterDomain(crate::ast::AlterDomain),
656    CreateEnum(crate::ast::CreateEnum),
657    CreateCompositeType(crate::ast::CreateCompositeType),
658    AlterEnum(crate::ast::AlterEnum),
659    AlterTypeObject(crate::ast::AlterTypeObject),
660    GrantType(crate::ast::GrantTypeStmt),
661    AlterSequence(crate::ast::AlterSequence),
662    CreateTableAs {
663        name: String,
664        if_not_exists: bool,
665        column_names: Vec<String>,
666        with_no_data: bool,
667        /// Written as `SELECT ... INTO`, which `PostgreSQL` tags `SELECT INTO`.
668        #[serde(default)]
669        select_into: bool,
670        persistence: crate::ast::RelationPersistence,
671        on_commit: crate::ast::OnCommitAction,
672        query: Box<QueryPlan>,
673    },
674    Prepare {
675        name: String,
676        #[serde(default)]
677        parameter_types: Vec<crate::ast::ColumnType>,
678        body: Box<UnifiedPlan>,
679    },
680    Execute {
681        name: String,
682        params: Vec<ExpressionPlan>,
683    },
684    Deallocate {
685        name: Option<String>,
686    },
687    CreateForeignWrapper(crate::ast::CreateForeignWrapper),
688    CreateForeignServer(crate::ast::CreateForeignServer),
689    CreateForeignTable(crate::ast::CreateForeignTable),
690    #[serde(alias = "CreateForeignTableIfNotExists")]
691    CreateForeignTableDefinition(crate::ast::DeferredCreateForeignTable),
692    AlterForeignTable(crate::ast::AlterForeignTableStmt),
693    Merge(Box<MergePlan>),
694    CreateFunction(Box<crate::ast::CreateFunction>),
695    DropFunction(crate::ast::DropFunctionStmt),
696    AlterRoutine(crate::ast::AlterRoutineStmt),
697    AlterRoutineOwner(crate::ast::AlterRoutineOwnerStmt),
698    RenameRoutine(crate::ast::RenameRoutineStmt),
699    GrantRoutine(crate::ast::GrantRoutineStmt),
700    GrantTable(crate::ast::GrantTableStmt),
701    GrantSequence(crate::ast::GrantSequenceStmt),
702    GrantDatabase(crate::ast::GrantDatabaseStmt),
703    GrantSchema(crate::ast::GrantSchemaStmt),
704    GrantRole(crate::ast::GrantRoleStmt),
705    CreateRole(crate::ast::CreateRoleStmt),
706    AlterRole(crate::ast::AlterRoleStmt),
707    RenameRole(crate::ast::RenameRoleStmt),
708    DropRole(crate::ast::DropRoleStmt),
709    CreateTrigger(crate::ast::CreateTrigger),
710    DropTrigger(crate::ast::DropTrigger),
711    CreateRule(crate::ast::CreateRule),
712    DropRule(crate::ast::DropRule),
713    DoBlock {
714        language: String,
715        body: String,
716    },
717    Call {
718        name: String,
719        args: Vec<ExpressionPlan>,
720    },
721}
722
723/// Classification hook for engine-registered aggregate functions. Built-in
724/// aggregates are always recognised; the callback extends that set without
725/// making the planner depend on the engine.
726pub trait AggregateClassifier {
727    fn is_registered_aggregate(&self, name: &str) -> bool;
728}
729
730impl<F> AggregateClassifier for F
731where
732    F: Fn(&str) -> bool,
733{
734    fn is_registered_aggregate(&self, name: &str) -> bool {
735        self(name)
736    }
737}
738
739pub(super) struct NoRegisteredAggregates;
740
741impl AggregateClassifier for NoRegisteredAggregates {
742    fn is_registered_aggregate(&self, _name: &str) -> bool {
743        false
744    }
745}