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