use super::{
optimize_unified_plan, reorder_unified_plan_joins, AggregateClassifier, JoinGraphError,
RelationStats, ScalarExpr, SourcePlan, UnifiedPlan,
};
use crate::LocalAccessEstimate;
#[derive(Debug, thiserror::Error)]
pub enum OptimizerError {
#[error(transparent)]
Expression(#[from] uqa_sql::SQLError),
#[error(transparent)]
JoinGraph(#[from] JoinGraphError),
}
pub type OptimizerResult<T> = Result<T, OptimizerError>;
pub type ConstantEvaluator = fn(&ScalarExpr) -> Result<uqa_core::Value, uqa_sql::SQLError>;
#[derive(Debug, Clone)]
pub struct OptimizerConfig {
pub enable_filter_pushdown: bool,
pub enable_boolean_simplify: bool,
pub enable_vector_threshold_merge: bool,
pub enable_join_reordering: bool,
pub constant_evaluator: ConstantEvaluator,
}
impl OptimizerConfig {
#[must_use]
pub const fn new(constant_evaluator: ConstantEvaluator) -> Self {
Self {
enable_filter_pushdown: true,
enable_boolean_simplify: true,
enable_vector_threshold_merge: true,
enable_join_reordering: true,
constant_evaluator,
}
}
}
pub trait SourceStatistics {
fn relation_statistics(&self, table: &str) -> Option<RelationStats>;
fn source_access_estimate(&self, _source: &SourcePlan) -> Option<LocalAccessEstimate> {
None
}
fn local_access_estimate(
&self,
_table: &str,
_predicate: &ScalarExpr,
) -> Option<LocalAccessEstimate> {
None
}
}
impl<F> SourceStatistics for F
where
F: Fn(&str) -> Option<RelationStats>,
{
fn relation_statistics(&self, table: &str) -> Option<RelationStats> {
self(table)
}
}
struct NoSourceStatistics;
impl SourceStatistics for NoSourceStatistics {
fn relation_statistics(&self, _table: &str) -> Option<RelationStats> {
None
}
}
struct NoRegisteredAggregates;
impl AggregateClassifier for NoRegisteredAggregates {
fn is_registered_aggregate(&self, _name: &str) -> bool {
false
}
}
pub fn optimize(plan: UnifiedPlan, config: &OptimizerConfig) -> OptimizerResult<UnifiedPlan> {
optimize_with_aggregates_and_statistics(
plan,
config,
&NoRegisteredAggregates,
&NoSourceStatistics,
)
}
pub fn optimize_with_aggregates(
plan: UnifiedPlan,
config: &OptimizerConfig,
aggregates: &dyn AggregateClassifier,
) -> OptimizerResult<UnifiedPlan> {
optimize_with_aggregates_and_statistics(plan, config, aggregates, &NoSourceStatistics)
}
pub fn optimize_with_statistics(
plan: UnifiedPlan,
config: &OptimizerConfig,
statistics: &dyn SourceStatistics,
) -> OptimizerResult<UnifiedPlan> {
optimize_with_aggregates_and_statistics(plan, config, &NoRegisteredAggregates, statistics)
}
pub fn optimize_with_aggregates_and_statistics(
mut plan: UnifiedPlan,
config: &OptimizerConfig,
aggregates: &dyn AggregateClassifier,
statistics: &dyn SourceStatistics,
) -> OptimizerResult<UnifiedPlan> {
optimize_unified_plan(&mut plan, config, aggregates)?;
if config.enable_join_reordering {
reorder_unified_plan_joins(&mut plan, statistics)?;
}
Ok(plan)
}