Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander

Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
This commit is contained in:
Richard Newman 2017-03-29 13:18:17 -07:00
parent 8ae8466cf9
commit a5023c70cb
20 changed files with 285 additions and 216 deletions

View file

@ -17,6 +17,7 @@ extern crate edn;
pub mod values;
use std::collections::BTreeMap;
use std::rc::Rc;
use self::ordered_float::OrderedFloat;
use self::edn::NamespacedKeyword;
@ -66,8 +67,8 @@ pub enum TypedValue {
Long(i64),
Double(OrderedFloat<f64>),
// TODO: &str throughout?
String(String),
Keyword(NamespacedKeyword),
String(Rc<String>),
Keyword(Rc<NamespacedKeyword>),
}
impl TypedValue {
@ -95,17 +96,17 @@ impl TypedValue {
}
/// Construct a new `TypedValue::Keyword` instance by cloning the provided
/// values. This is expensive, so this might
/// values and wrapping them in a new `Rc`. This is expensive, so this might
/// be best limited to tests.
pub fn typed_ns_keyword(ns: &str, name: &str) -> TypedValue {
TypedValue::Keyword(NamespacedKeyword::new(ns, name))
TypedValue::Keyword(Rc::new(NamespacedKeyword::new(ns, name)))
}
/// Construct a new `TypedValue::String` instance by cloning the provided
/// value. This is expensive, so this might
/// value and wrapping it in a new `Rc`. This is expensive, so this might
/// be best limited to tests.
pub fn typed_string(s: &str) -> TypedValue {
TypedValue::String(s.to_string())
TypedValue::String(Rc::new(s.to_string()))
}
}

View file

@ -16,6 +16,7 @@ use std::fmt::Display;
use std::iter::{once, repeat};
use std::ops::Range;
use std::path::Path;
use std::rc::Rc;
use itertools;
use itertools::Itertools;
@ -350,9 +351,9 @@ impl TypedSQLValue for TypedValue {
// share a tag.
(5, rusqlite::types::Value::Integer(x)) => Ok(TypedValue::Long(x)),
(5, rusqlite::types::Value::Real(x)) => Ok(TypedValue::Double(x.into())),
(10, rusqlite::types::Value::Text(x)) => Ok(TypedValue::String(x)),
(10, rusqlite::types::Value::Text(x)) => Ok(TypedValue::String(Rc::new(x))),
(13, rusqlite::types::Value::Text(x)) => {
to_namespaced_keyword(&x).map(|k| TypedValue::Keyword(k))
to_namespaced_keyword(&x).map(|k| TypedValue::Keyword(Rc::new(k)))
},
(_, value) => bail!(ErrorKind::BadSQLValuePair(value, value_type_tag)),
}
@ -370,8 +371,8 @@ impl TypedSQLValue for TypedValue {
&Value::Boolean(x) => Some(TypedValue::Boolean(x)),
&Value::Integer(x) => Some(TypedValue::Long(x)),
&Value::Float(ref x) => Some(TypedValue::Double(x.clone())),
&Value::Text(ref x) => Some(TypedValue::String(x.clone())),
&Value::NamespacedKeyword(ref x) => Some(TypedValue::Keyword(x.clone())),
&Value::Text(ref x) => Some(TypedValue::String(Rc::new(x.clone()))),
&Value::NamespacedKeyword(ref x) => Some(TypedValue::Keyword(Rc::new(x.clone()))),
_ => None
}
}
@ -396,8 +397,8 @@ impl TypedSQLValue for TypedValue {
&TypedValue::Boolean(x) => (Value::Boolean(x), ValueType::Boolean),
&TypedValue::Long(x) => (Value::Integer(x), ValueType::Long),
&TypedValue::Double(x) => (Value::Float(x), ValueType::Double),
&TypedValue::String(ref x) => (Value::Text(x.clone()), ValueType::String),
&TypedValue::Keyword(ref x) => (Value::NamespacedKeyword(x.clone()), ValueType::Keyword),
&TypedValue::String(ref x) => (Value::Text(x.as_ref().clone()), ValueType::String),
&TypedValue::Keyword(ref x) => (Value::NamespacedKeyword(x.as_ref().clone()), ValueType::Keyword),
}
}
}
@ -434,7 +435,7 @@ fn read_ident_map(conn: &rusqlite::Connection) -> Result<IdentMap> {
bail!(ErrorKind::NotYetImplemented(format!("bad idents materialized view: expected :db/ident but got {}", a)));
}
if let TypedValue::Keyword(keyword) = typed_value {
Ok((keyword, e))
Ok((keyword.as_ref().clone(), e))
} else {
bail!(ErrorKind::NotYetImplemented(format!("bad idents materialized view: expected [entid :db/ident keyword] but got [entid :db/ident {:?}]", typed_value)));
}

View file

@ -14,6 +14,7 @@
use std::borrow::Borrow;
use std::io::{Write};
use std::rc::Rc;
use itertools::Itertools;
use rusqlite;
@ -110,7 +111,7 @@ trait ToIdent {
impl ToIdent for TypedValue {
fn map_ident(self, schema: &Schema) -> Self {
if let TypedValue::Ref(e) = self {
schema.get_ident(e).cloned().map(TypedValue::Keyword).unwrap_or(TypedValue::Ref(e))
schema.get_ident(e).cloned().map(|i| TypedValue::Keyword(Rc::new(i))).unwrap_or(TypedValue::Ref(e))
} else {
self
}

View file

@ -222,7 +222,7 @@ pub fn update_schema_from_entid_quadruples<U>(schema: &mut Schema, assertions: U
// Here we handle :db/ident assertions.
if a == entids::DB_IDENT {
if let TypedValue::Keyword(ref keyword) = typed_value {
ident_set.witness(e, keyword.clone(), added);
ident_set.witness(e, keyword.as_ref().clone(), added);
continue
} else {
// Something is terribly wrong: the schema ensures we have a keyword.

View file

@ -14,10 +14,12 @@ extern crate mentat_db;
extern crate ordered_float;
extern crate rusqlite;
use ordered_float::OrderedFloat;
use edn::symbols;
use mentat_core::{TypedValue, ValueType};
use mentat_db::db::TypedSQLValue;
use ordered_float::OrderedFloat;
use edn::symbols;
// It's not possible to test to_sql_value_pair since rusqlite::ToSqlOutput doesn't implement
// PartialEq.
@ -34,8 +36,8 @@ fn test_from_sql_value_pair() {
assert_eq!(TypedValue::from_sql_value_pair(rusqlite::types::Value::Real(0.0), 5).unwrap(), TypedValue::Double(OrderedFloat(0.0)));
assert_eq!(TypedValue::from_sql_value_pair(rusqlite::types::Value::Real(0.5), 5).unwrap(), TypedValue::Double(OrderedFloat(0.5)));
assert_eq!(TypedValue::from_sql_value_pair(rusqlite::types::Value::Text(":db/keyword".into()), 10).unwrap(), TypedValue::String(":db/keyword".into()));
assert_eq!(TypedValue::from_sql_value_pair(rusqlite::types::Value::Text(":db/keyword".into()), 13).unwrap(), TypedValue::Keyword(symbols::NamespacedKeyword::new("db", "keyword")));
assert_eq!(TypedValue::from_sql_value_pair(rusqlite::types::Value::Text(":db/keyword".into()), 10).unwrap(), TypedValue::typed_string(":db/keyword"));
assert_eq!(TypedValue::from_sql_value_pair(rusqlite::types::Value::Text(":db/keyword".into()), 13).unwrap(), TypedValue::typed_ns_keyword("db", "keyword"));
}
#[test]
@ -51,6 +53,6 @@ fn test_to_edn_value_pair() {
assert_eq!(TypedValue::Double(OrderedFloat(0.0)).to_edn_value_pair(), (edn::Value::Float(OrderedFloat(0.0)), ValueType::Double));
assert_eq!(TypedValue::Double(OrderedFloat(0.5)).to_edn_value_pair(), (edn::Value::Float(OrderedFloat(0.5)), ValueType::Double));
assert_eq!(TypedValue::String(":db/keyword".into()).to_edn_value_pair(), (edn::Value::Text(":db/keyword".into()), ValueType::String));
assert_eq!(TypedValue::Keyword(symbols::NamespacedKeyword::new("db", "keyword")).to_edn_value_pair(), (edn::Value::NamespacedKeyword(symbols::NamespacedKeyword::new("db", "keyword")), ValueType::Keyword));
assert_eq!(TypedValue::typed_string(":db/keyword").to_edn_value_pair(), (edn::Value::Text(":db/keyword".into()), ValueType::String));
assert_eq!(TypedValue::typed_ns_keyword("db", "keyword").to_edn_value_pair(), (edn::Value::NamespacedKeyword(symbols::NamespacedKeyword::new("db", "keyword")), ValueType::Keyword));
}

View file

@ -62,6 +62,17 @@ mod resolve;
use validate::validate_or_join;
// We do this a lot for errors.
trait RcCloned<T> {
fn cloned(&self) -> T;
}
impl<T: Clone> RcCloned<T> for ::std::rc::Rc<T> {
fn cloned(&self) -> T {
self.as_ref().clone()
}
}
/// A thing that's capable of aliasing a table name for us.
/// This exists so that we can obtain predictable names in tests.
pub type TableAliaser = Box<FnMut(DatomsTable) -> TableAlias>;
@ -231,7 +242,7 @@ impl ConjoiningClauses {
// For attributes this shouldn't occur, because we check the binding in
// `table_for_places`/`alias_table`, and if it didn't resolve to a valid
// attribute then we should have already marked the pattern as empty.
self.mark_known_empty(EmptyBecause::UnresolvedIdent(kw.clone()));
self.mark_known_empty(EmptyBecause::UnresolvedIdent(kw.cloned()));
}
},
TypedValue::Ref(entid) => {
@ -438,7 +449,7 @@ impl ConjoiningClauses {
match attribute {
&PatternNonValuePlace::Ident(ref kw) =>
schema.attribute_for_ident(kw)
.ok_or_else(|| EmptyBecause::InvalidAttributeIdent(kw.clone()))
.ok_or_else(|| EmptyBecause::InvalidAttributeIdent(kw.cloned()))
.and_then(|attribute| self.table_for_attribute_and_value(attribute, value)),
&PatternNonValuePlace::Entid(id) =>
schema.attribute_for_entid(id)
@ -461,7 +472,7 @@ impl ConjoiningClauses {
Some(TypedValue::Keyword(ref kw)) =>
// Don't recurse: avoid needing to clone the keyword.
schema.attribute_for_ident(kw)
.ok_or_else(|| EmptyBecause::InvalidAttributeIdent(kw.clone()))
.ok_or_else(|| EmptyBecause::InvalidAttributeIdent(kw.cloned()))
.and_then(|attribute| self.table_for_attribute_and_value(attribute, value)),
Some(v) => {
// This pattern cannot match: the caller has bound a non-entity value to an
@ -590,4 +601,9 @@ fn associate_ident(schema: &mut Schema, i: NamespacedKeyword, e: Entid) {
#[cfg(test)]
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
schema.schema_map.insert(e, a);
}
#[cfg(test)]
pub fn ident(ns: &str, name: &str) -> PatternNonValuePlace {
PatternNonValuePlace::Ident(::std::rc::Rc::new(NamespacedKeyword::new(ns, name)))
}

View file

@ -8,6 +8,8 @@
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
use std::rc::Rc;
use mentat_core::{
Schema,
TypedValue,
@ -21,6 +23,8 @@ use mentat_query::{
SrcVar,
};
use super::RcCloned;
use clauses::ConjoiningClauses;
use types::{
@ -94,11 +98,11 @@ impl ConjoiningClauses {
PatternNonValuePlace::Entid(entid) =>
self.constrain_column_to_entity(col.clone(), DatomsColumn::Entity, entid),
PatternNonValuePlace::Ident(ref ident) => {
if let Some(entid) = self.entid_for_ident(schema, ident) {
if let Some(entid) = self.entid_for_ident(schema, ident.as_ref()) {
self.constrain_column_to_entity(col.clone(), DatomsColumn::Entity, entid)
} else {
// A resolution failure means we're done here.
self.mark_known_empty(EmptyBecause::UnresolvedIdent(ident.clone()));
self.mark_known_empty(EmptyBecause::UnresolvedIdent(ident.cloned()));
return;
}
}
@ -123,12 +127,12 @@ impl ConjoiningClauses {
self.constrain_attribute(col.clone(), entid);
if !schema.is_attribute(entid) {
self.mark_known_empty(EmptyBecause::InvalidAttributeIdent(ident.clone()));
self.mark_known_empty(EmptyBecause::InvalidAttributeIdent(ident.cloned()));
return;
}
} else {
// A resolution failure means we're done here.
self.mark_known_empty(EmptyBecause::UnresolvedIdent(ident.clone()));
self.mark_known_empty(EmptyBecause::UnresolvedIdent(ident.cloned()));
return;
}
}
@ -190,7 +194,7 @@ impl ConjoiningClauses {
} else {
// A resolution failure means we're done here: this attribute must have an
// entity value.
self.mark_known_empty(EmptyBecause::UnresolvedIdent(kw.clone()));
self.mark_known_empty(EmptyBecause::UnresolvedIdent(kw.cloned()));
return;
}
} else {
@ -237,7 +241,6 @@ impl ConjoiningClauses {
},
}
}
pub fn apply_pattern<'s, 'p>(&mut self, schema: &'s Schema, pattern: Pattern) {
@ -274,13 +277,13 @@ mod testing {
use mentat_query::{
NamespacedKeyword,
NonIntegerConstant,
PlainSymbol,
Variable,
};
use clauses::{
add_attribute,
associate_ident,
ident,
unit_type_set,
};
@ -299,8 +302,8 @@ mod testing {
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?x"))),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
attribute: ident("foo", "bar"),
value: PatternValuePlace::Constant(NonIntegerConstant::Boolean(true)),
tx: PatternNonValuePlace::Placeholder,
});
@ -317,8 +320,8 @@ mod testing {
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?x"))),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
attribute: ident("foo", "bar"),
value: PatternValuePlace::Constant(NonIntegerConstant::Boolean(true)),
tx: PatternNonValuePlace::Placeholder,
});
@ -337,11 +340,11 @@ mod testing {
..Default::default()
});
let x = Variable(PlainSymbol::new("?x"));
let x = Variable::from_valid_name("?x");
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
attribute: ident("foo", "bar"),
value: PatternValuePlace::Constant(NonIntegerConstant::Boolean(true)),
tx: PatternNonValuePlace::Placeholder,
});
@ -377,7 +380,7 @@ mod testing {
let mut cc = ConjoiningClauses::default();
let schema = Schema::default();
let x = Variable(PlainSymbol::new("?x"));
let x = Variable::from_valid_name("?x");
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
@ -421,12 +424,12 @@ mod testing {
..Default::default()
});
let x = Variable(PlainSymbol::new("?x"));
let a = Variable(PlainSymbol::new("?a"));
let v = Variable(PlainSymbol::new("?v"));
let x = Variable::from_valid_name("?x");
let a = Variable::from_valid_name("?a");
let v = Variable::from_valid_name("?v");
cc.input_variables.insert(a.clone());
cc.value_bindings.insert(a.clone(), TypedValue::Keyword(NamespacedKeyword::new("foo", "bar")));
cc.value_bindings.insert(a.clone(), TypedValue::Keyword(Rc::new(NamespacedKeyword::new("foo", "bar"))));
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
@ -459,10 +462,10 @@ mod testing {
let mut cc = ConjoiningClauses::default();
let schema = Schema::default();
let x = Variable(PlainSymbol::new("?x"));
let a = Variable(PlainSymbol::new("?a"));
let v = Variable(PlainSymbol::new("?v"));
let hello = TypedValue::String("hello".to_string());
let x = Variable::from_valid_name("?x");
let a = Variable::from_valid_name("?a");
let v = Variable::from_valid_name("?v");
let hello = TypedValue::typed_string("hello");
cc.input_variables.insert(a.clone());
cc.value_bindings.insert(a.clone(), hello.clone());
@ -485,9 +488,9 @@ mod testing {
let mut cc = ConjoiningClauses::default();
let schema = Schema::default();
let x = Variable(PlainSymbol::new("?x"));
let a = Variable(PlainSymbol::new("?a"));
let v = Variable(PlainSymbol::new("?v"));
let x = Variable::from_valid_name("?x");
let a = Variable::from_valid_name("?a");
let v = Variable::from_valid_name("?v");
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
@ -517,12 +520,12 @@ mod testing {
let mut cc = ConjoiningClauses::default();
let schema = Schema::default();
let x = Variable(PlainSymbol::new("?x"));
let x = Variable::from_valid_name("?x");
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
attribute: PatternNonValuePlace::Placeholder,
value: PatternValuePlace::Constant(NonIntegerConstant::Text("hello".to_string())),
value: PatternValuePlace::Constant(NonIntegerConstant::Text(Rc::new("hello".to_string()))),
tx: PatternNonValuePlace::Placeholder,
});
@ -545,7 +548,7 @@ mod testing {
// - datoms0.value_type_tag = string
// TODO: implement expand_type_tags.
assert_eq!(cc.wheres, vec![
ColumnConstraint::Equals(d0_v, QueryValue::TypedValue(TypedValue::String("hello".to_string()))),
ColumnConstraint::Equals(d0_v, QueryValue::TypedValue(TypedValue::String(Rc::new("hello".to_string())))),
ColumnConstraint::HasType("all_datoms00".to_string(), ValueType::String),
].into());
}
@ -566,19 +569,19 @@ mod testing {
..Default::default()
});
let x = Variable(PlainSymbol::new("?x"));
let y = Variable(PlainSymbol::new("?y"));
let x = Variable::from_valid_name("?x");
let y = Variable::from_valid_name("?y");
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "roz")),
value: PatternValuePlace::Constant(NonIntegerConstant::Text("idgoeshere".to_string())),
attribute: ident("foo", "roz"),
value: PatternValuePlace::Constant(NonIntegerConstant::Text(Rc::new("idgoeshere".to_string()))),
tx: PatternNonValuePlace::Placeholder,
});
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
attribute: ident("foo", "bar"),
value: PatternValuePlace::Variable(y.clone()),
tx: PatternNonValuePlace::Placeholder,
});
@ -617,7 +620,7 @@ mod testing {
// - datoms1.e = datoms0.e
assert_eq!(cc.wheres, vec![
ColumnConstraint::Equals(d0_a, QueryValue::Entid(98)),
ColumnConstraint::Equals(d0_v, QueryValue::TypedValue(TypedValue::String("idgoeshere".to_string()))),
ColumnConstraint::Equals(d0_v, QueryValue::TypedValue(TypedValue::typed_string("idgoeshere"))),
ColumnConstraint::Equals(d1_a, QueryValue::Entid(99)),
ColumnConstraint::Equals(d0_e, QueryValue::Column(d1_e)),
].into());
@ -633,8 +636,8 @@ mod testing {
..Default::default()
});
let x = Variable(PlainSymbol::new("?x"));
let y = Variable(PlainSymbol::new("?y"));
let x = Variable::from_valid_name("?x");
let y = Variable::from_valid_name("?y");
let b: BTreeMap<Variable, TypedValue> =
vec![(y.clone(), TypedValue::Boolean(true))].into_iter().collect();
@ -643,7 +646,7 @@ mod testing {
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
attribute: ident("foo", "bar"),
value: PatternValuePlace::Variable(y.clone()),
tx: PatternNonValuePlace::Placeholder,
});
@ -678,8 +681,8 @@ mod testing {
..Default::default()
});
let x = Variable(PlainSymbol::new("?x"));
let y = Variable(PlainSymbol::new("?y"));
let x = Variable::from_valid_name("?x");
let y = Variable::from_valid_name("?y");
let b: BTreeMap<Variable, TypedValue> =
vec![(y.clone(), TypedValue::Long(42))].into_iter().collect();
@ -688,7 +691,7 @@ mod testing {
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
attribute: ident("foo", "bar"),
value: PatternValuePlace::Variable(y.clone()),
tx: PatternNonValuePlace::Placeholder,
});
@ -710,8 +713,8 @@ mod testing {
..Default::default()
});
let x = Variable(PlainSymbol::new("?x"));
let y = Variable(PlainSymbol::new("?y"));
let x = Variable::from_valid_name("?x");
let y = Variable::from_valid_name("?y");
let b: BTreeMap<Variable, TypedValue> =
vec![(y.clone(), TypedValue::Long(42))].into_iter().collect();
@ -720,7 +723,7 @@ mod testing {
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
attribute: ident("foo", "bar"),
value: PatternValuePlace::Variable(y.clone()),
tx: PatternNonValuePlace::Placeholder,
});
@ -749,19 +752,19 @@ mod testing {
..Default::default()
});
let x = Variable(PlainSymbol::new("?x"));
let y = Variable(PlainSymbol::new("?y"));
let x = Variable::from_valid_name("?x");
let y = Variable::from_valid_name("?y");
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "roz")),
attribute: ident("foo", "roz"),
value: PatternValuePlace::Variable(y.clone()),
tx: PatternNonValuePlace::Placeholder,
});
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
attribute: ident("foo", "bar"),
value: PatternValuePlace::Variable(y.clone()),
tx: PatternNonValuePlace::Placeholder,
});
@ -786,9 +789,9 @@ mod testing {
// [:find ?x :where
// [?x ?y true]
// [?z ?y ?x]]
let x = Variable(PlainSymbol::new("?x"));
let y = Variable(PlainSymbol::new("?y"));
let z = Variable(PlainSymbol::new("?z"));
let x = Variable::from_valid_name("?x");
let y = Variable::from_valid_name("?y");
let z = Variable::from_valid_name("?z");
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),

View file

@ -88,7 +88,6 @@ mod testing {
use super::*;
use std::collections::HashSet;
use mentat_core::attribute::Unique;
use mentat_core::{
Attribute,
@ -109,6 +108,7 @@ mod testing {
use clauses::{
add_attribute,
associate_ident,
ident,
};
use types::{
@ -117,7 +117,6 @@ mod testing {
QueryValue,
};
#[test]
/// Apply two patterns: a pattern and a numeric predicate.
/// Verify that after application of the predicate we know that the value
@ -132,8 +131,8 @@ mod testing {
..Default::default()
});
let x = Variable(PlainSymbol::new("?x"));
let y = Variable(PlainSymbol::new("?y"));
let x = Variable::from_valid_name("?x");
let y = Variable::from_valid_name("?y");
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
@ -148,7 +147,7 @@ mod testing {
assert!(cc.apply_numeric_predicate(&schema, comp, Predicate {
operator: op,
args: vec![
FnArg::Variable(Variable(PlainSymbol::new("?y"))), FnArg::EntidOrInteger(10),
FnArg::Variable(Variable::from_valid_name("?y")), FnArg::EntidOrInteger(10),
]}).is_ok());
assert!(!cc.is_known_empty);
@ -192,8 +191,8 @@ mod testing {
..Default::default()
});
let x = Variable(PlainSymbol::new("?x"));
let y = Variable(PlainSymbol::new("?y"));
let x = Variable::from_valid_name("?x");
let y = Variable::from_valid_name("?y");
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
@ -208,14 +207,14 @@ mod testing {
assert!(cc.apply_numeric_predicate(&schema, comp, Predicate {
operator: op,
args: vec![
FnArg::Variable(Variable(PlainSymbol::new("?y"))), FnArg::EntidOrInteger(10),
FnArg::Variable(Variable::from_valid_name("?y")), FnArg::EntidOrInteger(10),
]}).is_ok());
assert!(!cc.is_known_empty);
cc.apply_pattern(&schema, Pattern {
source: None,
entity: PatternNonValuePlace::Variable(x.clone()),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "roz")),
attribute: ident("foo", "roz"),
value: PatternValuePlace::Variable(y.clone()),
tx: PatternNonValuePlace::Placeholder,
});

View file

@ -46,7 +46,7 @@ impl ConjoiningClauses {
self.column_bindings
.get(&var)
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
.ok_or_else(|| Error::from_kind(ErrorKind::UnboundVariable(var)))
.ok_or_else(|| Error::from_kind(ErrorKind::UnboundVariable(var.name())))
},
// Can't be an entid.
EntidOrInteger(i) => Ok(QueryValue::TypedValue(TypedValue::Long(i))),
@ -73,13 +73,13 @@ impl ConjoiningClauses {
self.column_bindings
.get(&var)
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
.ok_or_else(|| Error::from_kind(ErrorKind::UnboundVariable(var)))
.ok_or_else(|| Error::from_kind(ErrorKind::UnboundVariable(var.name())))
},
EntidOrInteger(i) => Ok(QueryValue::PrimitiveLong(i)),
Ident(_) => unimplemented!(), // TODO
Constant(NonIntegerConstant::Boolean(val)) => Ok(QueryValue::TypedValue(TypedValue::Boolean(val))),
Constant(NonIntegerConstant::Float(f)) => Ok(QueryValue::TypedValue(TypedValue::Double(f))),
Constant(NonIntegerConstant::Text(s)) => Ok(QueryValue::TypedValue(TypedValue::String(s.clone()))),
Constant(NonIntegerConstant::Text(s)) => Ok(QueryValue::TypedValue(TypedValue::typed_string(s.as_str()))),
Constant(NonIntegerConstant::BigInteger(_)) => unimplemented!(),
SrcVar(_) => unimplemented!(),
}

View file

@ -12,7 +12,6 @@ extern crate mentat_query;
use self::mentat_query::{
PlainSymbol,
Variable,
};
error_chain! {
@ -31,9 +30,9 @@ error_chain! {
display("invalid number of arguments to {}: expected {}, got {}.", name, expected, number)
}
UnboundVariable(var: Variable) {
UnboundVariable(name: PlainSymbol) {
description("unbound variable in function call")
display("unbound variable: {}", var.0)
display("unbound variable: {}", name)
}
NonNumericArgument(function: PlainSymbol, position: usize) {

View file

@ -87,7 +87,6 @@ mod tests {
Pattern,
PatternNonValuePlace,
PatternValuePlace,
PlainSymbol,
UnifyVars,
Variable,
WhereClause,
@ -95,8 +94,14 @@ mod tests {
use self::mentat_query_parser::parse_find_string;
use clauses::ident;
use super::validate_or_join;
fn value_ident(ns: &str, name: &str) -> PatternValuePlace {
PatternValuePlace::IdentOrKeyword(::std::rc::Rc::new(NamespacedKeyword::new(ns, name)))
}
/// Tests that the top-level form is a valid `or`, returning the clauses.
fn valid_or_join(parsed: FindQuery, expected_unify: UnifyVars) -> Vec<OrWhereClause> {
let mut wheres = parsed.where_clauses.into_iter();
@ -134,9 +139,9 @@ mod tests {
left,
OrWhereClause::Clause(WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?artist"))),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("artist", "type")),
value: PatternValuePlace::IdentOrKeyword(NamespacedKeyword::new("artist.type", "group")),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?artist")),
attribute: ident("artist", "type"),
value: value_ident("artist.type", "group"),
tx: PatternNonValuePlace::Placeholder,
})));
assert_eq!(
@ -145,16 +150,16 @@ mod tests {
vec![
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?artist"))),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("artist", "type")),
value: PatternValuePlace::IdentOrKeyword(NamespacedKeyword::new("artist.type", "person")),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?artist")),
attribute: ident("artist", "type"),
value: value_ident("artist.type", "person"),
tx: PatternNonValuePlace::Placeholder,
}),
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?artist"))),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("artist", "gender")),
value: PatternValuePlace::IdentOrKeyword(NamespacedKeyword::new("artist.gender", "female")),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?artist")),
attribute: ident("artist", "gender"),
value: value_ident("artist.gender", "female"),
tx: PatternNonValuePlace::Placeholder,
}),
]));
@ -186,7 +191,7 @@ mod tests {
(and [?artist :artist/type ?type]
[?type :artist/role :artist.role/parody]))]"#;
let parsed = parse_find_string(query).expect("expected successful parse");
let clauses = valid_or_join(parsed, UnifyVars::Explicit(vec![Variable(PlainSymbol::new("?artist"))]));
let clauses = valid_or_join(parsed, UnifyVars::Explicit(vec![Variable::from_valid_name("?artist")]));
// Let's do some detailed parse checks.
let mut arms = clauses.into_iter();
@ -196,9 +201,9 @@ mod tests {
left,
OrWhereClause::Clause(WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?artist"))),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("artist", "type")),
value: PatternValuePlace::IdentOrKeyword(NamespacedKeyword::new("artist.type", "group")),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?artist")),
attribute: ident("artist", "type"),
value: value_ident("artist.type", "group"),
tx: PatternNonValuePlace::Placeholder,
})));
assert_eq!(
@ -207,16 +212,16 @@ mod tests {
vec![
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?artist"))),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("artist", "type")),
value: PatternValuePlace::Variable(Variable(PlainSymbol::new("?type"))),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?artist")),
attribute: ident("artist", "type"),
value: PatternValuePlace::Variable(Variable::from_valid_name("?type")),
tx: PatternNonValuePlace::Placeholder,
}),
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?type"))),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("artist", "role")),
value: PatternValuePlace::IdentOrKeyword(NamespacedKeyword::new("artist.role", "parody")),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?type")),
attribute: ident("artist", "role"),
value: value_ident("artist.role", "parody"),
tx: PatternNonValuePlace::Placeholder,
}),
]));

View file

@ -185,6 +185,8 @@ pub fn parse_find(expr: edn::Value) -> QueryParseResult {
mod test_parse {
extern crate edn;
use std::rc::Rc;
use self::edn::{NamespacedKeyword, PlainSymbol};
use self::edn::types::Value;
use super::mentat_query::{
@ -217,8 +219,8 @@ mod test_parse {
let parsed = parse_find(input).unwrap();
if let FindSpec::FindRel(elems) = parsed.find_spec {
assert_eq!(2, elems.len());
assert_eq!(vec![Element::Variable(Variable(edn::PlainSymbol::new("?x"))),
Element::Variable(Variable(edn::PlainSymbol::new("?y")))],
assert_eq!(vec![Element::Variable(Variable::from_valid_name("?x")),
Element::Variable(Variable::from_valid_name("?y"))],
elems);
} else {
panic!("Expected FindRel.");
@ -229,9 +231,9 @@ mod test_parse {
vec![
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?x"))),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
value: PatternValuePlace::Variable(Variable(PlainSymbol::new("?y"))),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
attribute: PatternNonValuePlace::Ident(Rc::new(NamespacedKeyword::new("foo", "bar"))),
value: PatternValuePlace::Variable(Variable::from_valid_name("?y")),
tx: PatternNonValuePlace::Placeholder,
})]);
}
@ -244,14 +246,14 @@ mod test_parse {
vec![
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?x"))),
attribute: PatternNonValuePlace::Ident(NamespacedKeyword::new("foo", "bar")),
value: PatternValuePlace::Variable(Variable(PlainSymbol::new("?y"))),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
attribute: PatternNonValuePlace::Ident(Rc::new(NamespacedKeyword::new("foo", "bar"))),
value: PatternValuePlace::Variable(Variable::from_valid_name("?y")),
tx: PatternNonValuePlace::Placeholder,
}),
WhereClause::Pred(Predicate {
operator: PlainSymbol::new("<"),
args: vec![FnArg::Variable(Variable(PlainSymbol::new("?y"))),
args: vec![FnArg::Variable(Variable::from_valid_name("?y")),
FnArg::EntidOrInteger(10)],
}),
]);

View file

@ -97,6 +97,7 @@ impl<I> Query<I>
}
}
// TODO: interning.
def_value_satisfy_parser_fn!(Query, variable, Variable, Variable::from_value);
def_value_satisfy_parser_fn!(Query, source_var, SrcVar, SrcVar::from_value);
def_value_satisfy_parser_fn!(Query, predicate_fn, PredicateFn, PredicateFn::from_value);
@ -405,6 +406,8 @@ mod test {
extern crate edn;
extern crate mentat_query;
use std::rc::Rc;
use self::combine::Parser;
use self::edn::OrderedFloat;
use self::mentat_query::{
@ -420,6 +423,18 @@ mod test {
use super::*;
fn variable(x: edn::PlainSymbol) -> Variable {
Variable(Rc::new(x))
}
fn ident_kw(kw: edn::NamespacedKeyword) -> PatternNonValuePlace {
PatternNonValuePlace::Ident(Rc::new(kw))
}
fn ident(ns: &str, name: &str) -> PatternNonValuePlace {
ident_kw(edn::NamespacedKeyword::new(ns, name))
}
#[test]
fn test_pattern_mixed() {
let e = edn::PlainSymbol::new("_");
@ -433,9 +448,9 @@ mod test {
assert_parses_to!(Where::pattern, input, WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Placeholder,
attribute: PatternNonValuePlace::Ident(a),
attribute: ident_kw(a),
value: PatternValuePlace::Constant(NonIntegerConstant::Float(v)),
tx: PatternNonValuePlace::Variable(Variable(tx)),
tx: PatternNonValuePlace::Variable(variable(tx)),
}));
}
@ -453,10 +468,10 @@ mod test {
edn::Value::PlainSymbol(tx.clone())))];
assert_parses_to!(Where::pattern, input, WhereClause::Pattern(Pattern {
source: Some(SrcVar::NamedSrc("x".to_string())),
entity: PatternNonValuePlace::Variable(Variable(e)),
attribute: PatternNonValuePlace::Variable(Variable(a)),
value: PatternValuePlace::Variable(Variable(v)),
tx: PatternNonValuePlace::Variable(Variable(tx)),
entity: PatternNonValuePlace::Variable(variable(e)),
attribute: PatternNonValuePlace::Variable(variable(a)),
value: PatternValuePlace::Variable(variable(v)),
tx: PatternNonValuePlace::Variable(variable(tx)),
}));
}
@ -491,10 +506,10 @@ mod test {
// switched places.
assert_parses_to!(Where::pattern, input, WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(v)),
attribute: PatternNonValuePlace::Ident(edn::NamespacedKeyword::new("foo", "bar")),
entity: PatternNonValuePlace::Variable(variable(v)),
attribute: ident("foo", "bar"),
value: PatternValuePlace::Placeholder,
tx: PatternNonValuePlace::Variable(Variable(tx)),
tx: PatternNonValuePlace::Variable(variable(tx)),
}));
}
@ -503,7 +518,7 @@ mod test {
let e = edn::PlainSymbol::new("?e");
let input = [edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone())])];
assert_parses_to!(Where::rule_vars, input,
vec![Variable(e.clone())]);
vec![variable(e.clone())]);
}
#[test]
@ -524,9 +539,9 @@ mod test {
clauses: vec![OrWhereClause::Clause(
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(e)),
attribute: PatternNonValuePlace::Variable(Variable(a)),
value: PatternValuePlace::Variable(Variable(v)),
entity: PatternNonValuePlace::Variable(variable(e)),
attribute: PatternNonValuePlace::Variable(variable(a)),
value: PatternValuePlace::Variable(variable(v)),
tx: PatternNonValuePlace::Placeholder,
}))],
}));
@ -547,13 +562,13 @@ mod test {
assert_parses_to!(Where::or_join_clause, input,
WhereClause::OrJoin(
OrJoin {
unify_vars: UnifyVars::Explicit(vec![Variable(e.clone())]),
unify_vars: UnifyVars::Explicit(vec![variable(e.clone())]),
clauses: vec![OrWhereClause::Clause(
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(e)),
attribute: PatternNonValuePlace::Variable(Variable(a)),
value: PatternValuePlace::Variable(Variable(v)),
entity: PatternNonValuePlace::Variable(variable(e)),
attribute: PatternNonValuePlace::Variable(variable(a)),
value: PatternValuePlace::Variable(variable(v)),
tx: PatternNonValuePlace::Placeholder,
}))],
}));
@ -563,7 +578,7 @@ mod test {
fn test_find_sp_variable() {
let sym = edn::PlainSymbol::new("?x");
let input = [edn::Value::PlainSymbol(sym.clone())];
assert_parses_to!(Query::variable, input, Variable(sym));
assert_parses_to!(Query::variable, input, variable(sym));
}
#[test]
@ -573,7 +588,7 @@ mod test {
let input = [edn::Value::PlainSymbol(sym.clone()), edn::Value::PlainSymbol(period.clone())];
assert_parses_to!(Find::find_scalar,
input,
FindSpec::FindScalar(Element::Variable(Variable(sym))));
FindSpec::FindScalar(Element::Variable(variable(sym))));
}
#[test]
@ -584,7 +599,7 @@ mod test {
edn::Value::PlainSymbol(period.clone())])];
assert_parses_to!(Find::find_coll,
input,
FindSpec::FindColl(Element::Variable(Variable(sym))));
FindSpec::FindColl(Element::Variable(variable(sym))));
}
#[test]
@ -594,8 +609,8 @@ mod test {
let input = [edn::Value::PlainSymbol(vx.clone()), edn::Value::PlainSymbol(vy.clone())];
assert_parses_to!(Find::find_rel,
input,
FindSpec::FindRel(vec![Element::Variable(Variable(vx)),
Element::Variable(Variable(vy))]));
FindSpec::FindRel(vec![Element::Variable(variable(vx)),
Element::Variable(variable(vy))]));
}
#[test]
@ -606,8 +621,8 @@ mod test {
edn::Value::PlainSymbol(vy.clone())])];
assert_parses_to!(Find::find_tuple,
input,
FindSpec::FindTuple(vec![Element::Variable(Variable(vx)),
Element::Variable(Variable(vy))]));
FindSpec::FindTuple(vec![Element::Variable(variable(vx)),
Element::Variable(variable(vy))]));
}
#[test]
@ -624,15 +639,15 @@ mod test {
edn::Value::PlainSymbol(ellipsis.clone())])];
let rel = [edn::Value::PlainSymbol(vx.clone()), edn::Value::PlainSymbol(vy.clone())];
assert_eq!(FindSpec::FindScalar(Element::Variable(Variable(vx.clone()))),
assert_eq!(FindSpec::FindScalar(Element::Variable(variable(vx.clone()))),
find_seq_to_find_spec(&scalar).unwrap());
assert_eq!(FindSpec::FindTuple(vec![Element::Variable(Variable(vx.clone())),
Element::Variable(Variable(vy.clone()))]),
assert_eq!(FindSpec::FindTuple(vec![Element::Variable(variable(vx.clone())),
Element::Variable(variable(vy.clone()))]),
find_seq_to_find_spec(&tuple).unwrap());
assert_eq!(FindSpec::FindColl(Element::Variable(Variable(vx.clone()))),
assert_eq!(FindSpec::FindColl(Element::Variable(variable(vx.clone()))),
find_seq_to_find_spec(&coll).unwrap());
assert_eq!(FindSpec::FindRel(vec![Element::Variable(Variable(vx.clone())),
Element::Variable(Variable(vy.clone()))]),
assert_eq!(FindSpec::FindRel(vec![Element::Variable(variable(vx.clone())),
Element::Variable(variable(vy.clone()))]),
find_seq_to_find_spec(&rel).unwrap());
}
}

View file

@ -45,18 +45,18 @@ fn can_parse_predicates() {
let p = parse_find_string(s).unwrap();
assert_eq!(p.find_spec,
FindSpec::FindColl(Element::Variable(Variable(PlainSymbol::new("?x")))));
FindSpec::FindColl(Element::Variable(Variable::from_valid_name("?x"))));
assert_eq!(p.where_clauses,
vec![
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?x"))),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
attribute: PatternNonValuePlace::Placeholder,
value: PatternValuePlace::Variable(Variable(PlainSymbol::new("?y"))),
value: PatternValuePlace::Variable(Variable::from_valid_name("?y")),
tx: PatternNonValuePlace::Placeholder,
}),
WhereClause::Pred(Predicate { operator: PlainSymbol::new("<"), args: vec![
FnArg::Variable(Variable(PlainSymbol::new("?y"))), FnArg::EntidOrInteger(10),
FnArg::Variable(Variable::from_valid_name("?y")), FnArg::EntidOrInteger(10),
]}),
]);
}
@ -67,7 +67,7 @@ fn can_parse_simple_or() {
let p = parse_find_string(s).unwrap();
assert_eq!(p.find_spec,
FindSpec::FindScalar(Element::Variable(Variable(PlainSymbol::new("?x")))));
FindSpec::FindScalar(Element::Variable(Variable::from_valid_name("?x"))));
assert_eq!(p.where_clauses,
vec![
WhereClause::OrJoin(OrJoin {
@ -76,7 +76,7 @@ fn can_parse_simple_or() {
OrWhereClause::Clause(
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?x"))),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
attribute: PatternNonValuePlace::Placeholder,
value: PatternValuePlace::EntidOrInteger(10),
tx: PatternNonValuePlace::Placeholder,
@ -84,7 +84,7 @@ fn can_parse_simple_or() {
OrWhereClause::Clause(
WhereClause::Pattern(Pattern {
source: None,
entity: PatternNonValuePlace::Variable(Variable(PlainSymbol::new("?x"))),
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
attribute: PatternNonValuePlace::Placeholder,
value: PatternValuePlace::EntidOrInteger(15),
tx: PatternNonValuePlace::Placeholder,
@ -100,16 +100,16 @@ fn can_parse_unit_or_join() {
let p = parse_find_string(s).unwrap();
assert_eq!(p.find_spec,
FindSpec::FindScalar(Element::Variable(Variable(PlainSymbol::new("?x")))))