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:
parent
ad9a1394a3
commit
1d8d94f887
10 changed files with 60 additions and 83 deletions
|
@ -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",
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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)]]");
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -16,9 +16,6 @@ extern crate maplit;
|
|||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
|
||||
#[macro_use]
|
||||
extern crate matches;
|
||||
|
||||
extern crate edn;
|
||||
|
||||
#[macro_use]
|
||||
|
|
|
@ -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"),
|
||||
}));
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
extern crate maplit;
|
||||
|
||||
extern crate edn;
|
||||
extern crate mentat_core;
|
||||
extern crate mentat_query;
|
||||
extern crate mentat_query_parser;
|
||||
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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)),
|
||||
|
|
Loading…
Reference in a new issue