diff --git a/core/src/lib.rs b/core/src/lib.rs index b8285137..73e28858 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -18,7 +18,11 @@ extern crate edn; pub mod values; -use std::collections::BTreeMap; +use std::collections::{ + BTreeMap, + BTreeSet, +}; + use std::fmt; use std::rc::Rc; @@ -64,6 +68,8 @@ pub enum ValueType { Uuid, } +pub type ValueTypeTag = i32; + impl ValueType { pub fn all_enums() -> EnumSet { // TODO: lazy_static. @@ -229,6 +235,178 @@ impl SQLValueType for ValueType { } } +trait EnumSetExtensions { + /// Return a set containing both `x` and `y`. + fn of_both(x: T, y: T) -> EnumSet; + + /// Return a clone of `self` with `y` added. + fn with(&self, y: T) -> EnumSet; +} + +impl EnumSetExtensions for EnumSet { + /// Return a set containing both `x` and `y`. + fn of_both(x: T, y: T) -> Self { + let mut o = EnumSet::new(); + o.insert(x); + o.insert(y); + o + } + + /// Return a clone of `self` with `y` added. + fn with(&self, y: T) -> EnumSet { + let mut o = self.clone(); + o.insert(y); + o + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct ValueTypeSet(pub EnumSet); + +impl Default for ValueTypeSet { + fn default() -> ValueTypeSet { + ValueTypeSet::any() + } +} + +impl ValueTypeSet { + pub fn any() -> ValueTypeSet { + ValueTypeSet(ValueType::all_enums()) + } + + pub fn none() -> ValueTypeSet { + ValueTypeSet(EnumSet::new()) + } + + /// Return a set containing only `t`. + pub fn of_one(t: ValueType) -> ValueTypeSet { + let mut s = EnumSet::new(); + s.insert(t); + ValueTypeSet(s) + } + + /// Return a set containing `Double` and `Long`. + pub fn of_numeric_types() -> ValueTypeSet { + ValueTypeSet(EnumSet::of_both(ValueType::Double, ValueType::Long)) + } + + /// Return a set containing `Ref` and `Keyword`. + pub fn of_keywords() -> ValueTypeSet { + ValueTypeSet(EnumSet::of_both(ValueType::Ref, ValueType::Keyword)) + } + + /// Return a set containing `Ref` and `Long`. + pub fn of_longs() -> ValueTypeSet { + ValueTypeSet(EnumSet::of_both(ValueType::Ref, ValueType::Long)) + } +} + +impl ValueTypeSet { + pub fn insert(&mut self, vt: ValueType) -> bool { + self.0.insert(vt) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns a set containing all the types in this set and `other`. + pub fn union(&self, other: &ValueTypeSet) -> ValueTypeSet { + ValueTypeSet(self.0.union(other.0)) + } + + pub fn intersection(&self, other: &ValueTypeSet) -> ValueTypeSet { + ValueTypeSet(self.0.intersection(other.0)) + } + + /// Return an arbitrary type that's part of this set. + /// For a set containing a single type, this will be that type. + pub fn exemplar(&self) -> Option { + self.0.iter().next() + } + + pub fn is_subset(&self, other: &ValueTypeSet) -> bool { + self.0.is_subset(&other.0) + } + + pub fn contains(&self, vt: ValueType) -> bool { + self.0.contains(&vt) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn is_unit(&self) -> bool { + self.0.len() == 1 + } +} + +impl IntoIterator for ValueTypeSet { + type Item = ValueType; + type IntoIter = ::enum_set::Iter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl ::std::iter::FromIterator for ValueTypeSet { + fn from_iter>(iterator: I) -> Self { + let mut ret = Self::none(); + ret.0.extend(iterator); + ret + } +} + +impl ::std::iter::Extend for ValueTypeSet { + fn extend>(&mut self, iter: I) { + for element in iter { + self.0.insert(element); + } + } +} + +pub trait SQLValueTypeSet { + fn value_type_tags(&self) -> BTreeSet; + fn has_unique_type_code(&self) -> bool; + fn unique_type_code(&self) -> Option; +} + +impl SQLValueTypeSet for ValueTypeSet { + // This is inefficient, but it'll do for now. + fn value_type_tags(&self) -> BTreeSet { + let mut out = BTreeSet::new(); + for t in self.0.iter() { + out.insert(t.value_type_tag()); + } + out + } + + fn unique_type_code(&self) -> Option { + if self.is_unit() || self.has_unique_type_code() { + self.exemplar().map(|t| t.value_type_tag()) + } else { + None + } + } + + fn has_unique_type_code(&self) -> bool { + if self.is_unit() { + return true; + } + + let mut acc = BTreeSet::new(); + for t in self.0.iter() { + if acc.insert(t.value_type_tag()) && acc.len() > 1 { + // We inserted a second or subsequent value. + return false; + } + } + !acc.is_empty() + } +} + #[test] fn test_typed_value() { assert!(TypedValue::Boolean(false).is_congruent_with(None)); diff --git a/query-algebrizer/Cargo.toml b/query-algebrizer/Cargo.toml index 7df1b243..310af20d 100644 --- a/query-algebrizer/Cargo.toml +++ b/query-algebrizer/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.1" workspace = ".." [dependencies] -enum-set = { git = "https://github.com/rnewman/enum-set" } error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" } [dependencies.mentat_core] diff --git a/query-algebrizer/src/clauses/convert.rs b/query-algebrizer/src/clauses/convert.rs index f232ef64..bffb9783 100644 --- a/query-algebrizer/src/clauses/convert.rs +++ b/query-algebrizer/src/clauses/convert.rs @@ -15,6 +15,7 @@ use mentat_core::{ SQLValueType, TypedValue, ValueType, + ValueTypeSet, }; use mentat_query::{ @@ -34,7 +35,6 @@ use errors::{ use types::{ EmptyBecause, - ValueTypeSet, }; macro_rules! coerce_to_typed_value { diff --git a/query-algebrizer/src/clauses/ground.rs b/query-algebrizer/src/clauses/ground.rs index d7ad7557..f6d81283 100644 --- a/query-algebrizer/src/clauses/ground.rs +++ b/query-algebrizer/src/clauses/ground.rs @@ -12,6 +12,7 @@ use mentat_core::{ Schema, TypedValue, ValueType, + ValueTypeSet, }; use mentat_query::{ @@ -39,7 +40,6 @@ use types::{ ComputedTable, EmptyBecause, SourceAlias, - ValueTypeSet, VariableColumn, }; @@ -335,10 +335,6 @@ mod testing { associate_ident, }; - use types::{ - ValueTypeSet, - }; - #[test] fn test_apply_ground() { let vz = Variable::from_valid_name("?z"); diff --git a/query-algebrizer/src/clauses/mod.rs b/query-algebrizer/src/clauses/mod.rs index 7ee93032..2e26a07f 100644 --- a/query-algebrizer/src/clauses/mod.rs +++ b/query-algebrizer/src/clauses/mod.rs @@ -28,6 +28,7 @@ use mentat_core::{ Schema, TypedValue, ValueType, + ValueTypeSet, }; use mentat_core::counter::RcCounter; @@ -59,7 +60,6 @@ use types::{ QueryValue, SourceAlias, TableAlias, - ValueTypeSet, }; mod convert; // Converting args to values. @@ -365,8 +365,17 @@ impl ConjoiningClauses { self.value_bindings.get(var).cloned() } + pub fn is_value_bound(&self, var: &Variable) -> bool { + self.value_bindings.contains_key(var) + } + + /// Return an interator over the variables externally bound to values. + pub fn value_bound_variables(&self) -> ::std::collections::btree_map::Keys { + self.value_bindings.keys() + } + /// Return a set of the variables externally bound to values. - pub fn value_bound_variables(&self) -> BTreeSet { + pub fn value_bound_variable_set(&self) -> BTreeSet { self.value_bindings.keys().cloned().collect() } diff --git a/query-algebrizer/src/clauses/not.rs b/query-algebrizer/src/clauses/not.rs index 308cf28a..30a089e0 100644 --- a/query-algebrizer/src/clauses/not.rs +++ b/query-algebrizer/src/clauses/not.rs @@ -81,6 +81,7 @@ mod testing { Attribute, TypedValue, ValueType, + ValueTypeSet, }; use mentat_query::{ @@ -113,7 +114,6 @@ mod testing { QualifiedAlias, QueryValue, SourceAlias, - ValueTypeSet, }; use { diff --git a/query-algebrizer/src/clauses/or.rs b/query-algebrizer/src/clauses/or.rs index ed1cc239..6f47ec6a 100644 --- a/query-algebrizer/src/clauses/or.rs +++ b/query-algebrizer/src/clauses/or.rs @@ -16,6 +16,7 @@ use std::collections::{ use mentat_core::{ Schema, + ValueTypeSet, }; use mentat_query::{ @@ -47,7 +48,6 @@ use types::{ EmptyBecause, QualifiedAlias, SourceAlias, - ValueTypeSet, VariableColumn, }; diff --git a/query-algebrizer/src/clauses/pattern.rs b/query-algebrizer/src/clauses/pattern.rs index 516816b6..6bba9d44 100644 --- a/query-algebrizer/src/clauses/pattern.rs +++ b/query-algebrizer/src/clauses/pattern.rs @@ -294,6 +294,7 @@ mod testing { use mentat_core::attribute::Unique; use mentat_core::{ Attribute, + ValueTypeSet, }; use mentat_query::{ @@ -320,7 +321,6 @@ mod testing { QualifiedAlias, QueryValue, SourceAlias, - ValueTypeSet, }; use algebrize; diff --git a/query-algebrizer/src/clauses/predicate.rs b/query-algebrizer/src/clauses/predicate.rs index 0a77ac21..a7b22549 100644 --- a/query-algebrizer/src/clauses/predicate.rs +++ b/query-algebrizer/src/clauses/predicate.rs @@ -11,6 +11,7 @@ use mentat_core::{ Schema, ValueType, + ValueTypeSet, }; use mentat_query::{ @@ -31,7 +32,6 @@ use types::{ ColumnConstraint, EmptyBecause, Inequality, - ValueTypeSet, }; /// Application of predicates. @@ -177,7 +177,6 @@ mod testing { ColumnConstraint, EmptyBecause, QueryValue, - ValueTypeSet, }; #[test] diff --git a/query-algebrizer/src/lib.rs b/query-algebrizer/src/lib.rs index b25b221d..f81e55e8 100644 --- a/query-algebrizer/src/lib.rs +++ b/query-algebrizer/src/lib.rs @@ -8,8 +8,6 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -extern crate enum_set; - #[macro_use] extern crate error_chain; @@ -58,7 +56,6 @@ pub use clauses::{ pub use types::{ EmptyBecause, - ValueTypeSet, }; #[derive(Debug)] @@ -81,7 +78,7 @@ impl AlgebraicQuery { /// Return a set of the input variables mentioned in the `:in` clause that have not yet been /// bound. We do this by looking at the CC. pub fn unbound_variables(&self) -> BTreeSet { - self.cc.input_variables.sub(&self.cc.value_bound_variables()) + self.cc.input_variables.sub(&self.cc.value_bound_variable_set()) } } diff --git a/query-algebrizer/src/types.rs b/query-algebrizer/src/types.rs index 970404a1..57132a99 100644 --- a/query-algebrizer/src/types.rs +++ b/query-algebrizer/src/types.rs @@ -15,15 +15,11 @@ use std::fmt::{ Result, }; -use enum_set::{ - CLike, - EnumSet, -}; - use mentat_core::{ Entid, TypedValue, ValueType, + ValueTypeSet, }; use mentat_query::{ @@ -540,135 +536,3 @@ impl Debug for EmptyBecause { } } } - -trait EnumSetExtensions { - /// Return a set containing both `x` and `y`. - fn of_both(x: T, y: T) -> EnumSet; - - /// Return a clone of `self` with `y` added. - fn with(&self, y: T) -> EnumSet; -} - -impl EnumSetExtensions for EnumSet { - /// Return a set containing both `x` and `y`. - fn of_both(x: T, y: T) -> Self { - let mut o = EnumSet::new(); - o.insert(x); - o.insert(y); - o - } - - /// Return a clone of `self` with `y` added. - fn with(&self, y: T) -> EnumSet { - let mut o = self.clone(); - o.insert(y); - o - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct ValueTypeSet(pub EnumSet); - -impl Default for ValueTypeSet { - fn default() -> ValueTypeSet { - ValueTypeSet::any() - } -} - -impl ValueTypeSet { - pub fn any() -> ValueTypeSet { - ValueTypeSet(ValueType::all_enums()) - } - - pub fn none() -> ValueTypeSet { - ValueTypeSet(EnumSet::new()) - } - - /// Return a set containing only `t`. - pub fn of_one(t: ValueType) -> ValueTypeSet { - let mut s = EnumSet::new(); - s.insert(t); - ValueTypeSet(s) - } - - /// Return a set containing `Double` and `Long`. - pub fn of_numeric_types() -> ValueTypeSet { - ValueTypeSet(EnumSet::of_both(ValueType::Double, ValueType::Long)) - } - - /// Return a set containing `Ref` and `Keyword`. - pub fn of_keywords() -> ValueTypeSet { - ValueTypeSet(EnumSet::of_both(ValueType::Ref, ValueType::Keyword)) - } - - /// Return a set containing `Ref` and `Long`. - pub fn of_longs() -> ValueTypeSet { - ValueTypeSet(EnumSet::of_both(ValueType::Ref, ValueType::Long)) - } -} - -impl ValueTypeSet { - pub fn insert(&mut self, vt: ValueType) -> bool { - self.0.insert(vt) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - /// Returns a set containing all the types in this set and `other`. - pub fn union(&self, other: &ValueTypeSet) -> ValueTypeSet { - ValueTypeSet(self.0.union(other.0)) - } - - pub fn intersection(&self, other: &ValueTypeSet) -> ValueTypeSet { - ValueTypeSet(self.0.intersection(other.0)) - } - - /// Return an arbitrary type that's part of this set. - /// For a set containing a single type, this will be that type. - pub fn exemplar(&self) -> Option { - self.0.iter().next() - } - - pub fn is_subset(&self, other: &ValueTypeSet) -> bool { - self.0.is_subset(&other.0) - } - - pub fn contains(&self, vt: ValueType) -> bool { - self.0.contains(&vt) - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub fn is_unit(&self) -> bool { - self.0.len() == 1 - } -} - -impl IntoIterator for ValueTypeSet { - type Item = ValueType; - type IntoIter = ::enum_set::Iter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl ::std::iter::FromIterator for ValueTypeSet { - fn from_iter>(iterator: I) -> Self { - let mut ret = Self::none(); - ret.0.extend(iterator); - ret - } -} - -impl ::std::iter::Extend for ValueTypeSet { - fn extend>(&mut self, iter: I) { - for element in iter { - self.0.insert(element); - } - } -} diff --git a/query-algebrizer/tests/predicate.rs b/query-algebrizer/tests/predicate.rs index 4c34d4d9..2ecf74ed 100644 --- a/query-algebrizer/tests/predicate.rs +++ b/query-algebrizer/tests/predicate.rs @@ -18,6 +18,7 @@ use mentat_core::{ Entid, Schema, ValueType, + ValueTypeSet, }; use mentat_query_parser::{ @@ -35,7 +36,6 @@ use mentat_query_algebrizer::{ EmptyBecause, Error, ErrorKind, - ValueTypeSet, algebrize, }; diff --git a/query-parser/src/parse.rs b/query-parser/src/parse.rs index f671489c..79614105 100644 --- a/query-parser/src/parse.rs +++ b/query-parser/src/parse.rs @@ -54,7 +54,7 @@ use self::mentat_query::{ PatternNonValuePlace, PatternValuePlace, Predicate, - PredicateFn, + QueryFunction, SrcVar, UnifyVars, Variable, @@ -132,8 +132,8 @@ def_parser!(Query, source_var, SrcVar, { }); // TODO: interning. -def_parser!(Query, predicate_fn, PredicateFn, { - satisfy_map(PredicateFn::from_value) +def_parser!(Query, query_function, QueryFunction, { + satisfy_map(QueryFunction::from_value) }); def_parser!(Query, fn_arg, FnArg, { @@ -266,13 +266,17 @@ def_parser!(Where, not_join_clause, WhereClause, { })) }); +def_parser!(Query, func, (QueryFunction, Vec), { + (Query::query_function(), Query::arguments()) +}); + /// A vector containing just a parenthesized filter expression. def_parser!(Where, pred, WhereClause, { // Accept either a nested list or a nested vector here: // `[(foo ?x ?y)]` or `[[foo ?x ?y]]` vector() .of_exactly(seq() - .of_exactly((Query::predicate_fn(), Query::arguments()) + .of_exactly(Query::func() .map(|(f, args)| { WhereClause::Pred( Predicate { @@ -289,7 +293,7 @@ def_parser!(Where, where_fn, WhereClause, { vector() .of_exactly( (seq().of_exactly( - (Query::predicate_fn(), Query::arguments())), + Query::func()), Bind::binding()) .map(|((f, args), binding)| { WhereClause::WhereFn( @@ -370,21 +374,22 @@ def_matches_plain_symbol!(Find, ellipsis, "..."); def_matches_plain_symbol!(Find, placeholder, "_"); +def_parser!(Find, elem, Element, { + Query::variable().map(Element::Variable) +}); + def_parser!(Find, find_scalar, FindSpec, { - Query::variable() - .skip(Find::period()) - .map(|var| FindSpec::FindScalar(Element::Variable(var))) + Find::elem().skip(Find::period()) + .map(FindSpec::FindScalar) }); def_parser!(Find, find_coll, FindSpec, { - vector() - .of_exactly(Query::variable() - .skip(Find::ellipsis())) - .map(|var| FindSpec::FindColl(Element::Variable(var))) + vector().of_exactly(Find::elem().skip(Find::ellipsis())) + .map(FindSpec::FindColl) }); def_parser!(Find, elements, Vec, { - many1::, _>(Query::variable().map(Element::Variable)) + many1::, _>(Find::elem()) }); def_parser!(Find, find_rel, FindSpec, { @@ -392,8 +397,8 @@ def_parser!(Find, find_rel, FindSpec, { }); def_parser!(Find, find_tuple, FindSpec, { - vector() - .of_exactly(Find::elements().map(FindSpec::FindTuple)) + vector().of_exactly(Find::elements()) + .map(FindSpec::FindTuple) }); /// Parse a stream of values into one of four find specs. @@ -462,7 +467,7 @@ def_parser!(Find, query, FindQuery, { Ok(FindQuery { default_source: SrcVar::DefaultSrc, - find_spec: find_spec.clone().ok_or(combine::primitives::Error::Unexpected("expected :find".into()))?, + find_spec: find_spec.ok_or(combine::primitives::Error::Unexpected("expected :find".into()))?, in_sources: BTreeSet::default(), // TODO in_vars: in_vars, limit: limit, diff --git a/query-projector/src/lib.rs b/query-projector/src/lib.rs index b9922731..f7e963f0 100644 --- a/query-projector/src/lib.rs +++ b/query-projector/src/lib.rs @@ -27,8 +27,11 @@ use rusqlite::{ use mentat_core::{ SQLValueType, + SQLValueTypeSet, TypedValue, ValueType, + ValueTypeTag, + ValueTypeSet, }; use mentat_db::{ @@ -121,7 +124,6 @@ impl QueryResults { } type Index = i32; // See rusqlite::RowIndex. -type ValueTypeTag = i32; enum TypedIndex { Known(Index, ValueTypeTag), Unknown(Index, Index), @@ -211,7 +213,7 @@ pub fn projected_column_for_var(var: &Variable, cc: &ConjoiningClauses) -> (Proj fn project_elements<'a, I: IntoIterator>( count: usize, elements: I, - query: &AlgebraicQuery) -> (Projection, Vec) { + query: &AlgebraicQuery) -> Result<(Projection, Vec)> { let mut cols = Vec::with_capacity(count); let mut i: i32 = 0; @@ -257,7 +259,7 @@ fn project_elements<'a, I: IntoIterator>( } } - (Projection::Columns(cols), templates) + Ok((Projection::Columns(cols), templates)) } pub trait Projector { @@ -293,13 +295,13 @@ impl ScalarProjector { } } - fn combine(sql: Projection, mut templates: Vec) -> CombinedProjection { + fn combine(sql: Projection, mut templates: Vec) -> Result { let template = templates.pop().expect("Expected a single template"); - CombinedProjection { + Ok(CombinedProjection { sql_projection: sql, datalog_projector: Box::new(ScalarProjector::with_template(template)), distinct: false, - } + }) } } @@ -338,13 +340,13 @@ impl TupleProjector { .collect::>>() } - fn combine(column_count: usize, sql: Projection, templates: Vec) -> CombinedProjection { + fn combine(column_count: usize, sql: Projection, templates: Vec) -> Result { let p = TupleProjector::with_templates(column_count, templates); - CombinedProjection { + Ok(CombinedProjection { sql_projection: sql, datalog_projector: Box::new(p), distinct: false, - } + }) } } @@ -386,13 +388,13 @@ impl RelProjector { .collect::>>() } - fn combine(column_count: usize, sql: Projection, templates: Vec) -> CombinedProjection { + fn combine(column_count: usize, sql: Projection, templates: Vec) -> Result { let p = RelProjector::with_templates(column_count, templates); - CombinedProjection { + Ok(CombinedProjection { sql_projection: sql, datalog_projector: Box::new(p), distinct: true, - } + }) } } @@ -421,13 +423,13 @@ impl CollProjector { } } - fn combine(sql: Projection, mut templates: Vec) -> CombinedProjection { + fn combine(sql: Projection, mut templates: Vec) -> Result { let template = templates.pop().expect("Expected a single template"); - CombinedProjection { + Ok(CombinedProjection { sql_projection: sql, datalog_projector: Box::new(CollProjector::with_template(template)), distinct: true, - } + }) } } @@ -475,39 +477,39 @@ impl CombinedProjection { /// - The bindings established by the topmost CC. /// - The types known at algebrizing time. /// - The types extracted from the store for unknown attributes. -pub fn query_projection(query: &AlgebraicQuery) -> CombinedProjection { +pub fn query_projection(query: &AlgebraicQuery) -> Result { use self::FindSpec::*; if query.is_known_empty() { // Do a few gyrations to produce empty results of the right kind for the query. let empty = QueryResults::empty_factory(&query.find_spec); let constant_projector = ConstantProjector::new(empty); - CombinedProjection { + Ok(CombinedProjection { sql_projection: Projection::One, datalog_projector: Box::new(constant_projector), distinct: false, - } + }) } else { match query.find_spec { FindColl(ref element) => { - let (cols, templates) = project_elements(1, iter::once(element), query); - CollProjector::combine(cols, templates).flip_distinct_for_limit(&query.limit) + let (cols, templates) = project_elements(1, iter::once(element), query)?; + CollProjector::combine(cols, templates).map(|p| p.flip_distinct_for_limit(&query.limit)) }, FindScalar(ref element) => { - let (cols, templates) = project_elements(1, iter::once(element), query); + let (cols, templates) = project_elements(1, iter::once(element), query)?; ScalarProjector::combine(cols, templates) }, FindRel(ref elements) => { let column_count = query.find_spec.expected_column_count(); - let (cols, templates) = project_elements(column_count, elements, query); - RelProjector::combine(column_count, cols, templates).flip_distinct_for_limit(&query.limit) + let (cols, templates) = project_elements(column_count, elements, query)?; + RelProjector::combine(column_count, cols, templates).map(|p| p.flip_distinct_for_limit(&query.limit)) }, FindTuple(ref elements) => { let column_count = query.find_spec.expected_column_count(); - let (cols, templates) = project_elements(column_count, elements, query); + let (cols, templates) = project_elements(column_count, elements, query)?; TupleProjector::combine(column_count, cols, templates) }, } diff --git a/query-translator/Cargo.toml b/query-translator/Cargo.toml index e2a8f141..1edc8728 100644 --- a/query-translator/Cargo.toml +++ b/query-translator/Cargo.toml @@ -4,6 +4,8 @@ version = "0.0.1" workspace = ".." [dependencies] +error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" } + [dependencies.mentat_core] path = "../core" diff --git a/query-translator/src/lib.rs b/query-translator/src/lib.rs index 30a8395c..003e187a 100644 --- a/query-translator/src/lib.rs +++ b/query-translator/src/lib.rs @@ -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. +#[macro_use] +extern crate error_chain; extern crate mentat_core; extern crate mentat_query; extern crate mentat_query_algebrizer; @@ -25,3 +27,16 @@ pub use translate::{ cc_to_exists, query_to_select, }; + +error_chain! { + types { + Error, ErrorKind, ResultExt, Result; + } + + foreign_links { + } + + links { + ProjectorError(mentat_query_projector::Error, mentat_query_projector::ErrorKind); + } +} diff --git a/query-translator/src/translate.rs b/query-translator/src/translate.rs index 52170413..47019a1e 100644 --- a/query-translator/src/translate.rs +++ b/query-translator/src/translate.rs @@ -10,6 +10,7 @@ use mentat_core::{ SQLValueType, + SQLValueTypeSet, TypedValue, ValueType, }; @@ -55,6 +56,8 @@ use mentat_query_sql::{ Values, }; +use super::Result; + trait ToConstraint { fn to_constraint(self) -> Constraint; } @@ -329,12 +332,12 @@ pub fn cc_to_exists(cc: ConjoiningClauses) -> SelectQuery { /// Consume a provided `AlgebraicQuery` to yield a new /// `ProjectedSelect`. -pub fn query_to_select(query: AlgebraicQuery) -> ProjectedSelect { +pub fn query_to_select(query: AlgebraicQuery) -> Result { // TODO: we can't pass `query.limit` here if we aggregate during projection. // SQL-based aggregation -- `SELECT SUM(datoms00.e)` -- is fine. - let CombinedProjection { sql_projection, datalog_projector, distinct } = query_projection(&query); - ProjectedSelect { + let CombinedProjection { sql_projection, datalog_projector, distinct } = query_projection(&query)?; + Ok(ProjectedSelect { query: cc_to_select_query(sql_projection, query.cc, distinct, query.order, query.limit), projector: datalog_projector, - } + }) } diff --git a/query-translator/tests/translate.rs b/query-translator/tests/translate.rs index fb9d0aec..605a4e71 100644 --- a/query-translator/tests/translate.rs +++ b/query-translator/tests/translate.rs @@ -54,9 +54,9 @@ fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) { } fn translate_with_inputs(schema: &Schema, query: &'static str, inputs: QueryInputs) -> SQLQuery { - let parsed = parse_find_string(query).expect("parse failed"); - let algebrized = algebrize_with_inputs(schema, parsed, 0, inputs).expect("algebrize failed"); - let select = query_to_select(algebrized); + let parsed = parse_find_string(query).expect("parse to succeed"); + let algebrized = algebrize_with_inputs(schema, parsed, 0, inputs).expect("algebrize to succeed"); + let select = query_to_select(algebrized).expect("translate to succeed"); select.query.to_sql_query().unwrap() } @@ -191,7 +191,7 @@ fn test_bound_variable_limit_affects_types() { assert_eq!(Some(ValueType::Long), algebrized.cc.known_type(&Variable::from_valid_name("?limit"))); - let select = query_to_select(algebrized); + let select = query_to_select(algebrized).expect("query to translate"); let SQLQuery { sql, args } = select.query.to_sql_query().unwrap(); // TODO: this query isn't actually correct -- we don't yet algebrize for variables that are @@ -281,7 +281,7 @@ fn test_unknown_ident() { assert!(algebrized.is_known_empty()); // If you insist… - let select = query_to_select(algebrized); + let select = query_to_select(algebrized).expect("query to translate"); let sql = select.query.to_sql_query().unwrap().sql; assert_eq!("SELECT 1 LIMIT 0", sql); } diff --git a/query/src/lib.rs b/query/src/lib.rs index 85703534..1dc7168b 100644 --- a/query/src/lib.rs +++ b/query/src/lib.rs @@ -126,23 +126,23 @@ impl fmt::Debug for Variable { } } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct PredicateFn(pub PlainSymbol); +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct QueryFunction(pub PlainSymbol); -impl FromValue for PredicateFn { - fn from_value(v: &edn::ValueAndSpan) -> Option { +impl FromValue for QueryFunction { + fn from_value(v: &edn::ValueAndSpan) -> Option { if let edn::SpannedValue::PlainSymbol(ref s) = v.inner { - PredicateFn::from_symbol(s) + QueryFunction::from_symbol(s) } else { None } } } -impl PredicateFn { - pub fn from_symbol(sym: &PlainSymbol) -> Option { +impl QueryFunction { + pub fn from_symbol(sym: &PlainSymbol) -> Option { // TODO: validate the acceptable set of function names. - Some(PredicateFn(sym.clone())) + Some(QueryFunction(sym.clone())) } } @@ -435,7 +435,7 @@ pub struct Aggregate { } */ -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq)] pub enum Element { Variable(Variable), // Aggregate(Aggregate), // TODO @@ -480,7 +480,7 @@ pub enum Limit { /// # } /// ``` /// -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq)] pub enum FindSpec { /// Returns an array of arrays. FindRel(Vec), @@ -775,7 +775,7 @@ pub enum WhereClause { } #[allow(dead_code)] -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq)] pub struct FindQuery { pub find_spec: FindSpec, pub default_source: SrcVar, diff --git a/src/errors.rs b/src/errors.rs index 96587dc8..17c9f35a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -19,6 +19,7 @@ use mentat_db; use mentat_query_algebrizer; use mentat_query_parser; use mentat_query_projector; +use mentat_query_translator; use mentat_sql; use mentat_tx_parser; @@ -37,6 +38,7 @@ error_chain! { QueryError(mentat_query_algebrizer::Error, mentat_query_algebrizer::ErrorKind); // Let's not leak the term 'algebrizer'. QueryParseError(mentat_query_parser::Error, mentat_query_parser::ErrorKind); ProjectorError(mentat_query_projector::Error, mentat_query_projector::ErrorKind); + TranslatorError(mentat_query_translator::Error, mentat_query_translator::ErrorKind); SqlError(mentat_sql::Error, mentat_sql::ErrorKind); TxParseError(mentat_tx_parser::Error, mentat_tx_parser::ErrorKind); } diff --git a/src/query.rs b/src/query.rs index 010f4485..da12f601 100644 --- a/src/query.rs +++ b/src/query.rs @@ -81,7 +81,7 @@ pub fn q_once<'sqlite, 'schema, 'query, T> if !unbound.is_empty() { bail!(ErrorKind::UnboundVariables(unbound.into_iter().map(|v| v.to_string()).collect())); } - let select = query_to_select(algebrized); + let select = query_to_select(algebrized)?; let SQLQuery { sql, args } = select.query.to_sql_query()?; let mut statement = sqlite.prepare(sql.as_str())?;