diff --git a/query-algebrizer/src/clauses/mod.rs b/query-algebrizer/src/clauses/mod.rs index 1f17d73f..19c826f1 100644 --- a/query-algebrizer/src/clauses/mod.rs +++ b/query-algebrizer/src/clauses/mod.rs @@ -142,8 +142,7 @@ impl Intersection for BTreeMap { /// * 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, /// 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) -> 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 { self.table_for_places(schema, &pattern.attribute, &pattern.value) .map_err(|reason| { diff --git a/query-algebrizer/src/clauses/or.rs b/query-algebrizer/src/clauses/or.rs index 09bacc48..29ea3dec 100644 --- a/query-algebrizer/src/clauses/or.rs +++ b/query-algebrizer/src/clauses/or.rs @@ -342,7 +342,7 @@ impl ConjoiningClauses { /// ``` /// fn apply_simple_or_join(&mut self, schema: &Schema, patterns: Vec, mentioned_vars: BTreeSet) -> 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())), diff --git a/query-algebrizer/src/clauses/pattern.rs b/query-algebrizer/src/clauses/pattern.rs index efce9982..6aadfa52 100644 --- a/query-algebrizer/src/clauses/pattern.rs +++ b/query-algebrizer/src/clauses/pattern.rs @@ -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)); } diff --git a/query-algebrizer/src/clauses/predicate.rs b/query-algebrizer/src/clauses/predicate.rs index 4242d9a0..031d20be 100644 --- a/query-algebrizer/src/clauses/predicate.rs +++ b/query-algebrizer/src/clauses/predicate.rs @@ -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() diff --git a/query-algebrizer/src/lib.rs b/query-algebrizer/src/lib.rs index 597d81f1..8017de5e 100644 --- a/query-algebrizer/src/lib.rs +++ b/query-algebrizer/src/lib.rs @@ -63,8 +63,9 @@ impl AlgebraicQuery { }; } + #[inline] pub fn is_known_empty(&self) -> bool { - self.cc.is_known_empty + self.cc.is_known_empty() } } diff --git a/query-translator/src/translate.rs b/query-translator/src/translate.rs index 3f50af04..4e72334a 100644 --- a/query-translator/src/translate.rs +++ b/query-translator/src/translate.rs @@ -158,7 +158,7 @@ fn cc_to_select_query>>(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>>(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,