Partial work from simple aggregates work (#497) r=nalexander

* Pre: make FindQuery, FindSpec, and Element non-Clone.
* Pre: make query translator return a Result.
* Pre: make projection return a Result.
* Pre: refactor query parser in preparation for parsing aggregates.
* Pre: rename PredicateFn -> QueryFunction.
* Pre: expose more about bound variables from CC.
* Pre: move ValueTypeSet to core.
This commit is contained in:
Richard Newman 2017-11-30 15:02:07 -08:00 committed by GitHub
parent 55588209c2
commit df90c366af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 289 additions and 218 deletions

View file

@ -18,7 +18,11 @@ extern crate edn;
pub mod values; pub mod values;
use std::collections::BTreeMap; use std::collections::{
BTreeMap,
BTreeSet,
};
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
@ -64,6 +68,8 @@ pub enum ValueType {
Uuid, Uuid,
} }
pub type ValueTypeTag = i32;
impl ValueType { impl ValueType {
pub fn all_enums() -> EnumSet<ValueType> { pub fn all_enums() -> EnumSet<ValueType> {
// TODO: lazy_static. // TODO: lazy_static.
@ -229,6 +235,178 @@ impl SQLValueType for ValueType {
} }
} }
trait EnumSetExtensions<T: enum_set::CLike + Clone> {
/// Return a set containing both `x` and `y`.
fn of_both(x: T, y: T) -> EnumSet<T>;
/// Return a clone of `self` with `y` added.
fn with(&self, y: T) -> EnumSet<T>;
}
impl<T: enum_set::CLike + Clone> EnumSetExtensions<T> for EnumSet<T> {
/// 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<T> {
let mut o = self.clone();
o.insert(y);
o
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ValueTypeSet(pub EnumSet<ValueType>);
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<ValueType> {
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<ValueType>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl ::std::iter::FromIterator<ValueType> for ValueTypeSet {
fn from_iter<I: IntoIterator<Item = ValueType>>(iterator: I) -> Self {
let mut ret = Self::none();
ret.0.extend(iterator);
ret
}
}
impl ::std::iter::Extend<ValueType> for ValueTypeSet {
fn extend<I: IntoIterator<Item = ValueType>>(&mut self, iter: I) {
for element in iter {
self.0.insert(element);
}
}
}
pub trait SQLValueTypeSet {
fn value_type_tags(&self) -> BTreeSet<ValueTypeTag>;
fn has_unique_type_code(&self) -> bool;
fn unique_type_code(&self) -> Option<ValueTypeTag>;
}
impl SQLValueTypeSet for ValueTypeSet {
// This is inefficient, but it'll do for now.
fn value_type_tags(&self) -> BTreeSet<ValueTypeTag> {
let mut out = BTreeSet::new();
for t in self.0.iter() {
out.insert(t.value_type_tag());
}
out
}
fn unique_type_code(&self) -> Option<ValueTypeTag> {
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] #[test]
fn test_typed_value() { fn test_typed_value() {
assert!(TypedValue::Boolean(false).is_congruent_with(None)); assert!(TypedValue::Boolean(false).is_congruent_with(None));

View file

@ -4,7 +4,6 @@ version = "0.0.1"
workspace = ".." workspace = ".."
[dependencies] [dependencies]
enum-set = { git = "https://github.com/rnewman/enum-set" }
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" } error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
[dependencies.mentat_core] [dependencies.mentat_core]

View file

@ -15,6 +15,7 @@ use mentat_core::{
SQLValueType, SQLValueType,
TypedValue, TypedValue,
ValueType, ValueType,
ValueTypeSet,
}; };
use mentat_query::{ use mentat_query::{
@ -34,7 +35,6 @@ use errors::{
use types::{ use types::{
EmptyBecause, EmptyBecause,
ValueTypeSet,
}; };
macro_rules! coerce_to_typed_value { macro_rules! coerce_to_typed_value {

View file

@ -12,6 +12,7 @@ use mentat_core::{
Schema, Schema,
TypedValue, TypedValue,
ValueType, ValueType,
ValueTypeSet,
}; };
use mentat_query::{ use mentat_query::{
@ -39,7 +40,6 @@ use types::{
ComputedTable, ComputedTable,
EmptyBecause, EmptyBecause,
SourceAlias, SourceAlias,
ValueTypeSet,
VariableColumn, VariableColumn,
}; };
@ -335,10 +335,6 @@ mod testing {
associate_ident, associate_ident,
}; };
use types::{
ValueTypeSet,
};
#[test] #[test]
fn test_apply_ground() { fn test_apply_ground() {
let vz = Variable::from_valid_name("?z"); let vz = Variable::from_valid_name("?z");

View file

@ -28,6 +28,7 @@ use mentat_core::{
Schema, Schema,
TypedValue, TypedValue,
ValueType, ValueType,
ValueTypeSet,
}; };
use mentat_core::counter::RcCounter; use mentat_core::counter::RcCounter;
@ -59,7 +60,6 @@ use types::{
QueryValue, QueryValue,
SourceAlias, SourceAlias,
TableAlias, TableAlias,
ValueTypeSet,
}; };
mod convert; // Converting args to values. mod convert; // Converting args to values.
@ -365,8 +365,17 @@ impl ConjoiningClauses {
self.value_bindings.get(var).cloned() 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<Variable, TypedValue> {
self.value_bindings.keys()
}
/// Return a set of the variables externally bound to values. /// Return a set of the variables externally bound to values.
pub fn value_bound_variables(&self) -> BTreeSet<Variable> { pub fn value_bound_variable_set(&self) -> BTreeSet<Variable> {
self.value_bindings.keys().cloned().collect() self.value_bindings.keys().cloned().collect()
} }

View file

@ -81,6 +81,7 @@ mod testing {
Attribute, Attribute,
TypedValue, TypedValue,
ValueType, ValueType,
ValueTypeSet,
}; };
use mentat_query::{ use mentat_query::{
@ -113,7 +114,6 @@ mod testing {
QualifiedAlias, QualifiedAlias,
QueryValue, QueryValue,
SourceAlias, SourceAlias,
ValueTypeSet,
}; };
use { use {

View file

@ -16,6 +16,7 @@ use std::collections::{
use mentat_core::{ use mentat_core::{
Schema, Schema,
ValueTypeSet,
}; };
use mentat_query::{ use mentat_query::{
@ -47,7 +48,6 @@ use types::{
EmptyBecause, EmptyBecause,
QualifiedAlias, QualifiedAlias,
SourceAlias, SourceAlias,
ValueTypeSet,
VariableColumn, VariableColumn,
}; };

View file

@ -294,6 +294,7 @@ mod testing {
use mentat_core::attribute::Unique; use mentat_core::attribute::Unique;
use mentat_core::{ use mentat_core::{
Attribute, Attribute,
ValueTypeSet,
}; };
use mentat_query::{ use mentat_query::{
@ -320,7 +321,6 @@ mod testing {
QualifiedAlias, QualifiedAlias,
QueryValue, QueryValue,
SourceAlias, SourceAlias,
ValueTypeSet,
}; };
use algebrize; use algebrize;

View file

@ -11,6 +11,7 @@
use mentat_core::{ use mentat_core::{
Schema, Schema,
ValueType, ValueType,
ValueTypeSet,
}; };
use mentat_query::{ use mentat_query::{
@ -31,7 +32,6 @@ use types::{
ColumnConstraint, ColumnConstraint,
EmptyBecause, EmptyBecause,
Inequality, Inequality,
ValueTypeSet,
}; };
/// Application of predicates. /// Application of predicates.
@ -177,7 +177,6 @@ mod testing {
ColumnConstraint, ColumnConstraint,
EmptyBecause, EmptyBecause,
QueryValue, QueryValue,
ValueTypeSet,
}; };
#[test] #[test]

View file

@ -8,8 +8,6 @@
// CONDITIONS OF ANY KIND, either express or implied. See the License for the // CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License. // specific language governing permissions and limitations under the License.
extern crate enum_set;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
@ -58,7 +56,6 @@ pub use clauses::{
pub use types::{ pub use types::{
EmptyBecause, EmptyBecause,
ValueTypeSet,
}; };
#[derive(Debug)] #[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 /// 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. /// bound. We do this by looking at the CC.
pub fn unbound_variables(&self) -> BTreeSet<Variable> { pub fn unbound_variables(&self) -> BTreeSet<Variable> {
self.cc.input_variables.sub(&self.cc.value_bound_variables()) self.cc.input_variables.sub(&self.cc.value_bound_variable_set())
} }
} }

View file

@ -15,15 +15,11 @@ use std::fmt::{
Result, Result,
}; };
use enum_set::{
CLike,
EnumSet,
};
use mentat_core::{ use mentat_core::{
Entid, Entid,
TypedValue, TypedValue,
ValueType, ValueType,
ValueTypeSet,
}; };
use mentat_query::{ use mentat_query::{
@ -540,135 +536,3 @@ impl Debug for EmptyBecause {
} }
} }
} }
trait EnumSetExtensions<T: CLike + Clone> {
/// Return a set containing both `x` and `y`.
fn of_both(x: T, y: T) -> EnumSet<T>;
/// Return a clone of `self` with `y` added.
fn with(&self, y: T) -> EnumSet<T>;
}
impl<T: CLike + Clone> EnumSetExtensions<T> for EnumSet<T> {
/// 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<T> {
let mut o = self.clone();
o.insert(y);
o
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ValueTypeSet(pub EnumSet<ValueType>);
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<ValueType> {
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<ValueType>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl ::std::iter::FromIterator<ValueType> for ValueTypeSet {
fn from_iter<I: IntoIterator<Item = ValueType>>(iterator: I) -> Self {
let mut ret = Self::none();
ret.0.extend(iterator);
ret
}
}
impl ::std::iter::Extend<ValueType> for ValueTypeSet {
fn extend<I: IntoIterator<Item = ValueType>>(&mut self, iter: I) {
for element in iter {
self.0.insert(element);
}
}
}

View file

@ -18,6 +18,7 @@ use mentat_core::{
Entid, Entid,
Schema, Schema,
ValueType, ValueType,
ValueTypeSet,
}; };
use mentat_query_parser::{ use mentat_query_parser::{
@ -35,7 +36,6 @@ use mentat_query_algebrizer::{
EmptyBecause, EmptyBecause,
Error, Error,
ErrorKind, ErrorKind,
ValueTypeSet,
algebrize, algebrize,
}; };

View file

@ -54,7 +54,7 @@ use self::mentat_query::{
PatternNonValuePlace, PatternNonValuePlace,
PatternValuePlace, PatternValuePlace,
Predicate, Predicate,
PredicateFn, QueryFunction,
SrcVar, SrcVar,
UnifyVars, UnifyVars,
Variable, Variable,
@ -132,8 +132,8 @@ def_parser!(Query, source_var, SrcVar, {
}); });
// TODO: interning. // TODO: interning.
def_parser!(Query, predicate_fn, PredicateFn, { def_parser!(Query, query_function, QueryFunction, {
satisfy_map(PredicateFn::from_value) satisfy_map(QueryFunction::from_value)
}); });
def_parser!(Query, fn_arg, FnArg, { def_parser!(Query, fn_arg, FnArg, {
@ -266,13 +266,17 @@ def_parser!(Where, not_join_clause, WhereClause, {
})) }))
}); });
def_parser!(Query, func, (QueryFunction, Vec<FnArg>), {
(Query::query_function(), Query::arguments())
});
/// A vector containing just a parenthesized filter expression. /// A vector containing just a parenthesized filter expression.
def_parser!(Where, pred, WhereClause, { def_parser!(Where, pred, WhereClause, {
// Accept either a nested list or a nested vector here: // Accept either a nested list or a nested vector here:
// `[(foo ?x ?y)]` or `[[foo ?x ?y]]` // `[(foo ?x ?y)]` or `[[foo ?x ?y]]`
vector() vector()
.of_exactly(seq() .of_exactly(seq()
.of_exactly((Query::predicate_fn(), Query::arguments()) .of_exactly(Query::func()
.map(|(f, args)| { .map(|(f, args)| {
WhereClause::Pred( WhereClause::Pred(
Predicate { Predicate {
@ -289,7 +293,7 @@ def_parser!(Where, where_fn, WhereClause, {
vector() vector()
.of_exactly( .of_exactly(
(seq().of_exactly( (seq().of_exactly(
(Query::predicate_fn(), Query::arguments())), Query::func()),
Bind::binding()) Bind::binding())
.map(|((f, args), binding)| { .map(|((f, args), binding)| {
WhereClause::WhereFn( WhereClause::WhereFn(
@ -370,21 +374,22 @@ def_matches_plain_symbol!(Find, ellipsis, "...");
def_matches_plain_symbol!(Find, placeholder, "_"); def_matches_plain_symbol!(Find, placeholder, "_");
def_parser!(Find, elem, Element, {
Query::variable().map(Element::Variable)
});
def_parser!(Find, find_scalar, FindSpec, { def_parser!(Find, find_scalar, FindSpec, {
Query::variable() Find::elem().skip(Find::period())
.skip(Find::period()) .map(FindSpec::FindScalar)
.map(|var| FindSpec::FindScalar(Element::Variable(var)))
}); });
def_parser!(Find, find_coll, FindSpec, { def_parser!(Find, find_coll, FindSpec, {
vector() vector().of_exactly(Find::elem().skip(Find::ellipsis()))
.of_exactly(Query::variable() .map(FindSpec::FindColl)
.skip(Find::ellipsis()))
.map(|var| FindSpec::FindColl(Element::Variable(var)))
}); });
def_parser!(Find, elements, Vec<Element>, { def_parser!(Find, elements, Vec<Element>, {
many1::<Vec<Element>, _>(Query::variable().map(Element::Variable)) many1::<Vec<Element>, _>(Find::elem())
}); });
def_parser!(Find, find_rel, FindSpec, { def_parser!(Find, find_rel, FindSpec, {
@ -392,8 +397,8 @@ def_parser!(Find, find_rel, FindSpec, {
}); });
def_parser!(Find, find_tuple, FindSpec, { def_parser!(Find, find_tuple, FindSpec, {
vector() vector().of_exactly(Find::elements())
.of_exactly(Find::elements().map(FindSpec::FindTuple)) .map(FindSpec::FindTuple)
}); });
/// Parse a stream of values into one of four find specs. /// Parse a stream of values into one of four find specs.
@ -462,7 +467,7 @@ def_parser!(Find, query, FindQuery, {
Ok(FindQuery { Ok(FindQuery {
default_source: SrcVar::DefaultSrc, 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_sources: BTreeSet::default(), // TODO
in_vars: in_vars, in_vars: in_vars,
limit: limit, limit: limit,

View file

@ -27,8 +27,11 @@ use rusqlite::{
use mentat_core::{ use mentat_core::{
SQLValueType, SQLValueType,
SQLValueTypeSet,
TypedValue, TypedValue,
ValueType, ValueType,
ValueTypeTag,
ValueTypeSet,
}; };
use mentat_db::{ use mentat_db::{
@ -121,7 +124,6 @@ impl QueryResults {
} }
type Index = i32; // See rusqlite::RowIndex. type Index = i32; // See rusqlite::RowIndex.
type ValueTypeTag = i32;
enum TypedIndex { enum TypedIndex {
Known(Index, ValueTypeTag), Known(Index, ValueTypeTag),
Unknown(Index, Index), Unknown(Index, Index),
@ -211,7 +213,7 @@ pub fn projected_column_for_var(var: &Variable, cc: &ConjoiningClauses) -> (Proj
fn project_elements<'a, I: IntoIterator<Item = &'a Element>>( fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
count: usize, count: usize,
elements: I, elements: I,
query: &AlgebraicQuery) -> (Projection, Vec<TypedIndex>) { query: &AlgebraicQuery) -> Result<(Projection, Vec<TypedIndex>)> {
let mut cols = Vec::with_capacity(count); let mut cols = Vec::with_capacity(count);
let mut i: i32 = 0; let mut i: i32 = 0;
@ -257,7 +259,7 @@ fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
} }
} }
(Projection::Columns(cols), templates) Ok((Projection::Columns(cols), templates))
} }
pub trait Projector { pub trait Projector {
@ -293,13 +295,13 @@ impl ScalarProjector {
} }
} }
fn combine(sql: Projection, mut templates: Vec<TypedIndex>) -> CombinedProjection { fn combine(sql: Projection, mut templates: Vec<TypedIndex>) -> Result<CombinedProjection> {
let template = templates.pop().expect("Expected a single template"); let template = templates.pop().expect("Expected a single template");
CombinedProjection { Ok(CombinedProjection {
sql_projection: sql, sql_projection: sql,
datalog_projector: Box::new(ScalarProjector::with_template(template)), datalog_projector: Box::new(ScalarProjector::with_template(template)),
distinct: false, distinct: false,
} })
} }
} }
@ -338,13 +340,13 @@ impl TupleProjector {
.collect::<Result<Vec<TypedValue>>>() .collect::<Result<Vec<TypedValue>>>()
} }
fn combine(column_count: usize, sql: Projection, templates: Vec<TypedIndex>) -> CombinedProjection { fn combine(column_count: usize, sql: Projection, templates: Vec<TypedIndex>) -> Result<CombinedProjection> {
let p = TupleProjector::with_templates(column_count, templates); let p = TupleProjector::with_templates(column_count, templates);
CombinedProjection { Ok(CombinedProjection {
sql_projection: sql, sql_projection: sql,
datalog_projector: Box::new(p), datalog_projector: Box::new(p),
distinct: false, distinct: false,
} })
} }
} }
@ -386,13 +388,13 @@ impl RelProjector {
.collect::<Result<Vec<TypedValue>>>() .collect::<Result<Vec<TypedValue>>>()
} }
fn combine(column_count: usize, sql: Projection, templates: Vec<TypedIndex>) -> CombinedProjection { fn combine(column_count: usize, sql: Projection, templates: Vec<TypedIndex>) -> Result<CombinedProjection> {
let p = RelProjector::with_templates(column_count, templates); let p = RelProjector::with_templates(column_count, templates);
CombinedProjection { Ok(CombinedProjection {
sql_projection: sql, sql_projection: sql,
datalog_projector: Box::new(p), datalog_projector: Box::new(p),
distinct: true, distinct: true,
} })
} }
} }
@ -421,13 +423,13 @@ impl CollProjector {
} }
} }
fn combine(sql: Projection, mut templates: Vec<TypedIndex>) -> CombinedProjection { fn combine(sql: Projection, mut templates: Vec<TypedIndex>) -> Result<CombinedProjection> {
let template = templates.pop().expect("Expected a single template"); let template = templates.pop().expect("Expected a single template");
CombinedProjection { Ok(CombinedProjection {
sql_projection: sql, sql_projection: sql,
datalog_projector: Box::new(CollProjector::with_template(template)), datalog_projector: Box::new(CollProjector::with_template(template)),
distinct: true, distinct: true,
} })
} }
} }
@ -475,39 +477,39 @@ impl CombinedProjection {
/// - The bindings established by the topmost CC. /// - The bindings established by the topmost CC.
/// - The types known at algebrizing time. /// - The types known at algebrizing time.
/// - The types extracted from the store for unknown attributes. /// - The types extracted from the store for unknown attributes.
pub fn query_projection(query: &AlgebraicQuery) -> CombinedProjection { pub fn query_projection(query: &AlgebraicQuery) -> Result<CombinedProjection> {
use self::FindSpec::*; use self::FindSpec::*;
if query.is_known_empty() { if query.is_known_empty() {
// Do a few gyrations to produce empty results of the right kind for the query. // Do a few gyrations to produce empty results of the right kind for the query.
let empty = QueryResults::empty_factory(&query.find_spec); let empty = QueryResults::empty_factory(&query.find_spec);
let constant_projector = ConstantProjector::new(empty); let constant_projector = ConstantProjector::new(empty);
CombinedProjection { Ok(CombinedProjection {
sql_projection: Projection::One, sql_projection: Projection::One,
datalog_projector: Box::new(constant_projector), datalog_projector: Box::new(constant_projector),
distinct: false, distinct: false,
} })
} else { } else {
match query.find_spec { match query.find_spec {
FindColl(ref element) => { FindColl(ref element) => {
let (cols, templates) = project_elements(1, iter::once(element), query); let (cols, templates) = project_elements(1, iter::once(element), query)?;
CollProjector::combine(cols, templates).flip_distinct_for_limit(&query.limit) CollProjector::combine(cols, templates).map(|p| p.flip_distinct_for_limit(&query.limit))
}, },
FindScalar(ref element) => { 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) ScalarProjector::combine(cols, templates)
}, },
FindRel(ref elements) => { FindRel(ref elements) => {
let column_count = query.find_spec.expected_column_count(); 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)?;
RelProjector::combine(column_count, cols, templates).flip_distinct_for_limit(&query.limit) RelProjector::combine(column_count, cols, templates).map(|p| p.flip_distinct_for_limit(&query.limit))
}, },
FindTuple(ref elements) => { FindTuple(ref elements) => {
let column_count = query.find_spec.expected_column_count(); 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) TupleProjector::combine(column_count, cols, templates)
}, },
} }

View file

@ -4,6 +4,8 @@ version = "0.0.1"
workspace = ".." workspace = ".."
[dependencies] [dependencies]
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
[dependencies.mentat_core] [dependencies.mentat_core]
path = "../core" path = "../core"

View file

@ -8,6 +8,8 @@
// CONDITIONS OF ANY KIND, either express or implied. See the License for the // CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License. // specific language governing permissions and limitations under the License.
#[macro_use]
extern crate error_chain;
extern crate mentat_core; extern crate mentat_core;
extern crate mentat_query; extern crate mentat_query;
extern crate mentat_query_algebrizer; extern crate mentat_query_algebrizer;
@ -25,3 +27,16 @@ pub use translate::{
cc_to_exists, cc_to_exists,
query_to_select, query_to_select,
}; };
error_chain! {
types {
Error, ErrorKind, ResultExt, Result;
}
foreign_links {
}
links {
ProjectorError(mentat_query_projector::Error, mentat_query_projector::ErrorKind);
}
}

View file

@ -10,6 +10,7 @@
use mentat_core::{ use mentat_core::{
SQLValueType, SQLValueType,
SQLValueTypeSet,
TypedValue, TypedValue,
ValueType, ValueType,
}; };
@ -55,6 +56,8 @@ use mentat_query_sql::{
Values, Values,
}; };
use super::Result;
trait ToConstraint { trait ToConstraint {
fn to_constraint(self) -> Constraint; 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 /// Consume a provided `AlgebraicQuery` to yield a new
/// `ProjectedSelect`. /// `ProjectedSelect`.
pub fn query_to_select(query: AlgebraicQuery) -> ProjectedSelect { pub fn query_to_select(query: AlgebraicQuery) -> Result<ProjectedSelect> {
// TODO: we can't pass `query.limit` here if we aggregate during projection. // TODO: we can't pass `query.limit` here if we aggregate during projection.
// SQL-based aggregation -- `SELECT SUM(datoms00.e)` -- is fine. // SQL-based aggregation -- `SELECT SUM(datoms00.e)` -- is fine.
let CombinedProjection { sql_projection, datalog_projector, distinct } = query_projection(&query); let CombinedProjection { sql_projection, datalog_projector, distinct } = query_projection(&query)?;
ProjectedSelect { Ok(ProjectedSelect {
query: cc_to_select_query(sql_projection, query.cc, distinct, query.order, query.limit), query: cc_to_select_query(sql_projection, query.cc, distinct, query.order, query.limit),
projector: datalog_projector, projector: datalog_projector,
} })
} }

View file

@ -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 { fn translate_with_inputs(schema: &Schema, query: &'static str, inputs: QueryInputs) -> SQLQuery {
let parsed = parse_find_string(query).expect("parse failed"); let parsed = parse_find_string(query).expect("parse to succeed");
let algebrized = algebrize_with_inputs(schema, parsed, 0, inputs).expect("algebrize failed"); let algebrized = algebrize_with_inputs(schema, parsed, 0, inputs).expect("algebrize to succeed");
let select = query_to_select(algebrized); let select = query_to_select(algebrized).expect("translate to succeed");
select.query.to_sql_query().unwrap() select.query.to_sql_query().unwrap()
} }
@ -191,7 +191,7 @@ fn test_bound_variable_limit_affects_types() {
assert_eq!(Some(ValueType::Long), assert_eq!(Some(ValueType::Long),
algebrized.cc.known_type(&Variable::from_valid_name("?limit"))); 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(); 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 // 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()); assert!(algebrized.is_known_empty());
// If you insist… // 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; let sql = select.query.to_sql_query().unwrap().sql;
assert_eq!("SELECT 1 LIMIT 0", sql); assert_eq!("SELECT 1 LIMIT 0", sql);
} }

View file

@ -126,23 +126,23 @@ impl fmt::Debug for Variable {
} }
} }
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct PredicateFn(pub PlainSymbol); pub struct QueryFunction(pub PlainSymbol);
impl FromValue<PredicateFn> for PredicateFn { impl FromValue<QueryFunction> for QueryFunction {
fn from_value(v: &edn::ValueAndSpan) -> Option<PredicateFn> { fn from_value(v: &edn::ValueAndSpan) -> Option<QueryFunction> {
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner { if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
PredicateFn::from_symbol(s) QueryFunction::from_symbol(s)
} else { } else {
None None
} }
} }
} }
impl PredicateFn { impl QueryFunction {
pub fn from_symbol(sym: &PlainSymbol) -> Option<PredicateFn> { pub fn from_symbol(sym: &PlainSymbol) -> Option<QueryFunction> {
// TODO: validate the acceptable set of function names. // 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 { pub enum Element {
Variable(Variable), Variable(Variable),
// Aggregate(Aggregate), // TODO // Aggregate(Aggregate), // TODO
@ -480,7 +480,7 @@ pub enum Limit {
/// # } /// # }
/// ``` /// ```
/// ///
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub enum FindSpec { pub enum FindSpec {
/// Returns an array of arrays. /// Returns an array of arrays.
FindRel(Vec<Element>), FindRel(Vec<Element>),
@ -775,7 +775,7 @@ pub enum WhereClause {
} }
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct FindQuery { pub struct FindQuery {
pub find_spec: FindSpec, pub find_spec: FindSpec,
pub default_source: SrcVar, pub default_source: SrcVar,

View file

@ -19,6 +19,7 @@ use mentat_db;
use mentat_query_algebrizer; use mentat_query_algebrizer;
use mentat_query_parser; use mentat_query_parser;
use mentat_query_projector; use mentat_query_projector;
use mentat_query_translator;
use mentat_sql; use mentat_sql;
use mentat_tx_parser; 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'. 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); QueryParseError(mentat_query_parser::Error, mentat_query_parser::ErrorKind);
ProjectorError(mentat_query_projector::Error, mentat_query_projector::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); SqlError(mentat_sql::Error, mentat_sql::ErrorKind);
TxParseError(mentat_tx_parser::Error, mentat_tx_parser::ErrorKind); TxParseError(mentat_tx_parser::Error, mentat_tx_parser::ErrorKind);
} }

View file

@ -81,7 +81,7 @@ pub fn q_once<'sqlite, 'schema, 'query, T>
if !unbound.is_empty() { if !unbound.is_empty() {
bail!(ErrorKind::UnboundVariables(unbound.into_iter().map(|v| v.to_string()).collect())); 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 SQLQuery { sql, args } = select.query.to_sql_query()?;
let mut statement = sqlite.prepare(sql.as_str())?; let mut statement = sqlite.prepare(sql.as_str())?;