Address review comments rnewman.
* Remove `WhereNotClause` and populate `NotJoin` with `WhereClause`. * Fix validation for `not` and `not-join`, removing tests that were invalid. * Address rustification comments.
This commit is contained in:
parent
fb2dde1d2f
commit
7eea65b3e2
4 changed files with 48 additions and 157 deletions
|
@ -62,8 +62,8 @@ mod predicate;
|
||||||
mod resolve;
|
mod resolve;
|
||||||
|
|
||||||
use validate::{
|
use validate::{
|
||||||
validate_or_join,
|
|
||||||
validate_not_join,
|
validate_not_join,
|
||||||
|
validate_or_join,
|
||||||
};
|
};
|
||||||
|
|
||||||
// We do this a lot for errors.
|
// We do this a lot for errors.
|
||||||
|
|
|
@ -79,36 +79,13 @@ pub fn validate_not_join(not_join: &NotJoin) -> Result<()> {
|
||||||
// Grab our mentioned variables and ensure that the rules are followed.
|
// Grab our mentioned variables and ensure that the rules are followed.
|
||||||
match not_join.unify_vars {
|
match not_join.unify_vars {
|
||||||
UnifyVars::Implicit => {
|
UnifyVars::Implicit => {
|
||||||
if not_join.clauses.len() < 2 {
|
Ok(())
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
let mut clauses = not_join.clauses.iter();
|
|
||||||
let template = clauses.next().unwrap().collect_mentioned_variables();
|
|
||||||
for clause in clauses {
|
|
||||||
if template != clause.collect_mentioned_variables() {
|
|
||||||
bail!(ErrorKind::NonMatchingVariablesInNotClause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
UnifyVars::Explicit(ref vars) => {
|
UnifyVars::Explicit(ref vars) => {
|
||||||
// The joined vars must each appear somewhere in the clauses mentioned variables.
|
// The joined vars must each appear somewhere in the clauses mentioned variables.
|
||||||
let var_set: BTreeSet<Variable> = vars.iter().cloned().collect();
|
let var_set: BTreeSet<Variable> = vars.iter().cloned().collect();
|
||||||
for var in &var_set {
|
if !var_set.is_subset(¬_join.collect_mentioned_variables()) {
|
||||||
if !¬_join.clauses.iter().any(|clause| clause.collect_mentioned_variables().contains(&var)) {
|
bail!(ErrorKind::NonMatchingVariablesInNotClause);
|
||||||
bail!(ErrorKind::NonMatchingVariablesInNotClause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// any other mentioned variables must also unify
|
|
||||||
let mut clauses = not_join.clauses.iter();
|
|
||||||
let template: BTreeSet<Variable> = clauses.next().unwrap().collect_mentioned_variables().difference(&var_set).cloned().collect();
|
|
||||||
for clause in clauses {
|
|
||||||
let mentioned_vars = clause.collect_mentioned_variables().difference(&var_set).cloned().collect();
|
|
||||||
if template != mentioned_vars {
|
|
||||||
bail!(ErrorKind::NonMatchingVariablesInOrClause);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
@ -125,7 +102,6 @@ mod tests {
|
||||||
FindQuery,
|
FindQuery,
|
||||||
NamespacedKeyword,
|
NamespacedKeyword,
|
||||||
OrWhereClause,
|
OrWhereClause,
|
||||||
WhereNotClause,
|
|
||||||
Pattern,
|
Pattern,
|
||||||
PatternNonValuePlace,
|
PatternNonValuePlace,
|
||||||
PatternValuePlace,
|
PatternValuePlace,
|
||||||
|
@ -139,8 +115,8 @@ mod tests {
|
||||||
use clauses::ident;
|
use clauses::ident;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
validate_or_join,
|
|
||||||
validate_not_join,
|
validate_not_join,
|
||||||
|
validate_or_join,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn value_ident(ns: &str, name: &str) -> PatternValuePlace {
|
fn value_ident(ns: &str, name: &str) -> PatternValuePlace {
|
||||||
|
@ -277,15 +253,15 @@ mod tests {
|
||||||
|
|
||||||
|
|
||||||
/// Tests that the top-level form is a valid `not`, returning the clauses.
|
/// Tests that the top-level form is a valid `not`, returning the clauses.
|
||||||
fn valid_not_join(parsed: FindQuery, expected_unify: UnifyVars) -> Vec<WhereNotClause> {
|
fn valid_not_join(parsed: FindQuery, expected_unify: UnifyVars) -> Vec<WhereClause> {
|
||||||
// filter all the not clauses
|
// Filter out all the clauses that are not `not`s.
|
||||||
let mut nots = parsed.where_clauses.iter().filter(|&x| match *x {
|
let mut nots = parsed.where_clauses.into_iter().filter(|x| match x {
|
||||||
WhereClause::NotJoin(_) => true,
|
&WhereClause::NotJoin(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// there should be only one not clause
|
// There should be only one not clause.
|
||||||
let clause = nots.next().unwrap().clone();
|
let clause = nots.next().unwrap();
|
||||||
assert_eq!(None, nots.next());
|
assert_eq!(None, nots.next());
|
||||||
|
|
||||||
match clause {
|
match clause {
|
||||||
|
@ -299,7 +275,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test that a `not` is valid if all of its arms refer to the same variables.
|
/// Test that a `not` is valid if it is implicit.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_success_not() {
|
fn test_success_not() {
|
||||||
let query = r#"[:find ?name
|
let query = r#"[:find ?name
|
||||||
|
@ -315,54 +291,27 @@ mod tests {
|
||||||
(Some(clause1), Some(clause2), None) => {
|
(Some(clause1), Some(clause2), None) => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
clause1,
|
clause1,
|
||||||
WhereNotClause::Clause(
|
WhereClause::Pattern(Pattern {
|
||||||
WhereClause::Pattern(Pattern {
|
source: None,
|
||||||
source: None,
|
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?id")),
|
||||||
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?id")),
|
attribute: ident("artist", "country"),
|
||||||
attribute: ident("artist", "country"),
|
value: value_ident("country", "CA"),
|
||||||
value: value_ident("country", "CA"),
|
tx: PatternNonValuePlace::Placeholder,
|
||||||
tx: PatternNonValuePlace::Placeholder,
|
}));
|
||||||
}),
|
|
||||||
));
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
clause2,
|
clause2,
|
||||||
WhereNotClause::Clause(
|
WhereClause::Pattern(Pattern {
|
||||||
WhereClause::Pattern(Pattern {
|
source: None,
|
||||||
source: None,
|
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?id")),
|
||||||
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?id")),
|
attribute: ident("artist", "country"),
|
||||||
attribute: ident("artist", "country"),
|
value: value_ident("country", "GB"),
|
||||||
value: value_ident("country", "GB"),
|
tx: PatternNonValuePlace::Placeholder,
|
||||||
tx: PatternNonValuePlace::Placeholder,
|
}));
|
||||||
}),
|
|
||||||
));
|
|
||||||
},
|
},
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Test that a `not` with differing variable sets in each body part will fail to validate.
|
|
||||||
#[test]
|
|
||||||
fn test_invalid_implicit_not() {
|
|
||||||
let query = r#"[:find ?name
|
|
||||||
:where [?id :artist/name ?name]
|
|
||||||
(not [?id :artist/country :country/CA]
|
|
||||||
[?release :release/year 1970])]"#;
|
|
||||||
let parsed = parse_find_string(query).expect("expected successful parse");
|
|
||||||
let mut nots = parsed.where_clauses.iter().filter(|&x| match *x {
|
|
||||||
WhereClause::NotJoin(_) => true,
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let clause = nots.next().unwrap().clone();
|
|
||||||
assert_eq!(None, nots.next());
|
|
||||||
|
|
||||||
match clause {
|
|
||||||
WhereClause::NotJoin(not_join) => assert!(validate_not_join(¬_join).is_err()),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_success_not_join() {
|
fn test_success_not_join() {
|
||||||
let query = r#"[:find ?artist
|
let query = r#"[:find ?artist
|
||||||
|
@ -373,30 +322,28 @@ mod tests {
|
||||||
let parsed = parse_find_string(query).expect("expected successful parse");
|
let parsed = parse_find_string(query).expect("expected successful parse");
|
||||||
let clauses = valid_not_join(parsed, UnifyVars::Explicit(vec![Variable::from_valid_name("?artist")]));
|
let clauses = valid_not_join(parsed, UnifyVars::Explicit(vec![Variable::from_valid_name("?artist")]));
|
||||||
|
|
||||||
// // Let's do some detailed parse checks.
|
// Let's do some detailed parse checks.
|
||||||
let mut parts = clauses.into_iter();
|
let mut parts = clauses.into_iter();
|
||||||
match (parts.next(), parts.next(), parts.next()) {
|
match (parts.next(), parts.next(), parts.next()) {
|
||||||
(Some(clause1), Some(clause2), None) => {
|
(Some(clause1), Some(clause2), None) => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
clause1,
|
clause1,
|
||||||
WhereNotClause::Clause(
|
WhereClause::Pattern(Pattern {
|
||||||
WhereClause::Pattern(Pattern {
|
source: None,
|
||||||
source: None,
|
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?release")),
|
||||||
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?release")),
|
attribute: ident("release", "artists"),
|
||||||
attribute: ident("release", "artists"),
|
value: PatternValuePlace::Variable(Variable::from_valid_name("?artist")),
|
||||||
value: PatternValuePlace::Variable(Variable::from_valid_name("?artist")),
|
tx: PatternNonValuePlace::Placeholder,
|
||||||
tx: PatternNonValuePlace::Placeholder,
|
}));
|
||||||
})));
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
clause2,
|
clause2,
|
||||||
WhereNotClause::Clause(
|
WhereClause::Pattern(Pattern {
|
||||||
WhereClause::Pattern(Pattern {
|
source: None,
|
||||||
source: None,
|
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?release")),
|
||||||
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?release")),
|
attribute: ident("release", "year"),
|
||||||
attribute: ident("release", "year"),
|
value: PatternValuePlace::EntidOrInteger(1970),
|
||||||
value: PatternValuePlace::EntidOrInteger(1970),
|
tx: PatternNonValuePlace::Placeholder,
|
||||||
tx: PatternNonValuePlace::Placeholder,
|
}));
|
||||||
})));
|
|
||||||
},
|
},
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
|
@ -424,27 +371,4 @@ mod tests {
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test that a `not-join` that does not match any vars declared inside it fails to validate.
|
|
||||||
#[test]
|
|
||||||
fn test_invalid_explicit_not_join_non_matching_internal_vars() {
|
|
||||||
let query = r#"[:find ?artist
|
|
||||||
:where [?artist :artist/name]
|
|
||||||
(not-join [?artist]
|
|
||||||
[?release :release/artists ?artist]
|
|
||||||
[?year :release/year 1970])]"#;
|
|
||||||
let parsed = parse_find_string(query).expect("expected successful parse");
|
|
||||||
let mut nots = parsed.where_clauses.iter().filter(|&x| match *x {
|
|
||||||
WhereClause::NotJoin(_) => true,
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let clause = nots.next().unwrap().clone();
|
|
||||||
assert_eq!(None, nots.next());
|
|
||||||
|
|
||||||
match clause {
|
|
||||||
WhereClause::NotJoin(not_join) => assert!(validate_not_join(¬_join).is_err()),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -43,7 +43,6 @@ use self::mentat_query::{
|
||||||
OrJoin,
|
OrJoin,
|
||||||
OrWhereClause,
|
OrWhereClause,
|
||||||
NotJoin,
|
NotJoin,
|
||||||
WhereNotClause,
|
|
||||||
Pattern,
|
Pattern,
|
||||||
PatternNonValuePlace,
|
PatternNonValuePlace,
|
||||||
PatternValuePlace,
|
PatternValuePlace,
|
||||||
|
@ -188,19 +187,11 @@ def_parser!(Where, or_join_clause, WhereClause, {
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
|
|
||||||
def_value_parser_fn!(Where, not_pattern_clause, WhereNotClause, input, {
|
|
||||||
Where::clause().map(|clause| WhereNotClause::Clause(clause)).parse_stream(input)
|
|
||||||
});
|
|
||||||
|
|
||||||
def_value_parser_fn!(Where, where_not_clause, WhereNotClause, input, {
|
|
||||||
choice([Where::not_pattern_clause()]).parse_stream(input)
|
|
||||||
});
|
|
||||||
|
|
||||||
def_value_parser_fn!(Where, not_clause, WhereClause, input, {
|
def_value_parser_fn!(Where, not_clause, WhereClause, input, {
|
||||||
satisfy_map(|x: edn::Value| {
|
satisfy_map(|x: edn::Value| {
|
||||||
seq(x).and_then(|items| {
|
seq(x).and_then(|items| {
|
||||||
let mut p = Where::not()
|
let mut p = Where::not()
|
||||||
.with(many1(Where::where_not_clause()))
|
.with(many1(Where::clause()))
|
||||||
.skip(eof())
|
.skip(eof())
|
||||||
.map(|clauses| {
|
.map(|clauses| {
|
||||||
WhereClause::NotJoin(
|
WhereClause::NotJoin(
|
||||||
|
@ -220,7 +211,7 @@ def_value_parser_fn!(Where, not_join_clause, WhereClause, input, {
|
||||||
seq(x).and_then(|items| {
|
seq(x).and_then(|items| {
|
||||||
let mut p = Where::not_join()
|
let mut p = Where::not_join()
|
||||||
.with(Where::rule_vars())
|
.with(Where::rule_vars())
|
||||||
.and(many1(Where::where_not_clause()))
|
.and(many1(Where::clause()))
|
||||||
.skip(eof())
|
.skip(eof())
|
||||||
.map(|(vars, clauses)| {
|
.map(|(vars, clauses)| {
|
||||||
WhereClause::NotJoin(
|
WhereClause::NotJoin(
|
||||||
|
@ -618,14 +609,14 @@ mod test {
|
||||||
WhereClause::NotJoin(
|
WhereClause::NotJoin(
|
||||||
NotJoin {
|
NotJoin {
|
||||||
unify_vars: UnifyVars::Implicit,
|
unify_vars: UnifyVars::Implicit,
|
||||||
clauses: vec![WhereNotClause::Clause(
|
clauses: vec![
|
||||||
WhereClause::Pattern(Pattern {
|
WhereClause::Pattern(Pattern {
|
||||||
source: None,
|
source: None,
|
||||||
entity: PatternNonValuePlace::Variable(variable(e)),
|
entity: PatternNonValuePlace::Variable(variable(e)),
|
||||||
attribute: PatternNonValuePlace::Variable(variable(a)),
|
attribute: PatternNonValuePlace::Variable(variable(a)),
|
||||||
value: PatternValuePlace::Variable(variable(v)),
|
value: PatternValuePlace::Variable(variable(v)),
|
||||||
tx: PatternNonValuePlace::Placeholder,
|
tx: PatternNonValuePlace::Placeholder,
|
||||||
}))],
|
})],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,14 +636,13 @@ mod test {
|
||||||
WhereClause::NotJoin(
|
WhereClause::NotJoin(
|
||||||
NotJoin {
|
NotJoin {
|
||||||
unify_vars: UnifyVars::Explicit(vec![variable(e.clone())]),
|
unify_vars: UnifyVars::Explicit(vec![variable(e.clone())]),
|
||||||
clauses: vec![WhereNotClause::Clause(
|
clauses: vec![WhereClause::Pattern(Pattern {
|
||||||
WhereClause::Pattern(Pattern {
|
|
||||||
source: None,
|
source: None,
|
||||||
entity: PatternNonValuePlace::Variable(variable(e)),
|
entity: PatternNonValuePlace::Variable(variable(e)),
|
||||||
attribute: PatternNonValuePlace::Variable(variable(a)),
|
attribute: PatternNonValuePlace::Variable(variable(a)),
|
||||||
value: PatternValuePlace::Variable(variable(v)),
|
value: PatternValuePlace::Variable(variable(v)),
|
||||||
tx: PatternNonValuePlace::Placeholder,
|
tx: PatternNonValuePlace::Placeholder,
|
||||||
}))],
|
})],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -570,24 +570,10 @@ pub struct OrJoin {
|
||||||
pub clauses: Vec<OrWhereClause>,
|
pub clauses: Vec<OrWhereClause>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub enum WhereNotClause {
|
|
||||||
Clause(WhereClause),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WhereNotClause {
|
|
||||||
pub fn is_pattern_or_patterns(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
&WhereNotClause::Clause(WhereClause::Pattern(_)) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct NotJoin {
|
pub struct NotJoin {
|
||||||
pub unify_vars: UnifyVars,
|
pub unify_vars: UnifyVars,
|
||||||
pub clauses: Vec<WhereNotClause>,
|
pub clauses: Vec<WhereClause>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -680,15 +666,6 @@ impl ContainsVariables for NotJoin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContainsVariables for WhereNotClause {
|
|
||||||
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
|
||||||
use WhereNotClause::*;
|
|
||||||
match self {
|
|
||||||
&Clause(ref clause) => clause.accumulate_mentioned_variables(acc),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContainsVariables for Predicate {
|
impl ContainsVariables for Predicate {
|
||||||
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
||||||
for arg in &self.args {
|
for arg in &self.args {
|
||||||
|
|
Loading…
Reference in a new issue