diff --git a/README.md b/README.md index c92f26af..5ee017ae 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,6 @@ * [ ] function symbol * [ ] arithmetic * [ ] range scan -* [ ] public API \ No newline at end of file +* [ ] public API +* [ ] sorting +* [ ] limit, offset \ No newline at end of file diff --git a/src/query/compile.rs b/src/query/compile.rs index b48fff54..595df51f 100644 --- a/src/query/compile.rs +++ b/src/query/compile.rs @@ -430,18 +430,18 @@ impl SessionTx { let ret_vars_set = ret_vars.iter().cloned().collect(); ret.eliminate_temp_vars(&ret_vars_set)?; - let cur_ret_set: BTreeSet<_> = ret.bindings().into_iter().collect(); + let cur_ret_set: BTreeSet<_> = ret.bindings_after_eliminate().into_iter().collect(); if cur_ret_set != ret_vars_set { ret = ret.cartesian_join(Relation::unit()); ret.eliminate_temp_vars(&ret_vars_set)?; } - let cur_ret_set: BTreeSet<_> = ret.bindings().into_iter().collect(); + let cur_ret_set: BTreeSet<_> = ret.bindings_after_eliminate().into_iter().collect(); if cur_ret_set != ret_vars_set { let diff = cur_ret_set.sub(&cur_ret_set); return Err(QueryCompilationError::UnsafeUnboundVars(diff).into()); } - let cur_ret_bindings = ret.bindings(); + let cur_ret_bindings = ret.bindings_after_eliminate(); if ret_vars != cur_ret_bindings { ret = ret.reorder(ret_vars.to_vec()); } diff --git a/src/query/relation.rs b/src/query/relation.rs index 27f273a1..ce0f98da 100644 --- a/src/query/relation.rs +++ b/src/query/relation.rs @@ -27,13 +27,26 @@ pub enum Relation { pub struct FilteredRelation { parent: Box, pred: Expr, + pub(crate) to_eliminate: BTreeSet, } impl FilteredRelation { + pub(crate) fn do_eliminate_temp_vars(&mut self, used: &BTreeSet) -> Result<()> { + for binding in self.parent.bindings_before_eliminate() { + if !used.contains(&binding) { + self.to_eliminate.insert(binding.clone()); + } + } + let mut nxt = used.clone(); + nxt.extend(self.pred.bindings()); + self.parent.eliminate_temp_vars(&nxt)?; + Ok(()) + } + fn fill_binding_indices(&mut self) { let parent_bindings: BTreeMap<_, _> = self .parent - .bindings() + .bindings_after_eliminate() .into_iter() .enumerate() .map(|(a, b)| (b, a)) @@ -61,9 +74,9 @@ impl FilteredRelation { } } -struct BindingFormatter<'a>(&'a [Keyword]); +struct BindingFormatter(Vec); -impl Debug for BindingFormatter<'_> { +impl Debug for BindingFormatter { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let s = self.0.iter().map(|f| f.to_string_no_prefix()).join(", "); write!(f, "[{}]", s) @@ -72,30 +85,31 @@ impl Debug for BindingFormatter<'_> { impl Debug for Relation { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let bindings = BindingFormatter(self.bindings_after_eliminate()); match self { Relation::Fixed(r) => { if r.bindings.is_empty() && r.data.len() == 1 { f.write_str("Unit") } else if r.data.len() == 1 { f.debug_tuple("Singlet") - .field(&BindingFormatter(&r.bindings)) + .field(&bindings) .field(r.data.get(0).unwrap()) .finish() } else { f.debug_tuple("Fixed") - .field(&BindingFormatter(&r.bindings)) + .field(&bindings) .field(&["..."]) .finish() } } Relation::Triple(r) => f .debug_tuple("Triple") - .field(&BindingFormatter(&r.bindings)) + .field(&bindings) .field(&r.attr.keyword) .finish(), Relation::Derived(r) => f .debug_tuple("Derived") - .field(&BindingFormatter(&r.bindings)) + .field(&bindings) .field(&r.storage.id) .finish(), Relation::Join(r) => { @@ -103,7 +117,7 @@ impl Debug for Relation { r.right.fmt(f) } else { f.debug_tuple("Join") - .field(&BindingFormatter(&r.bindings())) + .field(&bindings) .field(&r.joiner) .field(&r.left) .field(&r.right) @@ -117,6 +131,7 @@ impl Debug for Relation { .finish(), Relation::Filter(r) => f .debug_tuple("Filter") + .field(&bindings) .field(&r.pred) .field(&r.parent) .finish(), @@ -191,6 +206,7 @@ impl Relation { Relation::Filter(FilteredRelation { parent: Box::new(self), pred: filter, + to_eliminate: Default::default(), }) } pub(crate) fn join( @@ -227,7 +243,7 @@ impl ReorderRelation { epoch: Option, use_delta: &BTreeSet, ) -> TupleIter<'a> { - let old_order = self.relation.bindings(); + let old_order = self.relation.bindings_after_eliminate(); let old_order_indices: BTreeMap<_, _> = old_order .into_iter() .enumerate() @@ -784,12 +800,9 @@ pub(crate) struct Joiner { impl Debug for Joiner { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{:?}<->{:?}", - BindingFormatter(&self.left_keys), - BindingFormatter(&self.right_keys) - ) + let left_bindings = BindingFormatter(self.left_keys.clone()); + let right_bindings = BindingFormatter(self.right_keys.clone()); + write!(f, "{:?}<->{:?}", left_bindings, right_bindings,) } } @@ -853,7 +866,7 @@ impl Relation { Relation::Derived(_r) => Ok(()), Relation::Join(r) => r.do_eliminate_temp_vars(used), Relation::Reorder(r) => r.relation.eliminate_temp_vars(used), - Relation::Filter(_) => Ok(()), + Relation::Filter(r) => r.do_eliminate_temp_vars(used), } } @@ -864,11 +877,11 @@ impl Relation { Relation::Derived(_) => None, Relation::Join(r) => Some(&r.to_eliminate), Relation::Reorder(_) => None, - Relation::Filter(_) => None, + Relation::Filter(r) => Some(&r.to_eliminate), } } - pub fn bindings(&self) -> Vec { + pub fn bindings_after_eliminate(&self) -> Vec { let ret = self.bindings_before_eliminate(); if let Some(to_eliminate) = self.eliminate_set() { ret.into_iter() @@ -886,7 +899,7 @@ impl Relation { Relation::Derived(d) => d.bindings.clone(), Relation::Join(j) => j.bindings(), Relation::Reorder(r) => r.bindings(), - Relation::Filter(r) => r.parent.bindings(), + Relation::Filter(r) => r.parent.bindings_after_eliminate(), } } pub fn iter<'a>( @@ -926,8 +939,8 @@ impl InnerJoin { } pub(crate) fn bindings(&self) -> Vec { - let mut ret = self.left.bindings(); - ret.extend(self.right.bindings()); + let mut ret = self.left.bindings_after_eliminate(); + ret.extend(self.right.bindings_after_eliminate()); debug_assert_eq!(ret.len(), ret.iter().collect::>().len()); ret } @@ -943,7 +956,10 @@ impl InnerJoin { Relation::Fixed(f) => { let join_indices = self .joiner - .join_indices(&self.left.bindings(), &self.right.bindings()) + .join_indices( + &self.left.bindings_after_eliminate(), + &self.right.bindings_after_eliminate(), + ) .unwrap(); f.join( self.left.iter(tx, epoch, use_delta), @@ -954,7 +970,10 @@ impl InnerJoin { Relation::Triple(r) => { let join_indices = self .joiner - .join_indices(&self.left.bindings(), &self.right.bindings()) + .join_indices( + &self.left.bindings_after_eliminate(), + &self.right.bindings_after_eliminate(), + ) .unwrap(); r.join( self.left.iter(tx, epoch, use_delta), @@ -966,7 +985,10 @@ impl InnerJoin { Relation::Derived(r) => { let join_indices = self .joiner - .join_indices(&self.left.bindings(), &self.right.bindings()) + .join_indices( + &self.left.bindings_after_eliminate(), + &self.right.bindings_after_eliminate(), + ) .unwrap(); if r.join_is_prefix(&join_indices.1) { r.prefix_join( @@ -995,10 +1017,10 @@ impl InnerJoin { epoch: Option, use_delta: &BTreeSet, ) -> TupleIter<'a> { - let right_bindings = self.right.bindings(); + let right_bindings = self.right.bindings_after_eliminate(); let (left_join_indices, right_join_indices) = self .joiner - .join_indices(&self.left.bindings(), &right_bindings) + .join_indices(&self.left.bindings_after_eliminate(), &right_bindings) .unwrap(); let right_join_indices_set = BTreeSet::from_iter(right_join_indices.iter().cloned()); let mut right_store_indices = right_join_indices; diff --git a/tests/creation.rs b/tests/creation.rs index f3faa614..440890e5 100644 --- a/tests/creation.rs +++ b/tests/creation.rs @@ -27,13 +27,13 @@ fn creation() { assert!(db.current_schema().unwrap().as_array().unwrap().is_empty()); let res = db.transact_attributes(&json!({ "attrs": [ - {"put": {"keyword": "person/idd", "cardinality": "one", "type": "string", "index": "identity", "history": false}}, - {"put": {"keyword": "person/first_name", "cardinality": "one", "type": "string", "index": true}}, - {"put": {"keyword": "person/last_name", "cardinality": "one", "type": "string", "index": true}}, - {"put": {"keyword": "person/age", "cardinality": "one", "type": "int"}}, - {"put": {"keyword": "person/friend", "cardinality": "many", "type": "ref"}}, - {"put": {"keyword": "person/weight", "cardinality": "one", "type": "float"}}, - {"put": {"keyword": "person/covid", "cardinality": "one", "type": "bool"}}, + {"put": {"keyword": "person.idd", "cardinality": "one", "type": "string", "index": "identity", "history": false}}, + {"put": {"keyword": "person.first_name", "cardinality": "one", "type": "string", "index": true}}, + {"put": {"keyword": "person.last_name", "cardinality": "one", "type": "string", "index": true}}, + {"put": {"keyword": "person.age", "cardinality": "one", "type": "int"}}, + {"put": {"keyword": "person.friend", "cardinality": "many", "type": "ref"}}, + {"put": {"keyword": "person.weight", "cardinality": "one", "type": "float"}}, + {"put": {"keyword": "person.covid", "cardinality": "one", "type": "bool"}}, ] })) .unwrap(); @@ -42,8 +42,8 @@ fn creation() { let last_id = res["results"][6][0].as_u64().unwrap(); db.transact_attributes(&json!({ "attrs": [ - {"put": {"id": first_id, "keyword": ":person/id", "cardinality": "one", "type": "string", "index": "identity", "history": false}}, - {"retract": {"id": last_id, "keyword": ":person/covid", "cardinality": "one", "type": "bool"}} + {"put": {"id": first_id, "keyword": ":person.id", "cardinality": "one", "type": "string", "index": "identity", "history": false}}, + {"retract": {"id": last_id, "keyword": ":person.covid", "cardinality": "one", "type": "bool"}} ] })).unwrap(); assert_eq!(db.current_schema().unwrap().as_array().unwrap().len(), 6); @@ -55,56 +55,56 @@ fn creation() { "tx": [ {"put": { "_temp_id": "alice", - "person/first_name": "Alice", - "person/age": 7, - "person/last_name": "Amorist", - "person/id": "alice_amorist", - "person/weight": 25, - "person/friend": "eve"}}, + "person.first_name": "Alice", + "person.age": 7, + "person.last_name": "Amorist", + "person.id": "alice_amorist", + "person.weight": 25, + "person.friend": "eve"}}, {"put": { "_temp_id": "bob", - "person/first_name": "Bob", - "person/age": 70, - "person/last_name": "Wonderland", - "person/id": "bob_wonderland", - "person/weight": 100, - "person/friend": "alice" + "person.first_name": "Bob", + "person.age": 70, + "person.last_name": "Wonderland", + "person.id": "bob_wonderland", + "person.weight": 100, + "person.friend": "alice" }}, {"put": { "_temp_id": "eve", - "person/first_name": "Eve", - "person/age": 18, - "person/last_name": "Faking", - "person/id": "eve_faking", - "person/weight": 50, - "person/friend": [ + "person.first_name": "Eve", + "person.age": 18, + "person.last_name": "Faking", + "person.id": "eve_faking", + "person.weight": 50, + "person.friend": [ "alice", "bob", { - "person/first_name": "Charlie", - "person/age": 22, - "person/last_name": "Goodman", - "person/id": "charlie_goodman", - "person/weight": 120, - "person/friend": "eve" + "person.first_name": "Charlie", + "person.age": 22, + "person.last_name": "Goodman", + "person.id": "charlie_goodman", + "person.weight": 120, + "person.friend": "eve" } ] }}, {"put": { "_temp_id": "david", - "person/first_name": "David", - "person/age": 7, - "person/last_name": "Dull", - "person/id": "david_dull", - "person/weight": 25, - "person/friend": { + "person.first_name": "David", + "person.age": 7, + "person.last_name": "Dull", + "person.id": "david_dull", + "person.weight": 25, + "person.friend": { "_temp_id": "george", - "person/first_name": "George", - "person/age": 7, - "person/last_name": "Geomancer", - "person/id": "george_geomancer", - "person/weight": 25, - "person/friend": "george"}}}, + "person.first_name": "George", + "person.age": 7, + "person.last_name": "Geomancer", + "person.id": "george_geomancer", + "person.weight": 25, + "person.friend": "george"}}}, ] })) .unwrap(); @@ -119,9 +119,9 @@ fn creation() { EntityId::MIN_PERM, &json!([ "_id", - "person/first_name", - "person/last_name", - {"pull":"person/friend", "as": "friends", "recurse": true}, + "person.first_name", + "person.last_name", + {"pull":"person.friend", "as": "friends", "recurse": true}, ]), Validity::current(), ) @@ -133,18 +133,18 @@ fn creation() { "q": [ { "rule": "ff", - "args": [["?a", "?b"], ["?a", "person/friend", "?b"]] + "args": [["?a", "?b"], ["?a", "person.friend", "?b"]] }, { "rule": "ff", - "args": [["?a", "?b"], ["?a", "person/friend", "?c"], {"rule": "ff", "args": ["?c", "?b"]}] + "args": [["?a", "?b"], ["?a", "person.friend", "?c"], {"rule": "ff", "args": ["?c", "?b"]}] }, { "rule": "?", - "args": [["?n", "?a"], {"pred": "Neq", "args": ["?n", "Alice"]}, {"rule": "ff", "args": [{"person/id": "alice_amorist"}, "?a"]}, ["?a", "person/first_name", "?n"]] + "args": [["?a"], {"pred": "Neq", "args": ["?n", "Alice"]}, {"rule": "ff", "args": [{"person.id": "alice_amorist"}, "?a"]}, ["?a", "person.first_name", "?n"]] } ], - // "out": {"friend": {"pull": "?a", "spec": ["*"]}} + "out": {"friend": {"pull": "?a", "spec": ["person.first_name"]}} }); let mut tx = db.transact().unwrap(); let ret = tx.run_query(&query).unwrap();