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;
|
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));
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())?;
|
||||||
|
|
Loading…
Reference in a new issue