chryso_optimizer/
estimation.rs1use 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}