Address review feedback unrelated to the translator

This commit is contained in:
Thom Chiovoloni 2018-01-26 18:41:14 -05:00
parent bd48e2c741
commit 6584f21a7e
8 changed files with 120 additions and 70 deletions

View file

@ -30,7 +30,6 @@ use errors::{
/// the bindings that will be used at execution time. /// the bindings that will be used at execution time.
/// When built correctly, `types` is guaranteed to contain the types of `values` -- use /// When built correctly, `types` is guaranteed to contain the types of `values` -- use
/// `QueryInputs::new` or `QueryInputs::with_values` to construct an instance. /// `QueryInputs::new` or `QueryInputs::with_values` to construct an instance.
#[derive(Clone)]
pub struct QueryInputs { pub struct QueryInputs {
// These should be crate-private. // These should be crate-private.
pub types: BTreeMap<Variable, ValueType>, pub types: BTreeMap<Variable, ValueType>,

View file

@ -83,8 +83,6 @@ use validate::{
validate_or_join, validate_or_join,
}; };
use self::predicate::parse_type_predicate;
pub use self::inputs::QueryInputs; pub use self::inputs::QueryInputs;
// We do this a lot for errors. // We do this a lot for errors.
@ -969,22 +967,18 @@ impl ConjoiningClauses {
impl ConjoiningClauses { impl ConjoiningClauses {
pub fn apply_clauses(&mut self, schema: &Schema, where_clauses: Vec<WhereClause>) -> Result<()> { pub fn apply_clauses(&mut self, schema: &Schema, where_clauses: Vec<WhereClause>) -> Result<()> {
let mut deferred = Vec::with_capacity(where_clauses.len());
// We apply (top level) type predicates first as an optimization. // We apply (top level) type predicates first as an optimization.
for c in where_clauses { for clause in where_clauses.iter() {
match &c { if let &WhereClause::TypeAnnotation(ref anno) = clause {
&WhereClause::Pred(ref p) => { self.apply_type_anno(anno)?;
if let Some(ty) = parse_type_predicate(p.operator.0.as_str()) { }
self.apply_type_requirement(p, ty)?;
}
},
_ => {}
};
deferred.push(c);
} }
// Then we apply everything else. // Then we apply everything else.
for c in deferred { for clause in where_clauses {
self.apply_clause(schema, c)?; if let &WhereClause::TypeAnnotation(_) = &clause {
continue;
}
self.apply_clause(schema, clause)?;
} }
Ok(()) Ok(())
} }
@ -1010,6 +1004,9 @@ impl ConjoiningClauses {
validate_not_join(&n)?; validate_not_join(&n)?;
self.apply_not_join(schema, n) self.apply_not_join(schema, n)
}, },
WhereClause::TypeAnnotation(anno) => {
self.apply_type_anno(&anno)
},
_ => unimplemented!(), _ => unimplemented!(),
} }
} }

View file

@ -17,6 +17,7 @@ use mentat_core::{
use mentat_query::{ use mentat_query::{
FnArg, FnArg,
Predicate, Predicate,
TypeAnnotation,
}; };
use clauses::ConjoiningClauses; use clauses::ConjoiningClauses;
@ -34,26 +35,11 @@ use types::{
Inequality, Inequality,
}; };
pub fn parse_type_predicate(s: &str) -> Option<ValueType> {
match s {
"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
}
}
/// Application of predicates. /// Application of predicates.
impl ConjoiningClauses { impl ConjoiningClauses {
/// There are several kinds of predicates in our Datalog: /// There are several kinds of predicates in our Datalog:
/// - A limited set of binary comparison operators: < > <= >= !=. /// - A limited set of binary comparison operators: < > <= >= !=.
/// These are converted into SQLite binary comparisons and some type constraints. /// These are converted into SQLite binary comparisons and some type constraints.
/// - A set of type requirements constraining their argument to be a specific ValueType.
/// - In the future, some predicates that are implemented via function calls in SQLite. /// - In the future, some predicates that are implemented via function calls in SQLite.
/// ///
/// At present we have implemented only the five built-in comparison binary operators. /// At present we have implemented only the five built-in comparison binary operators.
@ -62,8 +48,6 @@ impl ConjoiningClauses {
// and ultimately allowing user-specified predicates, we match on the predicate name first. // and ultimately allowing user-specified predicates, we match on the predicate name first.
if let Some(op) = Inequality::from_datalog_operator(predicate.operator.0.as_str()) { if let Some(op) = Inequality::from_datalog_operator(predicate.operator.0.as_str()) {
self.apply_inequality(schema, op, predicate) self.apply_inequality(schema, op, predicate)
} else if let Some(ty) = parse_type_predicate(predicate.operator.0.as_str()) {
self.apply_type_requirement(&predicate, ty)
} else { } else {
bail!(ErrorKind::UnknownFunction(predicate.operator.clone())) bail!(ErrorKind::UnknownFunction(predicate.operator.clone()))
} }
@ -76,17 +60,11 @@ impl ConjoiningClauses {
} }
} }
pub fn apply_type_requirement(&mut self, pred: &Predicate, ty: ValueType) -> Result<()> { /// Apply a type annotation, which is a construct like a predicate that constrains the argument
if pred.args.len() != 1 { /// to be a specific ValueType.
bail!(ErrorKind::InvalidNumberOfArguments(pred.operator.clone(), pred.args.len(), 1)); pub fn apply_type_anno(&mut self, anno: &TypeAnnotation) -> Result<()> {
} self.add_type_requirement(anno.variable.clone(), ValueTypeSet::of_one(anno.value_type));
Ok(())
if let &FnArg::Variable(ref v) = &pred.args[0] {
self.add_type_requirement(v.clone(), ValueTypeSet::of_one(ty));
Ok(())
} else {
bail!(ErrorKind::InvalidArgument(pred.operator.clone(), "variable".into(), 0))
}
} }
/// This function: /// This function:

View file

@ -12,6 +12,7 @@ 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.
@ -20,6 +21,8 @@ use std::collections::BTreeSet;
use self::combine::{eof, many, many1, optional, parser, satisfy, satisfy_map, Parser, ParseResult, Stream}; use self::combine::{eof, many, many1, optional, parser, satisfy, satisfy_map, Parser, ParseResult, Stream};
use self::combine::combinator::{any, choice, or, try}; use self::combine::combinator::{any, choice, or, try};
use self::mentat_core::ValueType;
use self::mentat_parser_utils::{ use self::mentat_parser_utils::{
KeywordMapParser, KeywordMapParser,
ResultParser, ResultParser,
@ -56,6 +59,7 @@ use self::mentat_query::{
Predicate, Predicate,
QueryFunction, QueryFunction,
SrcVar, SrcVar,
TypeAnnotation,
UnifyVars, UnifyVars,
Variable, Variable,
VariableOrPlaceholder, VariableOrPlaceholder,
@ -286,6 +290,44 @@ 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]]`
vector()
.of_exactly(seq()
.of_exactly((Query::type_anno_type(), Query::variable())
.map(|(ty, var)| {
WhereClause::TypeAnnotation(
TypeAnnotation {
value_type: ty,
variable: var,
})
})))
});
/// A vector containing a parenthesized function expression and a binding. /// A vector containing a parenthesized function expression and a binding.
def_parser!(Where, where_fn, WhereClause, { def_parser!(Where, where_fn, WhereClause, {
// Accept either a nested list or a nested vector here: // Accept either a nested list or a nested vector here:
@ -356,6 +398,7 @@ def_parser!(Where, clause, WhereClause, {
try(Where::not_join_clause()), try(Where::not_join_clause()),
try(Where::not_clause()), try(Where::not_clause()),
try(Where::type_annotation()),
try(Where::pred()), try(Where::pred()),
try(Where::where_fn()), try(Where::where_fn()),
]) ])
@ -949,4 +992,21 @@ mod test {
VariableOrPlaceholder::Variable(Variable::from_valid_name("?y"))]), VariableOrPlaceholder::Variable(Variable::from_valid_name("?y"))]),
})); }));
} }
#[test]
fn test_type_anno() {
assert_edn_parses_to!(Where::type_annotation,
"[(string ?x)]",
WhereClause::TypeAnnotation(TypeAnnotation {
value_type: ValueType::String,
variable: Variable::from_valid_name("?x"),
}));
assert_edn_parses_to!(Where::clause,
"[[long ?foo]]",
WhereClause::TypeAnnotation(TypeAnnotation {
value_type: ValueType::Long,
variable: Variable::from_valid_name("?foo"),
}));
}
} }

View file

@ -108,12 +108,12 @@ pub enum Constraint {
}, },
TypeCheck { TypeCheck {
value: ColumnOrExpression, value: ColumnOrExpression,
datatype: SQLDatatype affinity: SQLTypeAffinity
} }
} }
/// Type safe representation of the possible return values from `typeof` /// Type safe representation of the possible return values from `typeof`
pub enum SQLDatatype { pub enum SQLTypeAffinity {
Null, // "null" Null, // "null"
Integer, // "integer" Integer, // "integer"
Real, // "real" Real, // "real"
@ -326,14 +326,14 @@ impl QueryFragment for Op {
} }
} }
impl QueryFragment for SQLDatatype { impl QueryFragment for SQLTypeAffinity {
fn push_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult { fn push_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult {
out.push_sql(match *self { out.push_sql(match *self {
SQLDatatype::Null => "'null'", SQLTypeAffinity::Null => "'null'",
SQLDatatype::Integer => "'integer'", SQLTypeAffinity::Integer => "'integer'",
SQLDatatype::Real => "'real'", SQLTypeAffinity::Real => "'real'",
SQLDatatype::Text => "'text'", SQLTypeAffinity::Text => "'text'",
SQLDatatype::Blob => "'blob'", SQLTypeAffinity::Blob => "'blob'",
}); });
Ok(()) Ok(())
} }
@ -394,11 +394,11 @@ impl QueryFragment for Constraint {
out.push_sql(")"); out.push_sql(")");
Ok(()) Ok(())
}, },
&TypeCheck { ref value, ref datatype } => { &TypeCheck { ref value, ref affinity } => {
out.push_sql("typeof("); out.push_sql("typeof(");
value.push_sql(out)?; value.push_sql(out)?;
out.push_sql(") = "); out.push_sql(") = ");
datatype.push_sql(out)?; affinity.push_sql(out)?;
Ok(()) Ok(())
}, },
} }

View file

@ -51,7 +51,7 @@ use mentat_query_sql::{
ProjectedColumn, ProjectedColumn,
Projection, Projection,
SelectQuery, SelectQuery,
SQLDatatype, SQLTypeAffinity,
TableList, TableList,
TableOrSubquery, TableOrSubquery,
Values, Values,
@ -194,9 +194,9 @@ impl ToConstraint for ColumnConstraint {
loose, loose,
Constraint::TypeCheck { Constraint::TypeCheck {
value: val_column, value: val_column,
datatype: match ty { affinity: match ty {
ValueType::Long => SQLDatatype::Integer, ValueType::Long => SQLTypeAffinity::Integer,
ValueType::Double => SQLDatatype::Real, ValueType::Double => SQLTypeAffinity::Real,
_ => unreachable!() _ => unreachable!()
} }
} }

View file

@ -56,6 +56,7 @@ pub use edn::{
use mentat_core::{ use mentat_core::{
TypedValue, TypedValue,
ValueType,
}; };
pub type SrcVarName = String; // Do not include the required syntactic '$'. pub type SrcVarName = String; // Do not include the required syntactic '$'.
@ -769,6 +770,12 @@ pub struct NotJoin {
pub clauses: Vec<WhereClause>, pub clauses: Vec<WhereClause>,
} }
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TypeAnnotation {
pub value_type: ValueType,
pub variable: Variable,
}
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum WhereClause { pub enum WhereClause {
@ -778,6 +785,7 @@ pub enum WhereClause {
WhereFn(WhereFn), WhereFn(WhereFn),
RuleExpr, RuleExpr,
Pattern(Pattern), Pattern(Pattern),
TypeAnnotation(TypeAnnotation),
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -852,12 +860,13 @@ impl ContainsVariables for WhereClause {
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) { fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
use WhereClause::*; use WhereClause::*;
match self { match self {
&OrJoin(ref o) => o.accumulate_mentioned_variables(acc), &OrJoin(ref o) => o.accumulate_mentioned_variables(acc),
&Pred(ref p) => p.accumulate_mentioned_variables(acc), &Pred(ref p) => p.accumulate_mentioned_variables(acc),
&Pattern(ref p) => p.accumulate_mentioned_variables(acc), &Pattern(ref p) => p.accumulate_mentioned_variables(acc),
&NotJoin(ref n) => n.accumulate_mentioned_variables(acc), &NotJoin(ref n) => n.accumulate_mentioned_variables(acc),
&WhereFn(ref f) => f.accumulate_mentioned_variables(acc), &WhereFn(ref f) => f.accumulate_mentioned_variables(acc),
&RuleExpr => (), &TypeAnnotation(ref a) => a.accumulate_mentioned_variables(acc),
&RuleExpr => (),
} }
} }
} }
@ -920,6 +929,13 @@ impl ContainsVariables for Predicate {
} }
} }
impl ContainsVariables for TypeAnnotation {
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
acc_ref(acc, &self.variable);
}
}
impl ContainsVariables for Binding { impl ContainsVariables for Binding {
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) { fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
match self { match self {

View file

@ -558,13 +558,11 @@ fn test_type_reqs() {
"ref", "ref",
]; ];
let entid_binding = QueryInputs::with_value_sequence(vec![
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),
]);
for name in type_names { for name in type_names {
let q = format!("[:find [?v ...] :in ?e :where [?e _ ?v] [({} ?v)]]", name); let q = format!("[:find [?v ...] :in ?e :where [?e _ ?v] [({} ?v)]]", name);
let results = conn.q_once(&mut c, &q, entid_binding.clone()).unwrap(); let results = conn.q_once(&mut c, &q, QueryInputs::with_value_sequence(vec![
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),
])).unwrap();
match results { match results {
QueryResults::Coll(vals) => { QueryResults::Coll(vals) => {
assert_eq!(vals.len(), 1, "Query should find exactly 1 item"); assert_eq!(vals.len(), 1, "Query should find exactly 1 item");
@ -585,7 +583,9 @@ fn test_type_reqs() {
:in ?e :in ?e
:where [?e _ ?v] [(long ?v)]]"#; :where [?e _ ?v] [(long ?v)]]"#;
let res = conn.q_once(&mut c, longs_query, entid_binding.clone()).unwrap(); let res = conn.q_once(&mut c, longs_query, QueryInputs::with_value_sequence(vec![
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),
])).unwrap();
match res { match res {
QueryResults::Coll(vals) => { QueryResults::Coll(vals) => {
assert_eq!(vals, vec![TypedValue::Long(5), TypedValue::Long(33)]) assert_eq!(vals, vec![TypedValue::Long(5), TypedValue::Long(33)])