Part 2: Turn (type-function ?var) into (type ?var type-keyword).

This is more general (the parser doesn't encode the set of known
types), and avoids a dependency on `ValueType`.
This commit is contained in:
Nick Alexander 2018-05-28 14:34:42 -07:00
parent ad9a1394a3
commit 1d8d94f887
10 changed files with 60 additions and 83 deletions

View file

@ -131,6 +131,24 @@ impl ValueType {
})
}
pub fn from_keyword(keyword: &Keyword) -> Option<Self> {
if keyword.namespace() != Some("db.type") {
return None;
}
return match keyword.name() {
"ref" => Some(ValueType::Ref),
"boolean" => Some(ValueType::Boolean),
"instant" => Some(ValueType::Instant),
"long" => Some(ValueType::Long),
"double" => Some(ValueType::Double),
"string" => Some(ValueType::String),
"keyword" => Some(ValueType::Keyword),
"uuid" => Some(ValueType::Uuid),
_ => None,
}
}
pub fn into_typed_value(self) -> TypedValue {
TypedValue::typed_ns_keyword("db.type", match self {
ValueType::Ref => "ref",

View file

@ -16,6 +16,7 @@ use mentat_core::{
use mentat_query::{
FnArg,
PlainSymbol,
Predicate,
TypeAnnotation,
};
@ -66,7 +67,10 @@ impl ConjoiningClauses {
/// Apply a type annotation, which is a construct like a predicate that constrains the argument
/// to be a specific ValueType.
pub(crate) fn apply_type_anno(&mut self, anno: &TypeAnnotation) -> Result<()> {
self.add_type_requirement(anno.variable.clone(), ValueTypeSet::of_one(anno.value_type));
match ValueType::from_keyword(&anno.value_type) {
Some(value_type) => self.add_type_requirement(anno.variable.clone(), ValueTypeSet::of_one(value_type)),
None => bail!(ErrorKind::InvalidArgumentType(PlainSymbol::plain("type"), ValueTypeSet::any(), 2)),
}
Ok(())
}

View file

@ -43,22 +43,12 @@ fn prepopulated_schema() -> Schema {
#[test]
fn test_empty_known() {
let type_names = [
"boolean",
"long",
"double",
"string",
"keyword",
"uuid",
"instant",
"ref",
];
let schema = prepopulated_schema();
let known = Known::for_schema(&schema);
for known_type in type_names.iter() {
for required in type_names.iter() {
let q = format!("[:find ?e :where [?e :test/{} ?v] [({} ?v)]]",
known_type, required);
for known_type in ValueType::all_enums().iter() {
for required in ValueType::all_enums().iter() {
let q = format!("[:find ?e :where [?e :test/{} ?v] [(type ?v {})]]",
known_type.into_keyword().name(), required);
println!("Query: {}", q);
let cc = alg(known, &q);
// It should only be empty if the known type and our requirement differ.
@ -72,7 +62,7 @@ fn test_empty_known() {
fn test_multiple() {
let schema = prepopulated_schema();
let known = Known::for_schema(&schema);
let q = "[:find ?e :where [?e _ ?v] [(long ?v)] [(double ?v)]]";
let q = "[:find ?e :where [?e _ ?v] [(type ?v :db.type/long)] [(type ?v :db.type/double)]]";
let cc = alg(known, &q);
assert!(cc.empty_because.is_some());
}
@ -81,5 +71,5 @@ fn test_multiple() {
fn test_unbound() {
let schema = prepopulated_schema();
let known = Known::for_schema(&schema);
bails(known, "[:find ?e :where [(string ?e)]]");
bails(known, "[:find ?e :where [(type ?e :db.type/string)]]");
}

View file

@ -7,14 +7,10 @@ workspace = ".."
combine = "2.3.2"
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
maplit = "0.1"
matches = "0.1"
[dependencies.edn]
path = "../edn"
[dependencies.mentat_core]
path = "../core"
[dependencies.mentat_parser_utils]
path = "../parser-utils"

View file

@ -16,9 +16,6 @@ extern crate maplit;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate matches;
extern crate edn;
#[macro_use]

View file

@ -12,7 +12,6 @@ extern crate combine;
extern crate edn;
extern crate mentat_parser_utils;
extern crate mentat_query;
extern crate mentat_core;
use std; // To refer to std::result::Result.
@ -34,8 +33,6 @@ use self::combine::{
use self::combine::combinator::{any, choice, or, try};
use self::mentat_core::ValueType;
use errors::{
Error,
ErrorKind,
@ -100,6 +97,10 @@ def_parser!(Query, variable, Variable, {
satisfy_map(Variable::from_value)
});
def_parser!(Query, keyword, edn::Keyword, {
satisfy_map(|v: &edn::ValueAndSpan| v.inner.as_keyword().cloned())
});
def_parser!(Query, source_var, SrcVar, {
satisfy_map(SrcVar::from_value)
});
@ -178,6 +179,8 @@ def_matches_plain_symbol!(Where, not, "not");
def_matches_plain_symbol!(Where, not_join, "not-join");
def_matches_plain_symbol!(Where, type_symbol, "type");
def_parser!(Where, rule_vars, BTreeSet<Variable>, {
seq()
.of_exactly(many1(Query::variable()).and_then(unique_vars))
@ -339,36 +342,14 @@ def_parser!(Where, pred, WhereClause, {
})))
});
def_parser!(Query, type_anno_type, ValueType, {
satisfy_map(|v: &edn::ValueAndSpan| {
match v.inner {
edn::SpannedValue::PlainSymbol(ref s) => {
let name = s.0.as_str();
match name {
"ref" => Some(ValueType::Ref),
"boolean" => Some(ValueType::Boolean),
"instant" => Some(ValueType::Instant),
"long" => Some(ValueType::Long),
"double" => Some(ValueType::Double),
"string" => Some(ValueType::String),
"keyword" => Some(ValueType::Keyword),
"uuid" => Some(ValueType::Uuid),
_ => None
}
},
_ => None,
}
})
});
/// A type annotation.
def_parser!(Where, type_annotation, WhereClause, {
// Accept either a nested list or a nested vector here:
// `[(string ?x)]` or `[[string ?x]]`
// `[(type ?x :db.type/string)]` or `[[type ?x :db.type/long]]`
vector()
.of_exactly(seq()
.of_exactly((Query::type_anno_type(), Query::variable())
.map(|(ty, var)| {
.of_exactly((Where::type_symbol(), Query::variable(), Query::keyword())
.map(|(_, var, ty)| {
WhereClause::TypeAnnotation(
TypeAnnotation {
value_type: ty,
@ -759,7 +740,10 @@ mod test {
let input = input.with_spans();
let mut par = Where::pattern();
let result = par.parse(input.atom_stream());
assert!(matches!(result, Err(_)), "Expected a parse error.");
match result {
Err(_) => (),
_ => assert!(false, "Expected a parse error"),
}
}
#[test]
@ -1137,15 +1121,16 @@ mod test {
#[test]
fn test_type_anno() {
assert_edn_parses_to!(Where::type_annotation,
"[(string ?x)]",
"[(type ?x :db.type/string)]",
WhereClause::TypeAnnotation(TypeAnnotation {
value_type: ValueType::String,
value_type: edn::Keyword::namespaced("db.type", "string"),
variable: Variable::from_valid_name("?x"),
}));
// We don't check for valid types, or even that the type is namespaced.
assert_edn_parses_to!(Where::clause,
"[[long ?foo]]",
"[[type ?foo :db_type_long]]",
WhereClause::TypeAnnotation(TypeAnnotation {
value_type: ValueType::Long,
value_type: edn::Keyword::plain("db_type_long"),
variable: Variable::from_valid_name("?foo"),
}));

View file

@ -12,7 +12,6 @@
extern crate maplit;
extern crate edn;
extern crate mentat_core;
extern crate mentat_query;
extern crate mentat_query_parser;

View file

@ -348,7 +348,7 @@ fn test_unknown_ident() {
fn test_type_required_long() {
let schema = Schema::default();
let query = r#"[:find ?x :where [?x _ ?e] [(long ?e)]]"#;
let query = r#"[:find ?x :where [?x _ ?e] [(type ?e :db.type/long)]]"#;
let SQLQuery { sql, args } = translate(&schema, query);
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
@ -363,7 +363,7 @@ fn test_type_required_long() {
fn test_type_required_double() {
let schema = Schema::default();
let query = r#"[:find ?x :where [?x _ ?e] [(double ?e)]]"#;
let query = r#"[:find ?x :where [?x _ ?e] [(type ?e :db.type/double)]]"#;
let SQLQuery { sql, args } = translate(&schema, query);
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
@ -378,7 +378,7 @@ fn test_type_required_double() {
fn test_type_required_boolean() {
let schema = Schema::default();
let query = r#"[:find ?x :where [?x _ ?e] [(boolean ?e)]]"#;
let query = r#"[:find ?x :where [?x _ ?e] [(type ?e :db.type/boolean)]]"#;
let SQLQuery { sql, args } = translate(&schema, query);
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
@ -392,7 +392,7 @@ fn test_type_required_boolean() {
fn test_type_required_string() {
let schema = Schema::default();
let query = r#"[:find ?x :where [?x _ ?e] [(string ?e)]]"#;
let query = r#"[:find ?x :where [?x _ ?e] [(type ?e :db.type/string)]]"#;
let SQLQuery { sql, args } = translate(&schema, query);
// Note: strings should use `all_datoms` and not `datoms`.

View file

@ -58,7 +58,6 @@ use mentat_core::{
FromRc,
TypedValue,
ValueRc,
ValueType,
};
pub type SrcVarName = String; // Do not include the required syntactic '$'.
@ -967,7 +966,7 @@ pub struct NotJoin {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TypeAnnotation {
pub value_type: ValueType,
pub value_type: Keyword,
pub variable: Variable,
}

View file

@ -602,7 +602,7 @@ fn test_aggregates_type_handling() {
// You can't sum instants.
let r = store.q_once(r#"[:find (sum ?v) .
:where [_ _ ?v] [(instant ?v)]]"#,
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
None);
match r {
Result::Err(
@ -623,7 +623,7 @@ fn test_aggregates_type_handling() {
// But you can count them.
let r = store.q_once(r#"[:find (count ?v) .
:where [_ _ ?v] [(instant ?v)]]"#,
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
None)
.into_scalar_result()
.expect("results")
@ -634,7 +634,7 @@ fn test_aggregates_type_handling() {
// And you can min them, which returns an instant.
let r = store.q_once(r#"[:find (min ?v) .
:where [_ _ ?v] [(instant ?v)]]"#,
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
None)
.into_scalar_result()
.expect("results")
@ -644,7 +644,7 @@ fn test_aggregates_type_handling() {
assert_eq!(Binding::Scalar(TypedValue::Instant(earliest)), r);
let r = store.q_once(r#"[:find (sum ?v) .
:where [_ _ ?v] [(long ?v)]]"#,
:where [_ _ ?v] [(type ?v :db.type/long)]]"#,
None)
.into_scalar_result()
.expect("results")
@ -655,7 +655,7 @@ fn test_aggregates_type_handling() {
assert_eq!(Binding::Scalar(TypedValue::Long(total)), r);
let r = store.q_once(r#"[:find (avg ?v) .
:where [_ _ ?v] [(double ?v)]]"#,
:where [_ _ ?v] [(type ?v :db.type/double)]]"#,
None)
.into_scalar_result()
.expect("results")
@ -710,19 +710,8 @@ fn test_type_reqs() {
}
};
let type_names = &[
"boolean",
"long",
"double",
"string",
"keyword",
"uuid",
"instant",
"ref",
];
for name in type_names {
let q = format!("[:find [?v ...] :in ?e :where [?e _ ?v] [({} ?v)]]", name);
for value_type in ValueType::all_enums().iter() {
let q = format!("[:find [?v ...] :in ?e :where [?e _ ?v] [(type ?v {})]]", value_type.into_keyword());
let results = conn.q_once(&mut c, &q, QueryInputs::with_value_sequence(vec![
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),
]))
@ -746,7 +735,7 @@ fn test_type_reqs() {
let longs_query = r#"[:find [?v ...]
:order (asc ?v)
:in ?e
:where [?e _ ?v] [(long ?v)]]"#;
:where [?e _ ?v] [(type ?v :db.type/long)]]"#;
let res = conn.q_once(&mut c, longs_query, QueryInputs::with_value_sequence(vec![
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),