use crate::query_statistics_v1::{CostModel, TableStats, StatisticsCollector};
use crate::query_optimizer_v1::{LogicalExpr, PhysicalExpr, OptimizerContext, optimize};
use crate::join_strategies_v1::{Row, JoinStrategy, JoinStrategyConfig, choose_join_strategy, execute_join};
use crate::predicate_pushdown_v1::{Predicate, ChunkStats, PredicateResult, filter_chunks};
use crate::adaptive_executor_v1::{AdaptiveExecutionContext, AdaptiveQueryPipeline, ExecutionHint};
use std::collections::HashMap;
pub struct QueryOptimizationEngine {
pub table_stats: HashMap<String, TableStats>,
pub cost_model: CostModel,
pub join_config: JoinStrategyConfig,
pub enable_adaptive: bool,
}
impl QueryOptimizationEngine {
pub fn new() -> Self {
QueryOptimizationEngine {
table_stats: HashMap::new(),
cost_model: CostModel::default(),
join_config: JoinStrategyConfig::default(),
enable_adaptive: true,
}
}
pub fn register_table(&mut self, name: String, stats: TableStats) {
self.table_stats.insert(name, stats);
}
pub fn optimize_query(&self, logical: LogicalExpr) -> Result<PhysicalExpr, String> {
let context = OptimizerContext::new(self.table_stats.clone());
optimize(logical, &context)
}
pub fn execute(&self, physical: &PhysicalExpr) -> Result<Vec<Row>, String> {
self.execute_physical_plan(physical)
}
fn execute_physical_plan(&self, plan: &PhysicalExpr) -> Result<Vec<Row>, String> {
match plan {
PhysicalExpr::SeqScan { table, .. } => {
let mut row = HashMap::new();
row.insert("id".to_string(), "1".to_string());
Ok(vec![row])
}
PhysicalExpr::Filter { input, predicate, selectivity } => {
let input_rows = self.execute_physical_plan(input)?;
let filtered_count = ((input_rows.len() as f64) * selectivity).ceil() as usize;
Ok(input_rows.into_iter().take(filtered_count).collect())
}
PhysicalExpr::Project { input, columns } => {
let input_rows = self.execute_physical_plan(input)?;
Ok(input_rows)
}
PhysicalExpr::HashJoin { left, right, join_keys, .. } => {
let left_rows = self.execute_physical_plan(left)?;
let right_rows = self.execute_physical_plan(right)?;
execute_join(&left_rows, &right_rows, join_keys, JoinStrategy::Hash)
}
PhysicalExpr::NestedLoopJoin { left, right, join_keys, .. } => {
let left_rows = self.execute_physical_plan(left)?;
let right_rows = self.execute_physical_plan(right)?;
execute_join(&left_rows, &right_rows, join_keys, JoinStrategy::NestedLoop)
}
PhysicalExpr::SortMergeJoin { left, right, join_keys, .. } => {
let left_rows = self.execute_physical_plan(left)?;
let right_rows = self.execute_physical_plan(right)?;
execute_join(&left_rows, &right_rows, join_keys, JoinStrategy::SortMerge)
}
PhysicalExpr::GroupBy { input, group_keys, .. } => {
let input_rows = self.execute_physical_plan(input)?;
Ok(input_rows)
}
PhysicalExpr::Sort { input, order_keys, .. } => {
let mut rows = self.execute_physical_plan(input)?;
Ok(rows)
}
PhysicalExpr::IndexScan { .. } => {
Err("IndexScan not yet implemented".to_string())
}
}
}
pub fn estimate_cost(&self, plan: &PhysicalExpr) -> f64 {
plan.total_cost()
}
pub fn estimate_rows(&self, plan: &PhysicalExpr) -> u64 {
plan.estimated_rows(&self.table_stats)
}
pub fn full_pipeline(
&self,
logical: LogicalExpr,
) -> Result<(PhysicalExpr, Vec<Row>, f64), String> {
let physical = self.optimize_query(logical)?;
let cost = self.estimate_cost(&physical);
let results = self.execute(&physical)?;
Ok((physical, results, cost))
}
}
pub fn example_optimization_pipeline() -> Result<(), String> {
let mut engine = QueryOptimizationEngine::new();
let mut users_stats = TableStats::new("users".to_string(), 10000);
let mut orders_stats = TableStats::new("orders".to_string(), 50000);
engine.register_table("users".to_string(), users_stats);
engine.register_table("orders".to_string(), orders_stats);
let logical = LogicalExpr::Filter {
input: Box::new(LogicalExpr::Scan {
table: "users".to_string(),
}),
predicate: "age > 18".to_string(),
};
let (physical, results, cost) = engine.full_pipeline(logical)?;
println!("Optimized plan: {:?}", physical);
println!("Estimated cost: {}", cost);
println!("Results: {} rows", results.len());
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_engine_creation() {
let engine = QueryOptimizationEngine::new();
assert_eq!(engine.table_stats.len(), 0);
}
#[test]
fn test_register_table() {
let mut engine = QueryOptimizationEngine::new();
let stats = TableStats::new("users".to_string(), 1000);
engine.register_table("users".to_string(), stats);
assert_eq!(engine.table_stats.len(), 1);
}
#[test]
fn test_optimize_scan() {
let mut engine = QueryOptimizationEngine::new();
let stats = TableStats::new("users".to_string(), 1000);
engine.register_table("users".to_string(), stats);
let logical = LogicalExpr::Scan {
table: "users".to_string(),
};
let result = engine.optimize_query(logical);
assert!(result.is_ok());
}
#[test]
fn test_optimize_filter() {
let mut engine = QueryOptimizationEngine::new();
let stats = TableStats::new("users".to_string(), 1000);
engine.register_table("users".to_string(), stats);
let logical = LogicalExpr::Filter {
input: Box::new(LogicalExpr::Scan {
table: "users".to_string(),
}),
predicate: "age > 18".to_string(),
};
let result = engine.optimize_query(logical);
assert!(result.is_ok());
let physical = result.unwrap();
assert!(matches!(physical, PhysicalExpr::Filter { .. }));
}
#[test]
fn test_full_pipeline() {
let mut engine = QueryOptimizationEngine::new();
let stats = TableStats::new("users".to_string(), 1000);
engine.register_table("users".to_string(), stats);
let logical = LogicalExpr::Scan {
table: "users".to_string(),
};
let result = engine.full_pipeline(logical);
assert!(result.is_ok());
let (physical, results, cost) = result.unwrap();
assert!(matches!(physical, PhysicalExpr::SeqScan { .. }));
assert!(cost > 0.0);
}
#[test]
fn test_estimate_cost() {
let engine = QueryOptimizationEngine::new();
let plan = PhysicalExpr::SeqScan {
table: "users".to_string(),
cost: 100.0,
};
let cost = engine.estimate_cost(&plan);
assert_eq!(cost, 100.0);
}
#[test]
fn test_example_pipeline() {
let result = example_optimization_pipeline();
assert!(result.is_ok());
}
}