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    pub ctes: Vec<CtePlan>,
391    pub rows: Vec<Vec<ScalarExpr>>,
392    pub source: Option<Box<QueryPlan>>,
393    pub on_conflict: Option<ConflictPlan>,
394    pub returning: Vec<ProjectionPlan>,
395    pub returning_aliases: crate::ast::ReturningAliases,
396    pub subqueries: Vec<QueryPlan>,
397    pub view_checks: Vec<ViewCheckPlan>,
398    pub view_rule_relations: Vec<String>,
399    pub view_rule_insert_plans: Vec<ViewRuleInsertPlan>,
400    pub view_rule_returning: Option<ViewRuleReturningPlan>,
401}
402
403#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
404pub struct ConflictPlan {
405    pub predicate: Option<Box<ScalarExpr>>,
406    pub constraint: Option<String>,
407    pub conflict_columns: Vec<String>,
408    pub expressions: Vec<ScalarExpr>,
409    pub action: ConflictActionPlan,
410}
411
412#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
413pub enum ConflictActionPlan {
414    Nothing,
415    Update {
416        assignments: Vec<AssignmentPlan>,
417        predicate: Option<Box<ScalarExpr>>,
418    },
419}
420
421#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
422pub struct UpdatePlan {
423    pub table: String,
424    pub target_relation_bound: bool,
425    /// Whether non-target relation references are stored catalog identities rather than names that must be resolved in the executing session.
426    pub relations_bound: bool,
427    /// Effective role used for non-target privilege checks in an internally rewritten statement.
428    pub statement_privilege_subject: Option<RoleReference>,
429    /// Effective role used only for privilege checks on an internally rewritten target relation.
430    pub target_privilege_subject: Option<RoleReference>,
431    pub target_qualifier: String,
432    pub include_descendants: bool,
433    pub assignments: Vec<AssignmentPlan>,
434    pub predicate: Option<ScalarExpr>,
435    pub ctes: Vec<CtePlan>,
436    pub source: Option<Box<SourcePlan>>,
437    pub returning: Vec<ProjectionPlan>,
438    pub returning_aliases: crate::ast::ReturningAliases,
439    pub subqueries: Vec<QueryPlan>,
440    pub view_checks: Vec<ViewCheckPlan>,
441    pub view_rule_relations: Vec<String>,
442    pub view_rule_update_plans: Vec<ViewRuleUpdatePlan>,
443    pub view_rule_returning: Option<ViewRuleReturningPlan>,
444}
445
446#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
447pub struct DeletePlan {
448    pub table: String,
449    pub target_relation_bound: bool,
450    /// Whether non-target relation references are stored catalog identities rather than names that must be resolved in the executing session.
451    pub relations_bound: bool,
452    /// Effective role used for non-target privilege checks in an internally rewritten statement.
453    pub statement_privilege_subject: Option<RoleReference>,
454    /// Effective role used only for privilege checks on an internally rewritten target relation.
455    pub target_privilege_subject: Option<RoleReference>,
456    pub target_qualifier: String,
457    pub include_descendants: bool,
458    pub predicate: Option<ScalarExpr>,
459    pub ctes: Vec<CtePlan>,
460    pub source: Option<Box<SourcePlan>>,
461    pub returning: Vec<ProjectionPlan>,
462    pub returning_aliases: crate::ast::ReturningAliases,
463    pub subqueries: Vec<QueryPlan>,
464    pub view_rule_relations: Vec<String>,
465    pub view_rule_returning: Option<ViewRuleReturningPlan>,
466}
467
468#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
469pub struct MergePlan {
470    #[serde(default)]
471    pub ctes: Vec<CtePlan>,
472    pub target: String,
473    /// Effective role used for non-target privilege checks in an internally rewritten statement.
474    pub statement_privilege_subject: Option<RoleReference>,
475    /// Effective role used only for privilege checks on an internally rewritten target relation.
476    pub target_privilege_subject: Option<RoleReference>,
477    pub target_qualifier: String,
478    pub target_alias: Option<String>,
479    pub include_descendants: bool,
480    pub target_predicate: Option<ScalarExpr>,
481    pub source: Box<SourcePlan>,
482    pub join_condition: ScalarExpr,
483    pub when_clauses: Vec<MergeWhenPlan>,
484    pub returning: Vec<ProjectionPlan>,
485    pub returning_aliases: crate::ast::ReturningAliases,
486    pub subqueries: Vec<QueryPlan>,
487    pub view_checks: Vec<ViewCheckPlan>,
488}
489
490#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
491pub enum MergeWhenPlan {
492    UpdateMatched {
493        condition: Option<ScalarExpr>,
494        assignments: Vec<AssignmentPlan>,
495    },
496    DeleteMatched {
497        condition: Option<ScalarExpr>,
498    },
499    UpdateNotMatchedBySource {
500        condition: Option<ScalarExpr>,
501        assignments: Vec<AssignmentPlan>,
502    },
503    DeleteNotMatchedBySource {
504        condition: Option<ScalarExpr>,
505    },
506    InsertNotMatched {
507        condition: Option<ScalarExpr>,
508        columns: Vec<crate::ast::AssignmentTarget<ScalarExpr>>,
509        values: Vec<ScalarExpr>,
510    },
511    NothingMatched {
512        condition: Option<ScalarExpr>,
513    },
514    NothingNotMatched {
515        condition: Option<ScalarExpr>,
516    },
517    NothingNotMatchedBySource {
518        condition: Option<ScalarExpr>,
519    },
520}
521
522/// Non-query statement plans. Mutations own physical sources and scalar IR;
523/// query-bearing catalog commands own explicit query children. Typed DDL and
524/// procedural payloads contain catalog data, never a second SQL dispatcher.
525#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
526pub enum CommandPlan {
527    CreateTable(Box<crate::ast::CreateTable>),
528    CreateTableIfNotExists(crate::ast::DeferredCreateTable),
529    CreateIndex(crate::ast::CreateIndex),
530    RenameIndex(crate::ast::RenameIndexStmt),
531    Insert(Box<InsertPlan>),
532    Update(Box<UpdatePlan>),
533    Delete(Box<DeletePlan>),
534    Drop(crate::ast::DropStmt),
535    AlterTable(Box<crate::ast::AlterTableStmt>),
536    AlterView(crate::ast::AlterViewStmt),
537    CreateView {
538        name: String,
539        column_names: Vec<String>,
540        query: Box<QueryPlan>,
541        or_replace: bool,
542        persistence: crate::ast::RelationPersistence,
543        options: Vec<(String, String)>,
544    },
545    CreateMaterializedView {
546        name: String,
547        column_names: Vec<String>,
548        if_not_exists: bool,
549        with_no_data: bool,
550        options: Vec<(String, String)>,
551        query: Box<QueryPlan>,
552    },
553    RefreshMaterializedView {
554        name: String,
555        concurrently: bool,
556        with_no_data: bool,
557    },
558    CreateSchema {
559        name: Option<String>,
560        if_not_exists: bool,
561        #[serde(default, skip_serializing_if = "Option::is_none")]
562        authorization: Option<crate::ast::SchemaAuthorization>,
563    },
564    AlterSchemaOwner {
565        name: String,
566        new_owner: crate::ast::RoleSpecification,
567    },
568    Notify {
569        channel: String,
570        payload: String,
571    },
572    Listen {
573        channel: String,
574    },
575    Unlisten {
576        channel: Option<String>,
577    },
578    SetVariable {
579        name: String,
580        value: String,
581        #[serde(default)]
582        local: bool,
583        #[serde(default)]
584        is_default: bool,
585    },
586    ResetVariable {
587        name: String,
588    },
589    ResetAllVariables,
590    SetConstraints {
591        constraints: Vec<crate::ast::SetConstraintName>,
592        deferred: bool,
593    },
594    ShowVariable {
595        name: String,
596    },
597    Discard {
598        target: crate::ast::DiscardTarget,
599    },
600    Load {
601        library: String,
602    },
603    Explain {
604        analyze: bool,
605        verbose: bool,
606        format: Option<String>,
607        body: Box<UnifiedPlan>,
608    },
609    Analyze {
610        table: Option<String>,
611    },
612    Vacuum(crate::ast::VacuumStmt),
613    LockTable(crate::ast::LockTableStmt),
614    Truncate {
615        tables: Vec<crate::ast::TruncateTarget>,
616        cascade: bool,
617        restart_identity: bool,
618    },
619    Transaction(crate::ast::TransactionStmt),
620    DeclareCursor {
621        name: String,
622        binary: bool,
623        scroll: Option<bool>,
624        hold: bool,
625        query: Box<QueryPlan>,
626    },
627    FetchCursor(crate::ast::FetchCursorStmt),
628    CloseCursor {
629        name: Option<String>,
630    },
631    CreateSequence(crate::ast::CreateSequence),
632    CreateDomain(crate::ast::CreateDomain),
633    AlterSequence(crate::ast::AlterSequence),
634    CreateTableAs {
635        name: String,
636        if_not_exists: bool,
637        column_names: Vec<String>,
638        with_no_data: bool,
639        persistence: crate::ast::RelationPersistence,
640        on_commit: crate::ast::OnCommitAction,
641        query: Box<QueryPlan>,
642    },
643    Prepare {
644        name: String,
645        #[serde(default)]
646        parameter_types: Vec<crate::ast::ColumnType>,
647        body: Box<UnifiedPlan>,
648    },
649    Execute {
650        name: String,
651        params: Vec<ExpressionPlan>,
652    },
653    Deallocate {
654        name: Option<String>,
655    },
656    CreateForeignServer(crate::ast::CreateForeignServer),
657    CreateForeignTable(crate::ast::CreateForeignTable),
658    CreateForeignTableIfNotExists(crate::ast::DeferredCreateForeignTable),
659    AlterForeignTable(crate::ast::AlterForeignTableStmt),
660    Merge(Box<MergePlan>),
661    CreateFunction(Box<crate::ast::CreateFunction>),
662    DropFunction(crate::ast::DropFunctionStmt),
663    AlterRoutine(crate::ast::AlterRoutineStmt),
664    AlterRoutineOwner(crate::ast::AlterRoutineOwnerStmt),
665    RenameRoutine(crate::ast::RenameRoutineStmt),
666    GrantRoutine(crate::ast::GrantRoutineStmt),
667    GrantTable(crate::ast::GrantTableStmt),
668    GrantSequence(crate::ast::GrantSequenceStmt),
669    GrantDatabase(crate::ast::GrantDatabaseStmt),
670    GrantSchema(crate::ast::GrantSchemaStmt),
671    GrantRole(crate::ast::GrantRoleStmt),
672    CreateRole(crate::ast::CreateRoleStmt),
673    AlterRole(crate::ast::AlterRoleStmt),
674    RenameRole(crate::ast::RenameRoleStmt),
675    DropRole(crate::ast::DropRoleStmt),
676    CreateTrigger(crate::ast::CreateTrigger),
677    DropTrigger(crate::ast::DropTrigger),
678    CreateRule(crate::ast::CreateRule),
679    DropRule(crate::ast::DropRule),
680    DoBlock {
681        language: String,
682        body: String,
683    },
684    Call {
685        name: String,
686        args: Vec<ExpressionPlan>,
687    },
688}
689
690/// Classification hook for engine-registered aggregate functions. Built-in
691/// aggregates are always recognised; the callback extends that set without
692/// making the planner depend on the engine.
693pub trait AggregateClassifier {
694    fn is_registered_aggregate(&self, name: &str) -> bool;
695}
696
697impl<F> AggregateClassifier for F
698where
699    F: Fn(&str) -> bool,
700{
701    fn is_registered_aggregate(&self, name: &str) -> bool {
702        self(name)
703    }
704}
705
706pub(super) struct NoRegisteredAggregates;
707
708impl AggregateClassifier for NoRegisteredAggregates {
709    fn is_registered_aggregate(&self, _name: &str) -> bool {
710        false
711    }
712}