filter respects eager binding elimination

main
Ziyang Hu 2 years ago
parent cf7a48d09d
commit 93e82a001e

@ -9,4 +9,6 @@
* [ ] function symbol * [ ] function symbol
* [ ] arithmetic * [ ] arithmetic
* [ ] range scan * [ ] range scan
* [ ] public API * [ ] public API
* [ ] sorting
* [ ] limit, offset

@ -430,18 +430,18 @@ impl SessionTx {
let ret_vars_set = ret_vars.iter().cloned().collect(); let ret_vars_set = ret_vars.iter().cloned().collect();
ret.eliminate_temp_vars(&ret_vars_set)?; 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 { if cur_ret_set != ret_vars_set {
ret = ret.cartesian_join(Relation::unit()); ret = ret.cartesian_join(Relation::unit());
ret.eliminate_temp_vars(&ret_vars_set)?; 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 { if cur_ret_set != ret_vars_set {
let diff = cur_ret_set.sub(&cur_ret_set); let diff = cur_ret_set.sub(&cur_ret_set);
return Err(QueryCompilationError::UnsafeUnboundVars(diff).into()); 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 { if ret_vars != cur_ret_bindings {
ret = ret.reorder(ret_vars.to_vec()); ret = ret.reorder(ret_vars.to_vec());
} }

@ -27,13 +27,26 @@ pub enum Relation {
pub struct FilteredRelation { pub struct FilteredRelation {
parent: Box<Relation>, parent: Box<Relation>,
pred: Expr, pred: Expr,
pub(crate) to_eliminate: BTreeSet<Keyword>,
} }
impl FilteredRelation { impl FilteredRelation {
pub(crate) fn do_eliminate_temp_vars(&mut self, used: &BTreeSet<Keyword>) -> 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) { fn fill_binding_indices(&mut self) {
let parent_bindings: BTreeMap<_, _> = self let parent_bindings: BTreeMap<_, _> = self
.parent .parent
.bindings() .bindings_after_eliminate()
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(a, b)| (b, a)) .map(|(a, b)| (b, a))
@ -61,9 +74,9 @@ impl FilteredRelation {
} }
} }
struct BindingFormatter<'a>(&'a [Keyword]); struct BindingFormatter(Vec<Keyword>);
impl Debug for BindingFormatter<'_> { impl Debug for BindingFormatter {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let s = self.0.iter().map(|f| f.to_string_no_prefix()).join(", "); let s = self.0.iter().map(|f| f.to_string_no_prefix()).join(", ");
write!(f, "[{}]", s) write!(f, "[{}]", s)
@ -72,30 +85,31 @@ impl Debug for BindingFormatter<'_> {
impl Debug for Relation { impl Debug for Relation {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let bindings = BindingFormatter(self.bindings_after_eliminate());
match self { match self {
Relation::Fixed(r) => { Relation::Fixed(r) => {
if r.bindings.is_empty() && r.data.len() == 1 { if r.bindings.is_empty() && r.data.len() == 1 {
f.write_str("Unit") f.write_str("Unit")
} else if r.data.len() == 1 { } else if r.data.len() == 1 {
f.debug_tuple("Singlet") f.debug_tuple("Singlet")
.field(&BindingFormatter(&r.bindings)) .field(&bindings)
.field(r.data.get(0).unwrap()) .field(r.data.get(0).unwrap())
.finish() .finish()
} else { } else {
f.debug_tuple("Fixed") f.debug_tuple("Fixed")
.field(&BindingFormatter(&r.bindings)) .field(&bindings)
.field(&["..."]) .field(&["..."])
.finish() .finish()
} }
} }
Relation::Triple(r) => f Relation::Triple(r) => f
.debug_tuple("Triple") .debug_tuple("Triple")
.field(&BindingFormatter(&r.bindings)) .field(&bindings)
.field(&r.attr.keyword) .field(&r.attr.keyword)
.finish(), .finish(),
Relation::Derived(r) => f Relation::Derived(r) => f
.debug_tuple("Derived") .debug_tuple("Derived")
.field(&BindingFormatter(&r.bindings)) .field(&bindings)
.field(&r.storage.id) .field(&r.storage.id)
.finish(), .finish(),
Relation::Join(r) => { Relation::Join(r) => {
@ -103,7 +117,7 @@ impl Debug for Relation {
r.right.fmt(f) r.right.fmt(f)
} else { } else {
f.debug_tuple("Join") f.debug_tuple("Join")
.field(&BindingFormatter(&r.bindings())) .field(&bindings)
.field(&r.joiner) .field(&r.joiner)
.field(&r.left) .field(&r.left)
.field(&r.right) .field(&r.right)
@ -117,6 +131,7 @@ impl Debug for Relation {
.finish(), .finish(),
Relation::Filter(r) => f Relation::Filter(r) => f
.debug_tuple("Filter") .debug_tuple("Filter")
.field(&bindings)
.field(&r.pred) .field(&r.pred)
.field(&r.parent) .field(&r.parent)
.finish(), .finish(),
@ -191,6 +206,7 @@ impl Relation {
Relation::Filter(FilteredRelation { Relation::Filter(FilteredRelation {
parent: Box::new(self), parent: Box::new(self),
pred: filter, pred: filter,
to_eliminate: Default::default(),
}) })
} }
pub(crate) fn join( pub(crate) fn join(
@ -227,7 +243,7 @@ impl ReorderRelation {
epoch: Option<u32>, epoch: Option<u32>,
use_delta: &BTreeSet<TempStoreId>, use_delta: &BTreeSet<TempStoreId>,
) -> TupleIter<'a> { ) -> TupleIter<'a> {
let old_order = self.relation.bindings(); let old_order = self.relation.bindings_after_eliminate();
let old_order_indices: BTreeMap<_, _> = old_order let old_order_indices: BTreeMap<_, _> = old_order
.into_iter() .into_iter()
.enumerate() .enumerate()
@ -784,12 +800,9 @@ pub(crate) struct Joiner {
impl Debug for Joiner { impl Debug for Joiner {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!( let left_bindings = BindingFormatter(self.left_keys.clone());
f, let right_bindings = BindingFormatter(self.right_keys.clone());
"{:?}<->{:?}", write!(f, "{:?}<->{:?}", left_bindings, right_bindings,)
BindingFormatter(&self.left_keys),
BindingFormatter(&self.right_keys)
)
} }
} }
@ -853,7 +866,7 @@ impl Relation {
Relation::Derived(_r) => Ok(()), Relation::Derived(_r) => Ok(()),
Relation::Join(r) => r.do_eliminate_temp_vars(used), Relation::Join(r) => r.do_eliminate_temp_vars(used),
Relation::Reorder(r) => r.relation.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::Derived(_) => None,
Relation::Join(r) => Some(&r.to_eliminate), Relation::Join(r) => Some(&r.to_eliminate),
Relation::Reorder(_) => None, Relation::Reorder(_) => None,
Relation::Filter(_) => None, Relation::Filter(r) => Some(&r.to_eliminate),
} }
} }
pub fn bindings(&self) -> Vec<Keyword> { pub fn bindings_after_eliminate(&self) -> Vec<Keyword> {
let ret = self.bindings_before_eliminate(); let ret = self.bindings_before_eliminate();
if let Some(to_eliminate) = self.eliminate_set() { if let Some(to_eliminate) = self.eliminate_set() {
ret.into_iter() ret.into_iter()
@ -886,7 +899,7 @@ impl Relation {
Relation::Derived(d) => d.bindings.clone(), Relation::Derived(d) => d.bindings.clone(),
Relation::Join(j) => j.bindings(), Relation::Join(j) => j.bindings(),
Relation::Reorder(r) => r.bindings(), Relation::Reorder(r) => r.bindings(),
Relation::Filter(r) => r.parent.bindings(), Relation::Filter(r) => r.parent.bindings_after_eliminate(),
} }
} }
pub fn iter<'a>( pub fn iter<'a>(
@ -926,8 +939,8 @@ impl InnerJoin {
} }
pub(crate) fn bindings(&self) -> Vec<Keyword> { pub(crate) fn bindings(&self) -> Vec<Keyword> {
let mut ret = self.left.bindings(); let mut ret = self.left.bindings_after_eliminate();
ret.extend(self.right.bindings()); ret.extend(self.right.bindings_after_eliminate());
debug_assert_eq!(ret.len(), ret.iter().collect::<BTreeSet<_>>().len()); debug_assert_eq!(ret.len(), ret.iter().collect::<BTreeSet<_>>().len());
ret ret
} }
@ -943,7 +956,10 @@ impl InnerJoin {
Relation::Fixed(f) => { Relation::Fixed(f) => {
let join_indices = self let join_indices = self
.joiner .joiner
.join_indices(&self.left.bindings(), &self.right.bindings()) .join_indices(
&self.left.bindings_after_eliminate(),
&self.right.bindings_after_eliminate(),
)
.unwrap(); .unwrap();
f.join( f.join(
self.left.iter(tx, epoch, use_delta), self.left.iter(tx, epoch, use_delta),
@ -954,7 +970,10 @@ impl InnerJoin {
Relation::Triple(r) => { Relation::Triple(r) => {
let join_indices = self let join_indices = self
.joiner .joiner
.join_indices(&self.left.bindings(), &self.right.bindings()) .join_indices(
&self.left.bindings_after_eliminate(),
&self.right.bindings_after_eliminate(),
)
.unwrap(); .unwrap();
r.join( r.join(
self.left.iter(tx, epoch, use_delta), self.left.iter(tx, epoch, use_delta),
@ -966,7 +985,10 @@ impl InnerJoin {
Relation::Derived(r) => { Relation::Derived(r) => {
let join_indices = self let join_indices = self
.joiner .joiner
.join_indices(&self.left.bindings(), &self.right.bindings()) .join_indices(
&self.left.bindings_after_eliminate(),
&self.right.bindings_after_eliminate(),
)
.unwrap(); .unwrap();
if r.join_is_prefix(&join_indices.1) { if r.join_is_prefix(&join_indices.1) {
r.prefix_join( r.prefix_join(
@ -995,10 +1017,10 @@ impl InnerJoin {
epoch: Option<u32>, epoch: Option<u32>,
use_delta: &BTreeSet<TempStoreId>, use_delta: &BTreeSet<TempStoreId>,
) -> TupleIter<'a> { ) -> TupleIter<'a> {
let right_bindings = self.right.bindings(); let right_bindings = self.right.bindings_after_eliminate();
let (left_join_indices, right_join_indices) = self let (left_join_indices, right_join_indices) = self
.joiner .joiner
.join_indices(&self.left.bindings(), &right_bindings) .join_indices(&self.left.bindings_after_eliminate(), &right_bindings)
.unwrap(); .unwrap();
let right_join_indices_set = BTreeSet::from_iter(right_join_indices.iter().cloned()); let right_join_indices_set = BTreeSet::from_iter(right_join_indices.iter().cloned());
let mut right_store_indices = right_join_indices; let mut right_store_indices = right_join_indices;

@ -27,13 +27,13 @@ fn creation() {
assert!(db.current_schema().unwrap().as_array().unwrap().is_empty()); assert!(db.current_schema().unwrap().as_array().unwrap().is_empty());
let res = db.transact_attributes(&json!({ let res = db.transact_attributes(&json!({
"attrs": [ "attrs": [
{"put": {"keyword": "person/idd", "cardinality": "one", "type": "string", "index": "identity", "history": false}}, {"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.first_name", "cardinality": "one", "type": "string", "index": true}},
{"put": {"keyword": "person/last_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.age", "cardinality": "one", "type": "int"}},
{"put": {"keyword": "person/friend", "cardinality": "many", "type": "ref"}}, {"put": {"keyword": "person.friend", "cardinality": "many", "type": "ref"}},
{"put": {"keyword": "person/weight", "cardinality": "one", "type": "float"}}, {"put": {"keyword": "person.weight", "cardinality": "one", "type": "float"}},
{"put": {"keyword": "person/covid", "cardinality": "one", "type": "bool"}}, {"put": {"keyword": "person.covid", "cardinality": "one", "type": "bool"}},
] ]
})) }))
.unwrap(); .unwrap();
@ -42,8 +42,8 @@ fn creation() {
let last_id = res["results"][6][0].as_u64().unwrap(); let last_id = res["results"][6][0].as_u64().unwrap();
db.transact_attributes(&json!({ db.transact_attributes(&json!({
"attrs": [ "attrs": [
{"put": {"id": first_id, "keyword": ":person/id", "cardinality": "one", "type": "string", "index": "identity", "history": false}}, {"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"}} {"retract": {"id": last_id, "keyword": ":person.covid", "cardinality": "one", "type": "bool"}}
] ]
})).unwrap(); })).unwrap();
assert_eq!(db.current_schema().unwrap().as_array().unwrap().len(), 6); assert_eq!(db.current_schema().unwrap().as_array().unwrap().len(), 6);
@ -55,56 +55,56 @@ fn creation() {
"tx": [ "tx": [
{"put": { {"put": {
"_temp_id": "alice", "_temp_id": "alice",
"person/first_name": "Alice", "person.first_name": "Alice",
"person/age": 7, "person.age": 7,
"person/last_name": "Amorist", "person.last_name": "Amorist",
"person/id": "alice_amorist", "person.id": "alice_amorist",
"person/weight": 25, "person.weight": 25,
"person/friend": "eve"}}, "person.friend": "eve"}},
{"put": { {"put": {
"_temp_id": "bob", "_temp_id": "bob",
"person/first_name": "Bob", "person.first_name": "Bob",
"person/age": 70, "person.age": 70,
"person/last_name": "Wonderland", "person.last_name": "Wonderland",
"person/id": "bob_wonderland", "person.id": "bob_wonderland",
"person/weight": 100, "person.weight": 100,
"person/friend": "alice" "person.friend": "alice"
}}, }},
{"put": { {"put": {
"_temp_id": "eve", "_temp_id": "eve",
"person/first_name": "Eve", "person.first_name": "Eve",
"person/age": 18, "person.age": 18,
"person/last_name": "Faking", "person.last_name": "Faking",
"person/id": "eve_faking", "person.id": "eve_faking",
"person/weight": 50, "person.weight": 50,
"person/friend": [ "person.friend": [
"alice", "alice",
"bob", "bob",
{ {
"person/first_name": "Charlie", "person.first_name": "Charlie",
"person/age": 22, "person.age": 22,
"person/last_name": "Goodman", "person.last_name": "Goodman",
"person/id": "charlie_goodman", "person.id": "charlie_goodman",
"person/weight": 120, "person.weight": 120,
"person/friend": "eve" "person.friend": "eve"
} }
] ]
}}, }},
{"put": { {"put": {
"_temp_id": "david", "_temp_id": "david",
"person/first_name": "David", "person.first_name": "David",
"person/age": 7, "person.age": 7,
"person/last_name": "Dull", "person.last_name": "Dull",
"person/id": "david_dull", "person.id": "david_dull",
"person/weight": 25, "person.weight": 25,
"person/friend": { "person.friend": {
"_temp_id": "george", "_temp_id": "george",
"person/first_name": "George", "person.first_name": "George",
"person/age": 7, "person.age": 7,
"person/last_name": "Geomancer", "person.last_name": "Geomancer",
"person/id": "george_geomancer", "person.id": "george_geomancer",
"person/weight": 25, "person.weight": 25,
"person/friend": "george"}}}, "person.friend": "george"}}},
] ]
})) }))
.unwrap(); .unwrap();
@ -119,9 +119,9 @@ fn creation() {
EntityId::MIN_PERM, EntityId::MIN_PERM,
&json!([ &json!([
"_id", "_id",
"person/first_name", "person.first_name",
"person/last_name", "person.last_name",
{"pull":"person/friend", "as": "friends", "recurse": true}, {"pull":"person.friend", "as": "friends", "recurse": true},
]), ]),
Validity::current(), Validity::current(),
) )
@ -133,18 +133,18 @@ fn creation() {
"q": [ "q": [
{ {
"rule": "ff", "rule": "ff",
"args": [["?a", "?b"], ["?a", "person/friend", "?b"]] "args": [["?a", "?b"], ["?a", "person.friend", "?b"]]
}, },
{ {
"rule": "ff", "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": "?", "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 mut tx = db.transact().unwrap();
let ret = tx.run_query(&query).unwrap(); let ret = tx.run_query(&query).unwrap();

Loading…
Cancel
Save