uqa_planner/optimizer/
api.rs1use super::{
10 optimize_unified_plan, reorder_unified_plan_joins, AggregateClassifier, JoinGraphError,
11 RelationStats, ScalarExpr, SourcePlan, UnifiedPlan,
12};
13use crate::LocalAccessEstimate;
14
15#[derive(Debug, thiserror::Error)]
17pub enum OptimizerError {
18 #[error(transparent)]
19 Expression(#[from] uqa_sql::SQLError),
20 #[error(transparent)]
21 JoinGraph(#[from] JoinGraphError),
22}
23
24pub type OptimizerResult<T> = Result<T, OptimizerError>;
25
26pub type ConstantEvaluator = fn(&ScalarExpr) -> Result<uqa_core::Value, uqa_sql::SQLError>;
28
29pub fn optimize_scalar_expression(
31 expression: &mut ScalarExpr,
32 config: &OptimizerConfig,
33) -> Result<(), uqa_sql::SQLError> {
34 super::optimize_scalar_slot(expression, config)
35}
36
37#[derive(Debug, Clone)]
38pub struct OptimizerConfig<'a> {
39 pub coerced_conditionals: bool,
41 pub enable_filter_pushdown: bool,
42 pub enable_boolean_simplify: bool,
43 pub enable_vector_threshold_merge: bool,
44 pub enable_join_reordering: bool,
45 pub constant_evaluator: ConstantEvaluator,
47 pub builtin_permissions: Option<
49 std::sync::Arc<dyn uqa_sql::catalog::security::builtin_routines::BuiltinRoutineExecution>,
50 >,
51 pub routine_inlining: Option<uqa_sql::routines::inlining::RoutineInliningContext<'a>>,
53 pub(super) active_inline_routines: Vec<[u8; 16]>,
54}
55
56impl OptimizerConfig<'_> {
57 #[must_use]
58 pub const fn new(constant_evaluator: ConstantEvaluator) -> Self {
59 Self {
60 coerced_conditionals: false,
61 enable_filter_pushdown: true,
62 enable_boolean_simplify: true,
63 enable_vector_threshold_merge: true,
64 enable_join_reordering: true,
65 constant_evaluator,
66 builtin_permissions: None,
67 routine_inlining: None,
68 active_inline_routines: Vec::new(),
69 }
70 }
71}
72
73pub trait SourceStatistics {
78 fn relation_statistics(&self, table: &str) -> Option<RelationStats>;
79
80 fn source_access_estimate(&self, _source: &SourcePlan) -> Option<LocalAccessEstimate> {
83 None
84 }
85
86 fn local_access_estimate(
89 &self,
90 _table: &str,
91 _predicate: &ScalarExpr,
92 ) -> Option<LocalAccessEstimate> {
93 None
94 }
95}
96
97impl<F> SourceStatistics for F
98where
99 F: Fn(&str) -> Option<RelationStats>,
100{
101 fn relation_statistics(&self, table: &str) -> Option<RelationStats> {
102 self(table)
103 }
104}
105
106struct NoSourceStatistics;
107
108impl SourceStatistics for NoSourceStatistics {
109 fn relation_statistics(&self, _table: &str) -> Option<RelationStats> {
110 None
111 }
112}
113
114struct NoRegisteredAggregates;
115
116impl AggregateClassifier for NoRegisteredAggregates {
117 fn is_registered_aggregate(&self, _name: &str) -> bool {
118 false
119 }
120}
121
122pub fn optimize(plan: UnifiedPlan, config: &OptimizerConfig) -> OptimizerResult<UnifiedPlan> {
124 optimize_with_aggregates_and_statistics(
125 plan,
126 config,
127 &NoRegisteredAggregates,
128 &NoSourceStatistics,
129 )
130}
131
132pub fn optimize_with_aggregates(
134 plan: UnifiedPlan,
135 config: &OptimizerConfig,
136 aggregates: &dyn AggregateClassifier,
137) -> OptimizerResult<UnifiedPlan> {
138 optimize_with_aggregates_and_statistics(plan, config, aggregates, &NoSourceStatistics)
139}
140
141pub fn optimize_with_statistics(
143 plan: UnifiedPlan,
144 config: &OptimizerConfig,
145 statistics: &dyn SourceStatistics,
146) -> OptimizerResult<UnifiedPlan> {
147 optimize_with_aggregates_and_statistics(plan, config, &NoRegisteredAggregates, statistics)
148}
149
150pub fn optimize_with_aggregates_and_statistics(
153 mut plan: UnifiedPlan,
154 config: &OptimizerConfig,
155 aggregates: &dyn AggregateClassifier,
156 statistics: &dyn SourceStatistics,
157) -> OptimizerResult<UnifiedPlan> {
158 optimize_unified_plan(&mut plan, config, aggregates)?;
159 if config.enable_join_reordering {
160 reorder_unified_plan_joins(&mut plan, statistics)?;
161 }
162 Ok(plan)
163}