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:
parent
55588209c2
commit
df90c366af
21 changed files with 289 additions and 218 deletions
180
core/src/lib.rs
180
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<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));
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ mod testing {
|
|||
Attribute,
|
||||
TypedValue,
|
||||
ValueType,
|
||||
ValueTypeSet,
|
||||
};
|
||||
|
||||
use mentat_query::{
|
||||
|
@ -113,7 +114,6 @@ mod testing {
|
|||
QualifiedAlias,
|
||||
QueryValue,
|
||||
SourceAlias,
|
||||
ValueTypeSet,
|
||||
};
|
||||
|
||||
use {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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())?;
|
||||
|
|
Loading…
Reference in a new issue