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;
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<ValueType> {
// 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]
fn test_typed_value() {
assert!(TypedValue::Boolean(false).is_congruent_with(None));

View file

@ -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]

View file

@ -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 {

View file

@ -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");

View file

@ -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<Variable, TypedValue> {
self.value_bindings.keys()
}
/// 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()
}

View file

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

View file

@ -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,
};

View file

@ -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;

View file

@ -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]

View file

@ -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<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,
};
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<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,
Schema,
ValueType,
ValueTypeSet,
};
use mentat_query_parser::{
@ -35,7 +36,6 @@ use mentat_query_algebrizer::{
EmptyBecause,
Error,
ErrorKind,
ValueTypeSet,
algebrize,
};

View file

@ -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<FnArg>), {
(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<Element>, {
many1::<Vec<Element>, _>(Query::variable().map(Element::Variable))
many1::<Vec<Element>, _>(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,

View file

@ -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<Item = &'a Element>>(
count: usize,
elements: I,
query: &AlgebraicQuery) -> (Projection, Vec<TypedIndex>) {
query: &AlgebraicQuery) -> Result<(Projection, Vec<TypedIndex>)> {
let mut cols = Vec::with_capacity(count);
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 {
@ -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");
CombinedProjection {
Ok(CombinedProjection {
sql_projection: sql,
datalog_projector: Box::new(ScalarProjector::with_template(template)),
distinct: false,
}
})
}
}
@ -338,13 +340,13 @@ impl TupleProjector {
.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);
CombinedProjection {
Ok(CombinedProjection {
sql_projection: sql,
datalog_projector: Box::new(p),
distinct: false,
}
})
}
}
@ -386,13 +388,13 @@ impl RelProjector {
.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);
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<TypedIndex>) -> CombinedProjection {
fn combine(sql: Projection, mut templates: Vec<TypedIndex>) -> Result<CombinedProjection> {
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<CombinedProjection> {
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)
},
}

View file

@ -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"

View file

@ -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);
}
}

View file

@ -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<ProjectedSelect> {
// 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,
}
})
}

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 {
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);
}

View file

@ -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<PredicateFn> for PredicateFn {
fn from_value(v: &edn::ValueAndSpan) -> Option<PredicateFn> {
impl FromValue<QueryFunction> for QueryFunction {
fn from_value(v: &edn::ValueAndSpan) -> Option<QueryFunction> {
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<PredicateFn> {
impl QueryFunction {
pub fn from_symbol(sym: &PlainSymbol) -> Option<QueryFunction> {
// 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<Element>),
@ -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,

View file

@ -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);
}

View file

@ -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())?;