Skip to main content

chryso_optimizer/
estimation.rs

1use chryso_metadata::{ColumnStats, StatsCache, TableStats};
2use chryso_planner::LogicalPlan;
3
4pub trait CardinalityEstimator {
5    fn estimate(&self, plan: &LogicalPlan, stats: &StatsCache) -> f64;
6}
7
8pub struct NaiveEstimator;
9
10impl CardinalityEstimator for NaiveEstimator {
11    fn estimate(&self, plan: &LogicalPlan, stats: &StatsCache) -> f64 {
12        match plan {
13            LogicalPlan::Scan { table } => stats
14                .table_stats(table)
15                .map(|s| s.row_count)
16                .unwrap_or(1000.0),
17            LogicalPlan::IndexScan { .. } => 100.0,
18            LogicalPlan::Dml { .. } => 1.0,
19            LogicalPlan::Derived { input, .. } => self.estimate(input, stats),
20            LogicalPlan::Filter { input, .. } => self.estimate(input, stats) * 0.25,
21            LogicalPlan::Projection { input, .. } => self.estimate(input, stats),
22            LogicalPlan::Join { left, right, .. } => {
23                self.estimate(left, stats) * self.estimate(right, stats) * 0.1
24            }
25            LogicalPlan::Aggregate { input, .. } => self.estimate(input, stats) * 0.1,
26            LogicalPlan::Distinct { input } => self.estimate(input, stats) * 0.1,
27            LogicalPlan::TopN { limit, .. } => *limit as f64,
28            LogicalPlan::Sort { input, .. } => self.estimate(input, stats),
29            LogicalPlan::Limit { limit, .. } => limit.unwrap_or(100) as f64,
30        }
31    }
32}
33
34pub fn default_table_stats() -> TableStats {
35    TableStats { row_count: 1000.0 }
36}
37
38pub fn default_column_stats() -> ColumnStats {
39    ColumnStats {
40        distinct_count: 100.0,
41        null_fraction: 0.0,
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::{CardinalityEstimator, NaiveEstimator};
48    use chryso_metadata::{StatsCache, TableStats};
49    use chryso_planner::LogicalPlan;
50
51    #[test]
52    fn estimate_scan_uses_stats() {
53        let estimator = NaiveEstimator;
54        let mut stats = StatsCache::new();
55        stats.insert_table_stats("orders", TableStats { row_count: 500.0 });
56        let plan = LogicalPlan::Scan {
57            table: "orders".to_string(),
58        };
59        let estimate = estimator.estimate(&plan, &stats);
60        assert_eq!(estimate, 500.0);
61    }
62}