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? /// * Inline expressions?
///--------------------------------------------------------------------------------------- ///---------------------------------------------------------------------------------------
pub struct ConjoiningClauses { pub struct ConjoiningClauses {
/// `true` if this set of clauses cannot yield results in the context of the current schema. /// `Some` if this set of clauses cannot yield results in the context of the current schema.
pub is_known_empty: bool,
pub empty_because: Option<EmptyBecause>, pub empty_because: Option<EmptyBecause>,
/// A function used to generate an alias for a table -- e.g., from "datoms" to "datoms123". /// 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 { impl Debug for ConjoiningClauses {
fn fmt(&self, fmt: &mut Formatter) -> ::std::fmt::Result { fn fmt(&self, fmt: &mut Formatter) -> ::std::fmt::Result {
fmt.debug_struct("ConjoiningClauses") fmt.debug_struct("ConjoiningClauses")
.field("is_known_empty", &self.is_known_empty) .field("empty_because", &self.empty_because)
.field("from", &self.from) .field("from", &self.from)
.field("wheres", &self.wheres) .field("wheres", &self.wheres)
.field("column_bindings", &self.column_bindings) .field("column_bindings", &self.column_bindings)
@ -204,7 +203,6 @@ impl Debug for ConjoiningClauses {
impl Default for ConjoiningClauses { impl Default for ConjoiningClauses {
fn default() -> ConjoiningClauses { fn default() -> ConjoiningClauses {
ConjoiningClauses { ConjoiningClauses {
is_known_empty: false,
empty_because: None, empty_because: None,
aliaser: default_table_aliaser(), aliaser: default_table_aliaser(),
from: vec![], from: vec![],
@ -222,7 +220,6 @@ impl Default for ConjoiningClauses {
impl ConjoiningClauses { impl ConjoiningClauses {
fn make_receptacle(&self) -> ConjoiningClauses { fn make_receptacle(&self) -> ConjoiningClauses {
let mut concrete = ConjoiningClauses::default(); let mut concrete = ConjoiningClauses::default();
concrete.is_known_empty = self.is_known_empty;
concrete.empty_because = self.empty_because.clone(); concrete.empty_because = self.empty_because.clone();
concrete.input_variables = self.input_variables.clone(); concrete.input_variables = self.input_variables.clone();
@ -238,7 +235,6 @@ impl ConjoiningClauses {
/// simple `or`. /// simple `or`.
fn use_as_template(&self, vars: &BTreeSet<Variable>) -> ConjoiningClauses { fn use_as_template(&self, vars: &BTreeSet<Variable>) -> ConjoiningClauses {
let mut template = ConjoiningClauses::default(); let mut template = ConjoiningClauses::default();
template.is_known_empty = self.is_known_empty;
template.empty_because = self.empty_because.clone(); template.empty_because = self.empty_because.clone();
template.input_variables = self.input_variables.intersection(vars).cloned().collect(); 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 /// Like `constrain_var_to_type` but in reverse: this expands the set of types
/// with which a variable is associated. /// 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 /// 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 /// 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. /// set-intersect them -- but this is worth bearing in mind.
@ -399,7 +395,7 @@ impl ConjoiningClauses {
e.insert(new_types); e.insert(new_types);
}, },
Entry::Occupied(mut e) => { 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.", panic!("Uh oh: we failed this pattern, probably because {:?} couldn't match, but now we're broadening its type.",
e.get()); 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) { fn mark_known_empty(&mut self, why: EmptyBecause) {
self.is_known_empty = true;
if self.empty_because.is_some() { if self.empty_because.is_some() {
return; return;
} }
@ -589,7 +589,7 @@ impl ConjoiningClauses {
/// Produce a (table, alias) pair to handle the provided pattern. /// Produce a (table, alias) pair to handle the provided pattern.
/// This is a mutating method because it mutates the aliaser function! /// 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 /// 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> { fn alias_table<'s, 'a>(&mut self, schema: &'s Schema, pattern: &'a Pattern) -> Option<SourceAlias> {
self.table_for_places(schema, &pattern.attribute, &pattern.value) self.table_for_places(schema, &pattern.attribute, &pattern.value)
.map_err(|reason| { .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<()> { 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(()) return Ok(())
} }
@ -391,7 +391,7 @@ impl ConjoiningClauses {
let mut receptacle = template.make_receptacle(); let mut receptacle = template.make_receptacle();
println!("Applying pattern with attribute {:?}", pattern.attribute); println!("Applying pattern with attribute {:?}", pattern.attribute);
receptacle.apply_pattern_clause_for_alias(schema, &pattern, &source_alias); receptacle.apply_pattern_clause_for_alias(schema, &pattern, &source_alias);
if receptacle.is_known_empty { if receptacle.is_known_empty() {
println!("Receptacle is empty."); println!("Receptacle is empty.");
let reason = receptacle.empty_because; let reason = receptacle.empty_because;
None None
@ -471,8 +471,7 @@ impl ConjoiningClauses {
} }
fn intersect(&mut self, mut cc: ConjoiningClauses) -> Result<()> { fn intersect(&mut self, mut cc: ConjoiningClauses) -> Result<()> {
if cc.is_known_empty { if cc.is_known_empty() {
self.is_known_empty = true;
self.empty_because = cc.empty_because; self.empty_because = cc.empty_because;
} }
self.wheres.append(&mut cc.wheres); self.wheres.append(&mut cc.wheres);
@ -573,7 +572,7 @@ mod testing {
[?x :foo/nope2 "Ámbar"] [?x :foo/nope2 "Ámbar"]
[?x :foo/nope3 "Daphne"])]"#; [?x :foo/nope3 "Daphne"])]"#;
let cc = alg(&schema, query); 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")))); assert_eq!(cc.empty_because, Some(EmptyBecause::InvalidAttributeIdent(NamespacedKeyword::new("foo", "nope3"))));
} }
@ -587,7 +586,7 @@ mod testing {
[?x :foo/parent "Ámbar"] [?x :foo/parent "Ámbar"]
[?x :foo/nope "Daphne"])]"#; [?x :foo/nope "Daphne"])]"#;
let cc = alg(&schema, query); 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"]]"#)); 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 ambar = QueryValue::TypedValue(TypedValue::typed_string("Ámbar"));
let daphne = QueryValue::TypedValue(TypedValue::typed_string("Daphne")); let daphne = QueryValue::TypedValue(TypedValue::typed_string("Daphne"));
assert!(!cc.is_known_empty); assert!(!cc.is_known_empty());
assert_eq!(cc.wheres, ColumnIntersection(vec![ assert_eq!(cc.wheres, ColumnIntersection(vec![
ColumnConstraintOrAlternation::Alternation( ColumnConstraintOrAlternation::Alternation(
ColumnAlternation(vec![ ColumnAlternation(vec![
@ -657,7 +656,7 @@ mod testing {
let ambar = QueryValue::TypedValue(TypedValue::typed_string("Ámbar")); let ambar = QueryValue::TypedValue(TypedValue::typed_string("Ámbar"));
let daphne = QueryValue::TypedValue(TypedValue::typed_string("Daphne")); let daphne = QueryValue::TypedValue(TypedValue::typed_string("Daphne"));
assert!(!cc.is_known_empty); assert!(!cc.is_known_empty());
assert_eq!(cc.wheres, ColumnIntersection(vec![ assert_eq!(cc.wheres, ColumnIntersection(vec![
ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d0a.clone(), name.clone())), ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d0a.clone(), name.clone())),
ColumnConstraintOrAlternation::Alternation( ColumnConstraintOrAlternation::Alternation(
@ -706,7 +705,7 @@ mod testing {
let john = QueryValue::TypedValue(TypedValue::typed_string("John")); let john = QueryValue::TypedValue(TypedValue::typed_string("John"));
let daphne = QueryValue::TypedValue(TypedValue::typed_string("Daphne")); let daphne = QueryValue::TypedValue(TypedValue::typed_string("Daphne"));
assert!(!cc.is_known_empty); assert!(!cc.is_known_empty());
assert_eq!(cc.wheres, ColumnIntersection(vec![ assert_eq!(cc.wheres, ColumnIntersection(vec![
ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d0a.clone(), age.clone())), ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d0a.clone(), age.clone())),
ColumnConstraintOrAlternation::Constraint(ColumnConstraint::NumericInequality { ColumnConstraintOrAlternation::Constraint(ColumnConstraint::NumericInequality {
@ -754,7 +753,7 @@ mod testing {
let knows = QueryValue::Entid(66); let knows = QueryValue::Entid(66);
let parent = QueryValue::Entid(67); let parent = QueryValue::Entid(67);
assert!(!cc.is_known_empty); assert!(!cc.is_known_empty());
assert_eq!(cc.wheres, ColumnIntersection(vec![ assert_eq!(cc.wheres, ColumnIntersection(vec![
ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d0a.clone(), knows.clone())), ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d0a.clone(), knows.clone())),
ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d1a.clone(), parent.clone())), ColumnConstraintOrAlternation::Constraint(ColumnConstraint::Equals(d1a.clone(), parent.clone())),

View file

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

View file

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

View file

@ -63,8 +63,9 @@ impl AlgebraicQuery {
}; };
} }
#[inline]
pub fn is_known_empty(&self) -> bool { 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)) 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 { SelectQuery {
distinct: distinct, distinct: distinct,
projection: projection, 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 /// Return a query that projects `1` if the `cc` matches the store, and returns no results
/// if it doesn't. /// if it doesn't.
pub fn cc_to_exists(cc: ConjoiningClauses) -> SelectQuery { 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. // In this case we can produce a very simple query that returns no results.
SelectQuery { SelectQuery {
distinct: false, distinct: false,