negation works, provided it is in the correct place

main
Ziyang Hu 2 years ago
parent 91772ca10b
commit 3c2f8c6b0b

@ -291,6 +291,7 @@ impl SessionTx {
.map(|el| self.parse_atom(el, default_vld))
.try_collect()?;
let rule_body = Self::reorder_rule_body_for_negations(rule_body)?;
let rule_body = Self::reorder_rule_body_for_predicates(rule_body)?;
if rule_head.len()
@ -316,6 +317,42 @@ impl SessionTx {
))
}
fn reorder_rule_body_for_negations(clauses: Vec<Atom>) -> Result<Vec<Atom>> {
let (predicates, others): (Vec<_>, _) = clauses.into_iter().partition(|a| a.is_predicate());
let mut predicates_with_meta = predicates
.into_iter()
.map(|p| {
let p = p.into_predicate().unwrap();
let bindings = p.bindings();
(Some(p), bindings)
})
.collect_vec();
let mut seen_bindings = BTreeSet::new();
let mut ret = vec![];
for a in others {
a.collect_bindings(&mut seen_bindings);
ret.push(a);
for (pred, pred_bindings) in predicates_with_meta.iter_mut() {
if pred.is_none() {
continue;
}
if seen_bindings.is_superset(pred_bindings) {
let pred = pred.take().unwrap();
ret.push(Atom::Predicate(pred));
}
}
}
for (p, bindings) in predicates_with_meta {
if let Some(p) = p {
let diff = bindings.difference(&seen_bindings).cloned().collect();
return Err(QueryCompilationError::UnsafeBindingInPredicate(p, diff).into());
}
}
// Ok(ret)
todo!()
}
fn reorder_rule_body_for_predicates(clauses: Vec<Atom>) -> Result<Vec<Atom>> {
let (predicates, others): (Vec<_>, _) = clauses.into_iter().partition(|a| a.is_predicate());
let mut predicates_with_meta = predicates

@ -65,6 +65,8 @@ pub enum QueryCompilationError {
NotAPredicate(&'static str),
#[error("unsafe bindings in expression {0:?}: {1:?}")]
UnsafeBindingInPredicate(Expr, BTreeSet<Keyword>),
#[error("negation safety: all variables {0:?} are unbound")]
NegationSafety(Vec<Keyword>),
}
#[derive(Clone, Debug)]
@ -156,7 +158,7 @@ impl Atom {
p.collect_bindings(coll);
}
Atom::Logical(_) => {
todo!()
// logical operators introduce no new bindings
}
Atom::BindUnify(_) => {
todo!()
@ -416,9 +418,159 @@ impl SessionTx {
ret = ret.join(right, prev_joiner_vars, right_joiner_vars);
}
Atom::Predicate(p) => ret = ret.filter(p.clone()),
Atom::Logical(_) => {
todo!()
}
Atom::Logical(l) => match l {
LogicalAtom::Negation(r) => match r as &Atom {
Atom::AttrTriple(a_triple) => {
match (&a_triple.entity, &a_triple.value) {
(Term::Const(eid), Term::Var(v_kw)) => {
let temp_join_key_left = gen_temp_kw();
let temp_join_key_right = gen_temp_kw();
let const_rel = Relation::singlet(
vec![temp_join_key_left.clone()],
vec![DataValue::EnId(*eid)],
);
if ret.is_unit() {
ret = const_rel;
} else {
ret = ret.cartesian_join(const_rel);
}
let mut join_left_keys = vec![temp_join_key_left];
let mut join_right_keys = vec![temp_join_key_right.clone()];
let v_kw = {
if seen_variables.contains(v_kw) {
let ret = gen_temp_kw();
// to_eliminate.insert(ret.clone());
join_left_keys.push(v_kw.clone());
join_right_keys.push(ret.clone());
ret
} else {
seen_variables.insert(v_kw.clone());
v_kw.clone()
}
};
let right =
Relation::triple(a_triple.attr.clone(), vld, temp_join_key_right, v_kw);
debug_assert_eq!(join_left_keys.len(), join_right_keys.len());
ret = ret.neg_join(right, join_left_keys, join_right_keys);
}
(Term::Var(e_kw), Term::Const(val)) => {
let temp_join_key_left = gen_temp_kw();
let temp_join_key_right = gen_temp_kw();
let const_rel =
Relation::singlet(vec![temp_join_key_left.clone()], vec![val.clone()]);
if ret.is_unit() {
ret = const_rel;
} else {
ret = ret.cartesian_join(const_rel);
}
let mut join_left_keys = vec![temp_join_key_left];
let mut join_right_keys = vec![temp_join_key_right.clone()];
let e_kw = {
if seen_variables.contains(&e_kw) {
let ret = gen_temp_kw();
join_left_keys.push(e_kw.clone());
join_right_keys.push(ret.clone());
ret
} else {
seen_variables.insert(e_kw.clone());
e_kw.clone()
}
};
let right =
Relation::triple(a_triple.attr.clone(), vld, e_kw, temp_join_key_right);
debug_assert_eq!(join_left_keys.len(), join_right_keys.len());
ret = ret.neg_join(right, join_left_keys, join_right_keys);
}
(Term::Var(e_kw), Term::Var(v_kw)) => {
return Err(QueryCompilationError::NegationSafety(vec![e_kw.clone(), v_kw.clone()]).into())
}
(Term::Const(eid), Term::Const(val)) => {
let (left_var_1, left_var_2) = (gen_temp_kw(), gen_temp_kw());
let const_rel = Relation::singlet(
vec![left_var_1.clone(), left_var_2.clone()],
vec![DataValue::EnId(*eid), val.clone()],
);
if ret.is_unit() {
ret = const_rel;
} else {
ret = ret.cartesian_join(const_rel);
}
let (right_var_1, right_var_2) = (gen_temp_kw(), gen_temp_kw());
let right = Relation::triple(
a_triple.attr.clone(),
vld,
right_var_1.clone(),
right_var_2.clone(),
);
ret = ret.neg_join(
right,
vec![left_var_1.clone(), left_var_2.clone()],
vec![right_var_1.clone(), right_var_2.clone()],
);
}
}
}
Atom::Rule(rule_app) => {
let (store, arity) = stores
.get(&rule_app.name)
.ok_or_else(|| QueryCompilationError::UndefinedRule(rule_app.name.clone()))?
.clone();
if arity != rule_app.args.len() {
return Err(
QueryCompilationError::ArityMismatch(rule_app.name.clone()).into()
);
}
let mut prev_joiner_vars = vec![];
let mut temp_left_bindings = vec![];
let mut temp_left_joiner_vals = vec![];
let mut right_joiner_vars = vec![];
let mut right_vars = vec![];
for term in &rule_app.args {
match term {
Term::Var(var) => {
if seen_variables.contains(var) {
prev_joiner_vars.push(var.clone());
let rk = gen_temp_kw();
right_vars.push(rk.clone());
right_joiner_vars.push(rk);
} else {
seen_variables.insert(var.clone());
right_vars.push(var.clone());
}
}
Term::Const(constant) => {
temp_left_joiner_vals.push(constant.clone());
let left_kw = gen_temp_kw();
prev_joiner_vars.push(left_kw.clone());
temp_left_bindings.push(left_kw);
let right_kw = gen_temp_kw();
right_joiner_vars.push(right_kw.clone());
right_vars.push(right_kw);
}
}
}
if !temp_left_joiner_vals.is_empty() {
let const_joiner =
Relation::singlet(temp_left_bindings, temp_left_joiner_vals);
ret = ret.cartesian_join(const_joiner);
}
let right = Relation::derived(right_vars, store);
debug_assert_eq!(prev_joiner_vars.len(), right_joiner_vars.len());
ret = ret.neg_join(right, prev_joiner_vars, right_joiner_vars);
}
_ => unreachable!(),
},
_ => unreachable!(),
},
Atom::BindUnify(_) => {
todo!()
}

@ -20,6 +20,7 @@ pub enum Relation {
Triple(TripleRelation),
Derived(StoredDerivedRelation),
Join(Box<InnerJoin>),
NegJoin(Box<NegJoin>),
Reorder(ReorderRelation),
Filter(FilteredRelation),
}
@ -143,6 +144,13 @@ impl Debug for Relation {
.finish()
}
}
Relation::NegJoin(r) => f
.debug_tuple("NegJoin")
.field(&bindings)
.field(&r.joiner)
.field(&r.left)
.field(&r.right)
.finish(),
Relation::Reorder(r) => f
.debug_tuple("Reorder")
.field(&r.new_order)
@ -175,6 +183,9 @@ impl Relation {
f.parent.fill_predicate_binding_indices();
f.fill_binding_indices()
}
Relation::NegJoin(r) => {
r.left.fill_predicate_binding_indices();
}
}
}
pub(crate) fn unit() -> Self {
@ -244,6 +255,22 @@ impl Relation {
to_eliminate: Default::default(),
}))
}
pub(crate) fn neg_join(
self,
right: Relation,
left_keys: Vec<Keyword>,
right_keys: Vec<Keyword>,
) -> Self {
Relation::NegJoin(Box::new(NegJoin {
left: self,
right,
joiner: Joiner {
left_keys,
right_keys,
},
to_eliminate: Default::default(),
}))
}
}
#[derive(Debug)]
@ -421,6 +448,7 @@ impl TripleRelation {
left_iter: TupleIter<'a>,
(left_join_indices, right_join_indices): (Vec<usize>, Vec<usize>),
tx: &'a SessionTx,
eliminate_indices: BTreeSet<usize>,
) -> TupleIter<'a> {
match right_join_indices.len() {
2 => {
@ -429,20 +457,24 @@ impl TripleRelation {
let left_first = *left_join_indices.first().unwrap();
let left_second = *left_join_indices.last().unwrap();
match (right_first, right_second) {
(0, 1) => self.neg_ev_join(left_iter, left_first, left_second, tx),
(1, 0) => self.neg_ev_join(left_iter, left_second, left_first, tx),
(0, 1) => {
self.neg_ev_join(left_iter, left_first, left_second, tx, eliminate_indices)
}
(1, 0) => {
self.neg_ev_join(left_iter, left_second, left_first, tx, eliminate_indices)
}
_ => panic!("should not happen"),
}
}
1 => {
if right_join_indices[0] == 0 {
self.neg_e_join(left_iter, left_join_indices[0], tx)
self.neg_e_join(left_iter, left_join_indices[0], tx, eliminate_indices)
} else if self.attr.val_type.is_ref_type() {
self.neg_v_ref_join(left_iter, left_join_indices[0], tx)
self.neg_v_ref_join(left_iter, left_join_indices[0], tx, eliminate_indices)
} else if self.attr.indexing.should_index() {
self.neg_v_index_join(left_iter, left_join_indices[0], tx)
self.neg_v_index_join(left_iter, left_join_indices[0], tx, eliminate_indices)
} else {
self.neg_v_no_index_join(left_iter, left_join_indices[0], tx)
self.neg_v_no_index_join(left_iter, left_join_indices[0], tx, eliminate_indices)
}
}
_ => unreachable!(),
@ -509,6 +541,7 @@ impl TripleRelation {
left_e_idx: usize,
left_v_idx: usize,
tx: &'a SessionTx,
eliminate_indices: BTreeSet<usize>,
) -> TupleIter<'a> {
Box::new(
left_iter
@ -516,7 +549,26 @@ impl TripleRelation {
let eid = tuple.0.get(left_e_idx).unwrap().get_entity_id()?;
let v = tuple.0.get(left_v_idx).unwrap();
let exists = tx.eav_exists(eid, self.attr.id, v, self.vld)?;
Ok(if exists { None } else { Some(tuple) })
Ok(if exists {
None
} else if !eliminate_indices.is_empty() {
Some(Tuple(
tuple
.0
.into_iter()
.enumerate()
.filter_map(|(i, v)| {
if eliminate_indices.contains(&i) {
None
} else {
Some(v)
}
})
.collect_vec(),
))
} else {
Some(tuple)
})
})
.map(flatten_err)
.filter_map(invert_option_err),
@ -570,13 +622,31 @@ impl TripleRelation {
left_iter: TupleIter<'a>,
left_e_idx: usize,
tx: &'a SessionTx,
eliminate_indices: BTreeSet<usize>,
) -> TupleIter<'a> {
Box::new(
left_iter
.map_ok(move |tuple| -> Result<Option<Tuple>> {
let eid = tuple.0.get(left_e_idx).unwrap().get_entity_id()?;
match tx.triple_ea_before_scan(eid, self.attr.id, self.vld).next() {
None => Ok(Some(tuple)),
None => Ok(if !eliminate_indices.is_empty() {
Some(Tuple(
tuple
.0
.into_iter()
.enumerate()
.filter_map(|(i, v)| {
if eliminate_indices.contains(&i) {
None
} else {
Some(v)
}
})
.collect_vec(),
))
} else {
Some(tuple)
}),
Some(Ok(_)) => Ok(None),
Some(Err(e)) => Err(e),
}
@ -635,6 +705,7 @@ impl TripleRelation {
left_iter: TupleIter<'a>,
left_v_idx: usize,
tx: &'a SessionTx,
eliminate_indices: BTreeSet<usize>,
) -> TupleIter<'a> {
Box::new(
left_iter
@ -644,7 +715,24 @@ impl TripleRelation {
.triple_vref_a_before_scan(v_eid, self.attr.id, self.vld)
.next()
{
None => Ok(Some(tuple)),
None => Ok(if !eliminate_indices.is_empty() {
Some(Tuple(
tuple
.0
.into_iter()
.enumerate()
.filter_map(|(i, v)| {
if eliminate_indices.contains(&i) {
None
} else {
Some(v)
}
})
.collect_vec(),
))
} else {
Some(tuple)
}),
Some(Ok(_)) => Ok(None),
Some(Err(e)) => Err(e),
}
@ -705,13 +793,31 @@ impl TripleRelation {
left_iter: TupleIter<'a>,
left_v_idx: usize,
tx: &'a SessionTx,
eliminate_indices: BTreeSet<usize>,
) -> TupleIter<'a> {
Box::new(
left_iter
.map_ok(move |tuple| -> Result<Option<Tuple>> {
let val = tuple.0.get(left_v_idx).unwrap();
match tx.triple_av_before_scan(self.attr.id, val, self.vld).next() {
None => Ok(Some(tuple)),
None => Ok(if !eliminate_indices.is_empty() {
Some(Tuple(
tuple
.0
.into_iter()
.enumerate()
.filter_map(|(i, v)| {
if eliminate_indices.contains(&i) {
None
} else {
Some(v)
}
})
.collect_vec(),
))
} else {
Some(tuple)
}),
Some(Ok(_)) => Ok(None),
Some(Err(e)) => Err(e),
}
@ -765,6 +871,7 @@ impl TripleRelation {
left_iter: TupleIter<'a>,
left_v_idx: usize,
tx: &'a SessionTx,
eliminate_indices: BTreeSet<usize>,
) -> TupleIter<'a> {
Box::new(
left_iter
@ -776,7 +883,24 @@ impl TripleRelation {
return Ok(None);
}
}
Ok(Some(tuple))
Ok(if !eliminate_indices.is_empty() {
Some(Tuple(
tuple
.0
.into_iter()
.enumerate()
.filter_map(|(i, v)| {
if eliminate_indices.contains(&i) {
None
} else {
Some(v)
}
})
.collect_vec(),
))
} else {
Some(tuple)
})
})
.map(flatten_err)
.filter_map(invert_option_err),
@ -885,6 +1009,7 @@ impl StoredDerivedRelation {
&'a self,
left_iter: TupleIter<'a>,
(left_join_indices, right_join_indices): (Vec<usize>, Vec<usize>),
eliminate_indices: BTreeSet<usize>,
) -> TupleIter<'a> {
debug_assert!(!right_join_indices.is_empty());
let mut right_invert_indices = right_join_indices.iter().enumerate().collect_vec();
@ -918,7 +1043,24 @@ impl StoredDerivedRelation {
}
return Ok(None);
}
Ok(Some(tuple))
Ok(Some(if !eliminate_indices.is_empty() {
Tuple(
tuple
.0
.into_iter()
.enumerate()
.filter_map(|(i, v)| {
if eliminate_indices.contains(&i) {
None
} else {
Some(v)
}
})
.collect_vec(),
)
} else {
tuple
}))
})
.map(flatten_err)
.filter_map(invert_option_err),
@ -1048,14 +1190,6 @@ impl Joiner {
}
}
#[derive(Debug)]
pub struct InnerJoin {
pub(crate) left: Relation,
pub(crate) right: Relation,
pub(crate) joiner: Joiner,
pub(crate) to_eliminate: BTreeSet<Keyword>,
}
impl Relation {
pub(crate) fn eliminate_temp_vars(&mut self, used: &BTreeSet<Keyword>) -> Result<()> {
match self {
@ -1065,6 +1199,7 @@ impl Relation {
Relation::Join(r) => r.do_eliminate_temp_vars(used),
Relation::Reorder(r) => r.relation.eliminate_temp_vars(used),
Relation::Filter(r) => r.do_eliminate_temp_vars(used),
Relation::NegJoin(r) => r.do_eliminate_temp_vars(used),
}
}
@ -1076,6 +1211,7 @@ impl Relation {
Relation::Join(r) => Some(&r.to_eliminate),
Relation::Reorder(_) => None,
Relation::Filter(r) => Some(&r.to_eliminate),
Relation::NegJoin(r) => Some(&r.to_eliminate),
}
}
@ -1098,6 +1234,7 @@ impl Relation {
Relation::Join(j) => j.bindings(),
Relation::Reorder(r) => r.bindings(),
Relation::Filter(r) => r.parent.bindings_after_eliminate(),
Relation::NegJoin(j) => j.left.bindings_after_eliminate(),
}
}
pub fn iter<'a>(
@ -1116,10 +1253,85 @@ impl Relation {
Relation::Join(j) => j.iter(tx, epoch, use_delta),
Relation::Reorder(r) => r.iter(tx, epoch, use_delta),
Relation::Filter(r) => r.iter(tx, epoch, use_delta),
Relation::NegJoin(r) => r.iter(tx, epoch, use_delta),
}
}
}
#[derive(Debug)]
pub struct NegJoin {
pub(crate) left: Relation,
pub(crate) right: Relation,
pub(crate) joiner: Joiner,
pub(crate) to_eliminate: BTreeSet<Keyword>,
}
impl NegJoin {
pub(crate) fn do_eliminate_temp_vars(&mut self, used: &BTreeSet<Keyword>) -> Result<()> {
for binding in self.left.bindings_after_eliminate() {
if !used.contains(&binding) {
self.to_eliminate.insert(binding.clone());
}
}
let mut left = used.clone();
left.extend(self.joiner.left_keys.clone());
self.left.eliminate_temp_vars(&left)?;
// right acts as a filter, introduces nothing, no need to eliminate
Ok(())
}
pub(crate) fn iter<'a>(
&'a self,
tx: &'a SessionTx,
epoch: Option<u32>,
use_delta: &BTreeSet<TempStoreId>,
) -> TupleIter<'a> {
let bindings = self.left.bindings_after_eliminate();
let eliminate_indices = get_eliminate_indices(&bindings, &self.to_eliminate);
match &self.right {
Relation::Triple(r) => {
let join_indices = self
.joiner
.join_indices(
&self.left.bindings_after_eliminate(),
&self.right.bindings_after_eliminate(),
)
.unwrap();
r.neg_join(
self.left.iter(tx, epoch, use_delta),
join_indices,
tx,
eliminate_indices,
)
}
Relation::Derived(r) => {
let join_indices = self
.joiner
.join_indices(
&self.left.bindings_after_eliminate(),
&self.right.bindings_after_eliminate(),
)
.unwrap();
r.neg_join(
self.left.iter(tx, epoch, use_delta),
join_indices,
eliminate_indices,
)
}
_ => {
unreachable!()
}
}
}
}
#[derive(Debug)]
pub struct InnerJoin {
pub(crate) left: Relation,
pub(crate) right: Relation,
pub(crate) joiner: Joiner,
pub(crate) to_eliminate: BTreeSet<Keyword>,
}
impl InnerJoin {
pub(crate) fn do_eliminate_temp_vars(&mut self, used: &BTreeSet<Keyword>) -> Result<()> {
for binding in self.bindings() {
@ -1206,6 +1418,9 @@ impl InnerJoin {
Relation::Reorder(_) => {
panic!("joining on reordered")
}
Relation::NegJoin(_) => {
panic!("joining on NegJoin")
}
}
}
fn materialized_join<'a>(

@ -142,9 +142,9 @@ fn creation() {
{
"rule": "?",
"args": [["?a"],
{"not_exists": ["?a", "person.last_name", "Goodman"]},
{"pred": "Neq", "args": ["?n", {"pred": "StrCat", "args": ["A", "l", "i", "c", "e"]}]},
{"rule": "ff", "args": [{"person.id": "alice_amorist"}, "?a"]},
{"not_exists": ["?a", "person.last_name", "Goodman"]},
["?a", "person.first_name", "?n"]]
}
],

Loading…
Cancel
Save