use std::cmp::{Ordering, PartialOrd};
#[derive(Debug,Clone,Serialize,Deserialize)]
pub enum DataType {
UnsignedLong,
String,
Double,
ComplexType(Vec<Field>)
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Field {
pub name: String,
pub data_type: DataType,
pub nullable: bool
}
impl Field {
pub fn new(name: &str, data_type: DataType, nullable: bool) -> Self {
Field {
name: name.to_string(),
data_type: data_type,
nullable: nullable
}
}
pub fn to_string(&self) -> String {
format!("{}: {:?}", self.name, self.data_type)
}
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct ComplexType {
name: String,
fields: Vec<Field>
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Schema {
pub columns: Vec<Field>
}
impl Schema {
pub fn empty() -> Self { Schema { columns: vec![] } }
pub fn new(columns: Vec<Field>) -> Self { Schema { columns: columns } }
pub fn column(&self, name: &str) -> Option<(usize, &Field)> {
self.columns.iter()
.enumerate()
.find(|&(_,c)| c.name == name)
}
pub fn to_string(&self) -> String {
let s : Vec<String> = self.columns.iter()
.map(|c| c.to_string())
.collect();
s.join(",")
}
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct FunctionMeta {
pub name: String,
pub args: Vec<Field>,
pub return_type: DataType
}
#[derive(Debug,Clone,PartialEq)]
pub struct Row {
pub values: Vec<Value>
}
impl Row {
pub fn new(v: Vec<Value>) -> Self {
Row { values: v }
}
pub fn to_string(&self) -> String {
let value_strings : Vec<String> = self.values.iter()
.map(|v| v.to_string())
.collect();
value_strings.join(",")
}
}
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub enum Value {
Boolean(bool),
Double(f64),
Long(i64),
UnsignedLong(u64),
String(String),
ComplexValue(Vec<Value>),
UserDefined(Vec<u8>)
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Value) -> Option<Ordering> {
match self {
&Value::Double(l) => match other {
&Value::Double(r) => l.partial_cmp(&r),
&Value::Long(r) => l.partial_cmp(&(r as f64)),
_ => unimplemented!("type coercion rules missing")
},
&Value::Long(l) => match other {
&Value::Double(r) => (l as f64).partial_cmp(&r),
&Value::Long(r) => l.partial_cmp(&r),
_ => unimplemented!("type coercion rules missing")
},
&Value::String(ref l) => match other {
&Value::String(ref r) => l.partial_cmp(r),
_ => unimplemented!("type coercion rules missing")
},
&Value::ComplexValue(_) => None,
_ => unimplemented!("type coercion rules missing")
}
}
}
impl Value {
fn to_string(&self) -> String {
match self {
&Value::Long(l) => l.to_string(),
&Value::UnsignedLong(l) => l.to_string(),
&Value::Double(d) => d.to_string(),
&Value::Boolean(b) => b.to_string(),
&Value::String(ref s) => s.clone(),
&Value::ComplexValue(ref v) => {
let s : Vec<String> = v.iter()
.map(|v| v.to_string())
.collect();
s.join(",")
},
_ => unimplemented!()
}
}
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub enum Operator {
Eq,
NotEq,
Lt,
LtEq,
Gt,
GtEq,
}
#[derive(Debug,Clone,Serialize, Deserialize)]
pub enum Expr {
TupleValue(usize),
Literal(Value),
BinaryExpr { left: Box<Expr>, op: Operator, right: Box<Expr> },
Sort { expr: Box<Expr>, asc: bool },
ScalarFunction { name: String, args: Vec<Expr> }
}
impl Expr {
pub fn eq(&self, other: &Expr) -> Expr {
Expr::BinaryExpr {
left: Box::new(self.clone()),
op: Operator::Eq,
right: Box::new(other.clone())
}
}
pub fn gt(&self, other: &Expr) -> Expr {
Expr::BinaryExpr {
left: Box::new(self.clone()),
op: Operator::Gt,
right: Box::new(other.clone())
}
}
pub fn lt(&self, other: &Expr) -> Expr {
Expr::BinaryExpr {
left: Box::new(self.clone()),
op: Operator::Lt,
right: Box::new(other.clone())
}
}
}
#[derive(Debug,Clone,Serialize, Deserialize)]
pub enum LogicalPlan {
Limit { limit: usize, input: Box<LogicalPlan>, schema: Schema },
Projection { expr: Vec<Expr>, input: Box<LogicalPlan>, schema: Schema },
Selection { expr: Expr, input: Box<LogicalPlan>, schema: Schema },
Sort { expr: Vec<Expr>, input: Box<LogicalPlan>, schema: Schema },
TableScan { schema_name: String, table_name: String, schema: Schema },
CsvFile { filename: String, schema: Schema },
EmptyRelation
}
impl LogicalPlan {
pub fn schema(&self) -> Schema {
match self {
&LogicalPlan::EmptyRelation => Schema::empty(),
&LogicalPlan::TableScan { ref schema, .. } => schema.clone(),
&LogicalPlan::CsvFile { ref schema, .. } => schema.clone(),
&LogicalPlan::Projection { ref schema, .. } => schema.clone(),
&LogicalPlan::Selection { ref schema, .. } => schema.clone(),
&LogicalPlan::Sort { ref schema, .. } => schema.clone(),
&LogicalPlan::Limit { ref schema, .. } => schema.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::LogicalPlan::*;
use super::Expr::*;
use super::Value::*;
extern crate serde_json;
#[test]
fn serde() {
let tt = Schema {
columns: vec![
Field { name: "id".to_string(), data_type: DataType::UnsignedLong, nullable: false },
Field { name: "name".to_string(), data_type: DataType::String, nullable: false }
]
};
let csv = CsvFile { filename: "test/data/people.csv".to_string(), schema: tt.clone() };
let filter_expr = BinaryExpr {
left: Box::new(TupleValue(0)),
op: Operator::Eq,
right: Box::new(Literal(Long(2)))
};
let plan = Selection {
expr: filter_expr,
input: Box::new(csv),
schema: tt.clone()
};
let s = serde_json::to_string(&plan).unwrap();
println!("serialized: {}", s);
}
}