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    pub projections: Vec<ProjectionPlan>,
161    pub from: Option<SourcePlan>,
162    pub r#where: Option<ScalarExpr>,
163    pub compute: ComputePlan,
164    pub group_by: Vec<ScalarExpr>,
165    pub grouping_sets: Vec<Vec<ScalarExpr>>,
166    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
167    pub group_distinct: bool,
168    pub having: Option<ScalarExpr>,
169    pub order_by: Vec<OrderPlan>,
170    pub limit: Option<ScalarExpr>,
171    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
172    pub with_ties: bool,
173    pub offset: Option<ScalarExpr>,
174    pub distinct: bool,
175    pub distinct_on: Vec<ScalarExpr>,
176    pub subqueries: Vec<QueryPlan>,
177    pub access: AccessPathPlan,
178    /// `FOR UPDATE` / `FOR SHARE` clauses belonging to this query block.
179    #[serde(default, skip_serializing_if = "Vec::is_empty")]
180    pub locking: Vec<crate::ast::LockingClause>,
181}
182
183/// Cross-paradigm access decision made after the relational and scalar
184/// portions of a query block have both been lowered.
185#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
186pub enum AccessPathPlan {
187    /// Ordinary row-source execution.
188    Row,
189    /// Use the shared document-support/operator algebra for the block predicate.
190    OperatorTree {
191        /// The relational ORDER BY/OFFSET/LIMIT can be pushed into the
192        /// retrieval function before row materialization.
193        score_limit_pushdown: bool,
194    },
195    /// Split a mixed predicate into posting-list candidates followed by
196    /// row-level residual evaluation.
197    Hybrid,
198}
199
200/// Physical strategy selected for a relational join.
201///
202/// `Auto` is used for an unreordered SQL join and lets physical lowering pick
203/// hash execution for a splittable equality predicate or nested-loop execution
204/// otherwise. `Hash` is an optimizer commitment produced by `DPccp` and must be
205/// executable; physical lowering reports an internal planning error if that
206/// invariant is violated.
207#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
208pub enum JoinExecutionStrategy {
209    #[default]
210    Auto,
211    Hash,
212}
213
214/// One independently resolved and bound function inside a range-function group.
215#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
216pub struct TableFunctionPlan {
217    pub name: String,
218    #[serde(default, skip_serializing_if = "Option::is_none")]
219    pub binding: Option<crate::ast::FunctionBinding>,
220    #[serde(default)]
221    pub output_name: String,
222    #[serde(default, skip_serializing_if = "Option::is_none")]
223    pub relations: Option<crate::ast::OperatorJoinRelations>,
224    pub args: Vec<ScalarExpr>,
225    pub column_aliases: Vec<String>,
226    pub column_types: Vec<String>,
227}
228
229/// The row-producing source below a query block.
230#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
231pub enum SourcePlan {
232    Table {
233        name: String,
234        #[serde(default)]
235        qualifier: String,
236        alias: Option<String>,
237        #[serde(default, skip_serializing_if = "Vec::is_empty")]
238        column_aliases: Vec<String>,
239        #[serde(default, skip_serializing_if = "Option::is_none")]
240        bound_columns: Option<Vec<String>>,
241        #[serde(default = "default_include_descendants")]
242        include_descendants: bool,
243    },
244    Join {
245        left: Box<SourcePlan>,
246        right: Box<SourcePlan>,
247        kind: crate::ast::JoinKind,
248        on: Option<ScalarExpr>,
249        #[serde(default, skip_serializing_if = "Option::is_none")]
250        using: Option<crate::ast::JoinUsing>,
251        #[serde(default)]
252        natural: bool,
253        #[serde(default, skip_serializing_if = "Option::is_none")]
254        alias: Option<String>,
255        #[serde(default, skip_serializing_if = "Vec::is_empty")]
256        column_aliases: Vec<String>,
257        lateral: bool,
258        #[serde(default)]
259        strategy: JoinExecutionStrategy,
260    },
261    Values {
262        rows: Vec<Vec<ScalarExpr>>,
263        alias: Option<String>,
264        column_aliases: Vec<String>,
265        #[serde(default, skip_serializing_if = "Option::is_none")]
266        internal_relation: Option<crate::ast::InternalRelationId>,
267        #[serde(default, skip_serializing_if = "Vec::is_empty")]
268        internal_column_types: Vec<Option<crate::ast::ColumnType>>,
269    },
270    Function {
271        name: String,
272        #[serde(default, skip_serializing_if = "Option::is_none")]
273        binding: Option<crate::ast::FunctionBinding>,
274        #[serde(default)]
275        output_name: String,
276        #[serde(default, skip_serializing_if = "Option::is_none")]
277        relations: Option<crate::ast::OperatorJoinRelations>,
278        args: Vec<ScalarExpr>,
279        alias: Option<String>,
280        column_aliases: Vec<String>,
281        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
282        ordinality: bool,
283        column_types: Vec<String>,
284    },
285    FunctionGroup {
286        functions: Vec<TableFunctionPlan>,
287        alias: Option<String>,
288        column_aliases: Vec<String>,
289        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
290        ordinality: bool,
291    },
292    Subquery {
293        body: Box<QueryPlan>,
294        alias: Option<String>,
295        column_aliases: Vec<String>,
296    },
297}
298
299/// The SELECT-list phase chosen during lowering.
300#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
301pub enum ComputePlan {
302    Project,
303    Aggregate,
304    Window,
305}
306
307#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
308pub struct ProjectionPlan {
309    pub expr: ScalarExpr,
310    pub alias: Option<String>,
311}
312
313#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
314pub struct OrderPlan {
315    pub expr: ScalarExpr,
316    pub descending: bool,
317    pub nulls: Option<NullsOrder>,
318}
319
320/// Executable scalar IR plus every query-valued descendant it owns.
321#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
322pub struct ExpressionPlan {
323    pub scalar: ScalarExpr,
324    pub subqueries: Vec<QueryPlan>,
325}
326
327#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
328pub struct AssignmentPlan {
329    #[serde(rename = "column")]
330    pub target: crate::ast::AssignmentTarget<ScalarExpr>,
331    pub value: ScalarExpr,
332}
333
334impl AssignmentPlan {
335    pub fn expressions(&self) -> impl Iterator<Item = &ScalarExpr> {
336        self.target
337            .expressions()
338            .chain(std::iter::once(&self.value))
339    }
340
341    pub fn expressions_mut(&mut self) -> impl Iterator<Item = &mut ScalarExpr> {
342        self.target
343            .expressions_mut()
344            .chain(std::iter::once(&mut self.value))
345    }
346}
347
348#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
349pub struct ViewCheckPlan {
350    pub view: String,
351    pub predicate: ScalarExpr,
352}
353
354#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
355pub struct ViewRuleReturningPlan {
356    pub relation: String,
357    pub target_qualifier: String,
358    pub returning: Vec<ProjectionPlan>,
359    pub aliases: crate::ast::ReturningAliases,
360    pub subqueries: Vec<QueryPlan>,
361}
362
363#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
364pub struct ViewRuleInsertPlan {
365    pub relation: String,
366    pub supplied_columns: Vec<String>,
367    pub input_columns: Vec<String>,
368}
369
370#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
371pub struct ViewRuleUpdatePlan {
372    pub relation: String,
373    pub assigned_columns: Vec<String>,
374    pub input_columns: Vec<String>,
375}
376
377#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
378pub struct InsertPlan {
379    pub table: String,
380    pub target_relation_bound: bool,
381    /// Whether non-target relation references are stored catalog identities rather than names that must be resolved in the executing session.
382    pub relations_bound: bool,
383    /// Effective role used for non-target privilege checks in an internally rewritten statement.
384    pub statement_privilege_subject: Option<RoleReference>,
385    /// Effective role used only for privilege checks on an internally rewritten target relation.
386    pub target_privilege_subject: Option<RoleReference>,
387    pub target_qualifier: String,
388    pub include_descendants: bool,
389    pub columns: Vec<crate::ast::AssignmentTarget<ScalarExpr>>,
390    #[serde(default, skip_serializing_if = "Option::is_none")]
391    pub overriding: Option<crate::ast::OverridingKind>,
392    pub ctes: Vec<CtePlan>,
393    pub rows: Vec<Vec<ScalarExpr>>,
394    pub source: Option<Box<QueryPlan>>,
395    pub on_conflict: Option<ConflictPlan>,
396    pub returning: Vec<ProjectionPlan>,
397    pub returning_aliases: crate::ast::ReturningAliases,
398    pub subqueries: Vec<QueryPlan>,
399    pub view_checks: Vec<ViewCheckPlan>,
400    pub view_rule_relations: Vec<String>,
401    pub view_rule_insert_plans: Vec<ViewRuleInsertPlan>,
402    pub view_rule_returning: Option<ViewRuleReturningPlan>,
403}
404
405#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
406pub struct ConflictPlan {
407    pub predicate: Option<Box<ScalarExpr>>,
408    pub constraint: Option<String>,
409    pub conflict_columns: Vec<String>,
410    pub expressions: Vec<ScalarExpr>,
411    pub action: ConflictActionPlan,
412}
413
414#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
415pub enum ConflictActionPlan {
416    Nothing,
417    Update {
418        assignments: Vec<AssignmentPlan>,
419        predicate: Option<Box<ScalarExpr>>,
420    },
421}
422
423#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
424pub struct UpdatePlan {
425    pub table: String,
426    pub target_relation_bound: bool,
427    /// Whether non-target relation references are stored catalog identities rather than names that must be resolved in the executing session.
428    pub relations_bound: bool,
429    /// Effective role used for non-target privilege checks in an internally rewritten statement.
430    pub statement_privilege_subject: Option<RoleReference>,
431    /// Effective role used only for privilege checks on an internally rewritten target relation.
432    pub target_privilege_subject: Option<RoleReference>,
433    pub target_qualifier: String,
434    pub include_descendants: bool,
435    pub assignments: Vec<AssignmentPlan>,
436    pub predicate: Option<ScalarExpr>,
437    pub ctes: Vec<CtePlan>,
438    pub source: Option<Box<SourcePlan>>,
439    pub returning: Vec<ProjectionPlan>,
440    pub returning_aliases: crate::ast::ReturningAliases,
441    pub subqueries: Vec<QueryPlan>,
442    pub view_checks: Vec<ViewCheckPlan>,
443    pub view_rule_relations: Vec<String>,
444    pub view_rule_update_plans: Vec<ViewRuleUpdatePlan>,
445    pub view_rule_returning: Option<ViewRuleReturningPlan>,
446}
447
448#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
449pub struct DeletePlan {
450    pub table: String,
451    pub target_relation_bound: bool,
452    /// Whether non-target relation references are stored catalog identities rather than names that must be resolved in the executing session.
453    pub relations_bound: bool,
454    /// Effective role used for non-target privilege checks in an internally rewritten statement.
455    pub statement_privilege_subject: Option<RoleReference>,
456    /// Effective role used only for privilege checks on an internally rewritten target relation.
457    pub target_privilege_subject: Option<RoleReference>,
458    pub target_qualifier: String,
459    pub include_descendants: bool,
460    pub predicate: Option<ScalarExpr>,
461    pub ctes: Vec<CtePlan>,
462    pub source: Option<Box<SourcePlan>>,
463    pub returning: Vec<ProjectionPlan>,
464    pub returning_aliases: crate::ast::ReturningAliases,
465    pub subqueries: Vec<QueryPlan>,
466    pub view_rule_relations: Vec<String>,
467    pub view_rule_returning: Option<ViewRuleReturningPlan>,
468}
469
470#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
471pub struct MergePlan {
472    #[serde(default)]
473    pub ctes: Vec<CtePlan>,
474    pub target: String,
475    /// Effective role used for non-target privilege checks in an internally rewritten statement.
476    pub statement_privilege_subject: Option<RoleReference>,
477    /// Effective role used only for privilege checks on an internally rewritten target relation.
478    pub target_privilege_subject: Option<RoleReference>,
479    pub target_qualifier: String,
480    pub target_alias: Option<String>,
481    pub include_descendants: bool,
482    pub target_predicate: Option<ScalarExpr>,
483    pub source: Box<SourcePlan>,
484    pub join_condition: ScalarExpr,
485    pub when_clauses: Vec<MergeWhenPlan>,
486    pub returning: Vec<ProjectionPlan>,
487    pub returning_aliases: crate::ast::ReturningAliases,
488    pub subqueries: Vec<QueryPlan>,
489    pub view_checks: Vec<ViewCheckPlan>,
490}
491
492#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
493pub enum MergeWhenPlan {
494    UpdateMatched {
495        condition: Option<ScalarExpr>,
496        assignments: Vec<AssignmentPlan>,
497    },
498    DeleteMatched {
499        condition: Option<ScalarExpr>,
500    },
501    UpdateNotMatchedBySource {
502        condition: Option<ScalarExpr>,
503        assignments: Vec<AssignmentPlan>,
504    },
505    DeleteNotMatchedBySource {
506        condition: Option<ScalarExpr>,
507    },
508    InsertNotMatched {
509        condition: Option<ScalarExpr>,
510        columns: Vec<crate::ast::AssignmentTarget<ScalarExpr>>,
511        #[serde(default, skip_serializing_if = "Option::is_none")]
512        overriding: Option<crate::ast::OverridingKind>,
513        values: Vec<ScalarExpr>,
514    },
515    NothingMatched {
516        condition: Option<ScalarExpr>,
517    },
518    NothingNotMatched {
519        condition: Option<ScalarExpr>,
520    },
521    NothingNotMatchedBySource {
522        condition: Option<ScalarExpr>,
523    },
524}
525
526/// Non-query statement plans. Mutations own physical sources and scalar IR;
527/// query-bearing catalog commands own explicit query children. Typed DDL and
528/// procedural payloads contain catalog data, never a second SQL dispatcher.
529#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
530pub enum CommandPlan {
531    CreateTable(Box<crate::ast::CreateTable>),
532    CreateTableIfNotExists(crate::ast::DeferredCreateTable),
533    CreateIndex(crate::ast::CreateIndex),
534    RenameIndex(crate::ast::RenameIndexStmt),
535    Insert(Box<InsertPlan>),
536    Update(Box<UpdatePlan>),
537    Delete(Box<DeletePlan>),
538    Drop(crate::ast::DropStmt),
539    AlterTable(Box<crate::ast::AlterTableStmt>),
540    AlterView(crate::ast::AlterViewStmt),
541    CreateView {
542        name: String,
543        column_names: Vec<String>,
544        query: Box<QueryPlan>,
545        or_replace: bool,
546        persistence: crate::ast::RelationPersistence,
547        options: Vec<(String, String)>,
548    },
549    CreateMaterializedView {
550        name: String,
551        column_names: Vec<String>,
552        if_not_exists: bool,
553        with_no_data: bool,
554        options: Vec<(String, String)>,
555        query: Box<QueryPlan>,
556    },
557    RefreshMaterializedView {
558        name: String,
559        concurrently: bool,
560        with_no_data: bool,
561    },
562    CreateSchema {
563        name: Option<String>,
564        if_not_exists: bool,
565        #[serde(default, skip_serializing_if = "Option::is_none")]
566        authorization: Option<crate::ast::SchemaAuthorization>,
567    },
568    AlterSchemaOwner {
569        name: String,
570        new_owner: crate::ast::RoleSpecification,
571    },
572    Notify {
573        channel: String,
574        payload: String,
575    },
576    Listen {
577        channel: String,
578    },
579    Unlisten {
580        channel: Option<String>,
581    },
582    SetVariable {
583        name: String,
584        value: String,
585        #[serde(default)]
586        local: bool,
587        #[serde(default)]
588        is_default: bool,
589    },
590    ResetVariable {
591        name: String,
592    },
593    ResetAllVariables,
594    SetConstraints {
595        constraints: Vec<crate::ast::SetConstraintName>,
596        deferred: bool,
597    },
598    ShowVariable {
599        name: String,
600    },
601    Discard {
602        target: crate::ast::DiscardTarget,
603    },
604    Load {
605        library: String,
606    },
607    Explain {
608        analyze: bool,
609        verbose: bool,
610        format: Option<String>,
611        body: Box<UnifiedPlan>,
612    },
613    Analyze {
614        table: Option<String>,
615    },
616    Vacuum(crate::ast::VacuumStmt),
617    LockTable(crate::ast::LockTableStmt),
618    Truncate {
619        tables: Vec<crate::ast::TruncateTarget>,
620        cascade: bool,
621        restart_identity: bool,
622    },
623    Transaction(crate::ast::TransactionStmt),
624    DeclareCursor {
625        name: String,
626        binary: bool,
627        scroll: Option<bool>,
628        hold: bool,
629        query: Box<QueryPlan>,
630    },
631    FetchCursor(crate::ast::FetchCursorStmt),
632    CloseCursor {
633        name: Option<String>,
634    },
635    CreateSequence(crate::ast::CreateSequence),
636    CreateDomain(crate::ast::CreateDomain),
637    AlterSequence(crate::ast::AlterSequence),
638    CreateTableAs {
639        name: String,
640        if_not_exists: bool,
641        column_names: Vec<String>,
642        with_no_data: bool,
643        persistence: crate::ast::RelationPersistence,
644        on_commit: crate::ast::OnCommitAction,
645        query: Box<QueryPlan>,
646    },
647    Prepare {
648        name: String,
649        #[serde(default)]
650        parameter_types: Vec<crate::ast::ColumnType>,
651        body: Box<UnifiedPlan>,
652    },
653    Execute {
654        name: String,
655        params: Vec<ExpressionPlan>,
656    },
657    Deallocate {
658        name: Option<String>,
659    },
660    CreateForeignServer(crate::ast::CreateForeignServer),
661    CreateForeignTable(crate::ast::CreateForeignTable),
662    CreateForeignTableIfNotExists(crate::ast::DeferredCreateForeignTable),
663    AlterForeignTable(crate::ast::AlterForeignTableStmt),
664    Merge(Box<MergePlan>),
665    CreateFunction(Box<crate::ast::CreateFunction>),
666    DropFunction(crate::ast::DropFunctionStmt),
667    AlterRoutine(crate::ast::AlterRoutineStmt),
668    AlterRoutineOwner(crate::ast::AlterRoutineOwnerStmt),
669    RenameRoutine(crate::ast::RenameRoutineStmt),
670    GrantRoutine(crate::ast::GrantRoutineStmt),
671    GrantTable(crate::ast::GrantTableStmt),
672    GrantSequence(crate::ast::GrantSequenceStmt),
673    GrantDatabase(crate::ast::GrantDatabaseStmt),
674    GrantSchema(crate::ast::GrantSchemaStmt),
675    GrantRole(crate::ast::GrantRoleStmt),
676    CreateRole(crate::ast::CreateRoleStmt),
677    AlterRole(crate::ast::AlterRoleStmt),
678    RenameRole(crate::ast::RenameRoleStmt),
679    DropRole(crate::ast::DropRoleStmt),
680    CreateTrigger(crate::ast::CreateTrigger),
681    DropTrigger(crate::ast::DropTrigger),
682    CreateRule(crate::ast::CreateRule),
683    DropRule(crate::ast::DropRule),
684    DoBlock {
685        language: String,
686        body: String,
687    },
688    Call {
689        name: String,
690        args: Vec<ExpressionPlan>,
691    },
692}
693
694/// Classification hook for engine-registered aggregate functions. Built-in
695/// aggregates are always recognised; the callback extends that set without
696/// making the planner depend on the engine.
697pub trait AggregateClassifier {
698    fn is_registered_aggregate(&self, name: &str) -> bool;
699}
700
701impl<F> AggregateClassifier for F
702where
703    F: Fn(&str) -> bool,
704{
705    fn is_registered_aggregate(&self, name: &str) -> bool {
706        self(name)
707    }
708}
709
710pub(super) struct NoRegisteredAggregates;
711
712impl AggregateClassifier for NoRegisteredAggregates {
713    fn is_registered_aggregate(&self, _name: &str) -> bool {
714        false
715    }
716}