use crate::schema::{DataType, Schema, Value};
#[derive(Debug, Clone)]
pub enum Expr {
Relation { name: String, schema: Schema },
Select {
input: Box<Expr>,
predicate: Predicate,
},
Project {
input: Box<Expr>,
columns: Vec<String>,
},
Join {
left: Box<Expr>,
right: Box<Expr>,
condition: JoinCondition,
},
Union { left: Box<Expr>, right: Box<Expr> },
Intersect { left: Box<Expr>, right: Box<Expr> },
Difference { left: Box<Expr>, right: Box<Expr> },
Rename {
input: Box<Expr>,
from: String,
to: String,
},
Aggregate {
input: Box<Expr>,
group_by: Vec<String>,
aggregates: Vec<AggregateFunc>,
},
Sort {
input: Box<Expr>,
columns: Vec<(String, SortOrder)>,
},
Limit {
input: Box<Expr>,
count: usize,
},
Offset {
input: Box<Expr>,
count: usize,
},
}
#[derive(Debug, Clone)]
pub enum Predicate {
Compare {
left: ColumnRef,
op: CompareOp,
right: Operand,
},
And(Box<Predicate>, Box<Predicate>),
Or(Box<Predicate>, Box<Predicate>),
Not(Box<Predicate>),
In {
column: ColumnRef,
values: Vec<Value>,
},
Like {
column: ColumnRef,
pattern: String,
},
IsNull(ColumnRef),
Between {
column: ColumnRef,
low: Value,
high: Value,
},
}
#[derive(Debug, Clone)]
pub enum Operand {
Column(ColumnRef),
Literal(Value),
}
#[derive(Debug, Clone)]
pub struct ColumnRef {
pub table: Option<String>,
pub name: String,
}
impl ColumnRef {
pub fn new(name: impl Into<String>) -> Self {
Self {
table: None,
name: name.into(),
}
}
pub fn qualified(table: impl Into<String>, name: impl Into<String>) -> Self {
Self {
table: Some(table.into()),
name: name.into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompareOp {
Eq,
NotEq,
Lt,
Lte,
Gt,
Gte,
}
#[derive(Debug, Clone)]
pub enum JoinCondition {
On(Predicate),
Using(Vec<String>),
}
#[derive(Debug, Clone)]
pub struct AggregateFunc {
pub name: String,
pub func: AggregateType,
pub input: String,
}
#[derive(Debug, Clone)]
pub enum AggregateType {
Count,
Sum,
Avg,
Min,
Max,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortOrder {
Asc,
Desc,
}
impl Expr {
pub fn relation(name: impl Into<String>, schema: Schema) -> Self {
Expr::Relation {
name: name.into(),
schema,
}
}
pub fn select(self, predicate: Predicate) -> Self {
Expr::Select {
input: Box::new(self),
predicate,
}
}
pub fn project(self, columns: Vec<String>) -> Self {
Expr::Project {
input: Box::new(self),
columns,
}
}
pub fn join(self, right: Expr, condition: JoinCondition) -> Self {
Expr::Join {
left: Box::new(self),
right: Box::new(right),
condition,
}
}
pub fn rename(self, from: impl Into<String>, to: impl Into<String>) -> Self {
Expr::Rename {
input: Box::new(self),
from: from.into(),
to: to.into(),
}
}
pub fn infer_schema(&self) -> Schema {
match self {
Expr::Relation { schema, .. } => schema.clone(),
Expr::Select { input, .. } => input.infer_schema(),
Expr::Project { input, columns } => {
let input_schema = input.infer_schema();
let mut output_schema = Schema::new(&input_schema.name);
for col_name in columns {
if let Some(col) = input_schema.find_column(col_name) {
output_schema.columns.push(col.clone());
}
}
output_schema
}
Expr::Rename { input, from, to } => {
let mut schema = input.infer_schema();
if let Some(col) = schema.columns.iter_mut().find(|c| c.name == *from) {
col.name = to.clone();
}
schema
}
_ => Schema::new("unknown"),
}
}
}