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 {
|
pub fn into_typed_value(self) -> TypedValue {
|
||||||
TypedValue::typed_ns_keyword("db.type", match self {
|
TypedValue::typed_ns_keyword("db.type", match self {
|
||||||
ValueType::Ref => "ref",
|
ValueType::Ref => "ref",
|
||||||
|
|
|
@ -16,6 +16,7 @@ use mentat_core::{
|
||||||
|
|
||||||
use mentat_query::{
|
use mentat_query::{
|
||||||
FnArg,
|
FnArg,
|
||||||
|
PlainSymbol,
|
||||||
Predicate,
|
Predicate,
|
||||||
TypeAnnotation,
|
TypeAnnotation,
|
||||||
};
|
};
|
||||||
|
@ -66,7 +67,10 @@ impl ConjoiningClauses {
|
||||||
/// Apply a type annotation, which is a construct like a predicate that constrains the argument
|
/// Apply a type annotation, which is a construct like a predicate that constrains the argument
|
||||||
/// to be a specific ValueType.
|
/// to be a specific ValueType.
|
||||||
pub(crate) fn apply_type_anno(&mut self, anno: &TypeAnnotation) -> Result<()> {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,22 +43,12 @@ fn prepopulated_schema() -> Schema {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty_known() {
|
fn test_empty_known() {
|
||||||
let type_names = [
|
|
||||||
"boolean",
|
|
||||||
"long",
|
|
||||||
"double",
|
|
||||||
"string",
|
|
||||||
"keyword",
|
|
||||||
"uuid",
|
|
||||||
"instant",
|
|
||||||
"ref",
|
|
||||||
];
|
|
||||||
let schema = prepopulated_schema();
|
let schema = prepopulated_schema();
|
||||||
let known = Known::for_schema(&schema);
|
let known = Known::for_schema(&schema);
|
||||||
for known_type in type_names.iter() {
|
for known_type in ValueType::all_enums().iter() {
|
||||||
for required in type_names.iter() {
|
for required in ValueType::all_enums().iter() {
|
||||||
let q = format!("[:find ?e :where [?e :test/{} ?v] [({} ?v)]]",
|
let q = format!("[:find ?e :where [?e :test/{} ?v] [(type ?v {})]]",
|
||||||
known_type, required);
|
known_type.into_keyword().name(), required);
|
||||||
println!("Query: {}", q);
|
println!("Query: {}", q);
|
||||||
let cc = alg(known, &q);
|
let cc = alg(known, &q);
|
||||||
// It should only be empty if the known type and our requirement differ.
|
// It should only be empty if the known type and our requirement differ.
|
||||||
|
@ -72,7 +62,7 @@ fn test_empty_known() {
|
||||||
fn test_multiple() {
|
fn test_multiple() {
|
||||||
let schema = prepopulated_schema();
|
let schema = prepopulated_schema();
|
||||||
let known = Known::for_schema(&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);
|
let cc = alg(known, &q);
|
||||||
assert!(cc.empty_because.is_some());
|
assert!(cc.empty_because.is_some());
|
||||||
}
|
}
|
||||||
|
@ -81,5 +71,5 @@ fn test_multiple() {
|
||||||
fn test_unbound() {
|
fn test_unbound() {
|
||||||
let schema = prepopulated_schema();
|
let schema = prepopulated_schema();
|
||||||
let known = Known::for_schema(&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"
|
combine = "2.3.2"
|
||||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
||||||
maplit = "0.1"
|
maplit = "0.1"
|
||||||
matches = "0.1"
|
|
||||||
|
|
||||||
[dependencies.edn]
|
[dependencies.edn]
|
||||||
path = "../edn"
|
path = "../edn"
|
||||||
|
|
||||||
[dependencies.mentat_core]
|
|
||||||
path = "../core"
|
|
||||||
|
|
||||||
[dependencies.mentat_parser_utils]
|
[dependencies.mentat_parser_utils]
|
||||||
path = "../parser-utils"
|
path = "../parser-utils"
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,6 @@ extern crate maplit;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate error_chain;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate matches;
|
|
||||||
|
|
||||||
extern crate edn;
|
extern crate edn;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|
|
@ -12,7 +12,6 @@ extern crate combine;
|
||||||
extern crate edn;
|
extern crate edn;
|
||||||
extern crate mentat_parser_utils;
|
extern crate mentat_parser_utils;
|
||||||
extern crate mentat_query;
|
extern crate mentat_query;
|
||||||
extern crate mentat_core;
|
|
||||||
|
|
||||||
use std; // To refer to std::result::Result.
|
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::combine::combinator::{any, choice, or, try};
|
||||||
|
|
||||||
use self::mentat_core::ValueType;
|
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
Error,
|
Error,
|
||||||
ErrorKind,
|
ErrorKind,
|
||||||
|
@ -100,6 +97,10 @@ def_parser!(Query, variable, Variable, {
|
||||||
satisfy_map(Variable::from_value)
|
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, {
|
def_parser!(Query, source_var, SrcVar, {
|
||||||
satisfy_map(SrcVar::from_value)
|
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, not_join, "not-join");
|
||||||
|
|
||||||
|
def_matches_plain_symbol!(Where, type_symbol, "type");
|
||||||
|
|
||||||
def_parser!(Where, rule_vars, BTreeSet<Variable>, {
|
def_parser!(Where, rule_vars, BTreeSet<Variable>, {
|
||||||
seq()
|
seq()
|
||||||
.of_exactly(many1(Query::variable()).and_then(unique_vars))
|
.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.
|
/// A type annotation.
|
||||||
def_parser!(Where, type_annotation, WhereClause, {
|
def_parser!(Where, type_annotation, WhereClause, {
|
||||||
// Accept either a nested list or a nested vector here:
|
// 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()
|
vector()
|
||||||
.of_exactly(seq()
|
.of_exactly(seq()
|
||||||
.of_exactly((Query::type_anno_type(), Query::variable())
|
.of_exactly((Where::type_symbol(), Query::variable(), Query::keyword())
|
||||||
.map(|(ty, var)| {
|
.map(|(_, var, ty)| {
|
||||||
WhereClause::TypeAnnotation(
|
WhereClause::TypeAnnotation(
|
||||||
TypeAnnotation {
|
TypeAnnotation {
|
||||||
value_type: ty,
|
value_type: ty,
|
||||||
|
@ -759,7 +740,10 @@ mod test {
|
||||||
let input = input.with_spans();
|
let input = input.with_spans();
|
||||||
let mut par = Where::pattern();
|
let mut par = Where::pattern();
|
||||||
let result = par.parse(input.atom_stream());
|
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]
|
#[test]
|
||||||
|
@ -1137,15 +1121,16 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_type_anno() {
|
fn test_type_anno() {
|
||||||
assert_edn_parses_to!(Where::type_annotation,
|
assert_edn_parses_to!(Where::type_annotation,
|
||||||
"[(string ?x)]",
|
"[(type ?x :db.type/string)]",
|
||||||
WhereClause::TypeAnnotation(TypeAnnotation {
|
WhereClause::TypeAnnotation(TypeAnnotation {
|
||||||
value_type: ValueType::String,
|
value_type: edn::Keyword::namespaced("db.type", "string"),
|
||||||
variable: Variable::from_valid_name("?x"),
|
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,
|
assert_edn_parses_to!(Where::clause,
|
||||||
"[[long ?foo]]",
|
"[[type ?foo :db_type_long]]",
|
||||||
WhereClause::TypeAnnotation(TypeAnnotation {
|
WhereClause::TypeAnnotation(TypeAnnotation {
|
||||||
value_type: ValueType::Long,
|
value_type: edn::Keyword::plain("db_type_long"),
|
||||||
variable: Variable::from_valid_name("?foo"),
|
variable: Variable::from_valid_name("?foo"),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
extern crate maplit;
|
extern crate maplit;
|
||||||
|
|
||||||
extern crate edn;
|
extern crate edn;
|
||||||
extern crate mentat_core;
|
|
||||||
extern crate mentat_query;
|
extern crate mentat_query;
|
||||||
extern crate mentat_query_parser;
|
extern crate mentat_query_parser;
|
||||||
|
|
||||||
|
|
|
@ -348,7 +348,7 @@ fn test_unknown_ident() {
|
||||||
fn test_type_required_long() {
|
fn test_type_required_long() {
|
||||||
let schema = Schema::default();
|
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);
|
let SQLQuery { sql, args } = translate(&schema, query);
|
||||||
|
|
||||||
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
|
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
|
||||||
|
@ -363,7 +363,7 @@ fn test_type_required_long() {
|
||||||
fn test_type_required_double() {
|
fn test_type_required_double() {
|
||||||
let schema = Schema::default();
|
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);
|
let SQLQuery { sql, args } = translate(&schema, query);
|
||||||
|
|
||||||
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
|
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
|
||||||
|
@ -378,7 +378,7 @@ fn test_type_required_double() {
|
||||||
fn test_type_required_boolean() {
|
fn test_type_required_boolean() {
|
||||||
let schema = Schema::default();
|
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);
|
let SQLQuery { sql, args } = translate(&schema, query);
|
||||||
|
|
||||||
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
|
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
|
||||||
|
@ -392,7 +392,7 @@ fn test_type_required_boolean() {
|
||||||
fn test_type_required_string() {
|
fn test_type_required_string() {
|
||||||
let schema = Schema::default();
|
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);
|
let SQLQuery { sql, args } = translate(&schema, query);
|
||||||
|
|
||||||
// Note: strings should use `all_datoms` and not `datoms`.
|
// Note: strings should use `all_datoms` and not `datoms`.
|
||||||
|
|
|
@ -58,7 +58,6 @@ use mentat_core::{
|
||||||
FromRc,
|
FromRc,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
ValueRc,
|
ValueRc,
|
||||||
ValueType,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type SrcVarName = String; // Do not include the required syntactic '$'.
|
pub type SrcVarName = String; // Do not include the required syntactic '$'.
|
||||||
|
@ -967,7 +966,7 @@ pub struct NotJoin {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct TypeAnnotation {
|
pub struct TypeAnnotation {
|
||||||
pub value_type: ValueType,
|
pub value_type: Keyword,
|
||||||
pub variable: Variable,
|
pub variable: Variable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -602,7 +602,7 @@ fn test_aggregates_type_handling() {
|
||||||
|
|
||||||
// You can't sum instants.
|
// You can't sum instants.
|
||||||
let r = store.q_once(r#"[:find (sum ?v) .
|
let r = store.q_once(r#"[:find (sum ?v) .
|
||||||
:where [_ _ ?v] [(instant ?v)]]"#,
|
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
|
||||||
None);
|
None);
|
||||||
match r {
|
match r {
|
||||||
Result::Err(
|
Result::Err(
|
||||||
|
@ -623,7 +623,7 @@ fn test_aggregates_type_handling() {
|
||||||
|
|
||||||
// But you can count them.
|
// But you can count them.
|
||||||
let r = store.q_once(r#"[:find (count ?v) .
|
let r = store.q_once(r#"[:find (count ?v) .
|
||||||
:where [_ _ ?v] [(instant ?v)]]"#,
|
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
|
||||||
None)
|
None)
|
||||||
.into_scalar_result()
|
.into_scalar_result()
|
||||||
.expect("results")
|
.expect("results")
|
||||||
|
@ -634,7 +634,7 @@ fn test_aggregates_type_handling() {
|
||||||
|
|
||||||
// And you can min them, which returns an instant.
|
// And you can min them, which returns an instant.
|
||||||
let r = store.q_once(r#"[:find (min ?v) .
|
let r = store.q_once(r#"[:find (min ?v) .
|
||||||
:where [_ _ ?v] [(instant ?v)]]"#,
|
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
|
||||||
None)
|
None)
|
||||||
.into_scalar_result()
|
.into_scalar_result()
|
||||||
.expect("results")
|
.expect("results")
|
||||||
|
@ -644,7 +644,7 @@ fn test_aggregates_type_handling() {
|
||||||
assert_eq!(Binding::Scalar(TypedValue::Instant(earliest)), r);
|
assert_eq!(Binding::Scalar(TypedValue::Instant(earliest)), r);
|
||||||
|
|
||||||
let r = store.q_once(r#"[:find (sum ?v) .
|
let r = store.q_once(r#"[:find (sum ?v) .
|
||||||
:where [_ _ ?v] [(long ?v)]]"#,
|
:where [_ _ ?v] [(type ?v :db.type/long)]]"#,
|
||||||
None)
|
None)
|
||||||
.into_scalar_result()
|
.into_scalar_result()
|
||||||
.expect("results")
|
.expect("results")
|
||||||
|
@ -655,7 +655,7 @@ fn test_aggregates_type_handling() {
|
||||||
assert_eq!(Binding::Scalar(TypedValue::Long(total)), r);
|
assert_eq!(Binding::Scalar(TypedValue::Long(total)), r);
|
||||||
|
|
||||||
let r = store.q_once(r#"[:find (avg ?v) .
|
let r = store.q_once(r#"[:find (avg ?v) .
|
||||||
:where [_ _ ?v] [(double ?v)]]"#,
|
:where [_ _ ?v] [(type ?v :db.type/double)]]"#,
|
||||||
None)
|
None)
|
||||||
.into_scalar_result()
|
.into_scalar_result()
|
||||||
.expect("results")
|
.expect("results")
|
||||||
|
@ -710,19 +710,8 @@ fn test_type_reqs() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let type_names = &[
|
for value_type in ValueType::all_enums().iter() {
|
||||||
"boolean",
|
let q = format!("[:find [?v ...] :in ?e :where [?e _ ?v] [(type ?v {})]]", value_type.into_keyword());
|
||||||
"long",
|
|
||||||
"double",
|
|
||||||
"string",
|
|
||||||
"keyword",
|
|
||||||
"uuid",
|
|
||||||
"instant",
|
|
||||||
"ref",
|
|
||||||
];
|
|
||||||
|
|
||||||
for name in type_names {
|
|
||||||
let q = format!("[:find [?v ...] :in ?e :where [?e _ ?v] [({} ?v)]]", name);
|
|
||||||
let results = conn.q_once(&mut c, &q, QueryInputs::with_value_sequence(vec![
|
let results = conn.q_once(&mut c, &q, QueryInputs::with_value_sequence(vec![
|
||||||
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),
|
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),
|
||||||
]))
|
]))
|
||||||
|
@ -746,7 +735,7 @@ fn test_type_reqs() {
|
||||||
let longs_query = r#"[:find [?v ...]
|
let longs_query = r#"[:find [?v ...]
|
||||||
:order (asc ?v)
|
:order (asc ?v)
|
||||||
:in ?e
|
: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![
|
let res = conn.q_once(&mut c, longs_query, QueryInputs::with_value_sequence(vec![
|
||||||
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),
|
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),
|
||||||
|
|
Loading…
Reference in a new issue