Part 5: eliminate is_known_empty in favor of empty_because and an accessor.
This commit is contained in:
parent
a07efc0a9e
commit
b693385495
6 changed files with 46 additions and 46 deletions
|
@ -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| {
|
||||
|
|
|
@ -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())),
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -63,8 +63,9 @@ impl AlgebraicQuery {
|
|||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_known_empty(&self) -> bool {
|
||||
self.cc.is_known_empty
|
||||
self.cc.is_known_empty()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue