Part 5: eliminate is_known_empty in favor of empty_because and an accessor.

This commit is contained in:
Richard Newman 2017-04-05 17:20:13 -07:00
parent a07efc0a9e
commit b693385495
6 changed files with 46 additions and 46 deletions

View file

@ -142,8 +142,7 @@ impl<K: Clone + Ord, V: Clone> Intersection<K> for BTreeMap<K, V> {
/// * Inline expressions?
///---------------------------------------------------------------------------------------
pub struct ConjoiningClauses {
/// `true` if this set of clauses cannot yield results in the context of the current schema.
pub is_known_empty: bool,
/// `Some` if this set of clauses cannot yield results in the context of the current schema.
pub empty_because: Option<EmptyBecause>,
/// A function used to generate an alias for a table -- e.g., from "datoms" to "datoms123".
@ -188,7 +187,7 @@ pub struct ConjoiningClauses {
impl Debug for ConjoiningClauses {
fn fmt(&self, fmt: &mut Formatter) -> ::std::fmt::Result {
fmt.debug_struct("ConjoiningClauses")
.field("is_known_empty", &self.is_known_empty)
.field("empty_because", &self.empty_because)
.field("from", &self.from)
.field("wheres", &self.wheres)
.field("column_bindings", &self.column_bindings)
@ -204,7 +203,6 @@ impl Debug for ConjoiningClauses {
impl Default for ConjoiningClauses {
fn default() -> ConjoiningClauses {
ConjoiningClauses {
is_known_empty: false,
empty_because: None,
aliaser: default_table_aliaser(),
from: vec![],
@ -222,7 +220,6 @@ impl Default for ConjoiningClauses {
impl ConjoiningClauses {
fn make_receptacle(&self) -> ConjoiningClauses {
let mut concrete = ConjoiningClauses::default();
concrete.is_known_empty = self.is_known_empty;
concrete.empty_because = self.empty_because.clone();
concrete.input_variables = self.input_variables.clone();
@ -238,7 +235,6 @@ impl ConjoiningClauses {
/// simple `or`.
fn use_as_template(&self, vars: &BTreeSet<Variable>) -> ConjoiningClauses {
let mut template = ConjoiningClauses::default();
template.is_known_empty = self.is_known_empty;
template.empty_because = self.empty_because.clone();
template.input_variables = self.input_variables.intersection(vars).cloned().collect();
@ -385,7 +381,7 @@ impl ConjoiningClauses {
/// Like `constrain_var_to_type` but in reverse: this expands the set of types
/// with which a variable is associated.
///
/// N.B.,: if we ever call `broaden_types` after `is_known_empty` has been set, we might
/// N.B.,: if we ever call `broaden_types` after `empty_because` has been set, we might
/// actually move from a state in which a variable can have no type to one that can
/// yield results! We never do so at present -- we carefully set-union types before we
/// set-intersect them -- but this is worth bearing in mind.
@ -399,7 +395,7 @@ impl ConjoiningClauses {
e.insert(new_types);
},
Entry::Occupied(mut e) => {
if e.get().is_empty() && self.is_known_empty {
if e.get().is_empty() && self.empty_because.is_some() {
panic!("Uh oh: we failed this pattern, probably because {:?} couldn't match, but now we're broadening its type.",
e.get());
}
@ -478,8 +474,12 @@ impl ConjoiningClauses {
}
}
#[inline]
pub fn is_known_empty(&self) -> bool {
self.empty_because.is_some()
}
fn mark_known_empty(&mut self, why: EmptyBecause) {
self.is_known_empty = true;
if self.empty_because.is_some() {
return;
}
@ -589,7 +589,7 @@ impl ConjoiningClauses {
/// Produce a (table, alias) pair to handle the provided pattern.
/// This is a mutating method because it mutates the aliaser function!
/// Note that if this function decides that a pattern cannot match, it will flip
/// `is_known_empty`.
/// `empty_because`.
fn alias_table<'s, 'a>(&mut self, schema: &'s Schema, pattern: &'a Pattern) -> Option<SourceAlias> {
self.table_for_places(schema, &pattern.attribute, &pattern.value)
.map_err(|reason| {

View file

@ -342,7 +342,7 @@ impl ConjoiningClauses {
/// ```
///
fn apply_simple_or_join(&mut self, schema: &Schema, patterns: Vec<Pattern>, mentioned_vars: BTreeSet<Variable>) -> Result<()> {
if self.is_known_empty {
if self.is_known_empty() {
return Ok(())
}
@ -391,7 +391,7 @@ impl ConjoiningClauses {
let mut receptacle = template.make_receptacle();
println!("Applying pattern with attribute {:?}", pattern.attribute);
receptacle.apply_pattern_clause_for_alias(schema, &pattern, &source_alias);
if receptacle.is_known_empty {
if receptacle.is_known_empty() {
println!("Receptacle is empty.");
let reason = receptacle.empty_because;
None
@ -471,8 +471,7 @@ impl ConjoiningClauses {
}
fn intersect(&mut self, mut cc: ConjoiningClauses) -> Result<()> {
if cc.is_known_empty {
self.is_known_empty = true;
if cc.is_known_empty() {
self.empty_because = cc.empty_because;
}
self.wheres.append(&mut cc.wheres);
@ -573,7 +572,7 @@ mod testing {
[?x :foo/nope2 "Ámbar"]
[?x :foo/nope3 "Daphne"])]"#;
let cc = alg(&schema, query);
assert!(cc.is_known_empty);
assert!(cc.is_known_empty());
assert_eq!(cc.empty_because, Some(EmptyBecause::InvalidAttributeIdent(NamespacedKeyword::new("foo", "nope3"))));
}
@ -587,7 +586,7 @@ mod testing {
[?x :foo/parent "Ámbar"]
[?x :foo/nope "Daphne"])]"#;
let cc = alg(&schema, query);
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
compare_ccs(cc, alg(&schema, r#"[:find ?x :where [?x :foo/parent "Ámbar"]]"#));
}
@ -612,7 +611,7 @@ mod testing {
let ambar = QueryValue::TypedValue(TypedValue::typed_string("Ámbar"));
let daphne = QueryValue::TypedValue(TypedValue::typed_string("Daphne"));
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
assert_eq!(cc.wheres, ColumnIntersection(vec![
ColumnConstraintOrAlternation::Alternation(
ColumnAlternation(vec![
@ -657,7 +656,7 @@ mod testing {
let ambar = QueryValue::TypedValue(TypedValue::typed_string("Ámbar"));
let daphne = QueryValue::TypedValue(TypedValue::typed_string("Daphne"));
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
assert_eq!(cc.wheres, ColumnIntersection(vec![
ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d0a.clone(), name.clone())),
ColumnConstraintOrAlternation::Alternation(
@ -706,7 +705,7 @@ mod testing {
let john = QueryValue::TypedValue(TypedValue::typed_string("John"));
let daphne = QueryValue::TypedValue(TypedValue::typed_string("Daphne"));
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
assert_eq!(cc.wheres, ColumnIntersection(vec![
ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d0a.clone(), age.clone())),
ColumnConstraintOrAlternation::Constraint(ColumnConstraint::NumericInequality {
@ -754,7 +753,7 @@ mod testing {
let knows = QueryValue::Entid(66);
let parent = QueryValue::Entid(67);
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
assert_eq!(cc.wheres, ColumnIntersection(vec![
ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d0a.clone(), knows.clone())),
ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d1a.clone(), parent.clone())),

View file

@ -41,7 +41,7 @@ impl ConjoiningClauses {
/// account all information spread across two patterns.
///
/// If the constraints cannot be satisfied -- for example, if this pattern includes a numeric
/// attribute and a string value -- then the `is_known_empty` field on the CC is flipped and
/// attribute and a string value -- then the `empty_because` field on the CC is flipped and
/// the function returns.
///
/// A pattern being impossible to satisfy isn't necessarily a bad thing -- this query might
@ -71,7 +71,7 @@ impl ConjoiningClauses {
///
/// This method is only public for use from `or.rs`.
pub fn apply_pattern_clause_for_alias<'s>(&mut self, schema: &'s Schema, pattern: &Pattern, alias: &SourceAlias) {
if self.is_known_empty {
if self.is_known_empty() {
return;
}
@ -154,7 +154,7 @@ impl ConjoiningClauses {
// Wouldn't it be nice if we didn't need to clone in the found case?
// It doesn't matter too much: collisons won't be too frequent.
self.constrain_var_to_type(v.clone(), this_type);
if self.is_known_empty {
if self.is_known_empty() {
return;
}
}
@ -322,7 +322,7 @@ mod testing {
tx: PatternNonValuePlace::Placeholder,
});
assert!(cc.is_known_empty);
assert!(cc.is_known_empty());
}
#[test]
@ -340,7 +340,7 @@ mod testing {
tx: PatternNonValuePlace::Placeholder,
});
assert!(cc.is_known_empty);
assert!(cc.is_known_empty());
}
#[test]
@ -370,7 +370,7 @@ mod testing {
let d0_v = QualifiedAlias("datoms00".to_string(), DatomsColumn::Value);
// After this, we know a lot of things:
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
assert_eq!(cc.from, vec![SourceAlias(DatomsTable::Datoms, "datoms00".to_string())]);
// ?x must be a ref.
@ -408,7 +408,7 @@ mod testing {
let d0_e = QualifiedAlias("datoms00".to_string(), DatomsColumn::Entity);
let d0_v = QualifiedAlias("datoms00".to_string(), DatomsColumn::Value);
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
assert_eq!(cc.from, vec![SourceAlias(DatomsTable::Datoms, "datoms00".to_string())]);
// ?x must be a ref.
@ -457,7 +457,7 @@ mod testing {
let d0_e = QualifiedAlias("datoms00".to_string(), DatomsColumn::Entity);
let d0_a = QualifiedAlias("datoms00".to_string(), DatomsColumn::Attribute);
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
assert_eq!(cc.from, vec![SourceAlias(DatomsTable::Datoms, "datoms00".to_string())]);
// ?x must be a ref, and ?v a boolean.
@ -495,7 +495,7 @@ mod testing {
tx: PatternNonValuePlace::Placeholder,
});
assert!(cc.is_known_empty);
assert!(cc.is_known_empty());
assert_eq!(cc.empty_because.unwrap(), EmptyBecause::InvalidBinding(DatomsColumn::Attribute, hello));
}
@ -521,7 +521,7 @@ mod testing {
let d0_e = QualifiedAlias("all_datoms00".to_string(), DatomsColumn::Entity);
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
assert_eq!(cc.from, vec![SourceAlias(DatomsTable::AllDatoms, "all_datoms00".to_string())]);
// ?x must be a ref.
@ -552,7 +552,7 @@ mod testing {
let d0_e = QualifiedAlias("all_datoms00".to_string(), DatomsColumn::Entity);
let d0_v = QualifiedAlias("all_datoms00".to_string(), DatomsColumn::Value);
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
assert_eq!(cc.from, vec![SourceAlias(DatomsTable::AllDatoms, "all_datoms00".to_string())]);
// ?x must be a ref.
@ -615,7 +615,7 @@ mod testing {
let d1_e = QualifiedAlias("datoms01".to_string(), DatomsColumn::Entity);
let d1_a = QualifiedAlias("datoms01".to_string(), DatomsColumn::Attribute);
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
assert_eq!(cc.from, vec![
SourceAlias(DatomsTable::Datoms, "datoms00".to_string()),
SourceAlias(DatomsTable::Datoms, "datoms01".to_string()),
@ -715,7 +715,7 @@ mod testing {
});
// The type of the provided binding doesn't match the type of the attribute.
assert!(cc.is_known_empty);
assert!(cc.is_known_empty());
}
#[test]
@ -747,7 +747,7 @@ mod testing {
});
// The type of the provided binding doesn't match the type of the attribute.
assert!(cc.is_known_empty);
assert!(cc.is_known_empty());
}
#[test]
@ -790,13 +790,13 @@ mod testing {
// Finally, expand column bindings to get the overlaps for ?x.
cc.expand_column_bindings();
assert!(cc.is_known_empty);
assert!(cc.is_known_empty());
assert_eq!(cc.empty_because.unwrap(),
EmptyBecause::TypeMismatch(y.clone(), unit_type_set(ValueType::String), ValueType::Boolean));
}
#[test]
#[should_panic(expected = "assertion failed: cc.is_known_empty")]
#[should_panic(expected = "assertion failed: cc.is_known_empty()")]
/// This test needs range inference in order to succeed: we must deduce that ?y must
/// simultaneously be a boolean-valued attribute and a ref-valued attribute, and thus
/// the CC can never return results.
@ -828,7 +828,7 @@ mod testing {
// Finally, expand column bindings to get the overlaps for ?x.
cc.expand_column_bindings();
assert!(cc.is_known_empty);
assert!(cc.is_known_empty());
assert_eq!(cc.empty_because.unwrap(),
EmptyBecause::TypeMismatch(x.clone(), unit_type_set(ValueType::Ref), ValueType::Boolean));
}

View file

@ -140,7 +140,7 @@ mod testing {
value: PatternValuePlace::Variable(y.clone()),
tx: PatternNonValuePlace::Placeholder,
});
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
let op = PlainSymbol::new("<");
let comp = NumericComparison::from_datalog_operator(op.plain_name()).unwrap();
@ -150,11 +150,11 @@ mod testing {
FnArg::Variable(Variable::from_valid_name("?y")), FnArg::EntidOrInteger(10),
]}).is_ok());
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
// Finally, expand column bindings to get the overlaps for ?x.
cc.expand_column_bindings();
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
// After processing those two clauses, we know that ?y must be numeric, but not exactly
// which type it must be.
@ -200,7 +200,7 @@ mod testing {
value: PatternValuePlace::Variable(y.clone()),
tx: PatternNonValuePlace::Placeholder,
});
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
let op = PlainSymbol::new(">=");
let comp = NumericComparison::from_datalog_operator(op.plain_name()).unwrap();
@ -210,7 +210,7 @@ mod testing {
FnArg::Variable(Variable::from_valid_name("?y")), FnArg::EntidOrInteger(10),
]}).is_ok());
assert!(!cc.is_known_empty);
assert!(!cc.is_known_empty());
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
@ -222,7 +222,7 @@ mod testing {
// Finally, expand column bindings to get the overlaps for ?x.
cc.expand_column_bindings();
assert!(cc.is_known_empty);
assert!(cc.is_known_empty());
assert_eq!(cc.empty_because.unwrap(),
EmptyBecause::TypeMismatch(y.clone(),
vec![ValueType::Double, ValueType::Long].into_iter()

View file

@ -63,8 +63,9 @@ impl AlgebraicQuery {
};
}
#[inline]
pub fn is_known_empty(&self) -> bool {
self.cc.is_known_empty
self.cc.is_known_empty()
}
}

View file

@ -158,7 +158,7 @@ fn cc_to_select_query<T: Into<Option<u64>>>(projection: Projection, cc: Conjoini
FromClause::TableList(TableList(cc.from))
};
let limit = if cc.is_known_empty { Some(0) } else { limit.into() };
let limit = if cc.empty_because.is_some() { Some(0) } else { limit.into() };
SelectQuery {
distinct: distinct,
projection: projection,
@ -174,7 +174,7 @@ fn cc_to_select_query<T: Into<Option<u64>>>(projection: Projection, cc: Conjoini
/// Return a query that projects `1` if the `cc` matches the store, and returns no results
/// if it doesn't.
pub fn cc_to_exists(cc: ConjoiningClauses) -> SelectQuery {
if cc.is_known_empty {
if cc.is_known_empty() {
// In this case we can produce a very simple query that returns no results.
SelectQuery {
distinct: false,