use rustc_hash::FxHashMap;
use std::fmt;
use crate::core::{DataType, Error, IndexType, Result, Row, Schema, Value};
use crate::storage::expression::Expression;
use crate::storage::traits::{Index, QueryResult, Scanner};
#[derive(Debug, Clone)]
pub enum ScanPlan {
SeqScan {
table: String,
filter: Option<String>,
},
ParallelSeqScan {
table: String,
filter: Option<String>,
workers: usize,
},
PkLookup {
table: String,
pk_column: String,
pk_value: String,
},
IndexScan {
table: String,
index_name: String,
column: String,
condition: String,
},
MultiIndexScan {
table: String,
indexes: Vec<(String, String, String)>, operation: String, },
CompositeIndexScan {
table: String,
index_name: String,
columns: Vec<String>,
conditions: Vec<String>,
},
}
impl fmt::Display for ScanPlan {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ScanPlan::SeqScan { table, filter } => {
write!(f, "Seq Scan on {}", table)?;
if let Some(flt) = filter {
write!(f, "\n Filter: {}", flt)?;
}
Ok(())
}
ScanPlan::ParallelSeqScan {
table,
filter,
workers,
} => {
write!(f, "Parallel Seq Scan on {} (workers={})", table, workers)?;
if let Some(flt) = filter {
write!(f, "\n Filter: {}", flt)?;
}
Ok(())
}
ScanPlan::PkLookup {
table,
pk_column,
pk_value,
} => {
write!(f, "PK Lookup on {}\n {} = {}", table, pk_column, pk_value)
}
ScanPlan::IndexScan {
table,
index_name,
column,
condition,
} => {
write!(
f,
"Index Scan using {} on {}\n Index Cond: {} {}",
index_name, table, column, condition
)
}
ScanPlan::MultiIndexScan {
table,
indexes,
operation,
} => {
write!(f, "Multi-Index Scan on {} ({})", table, operation)?;
for (idx_name, col, cond) in indexes {
write!(f, "\n -> {} on {}: {}", idx_name, col, cond)?;
}
Ok(())
}
ScanPlan::CompositeIndexScan {
table,
index_name,
columns,
conditions,
} => {
write!(
f,
"Composite Index Scan using {} on {}\n Columns: ({})",
index_name,
table,
columns.join(", ")
)?;
for (col, cond) in columns.iter().zip(conditions.iter()) {
write!(f, "\n {} {}", col, cond)?;
}
Ok(())
}
}
}
}
pub trait Table: Send + Sync {
fn name(&self) -> &str;
fn schema(&self) -> &Schema;
fn create_column(&mut self, name: &str, column_type: DataType, nullable: bool) -> Result<()>;
fn create_column_with_default(
&mut self,
name: &str,
column_type: DataType,
nullable: bool,
_default_expr: Option<String>,
) -> Result<()> {
self.create_column(name, column_type, nullable)
}
fn create_column_with_default_value(
&mut self,
name: &str,
column_type: DataType,
nullable: bool,
default_expr: Option<String>,
_default_value: Option<crate::core::Value>,
) -> Result<()> {
self.create_column_with_default(name, column_type, nullable, default_expr)
}
fn drop_column(&mut self, name: &str) -> Result<()>;
fn insert(&mut self, row: Row) -> Result<Row>;
fn insert_batch(&mut self, rows: Vec<Row>) -> Result<()>;
fn update(
&mut self,
where_expr: Option<&dyn Expression>,
setter: &mut dyn FnMut(Row) -> Result<(Row, bool)>,
) -> Result<i32>;
fn delete(&mut self, where_expr: Option<&dyn Expression>) -> Result<i32>;
fn scan(
&self,
column_indices: &[usize],
where_expr: Option<&dyn Expression>,
) -> Result<Box<dyn Scanner>>;
fn collect_all_rows(&self, where_expr: Option<&dyn Expression>) -> Result<Vec<Row>>;
fn collect_projected_rows(&self, column_indices: &[usize]) -> Result<Vec<Row>> {
let all_rows = self.collect_all_rows(None)?;
Ok(all_rows
.into_iter()
.map(|row| {
let values: Vec<crate::core::Value> = column_indices
.iter()
.map(|&idx| {
row.get(idx)
.cloned()
.unwrap_or(crate::core::Value::null_unknown())
})
.collect();
Row::from_values(values)
})
.collect())
}
fn collect_rows_with_limit(
&self,
where_expr: Option<&dyn Expression>,
limit: usize,
offset: usize,
) -> Result<Vec<Row>> {
let all_rows = self.collect_all_rows(where_expr)?;
Ok(all_rows.into_iter().skip(offset).take(limit).collect())
}
fn collect_rows_with_limit_unordered(
&self,
where_expr: Option<&dyn Expression>,
limit: usize,
offset: usize,
) -> Result<Vec<Row>> {
self.collect_rows_with_limit(where_expr, limit, offset)
}
fn close(&mut self) -> Result<()>;
fn commit(&mut self) -> Result<()>;
fn rollback(&mut self);
fn rollback_to_timestamp(&self, timestamp: i64);
fn has_local_changes(&self) -> bool;
fn get_pending_versions(&self) -> Vec<(i64, Row, bool, i64)> {
Vec::new() }
fn create_index(&self, name: &str, columns: &[&str], is_unique: bool) -> Result<()>;
fn create_index_with_type(
&self,
name: &str,
columns: &[&str],
is_unique: bool,
index_type: Option<IndexType>,
) -> Result<()> {
let _ = index_type;
self.create_index(name, columns, is_unique)
}
fn drop_index(&self, name: &str) -> Result<()>;
fn create_btree_index(
&self,
column_name: &str,
is_unique: bool,
custom_name: Option<&str>,
) -> Result<()>;
fn drop_btree_index(&self, column_name: &str) -> Result<()>;
fn create_multi_column_index(
&self,
name: &str,
columns: &[&str],
is_unique: bool,
) -> Result<()> {
let _ = (name, columns, is_unique);
Err(Error::NotSupportedMessage(
"Multi-column indexes not supported by this table type".to_string(),
))
}
fn has_index_on_column(&self, column_name: &str) -> bool {
let _ = column_name;
false }
fn get_index_on_column(&self, column_name: &str) -> Option<std::sync::Arc<dyn Index>> {
let _ = column_name;
None }
fn get_index(&self, name: &str) -> Option<std::sync::Arc<dyn Index>> {
let _ = name;
None }
fn get_multi_column_index(
&self,
predicate_columns: &[&str],
) -> Option<(std::sync::Arc<dyn Index>, usize)> {
let _ = predicate_columns;
None }
fn get_index_min_value(&self, column_name: &str) -> Option<Value> {
let _ = column_name;
None }
fn get_index_max_value(&self, column_name: &str) -> Option<Value> {
let _ = column_name;
None }
fn row_count(&self) -> usize {
0 }
fn collect_rows_ordered_by_index(
&self,
column_name: &str,
ascending: bool,
limit: usize,
offset: usize,
) -> Option<Vec<Row>> {
let _ = (column_name, ascending, limit, offset);
None }
fn collect_rows_grouped_by_partition(
&self,
column_name: &str,
) -> Option<Vec<(Value, Vec<Row>)>> {
let _ = column_name;
None }
fn get_partition_values(&self, column_name: &str) -> Option<Vec<Value>> {
let _ = column_name;
None }
fn get_rows_for_partition_value(
&self,
column_name: &str,
partition_value: &Value,
) -> Option<Vec<Row>> {
let _ = (column_name, partition_value);
None }
fn fetch_rows_by_ids(&self, row_ids: &[i64], filter: &dyn Expression) -> Vec<(i64, Row)> {
let _ = (row_ids, filter);
Vec::new() }
fn rename_column(&self, old_name: &str, new_name: &str) -> Result<()>;
fn modify_column(
&self,
name: &str,
column_type: DataType,
nullable: bool,
auto_increment: Option<bool>,
check_expr: Option<Option<String>>,
) -> Result<()>;
fn select(
&self,
columns: &[&str],
expr: Option<&dyn Expression>,
) -> Result<Box<dyn QueryResult>>;
fn select_with_aliases(
&self,
columns: &[&str],
expr: Option<&dyn Expression>,
aliases: &FxHashMap<String, String>,
) -> Result<Box<dyn QueryResult>>;
fn select_as_of(
&self,
columns: &[&str],
expr: Option<&dyn Expression>,
temporal_type: &str,
temporal_value: i64,
) -> Result<Box<dyn QueryResult>>;
fn explain_scan(&self, where_expr: Option<&dyn Expression>) -> ScanPlan {
ScanPlan::SeqScan {
table: self.name().to_string(),
filter: where_expr.map(|e| format!("{:?}", e)),
}
}
fn set_zone_maps(&self, _zone_maps: crate::storage::mvcc::zonemap::TableZoneMap) {
}
fn get_zone_maps(&self) -> Option<std::sync::Arc<crate::storage::mvcc::zonemap::TableZoneMap>> {
None }
fn get_segments_to_scan(
&self,
_column: &str,
_operator: crate::core::Operator,
_value: &Value,
) -> Option<Vec<u32>> {
None }
}
#[cfg(test)]
mod tests {
use super::*;
fn _assert_object_safe(_: &dyn Table) {}
}