diff --git a/src/db/eval.rs b/src/db/eval.rs index 8855fd4f..f95479af 100644 --- a/src/db/eval.rs +++ b/src/db/eval.rs @@ -194,7 +194,7 @@ fn sub_values<'a>(args: &'a [Value<'a>], tuples: &'a MegaTuple) -> Result (l + r).into(), + (Value::Int(l), Value::Int(r)) => (l - r).into(), (Value::Float(l), Value::Int(r)) => (l - (r as f64)).into(), (Value::Int(l), Value::Float(r)) => ((l as f64) - r.into_inner()).into(), (Value::Float(l), Value::Float(r)) => (l.into_inner() - r.into_inner()).into(), diff --git a/src/db/iterator.rs b/src/db/iterator.rs index fa16a623..bfc2d6cc 100644 --- a/src/db/iterator.rs +++ b/src/db/iterator.rs @@ -104,6 +104,33 @@ pub enum ExecPlan<'a> { }, } +impl<'a> ExecPlan<'a> { + pub fn tuple_widths(&self) -> (usize, usize) { + match self { + ExecPlan::NodeItPlan { .. } => (1, 1), + ExecPlan::EdgeItPlan { .. } => (1, 1), + ExecPlan::EdgeKeyOnlyBwdItPlan { .. } => (1, 0), + ExecPlan::KeySortedWithAssocItPlan { .. } => todo!(), + ExecPlan::CartesianProdItPlan { left, right } => { + let (l1, l2) = left.tuple_widths(); + let (r1, r2) = right.tuple_widths(); + (l1 + r1, l2 + r2) + } + ExecPlan::MergeJoinItPlan { .. } => todo!(), + ExecPlan::OuterMergeJoinItPlan { .. } => todo!(), + ExecPlan::KeyedUnionItPlan { .. } => todo!(), + ExecPlan::KeyedDifferenceItPlan { .. } => todo!(), + ExecPlan::FilterItPlan { source, .. } => { + source.tuple_widths() + } + ExecPlan::EvalItPlan { source, .. } => { + source.tuple_widths() + } + ExecPlan::BagsUnionIt { .. } => todo!() + } + } +} + #[derive(Debug)] pub struct OutputItPlan<'a> { pub source: ExecPlan<'a>, @@ -1104,6 +1131,25 @@ mod tests { for val in plan.iter()? { println!("{}", val?) } + + let start = Instant::now(); + + let s = r##"from e1:Employee, e2:Employee + where e1.id == e2.id - 10 + select { fid: e1.id, fname: e1.first_name, sid: e2.id, sname: e2.first_name }"##; + + let parsed = Parser::parse(Rule::relational_query, s)?.next().unwrap(); + let plan = sess.query_to_plan(parsed)?; + println!("{:?}", plan); + let plan = sess.reify_output_plan(plan)?; + println!("{:?}", plan); + for val in plan.iter()? { + println!("{}", val?) + } + + let duration = start.elapsed(); + println!("Time elapsed {:?}", duration); + } drop(engine); let _ = fs::remove_dir_all(db_path); diff --git a/src/db/plan.rs b/src/db/plan.rs index 0db2f82c..7484e1bc 100644 --- a/src/db/plan.rs +++ b/src/db/plan.rs @@ -1,3 +1,4 @@ +use std::collections::btree_map::Entry; use crate::db::engine::Session; use crate::db::iterator::{ExecPlan, IteratorSlot, OutputItPlan}; use crate::db::query::{FromEl, Selection}; @@ -20,24 +21,54 @@ pub enum OuterJoinType { pub type AccessorMap = BTreeMap>; +fn merge_accessor_map(mut left: AccessorMap, right: AccessorMap) -> AccessorMap { + // println!("Before {:?} {:?}", left, right); + for (k, vs) in right.into_iter() { + let entry = left.entry(k); + match &entry { + Entry::Vacant(_) => { + entry.or_insert(vs); + } + Entry::Occupied(_) => { + entry.and_modify(|existing| existing.extend(vs)); + } + } + } + // println!("After {:?}", left); + left +} + +fn convert_to_relative_accessor_map(amap: AccessorMap) -> AccessorMap { + // TODO this only handles the simplest case + fn convert_inner(inner: BTreeMap) -> BTreeMap { + inner.into_iter().map(|(k, (_tid, cid))| { + (k, (TableId::new(false, 0), cid)) + }).collect() + } + amap.into_iter().map(|(k, v)| (k, convert_inner(v))).collect() +} + +fn shift_accessor_map(amap: AccessorMap, (keyshift, valshift): (usize, usize)) -> AccessorMap { + let shift_inner = |inner: BTreeMap| -> BTreeMap { + inner.into_iter().map(|(k, (tid, cid))| { + (k, (TableId::new(tid.in_root, tid.id + if cid.is_key { keyshift as i64 } else { valshift as i64 }), cid)) + }).collect() + }; + amap.into_iter().map(|(k, v)| (k, shift_inner(v))).collect() +} + impl<'a> Session<'a> { pub fn reify_intermediate_plan(&'a self, plan: ExecPlan<'a>) -> Result> { - self.do_reify_intermediate_plan(plan).map(|v| v.0) - } - fn convert_to_relative_amap(&self, amap: AccessorMap) -> AccessorMap { - // TODO this only handles the simplest case - fn convert_inner(inner: BTreeMap) -> BTreeMap { - inner.into_iter().map(|(k, (_tid, cid))| { - (k, (TableId::new(false, 0), cid)) - }).collect() - } - amap.into_iter().map(|(k, v)| (k, convert_inner(v))).collect() + self.do_reify_intermediate_plan(plan).map(|v| { + // println!("Accessor map {:#?}", v.1); + v.0 + }) } fn do_reify_intermediate_plan(&'a self, plan: ExecPlan<'a>) -> Result<(ExecPlan<'a>, AccessorMap)> { let res = match plan { ExecPlan::NodeItPlan { info, binding, .. } => { let amap = self.node_accessor_map(&binding, &info); - let amap = self.convert_to_relative_amap(amap); + let amap = convert_to_relative_accessor_map(amap); let it = if info.table_id.in_root { self.txn.iterator(true, &self.perm_cf) } else { @@ -53,7 +84,7 @@ impl<'a> Session<'a> { } ExecPlan::EdgeItPlan { info, binding, .. } => { let amap = self.edge_accessor_map(&binding, &info); - let amap = self.convert_to_relative_amap(amap); + let amap = convert_to_relative_accessor_map(amap); let it = if info.table_id.in_root { self.txn.iterator(true, &self.perm_cf) } else { @@ -69,7 +100,16 @@ impl<'a> Session<'a> { } ExecPlan::EdgeKeyOnlyBwdItPlan { .. } => todo!(), ExecPlan::KeySortedWithAssocItPlan { .. } => todo!(), - ExecPlan::CartesianProdItPlan { .. } => todo!(), + ExecPlan::CartesianProdItPlan { left, right } => { + let (l_plan, l_map) = self.do_reify_intermediate_plan(*left)?; + let (r_plan, r_map) = self.do_reify_intermediate_plan(*right)?; + let r_map = shift_accessor_map(r_map, l_plan.tuple_widths()); + let plan = ExecPlan::CartesianProdItPlan { + left: l_plan.into(), + right: r_plan.into() + }; + (plan, merge_accessor_map(l_map, r_map)) + } ExecPlan::MergeJoinItPlan { .. } => todo!(), ExecPlan::OuterMergeJoinItPlan { .. } => todo!(), ExecPlan::KeyedUnionItPlan { .. } => todo!(), @@ -138,7 +178,7 @@ impl<'a> Session<'a> { let plan = self.convert_where_data_to_plan(plan, where_data)?; self.convert_select_data_to_plan(plan, select_data) } - fn convert_from_data_to_plan(&self, mut from_data: Vec) -> Result { + fn convert_from_data_to_plan(&self, from_data: Vec) -> Result { let convert_el = |el| match el { FromEl::Simple(el) => { @@ -165,9 +205,13 @@ impl<'a> Session<'a> { let mut from_data = from_data.into_iter(); let fst = from_data.next().ok_or_else(|| LogicError("Empty from clause".to_string()))?; - let res = convert_el(fst)?; - for _nxt in from_data { - // TODO build cartesian product + let mut res = convert_el(fst)?; + for nxt in from_data { + let nxt = convert_el(nxt)?; + res = ExecPlan::CartesianProdItPlan { + left: res.into(), + right: nxt.into() + }; } Ok(res) }