use crate::Result;
#[derive(Debug, Clone)]
pub enum Operator {
Scan { source: String },
Filter { predicate: String },
Project { columns: Vec<String> },
IndexScan { index_type: String, range: String },
Join { left: Box<ExecutionPlan>, right: Box<ExecutionPlan> },
}
#[derive(Debug, Clone)]
pub struct ExecutionPlan {
pub operator: Operator,
pub children: Vec<ExecutionPlan>,
pub cost: f64,
}
impl ExecutionPlan {
pub fn scan(source: String) -> Self {
Self {
operator: Operator::Scan { source },
children: Vec::new(),
cost: 100.0,
}
}
pub fn index_scan(index_type: String, range: String) -> Self {
Self {
operator: Operator::IndexScan { index_type, range },
children: Vec::new(),
cost: 10.0, }
}
pub fn with_filter(self, predicate: String) -> Self {
Self {
operator: Operator::Filter { predicate },
children: vec![self.clone()],
cost: self.cost + 5.0,
}
}
pub fn with_project(self, columns: Vec<String>) -> Self {
Self {
operator: Operator::Project { columns },
children: vec![self.clone()],
cost: self.cost + 1.0,
}
}
}
pub struct QueryPlanner {
enable_optimization: bool,
}
impl QueryPlanner {
pub fn new() -> Result<Self> {
Ok(Self {
enable_optimization: true,
})
}
pub fn without_optimization() -> Result<Self> {
Ok(Self {
enable_optimization: false,
})
}
pub fn plan_range_query(&self, index_type: &str, start: i64, end: i64) -> Result<ExecutionPlan> {
let range = format!("[{}, {})", start, end);
if self.enable_optimization && self.should_use_index(index_type) {
Ok(ExecutionPlan::index_scan(index_type.to_string(), range))
} else {
Ok(ExecutionPlan::scan("default".to_string())
.with_filter(format!("timestamp BETWEEN {} AND {}", start, end)))
}
}
fn should_use_index(&self, _index_type: &str) -> bool {
true
}
pub fn optimize(&self, plan: ExecutionPlan) -> Result<ExecutionPlan> {
if !self.enable_optimization {
return Ok(plan);
}
Ok(plan)
}
}
impl Default for QueryPlanner {
fn default() -> Self {
Self::new().unwrap()
}
}