Parse queries with rust-peg. r=rnewman,grisha (#728)

This commit is contained in:
Nick Alexander 2018-06-04 15:46:13 -07:00
commit 3d5ae797b2
56 changed files with 1856 additions and 2857 deletions

View file

@ -58,9 +58,6 @@ path = "query"
[dependencies.mentat_query_algebrizer]
path = "query-algebrizer"
[dependencies.mentat_query_parser]
path = "query-parser"
[dependencies.mentat_query_projector]
path = "query-projector"
@ -73,9 +70,6 @@ path = "query-sql"
[dependencies.mentat_query_translator]
path = "query-translator"
[dependencies.mentat_tx]
path = "tx"
[dependencies.mentat_tolstoy]
path = "tolstoy"

View file

@ -133,12 +133,12 @@ To run tests use:
# Run tests for everything.
cargo test --all
# Run tests for just the query-parser folder (specify the crate, not the folder),
# Run tests for just the query-algebrizer folder (specify the crate, not the folder),
# printing debug output.
cargo test -p mentat_query_parser -- --nocapture
cargo test -p mentat_query_algebrizer -- --nocapture
````
For most `cargo` commands you can pass the `-p` argument to run the command just on that package. So, `cargo build -p mentat_query_parser` will build just the "query-parser" folder.
For most `cargo` commands you can pass the `-p` argument to run the command just on that package. So, `cargo build -p mentat_query_algebrizer` will build just the "query-algebrizer" folder.
## What are all of these crates?
@ -183,16 +183,8 @@ This crate defines the structs and enums that are the output of the query parser
Similarly, this crate defines an abstract representation of a SQL query as understood by Mentat. This bridges between Mentat's types (_e.g._, `TypedValue`) and SQL concepts (`ColumnOrExpression`, `GroupBy`). It's produced by the algebrizer and consumed by the translator.
#### `mentat_tx`
Mentat has two main inputs: reads (queries) and writes (transacts). Just as `mentat_query` defines the types produced by the query parser, `mentat_tx` defines the types produced by the tx parser.
### Query processing
#### `mentat_query_parser`
This is a `combine` parser that uses `mentat_parser_utils` and `mentat_query` to turn a stream of EDN values into a more usable representation of a query.
#### `mentat_query_algebrizer`
This is the biggest piece of the query engine. It takes a parsed query, which at this point is _independent of a database_, and combines it with the current state of the schema and data. This involves translating keywords into attributes, abstract values into concrete values with a known type, and producing an `AlgebraicQuery`, which is a representation of how a query's Datalog semantics can be satisfied as SQL table joins and constraints over Mentat's SQL schema. An algebrized query is tightly coupled with both the disk schema and the vocabulary present in the store when the work is done.

View file

@ -38,10 +38,18 @@ pub use chrono::{
};
pub use edn::{
Cloned,
FromMicros,
FromRc,
Keyword,
ToMicros,
Utc,
ValueRc,
};
pub use edn::parse::{
parse_query,
ParseError as EdnParseError,
};
pub use cache::{
@ -56,15 +64,12 @@ mod sql_types;
pub use types::{
Binding,
Cloned,
Entid,
FromRc,
KnownEntid,
StructuredMap,
TypedValue,
ValueType,
ValueTypeTag,
ValueRc,
now,
};

View file

@ -39,103 +39,16 @@ use ::indexmap::{
use ::edn::{
self,
Cloned,
FromMicros,
FromRc,
Keyword,
Utc,
ValueRc,
};
use values;
pub trait FromRc<T> {
fn from_rc(val: Rc<T>) -> Self;
fn from_arc(val: Arc<T>) -> Self;
}
impl<T> FromRc<T> for Rc<T> where T: Sized + Clone {
fn from_rc(val: Rc<T>) -> Self {
val.clone()
}
fn from_arc(val: Arc<T>) -> Self {
match ::std::sync::Arc::<T>::try_unwrap(val) {
Ok(v) => Self::new(v),
Err(r) => Self::new(r.cloned()),
}
}
}
impl<T> FromRc<T> for Arc<T> where T: Sized + Clone {
fn from_rc(val: Rc<T>) -> Self {
match ::std::rc::Rc::<T>::try_unwrap(val) {
Ok(v) => Self::new(v),
Err(r) => Self::new(r.cloned()),
}
}
fn from_arc(val: Arc<T>) -> Self {
val.clone()
}
}
impl<T> FromRc<T> for Box<T> where T: Sized + Clone {
fn from_rc(val: Rc<T>) -> Self {
match ::std::rc::Rc::<T>::try_unwrap(val) {
Ok(v) => Self::new(v),
Err(r) => Self::new(r.cloned()),
}
}
fn from_arc(val: Arc<T>) -> Self {
match ::std::sync::Arc::<T>::try_unwrap(val) {
Ok(v) => Self::new(v),
Err(r) => Self::new(r.cloned()),
}
}
}
// We do this a lot for errors.
pub trait Cloned<T> {
fn cloned(&self) -> T;
fn to_value_rc(&self) -> ValueRc<T>;
}
impl<T: Clone> Cloned<T> for Rc<T> where T: Sized + Clone {
fn cloned(&self) -> T {
(*self.as_ref()).clone()
}
fn to_value_rc(&self) -> ValueRc<T> {
ValueRc::from_rc(self.clone())
}
}
impl<T: Clone> Cloned<T> for Arc<T> where T: Sized + Clone {
fn cloned(&self) -> T {
(*self.as_ref()).clone()
}
fn to_value_rc(&self) -> ValueRc<T> {
ValueRc::from_arc(self.clone())
}
}
impl<T: Clone> Cloned<T> for Box<T> where T: Sized + Clone {
fn cloned(&self) -> T {
self.as_ref().clone()
}
fn to_value_rc(&self) -> ValueRc<T> {
ValueRc::new(self.cloned())
}
}
///
/// This type alias exists to allow us to use different boxing mechanisms for values.
/// This type must implement `FromRc` and `Cloned`, and a `From` implementation must exist for
/// `TypedValue`.
///
pub type ValueRc<T> = Arc<T>;
/// Represents one entid in the entid space.
///
/// Per https://www.sqlite.org/datatype3.html (see also http://stackoverflow.com/a/8499544), SQLite
@ -218,6 +131,24 @@ impl ValueType {
})
}
pub fn from_keyword(keyword: &Keyword) -> Option<Self> {
if keyword.namespace() != Some("db.type") {
return None;
}
return match keyword.name() {
"ref" => Some(ValueType::Ref),
"boolean" => Some(ValueType::Boolean),
"instant" => Some(ValueType::Instant),
"long" => Some(ValueType::Long),
"double" => Some(ValueType::Double),
"string" => Some(ValueType::String),
"keyword" => Some(ValueType::Keyword),
"uuid" => Some(ValueType::Uuid),
_ => None,
}
}
pub fn into_typed_value(self) -> TypedValue {
TypedValue::typed_ns_keyword("db.type", match self {
ValueType::Ref => "ref",

View file

@ -27,9 +27,6 @@ path = "../core"
[dependencies.mentat_sql]
path = "../sql"
[dependencies.mentat_tx]
path = "../tx"
# Should be dev-dependencies.
[dependencies.tabwriter]
version = "1.0.3"

View file

@ -16,7 +16,7 @@ use edn::types::Value;
use edn::symbols;
use entids;
use db::TypedSQLValue;
use mentat_tx::entities::Entity;
use edn::entities::Entity;
use mentat_core::{
IdentMap,
Schema,

View file

@ -97,7 +97,7 @@ use mentat_sql::{
SQLQuery,
};
use mentat_tx::entities::{
use edn::entities::{
OpType,
};

View file

@ -1096,7 +1096,7 @@ mod tests {
InternSet,
};
use mentat_core::util::Either::*;
use mentat_tx::entities::{
use edn::entities::{
OpType,
TempId,
};

View file

@ -31,7 +31,9 @@ use mentat_core::{
TypedValue,
ValueType,
};
use mentat_tx::entities::{Entid};
use edn::entities::{
Entid,
};
use schema::{
SchemaBuilding,
};

View file

@ -17,7 +17,7 @@ use std::collections::{
use rusqlite;
use mentat_tx::entities::{
use edn::entities::{
TempId,
};
use mentat_core::{

View file

@ -47,8 +47,8 @@ use types::{
TypedValue,
ValueType,
};
use mentat_tx::entities;
use mentat_tx::entities::{
use edn::entities;
use edn::entities::{
EntityPlace,
OpType,
TempId,

View file

@ -26,7 +26,6 @@ extern crate time;
#[macro_use] extern crate edn;
#[macro_use] extern crate mentat_core;
extern crate mentat_sql;
extern crate mentat_tx;
use std::iter::repeat;

View file

@ -103,8 +103,8 @@ use mentat_core::{
use mentat_core::intern_set::InternSet;
use mentat_tx::entities as entmod;
use mentat_tx::entities::{
use edn::entities as entmod;
use edn::entities::{
AttributePlace,
Entity,
OpType,

View file

@ -32,7 +32,7 @@ use mentat_core::{
TypedValue,
};
use mentat_tx::entities::{
use edn::entities::{
OpType,
};

View file

@ -29,7 +29,7 @@ pub use self::mentat_core::{
ValueType,
};
use mentat_tx::entities::{
use edn::entities::{
EntityPlace,
TempId,
};

View file

@ -45,7 +45,7 @@ use mentat_core::{
Schema,
TypedValue,
};
use mentat_tx::entities::OpType;
use edn::entities::OpType;
use schema::SchemaBuilding;
/// A "Simple upsert" that looks like [:db/add TEMPID a v], where a is :db.unique/identity.

View file

@ -23,7 +23,7 @@ use mentat_core::{
TypedValue,
};
use mentat_tx::entities::{
use edn::entities::{
OpType,
};

View file

@ -1,3 +1,4 @@
/* -*- comment-start: "//"; -*- */
/* vim: set filetype=rust.rustpeg */
// Copyright 2016 Mozilla
@ -24,6 +25,8 @@ use ordered_float::OrderedFloat;
use uuid::Uuid;
use entities::*;
use query;
use query::FromValue;
use symbols::*;
use types::{SpannedValue, Span, ValueAndSpan};
@ -155,12 +158,14 @@ pub symbol -> SpannedValue =
ns:( sns:$(symbol_namespace) namespace_separator { sns })?
n:$(plain_symbol_name)
{ SpannedValue::from_symbol(ns, n) }
/ #expected("symbol")
pub keyword -> SpannedValue =
keyword_prefix
ns:( sns:$(symbol_namespace) namespace_separator { sns })?
n:$(symbol_name)
{ SpannedValue::from_keyword(ns, n) }
/ #expected("keyword")
pub list -> SpannedValue = "(" __ v:(value)* __ ")"
{ SpannedValue::List(LinkedList::from_iter(v)) }
@ -188,6 +193,7 @@ pub value -> ValueAndSpan =
span: Span::new(start, end)
}
}
/ #expected("value")
atom -> ValueAndSpan
= v:value {? if v.is_atom() { Ok(v) } else { Err("expected atom") } }
@ -199,32 +205,56 @@ comment = #quiet<";" [^\r\n]* [\r\n]?>
__ = (whitespace / comment)*
// Transaction entity parser starts here.
pub op -> OpType
= ":db/add" { OpType::Add }
/ ":db/retract" { OpType::Retract }
raw_keyword -> Keyword
= keyword_prefix ns:$(symbol_namespace) namespace_separator n:$(symbol_name) { Keyword::namespaced(ns, n) }
raw_keyword -> Keyword =
keyword_prefix
ns:( sns:$(symbol_namespace) namespace_separator { sns })?
n:$(symbol_name) {
match ns {
Some(ns) => Keyword::namespaced(ns, n),
None => Keyword::plain(n),
}
}
/ #expected("keyword")
raw_forward_keyword -> Keyword
= v:raw_keyword {? if v.is_forward() { Ok(v) } else { Err("expected :forward/keyword") } }
= v:raw_keyword {? if v.is_forward() { Ok(v) } else { Err("expected :forward or :forward/keyword") } }
raw_backward_keyword -> Keyword
= v:raw_keyword {? if v.is_backward() { Ok(v) } else { Err("expected :backward/_keyword") } }
= v:raw_keyword {? if v.is_backward() { Ok(v) } else { Err("expected :_backword or :backward/_keyword") } }
raw_namespaced_keyword -> Keyword
= keyword_prefix ns:$(symbol_namespace) namespace_separator n:$(symbol_name) { Keyword::namespaced(ns, n) }
/ #expected("namespaced keyword")
raw_forward_namespaced_keyword -> Keyword
= v:raw_namespaced_keyword {? if v.is_forward() { Ok(v) } else { Err("expected namespaced :forward/keyword") } }
raw_backward_namespaced_keyword -> Keyword
= v:raw_namespaced_keyword {? if v.is_backward() { Ok(v) } else { Err("expected namespaced :backward/_keyword") } }
entid -> Entid
= v:( raw_basedinteger / raw_hexinteger / raw_octalinteger / raw_integer ) { Entid::Entid(v) }
/ v:raw_keyword { Entid::Ident(v) }
/ v:raw_namespaced_keyword { Entid::Ident(v) }
/ #expected("entid")
forward_entid -> Entid
= v:( raw_basedinteger / raw_hexinteger / raw_octalinteger / raw_integer ) { Entid::Entid(v) }
/ v:raw_forward_keyword { Entid::Ident(v) }
/ v:raw_forward_namespaced_keyword { Entid::Ident(v) }
/ #expected("forward entid")
backward_entid -> Entid
= v:raw_backward_keyword { Entid::Ident(v.to_reversed()) }
= v:raw_backward_namespaced_keyword { Entid::Ident(v.to_reversed()) }
/ #expected("backward entid")
lookup_ref -> LookupRef<ValueAndSpan>
= "(" __ "lookup-ref" __ a:(entid) __ v:(value) __ ")" { LookupRef { a: AttributePlace::Entid(a), v } }
/ #expected("lookup-ref")
tx_function -> TxFunction
= "(" __ n:$(symbol_name) __ ")" { TxFunction { op: PlainSymbol::plain(n) } }
@ -252,6 +282,210 @@ pub entity -> Entity<ValueAndSpan>
= __ "[" __ op:(op) __ e:(entity_place) __ a:(forward_entid) __ v:(value_place) __ "]" __ { Entity::AddOrRetract { op, e: e, a: AttributePlace::Entid(a), v: v } }
/ __ "[" __ op:(op) __ e:(value_place) __ a:(backward_entid) __ v:(entity_place) __ "]" __ { Entity::AddOrRetract { op, e: v, a: AttributePlace::Entid(a), v: e } }
/ __ map:map_notation __ { Entity::MapNotation(map) }
/ #expected("entity")
pub entities -> Vec<Entity<ValueAndSpan>>
= __ "[" __ es:(entity*) __ "]" __ { es }
// Query parser starts here.
//
// We expect every rule except the `raw_*` rules to eat whitespace
// (with `__`) at its start and finish. That means that every string
// pattern (say "[") should be bracketed on either side with either a
// whitespace-eating rule or an explicit whitespace eating `__`.
query_function -> query::QueryFunction
= __ n:$(symbol_name) __ {? query::QueryFunction::from_symbol(&PlainSymbol::plain(n)).ok_or("expected query function") }
fn_arg -> query::FnArg
= v:value {? query::FnArg::from_value(&v).ok_or("expected query function argument") }
/ __ "[" args:fn_arg+ "]" __ { query::FnArg::Vector(args) }
find_elem -> query::Element
= __ v:variable __ { query::Element::Variable(v) }
/ __ "(" __ "the" v:variable ")" __ { query::Element::Corresponding(v) }
/ __ "(" __ "pull" var:variable "[" patterns:pull_attribute+ "]" __ ")" __ { query::Element::Pull(query::Pull { var, patterns }) }
/ __ "(" func:query_function args:fn_arg* ")" __ { query::Element::Aggregate(query::Aggregate { func, args }) }
find_spec -> query::FindSpec
= f:find_elem "." __ { query::FindSpec::FindScalar(f) }
/ fs:find_elem+ { query::FindSpec::FindRel(fs) }
/ __ "[" f:find_elem __ "..." __ "]" __ { query::FindSpec::FindColl(f) }
/ __ "[" fs:find_elem+ "]" __ { query::FindSpec::FindTuple(fs) }
pull_attribute -> query::PullAttributeSpec
= __ "*" __ { query::PullAttributeSpec::Wildcard }
/ __ k:raw_forward_namespaced_keyword __ alias:(":as" __ alias:raw_forward_keyword __ { alias })? {
let attribute = query::PullConcreteAttribute::Ident(::std::rc::Rc::new(k));
let alias = alias.map(|alias| ::std::rc::Rc::new(alias));
query::PullAttributeSpec::Attribute(
query::NamedPullAttribute {
attribute,
alias: alias,
})
}
limit -> query::Limit
= __ v:variable __ { query::Limit::Variable(v) }
/ __ n:(raw_octalinteger / raw_hexinteger / raw_basedinteger / raw_integer) __ {?
if n > 0 {
Ok(query::Limit::Fixed(n as u64))
} else {
Err("expected positive integer")
}
}
order -> query::Order
= __ "(" __ "asc" v:variable ")" __ { query::Order(query::Direction::Ascending, v) }
/ __ "(" __ "desc" v:variable ")" __ { query::Order(query::Direction::Descending, v) }
/ v:variable { query::Order(query::Direction::Ascending, v) }
pattern_value_place -> query::PatternValuePlace
= v:value {? query::PatternValuePlace::from_value(&v).ok_or("expected pattern_value_place") }
pattern_non_value_place -> query::PatternNonValuePlace
= v:value {? query::PatternNonValuePlace::from_value(&v).ok_or("expected pattern_non_value_place") }
pattern -> query::WhereClause
= __ "["
src:src_var?
e:pattern_non_value_place
a:pattern_non_value_place
v:pattern_value_place?
tx:pattern_non_value_place?
"]" __
{?
let v = v.unwrap_or(query::PatternValuePlace::Placeholder);
let tx = tx.unwrap_or(query::PatternNonValuePlace::Placeholder);
// Pattern::new takes care of reversal of reversed
// attributes: [?x :foo/_bar ?y] turns into
// [?y :foo/bar ?x].
//
// This is a bit messy: the inner conversion to a Pattern can
// fail if the input is something like
//
// ```edn
// [?x :foo/_reversed 23.4]
// ```
//
// because
//
// ```edn
// [23.4 :foo/reversed ?x]
// ```
//
// is nonsense. That leaves us with a nested optional, which we unwrap here.
query::Pattern::new(src, e, a, v, tx)
.map(query::WhereClause::Pattern)
.ok_or("expected pattern")
}
// TODO: this shouldn't be checked at parse time.
rule_vars -> BTreeSet<query::Variable>
= vs:variable+ {?
let given = vs.len();
let set: BTreeSet<query::Variable> = vs.into_iter().collect();
if given != set.len() {
Err("expected unique variables")
} else {
Ok(set)
}
}
or_pattern_clause -> query::OrWhereClause
= clause:where_clause { query::OrWhereClause::Clause(clause) }
or_and_clause -> query::OrWhereClause
= __ "(" __ "and" clauses:where_clause+ ")" __ { query::OrWhereClause::And(clauses) }
or_where_clause -> query::OrWhereClause
= or_pattern_clause
/ or_and_clause
or_clause -> query::WhereClause
= __ "(" __ "or" clauses:or_where_clause+ ")" __ {
query::WhereClause::OrJoin(query::OrJoin::new(query::UnifyVars::Implicit, clauses))
}
or_join_clause -> query::WhereClause
= __ "(" __ "or-join" __ "[" vars:rule_vars "]" clauses:or_where_clause+ ")" __ {
query::WhereClause::OrJoin(query::OrJoin::new(query::UnifyVars::Explicit(vars), clauses))
}
not_clause -> query::WhereClause
= __ "(" __ "not" clauses:where_clause+ ")" __ {
query::WhereClause::NotJoin(query::NotJoin::new(query::UnifyVars::Implicit, clauses))
}
not_join_clause -> query::WhereClause
= __ "(" __ "not-join" __ "[" vars:rule_vars "]" clauses:where_clause+ ")" __ {
query::WhereClause::NotJoin(query::NotJoin::new(query::UnifyVars::Explicit(vars), clauses))
}
type_annotation -> query::WhereClause
= __ "[" __ "(" __ "type" var:variable __ ty:raw_keyword __ ")" __ "]" __ {
query::WhereClause::TypeAnnotation(
query::TypeAnnotation {
value_type: ty,
variable: var,
})
}
pred -> query::WhereClause
= __ "[" __ "(" func:query_function args:fn_arg* ")" __ "]" __ {
query::WhereClause::Pred(
query::Predicate {
operator: func.0,
args: args,
})
}
pub where_fn -> query::WhereClause
= __ "[" __ "(" func:query_function args:fn_arg* ")" __ binding:binding "]" __ {
query::WhereClause::WhereFn(
query::WhereFn {
operator: func.0,
args: args,
binding,
})
}
where_clause -> query::WhereClause
// Right now we only support patterns and predicates. See #239 for more.
= pattern
/ or_join_clause
/ or_clause
/ not_join_clause
/ not_clause
/ type_annotation
/ pred
/ where_fn
query_part -> query::QueryPart
= __ ":find" fs:find_spec { query::QueryPart::FindSpec(fs) }
/ __ ":in" in_vars:variable+ { query::QueryPart::InVars(in_vars) }
/ __ ":limit" l:limit { query::QueryPart::Limit(l) }
/ __ ":order" os:order+ { query::QueryPart::Order(os) }
/ __ ":where" ws:where_clause+ { query::QueryPart::WhereClauses(ws) }
/ __ ":with" with_vars:variable+ { query::QueryPart::WithVars(with_vars) }
pub parse_query -> query::ParsedQuery
= __ "[" qps:query_part+ "]" __ {? query::ParsedQuery::from_parts(qps) }
variable -> query::Variable
= v:value {? query::Variable::from_value(&v).ok_or("expected variable") }
src_var -> query::SrcVar
= v:value {? query::SrcVar::from_value(&v).ok_or("expected src_var") }
variable_or_placeholder -> query::VariableOrPlaceholder
= v:variable { query::VariableOrPlaceholder::Variable(v) }
/ __ "_" __ { query::VariableOrPlaceholder::Placeholder }
binding -> query::Binding
= __ "[" __ "[" vs:variable_or_placeholder+ "]" __ "]" __ { query::Binding::BindRel(vs) }
/ __ "[" v:variable "..." __ "]" __ { query::Binding::BindColl(v) }
/ __ "[" vs:variable_or_placeholder+ "]" __ { query::Binding::BindTuple(vs) }
/ v:variable { query::Binding::BindScalar(v) }

View file

@ -25,11 +25,18 @@ extern crate serde_derive;
pub mod entities;
// Intentionally not pub.
mod namespaceable_name;
pub mod query;
pub mod symbols;
pub mod types;
pub mod pretty_print;
pub mod utils;
pub mod matcher;
pub mod value_rc;
pub use value_rc::{
Cloned,
FromRc,
ValueRc,
};
pub mod parse {
include!(concat!(env!("OUT_DIR"), "/edn.rs"));

1239
edn/src/query.rs Normal file

File diff suppressed because it is too large Load diff

107
edn/src/value_rc.rs Normal file
View file

@ -0,0 +1,107 @@
// Copyright 2018 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
use ::std::rc::{
Rc,
};
use ::std::sync::{
Arc,
};
pub trait FromRc<T> {
fn from_rc(val: Rc<T>) -> Self;
fn from_arc(val: Arc<T>) -> Self;
}
impl<T> FromRc<T> for Rc<T> where T: Sized + Clone {
fn from_rc(val: Rc<T>) -> Self {
val.clone()
}
fn from_arc(val: Arc<T>) -> Self {
match ::std::sync::Arc::<T>::try_unwrap(val) {
Ok(v) => Self::new(v),
Err(r) => Self::new(r.cloned()),
}
}
}
impl<T> FromRc<T> for Arc<T> where T: Sized + Clone {
fn from_rc(val: Rc<T>) -> Self {
match ::std::rc::Rc::<T>::try_unwrap(val) {
Ok(v) => Self::new(v),
Err(r) => Self::new(r.cloned()),
}
}
fn from_arc(val: Arc<T>) -> Self {
val.clone()
}
}
impl<T> FromRc<T> for Box<T> where T: Sized + Clone {
fn from_rc(val: Rc<T>) -> Self {
match ::std::rc::Rc::<T>::try_unwrap(val) {
Ok(v) => Self::new(v),
Err(r) => Self::new(r.cloned()),
}
}
fn from_arc(val: Arc<T>) -> Self {
match ::std::sync::Arc::<T>::try_unwrap(val) {
Ok(v) => Self::new(v),
Err(r) => Self::new(r.cloned()),
}
}
}
// We do this a lot for errors.
pub trait Cloned<T> {
fn cloned(&self) -> T;
fn to_value_rc(&self) -> ValueRc<T>;
}
impl<T: Clone> Cloned<T> for Rc<T> where T: Sized + Clone {
fn cloned(&self) -> T {
(*self.as_ref()).clone()
}
fn to_value_rc(&self) -> ValueRc<T> {
ValueRc::from_rc(self.clone())
}
}
impl<T: Clone> Cloned<T> for Arc<T> where T: Sized + Clone {
fn cloned(&self) -> T {
(*self.as_ref()).clone()
}
fn to_value_rc(&self) -> ValueRc<T> {
ValueRc::from_arc(self.clone())
}
}
impl<T: Clone> Cloned<T> for Box<T> where T: Sized + Clone {
fn cloned(&self) -> T {
self.as_ref().clone()
}
fn to_value_rc(&self) -> ValueRc<T> {
ValueRc::new(self.cloned())
}
}
///
/// This type alias exists to allow us to use different boxing mechanisms for values.
/// This type must implement `FromRc` and `Cloned`, and a `From` implementation must exist for
/// `TypedValue`.
///
pub type ValueRc<T> = Arc<T>;

View file

@ -8,20 +8,14 @@
// 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 maplit;
extern crate edn;
extern crate mentat_core;
extern crate mentat_query;
extern crate mentat_query_parser;
use edn::{
Keyword,
PlainSymbol,
};
use mentat_query::{
use edn::query::{
Direction,
Element,
FindSpec,
@ -40,7 +34,9 @@ use mentat_query::{
WhereClause,
};
use mentat_query_parser::parse_find_string;
use edn::parse::{
parse_query,
};
///! N.B., parsing a query can be done without reference to a DB.
///! Processing the parsed query into something we can work with
@ -50,7 +46,7 @@ use mentat_query_parser::parse_find_string;
#[test]
fn can_parse_predicates() {
let s = "[:find [?x ...] :where [?x _ ?y] [(< ?y 10)]]";
let p = parse_find_string(s).unwrap();
let p = parse_query(s).unwrap();
assert_eq!(p.find_spec,
FindSpec::FindColl(Element::Variable(Variable::from_valid_name("?x"))));
@ -72,7 +68,7 @@ fn can_parse_predicates() {
#[test]
fn can_parse_simple_or() {
let s = "[:find ?x . :where (or [?x _ 10] [?x _ 15])]";
let p = parse_find_string(s).unwrap();
let p = parse_query(s).unwrap();
assert_eq!(p.find_spec,
FindSpec::FindScalar(Element::Variable(Variable::from_valid_name("?x"))));
@ -105,14 +101,14 @@ fn can_parse_simple_or() {
#[test]
fn can_parse_unit_or_join() {
let s = "[:find ?x . :where (or-join [?x] [?x _ 15])]";
let p = parse_find_string(s).expect("to be able to parse find");
let p = parse_query(s).expect("to be able to parse find");
assert_eq!(p.find_spec,
FindSpec::FindScalar(Element::Variable(Variable::from_valid_name("?x"))));
assert_eq!(p.where_clauses,
vec![
WhereClause::OrJoin(OrJoin::new(
UnifyVars::Explicit(btreeset!{Variable::from_valid_name("?x")}),
UnifyVars::Explicit(std::iter::once(Variable::from_valid_name("?x")).collect()),
vec![
OrWhereClause::Clause(
WhereClause::Pattern(Pattern {
@ -130,14 +126,14 @@ fn can_parse_unit_or_join() {
#[test]
fn can_parse_simple_or_join() {
let s = "[:find ?x . :where (or-join [?x] [?x _ 10] [?x _ -15])]";
let p = parse_find_string(s).unwrap();
let p = parse_query(s).unwrap();
assert_eq!(p.find_spec,
FindSpec::FindScalar(Element::Variable(Variable::from_valid_name("?x"))));
assert_eq!(p.where_clauses,
vec![
WhereClause::OrJoin(OrJoin::new(
UnifyVars::Explicit(btreeset!{Variable::from_valid_name("?x")}),
UnifyVars::Explicit(std::iter::once(Variable::from_valid_name("?x")).collect()),
vec![
OrWhereClause::Clause(
WhereClause::Pattern(Pattern {
@ -168,7 +164,7 @@ fn ident(ns: &str, name: &str) -> PatternNonValuePlace {
#[test]
fn can_parse_simple_or_and_join() {
let s = "[:find ?x . :where (or [?x _ 10] (and (or [?x :foo/bar ?y] [?x :foo/baz ?y]) [(< ?y 1)]))]";
let p = parse_find_string(s).unwrap();
let p = parse_query(s).unwrap();
assert_eq!(p.find_spec,
FindSpec::FindScalar(Element::Variable(Variable::from_valid_name("?x"))));
@ -220,23 +216,23 @@ fn can_parse_simple_or_and_join() {
#[test]
fn can_parse_order_by() {
let invalid = "[:find ?x :where [?x :foo/baz ?y] :order]";
assert!(parse_find_string(invalid).is_err());
assert!(parse_query(invalid).is_err());
// Defaults to ascending.
let default = "[:find ?x :where [?x :foo/baz ?y] :order ?y]";
assert_eq!(parse_find_string(default).unwrap().order,
assert_eq!(parse_query(default).unwrap().order,
Some(vec![Order(Direction::Ascending, Variable::from_valid_name("?y"))]));
let ascending = "[:find ?x :where [?x :foo/baz ?y] :order (asc ?y)]";
assert_eq!(parse_find_string(ascending).unwrap().order,
assert_eq!(parse_query(ascending).unwrap().order,
Some(vec![Order(Direction::Ascending, Variable::from_valid_name("?y"))]));
let descending = "[:find ?x :where [?x :foo/baz ?y] :order (desc ?y)]";
assert_eq!(parse_find_string(descending).unwrap().order,
assert_eq!(parse_query(descending).unwrap().order,
Some(vec![Order(Direction::Descending, Variable::from_valid_name("?y"))]));
let mixed = "[:find ?x :where [?x :foo/baz ?y] :order (desc ?y) (asc ?x)]";
assert_eq!(parse_find_string(mixed).unwrap().order,
assert_eq!(parse_query(mixed).unwrap().order,
Some(vec![Order(Direction::Descending, Variable::from_valid_name("?y")),
Order(Direction::Ascending, Variable::from_valid_name("?x"))]));
}
@ -244,40 +240,56 @@ fn can_parse_order_by() {
#[test]
fn can_parse_limit() {
let invalid = "[:find ?x :where [?x :foo/baz ?y] :limit]";
assert!(parse_find_string(invalid).is_err());
assert!(parse_query(invalid).is_err());
let zero_invalid = "[:find ?x :where [?x :foo/baz ?y] :limit 00]";
assert!(parse_find_string(zero_invalid).is_err());
assert!(parse_query(zero_invalid).is_err());
let none = "[:find ?x :where [?x :foo/baz ?y]]";
assert_eq!(parse_find_string(none).unwrap().limit,
assert_eq!(parse_query(none).unwrap().limit,
Limit::None);
let one = "[:find ?x :where [?x :foo/baz ?y] :limit 1]";
assert_eq!(parse_find_string(one).unwrap().limit,
assert_eq!(parse_query(one).unwrap().limit,
Limit::Fixed(1));
let onethousand = "[:find ?x :where [?x :foo/baz ?y] :limit 1000]";
assert_eq!(parse_find_string(onethousand).unwrap().limit,
assert_eq!(parse_query(onethousand).unwrap().limit,
Limit::Fixed(1000));
let variable_with_in = "[:find ?x :in ?limit :where [?x :foo/baz ?y] :limit ?limit]";
assert_eq!(parse_find_string(variable_with_in).unwrap().limit,
assert_eq!(parse_query(variable_with_in).unwrap().limit,
Limit::Variable(Variable::from_valid_name("?limit")));
let variable_with_in_used = "[:find ?x :in ?limit :where [?x :foo/baz ?limit] :limit ?limit]";
assert_eq!(parse_find_string(variable_with_in_used).unwrap().limit,
assert_eq!(parse_query(variable_with_in_used).unwrap().limit,
Limit::Variable(Variable::from_valid_name("?limit")));
let variable_without_in = "[:find ?x :where [?x :foo/baz ?y] :limit ?limit]";
assert!(parse_find_string(variable_without_in).is_err());
}
#[test]
fn can_parse_uuid() {
let expected = edn::Uuid::parse_str("4cb3f828-752d-497a-90c9-b1fd516d5644").expect("valid uuid");
let s = "[:find ?x :where [?x :foo/baz #uuid \"4cb3f828-752d-497a-90c9-b1fd516d5644\"]]";
assert_eq!(parse_find_string(s).expect("parsed").where_clauses.pop().expect("a where clause"),
assert_eq!(parse_query(s).expect("parsed").where_clauses.pop().expect("a where clause"),
WhereClause::Pattern(
Pattern::new(None,
PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
Keyword::namespaced("foo", "baz").into(),
PatternValuePlace::Constant(NonIntegerConstant::Uuid(expected)),
PatternNonValuePlace::Placeholder)
.expect("valid pattern")));
}
#[test]
fn can_parse_exotic_whitespace() {
let expected = edn::Uuid::parse_str("4cb3f828-752d-497a-90c9-b1fd516d5644").expect("valid uuid");
// The query string from `can_parse_uuid`, with newlines, commas, and line comments interspersed.
let s = r#"[:find
?x ,, :where, ;atest
[?x :foo/baz #uuid
"4cb3f828-752d-497a-90c9-b1fd516d5644", ;testa
,],, ,],;"#;
assert_eq!(parse_query(s).expect("parsed").where_clauses.pop().expect("a where clause"),
WhereClause::Pattern(
Pattern::new(None,
PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),

View file

@ -12,10 +12,5 @@ path = "../core"
[dependencies.mentat_query]
path = "../query"
# Only for tests.
[dev-dependencies.mentat_query_parser]
path = "../query-parser"
[dev-dependencies]
itertools = "0.7"
maplit = "0.1"

View file

@ -86,8 +86,6 @@ impl ConjoiningClauses {
#[cfg(test)]
mod testing {
extern crate mentat_query_parser;
use std::collections::BTreeSet;
use super::*;
@ -106,8 +104,6 @@ mod testing {
Variable
};
use self::mentat_query_parser::parse_find_string;
use clauses::{
QueryInputs,
add_attribute,
@ -135,6 +131,7 @@ mod testing {
use {
algebrize,
algebrize_with_inputs,
parse_find_string,
};
fn alg(schema: &Schema, input: &str) -> ConjoiningClauses {
@ -338,7 +335,7 @@ mod testing {
[:find ?x ?age
:where
[?x :foo/age ?age]
[[< ?age 30]]
[(< ?age 30)]
(not [?x :foo/knows "John"]
[?x :foo/knows "Daphne"])]"#;
let cc = alg(&schema, query);

View file

@ -751,8 +751,6 @@ fn union_types(into: &mut BTreeMap<Variable, ValueTypeSet>,
#[cfg(test)]
mod testing {
extern crate mentat_query_parser;
use super::*;
use mentat_core::{
@ -767,10 +765,6 @@ mod testing {
Variable,
};
use self::mentat_query_parser::{
parse_find_string,
};
use clauses::{
add_attribute,
associate_ident,
@ -789,6 +783,7 @@ mod testing {
use {
algebrize,
algebrize_with_counter,
parse_find_string,
};
fn alg(known: Known, input: &str) -> ConjoiningClauses {
@ -973,7 +968,7 @@ mod testing {
[:find ?x ?age
:where
[?x :foo/age ?age]
[[< ?age 30]]
[(< ?age 30)]
(or [?x :foo/knows "John"]
[?x :foo/knows "Daphne"])]"#;
let cc = alg(known, query);

View file

@ -18,6 +18,7 @@ use mentat_core::{
};
use mentat_query::{
NonIntegerConstant,
Pattern,
PatternValuePlace,
PatternNonValuePlace,
@ -42,6 +43,17 @@ use types::{
use Known;
pub fn into_typed_value(nic: NonIntegerConstant) -> TypedValue {
match nic {
NonIntegerConstant::BigInteger(_) => unimplemented!(), // TODO: #280.
NonIntegerConstant::Boolean(v) => TypedValue::Boolean(v),
NonIntegerConstant::Float(v) => TypedValue::Double(v),
NonIntegerConstant::Text(v) => v.into(),
NonIntegerConstant::Instant(v) => TypedValue::Instant(v),
NonIntegerConstant::Uuid(v) => TypedValue::Uuid(v),
}
}
/// Application of patterns.
impl ConjoiningClauses {
@ -518,7 +530,7 @@ impl ConjoiningClauses {
}
},
PatternValuePlace::Constant(nic) => {
Place(EvolvedValuePlace::Value(nic.into_typed_value()))
Place(EvolvedValuePlace::Value(into_typed_value(nic)))
},
}
}
@ -639,8 +651,6 @@ impl ConjoiningClauses {
#[cfg(test)]
mod testing {
extern crate mentat_query_parser;
use super::*;
use std::collections::BTreeMap;
@ -655,14 +665,9 @@ mod testing {
use mentat_query::{
Keyword,
NonIntegerConstant,
Variable,
};
use self::mentat_query_parser::{
parse_find_string,
};
use clauses::{
QueryInputs,
add_attribute,
@ -679,7 +684,10 @@ mod testing {
SourceAlias,
};
use algebrize;
use {
algebrize,
parse_find_string,
};
fn alg(schema: &Schema, input: &str) -> ConjoiningClauses {
let parsed = parse_find_string(input).expect("parse failed");

View file

@ -16,6 +16,7 @@ use mentat_core::{
use mentat_query::{
FnArg,
PlainSymbol,
Predicate,
TypeAnnotation,
};
@ -66,7 +67,10 @@ impl ConjoiningClauses {
/// Apply a type annotation, which is a construct like a predicate that constrains the argument
/// to be a specific ValueType.
pub(crate) fn apply_type_anno(&mut self, anno: &TypeAnnotation) -> Result<()> {
self.add_type_requirement(anno.variable.clone(), ValueTypeSet::of_one(anno.value_type));
match ValueType::from_keyword(&anno.value_type) {
Some(value_type) => self.add_type_requirement(anno.variable.clone(), ValueTypeSet::of_one(value_type)),
None => bail!(ErrorKind::InvalidArgumentType(PlainSymbol::plain("type"), ValueTypeSet::any(), 2)),
}
Ok(())
}

View file

@ -11,6 +11,7 @@
extern crate mentat_query;
use mentat_core::{
EdnParseError,
ValueType,
ValueTypeSet,
};
@ -44,6 +45,10 @@ error_chain! {
Error, ErrorKind, ResultExt, Result;
}
foreign_links {
EdnParseError(EdnParseError);
}
errors {
UnsupportedArgument {
description("unexpected FnArg")
@ -117,6 +122,16 @@ error_chain! {
description("non-matching variables in 'not' clause")
display("non-matching variables in 'not' clause")
}
DuplicateVariableError(name: PlainSymbol, clause: &'static str) {
description("duplicate variables")
display("{} var {} is duplicated", clause, name)
}
UnknownLimitVar(name: PlainSymbol) {
description(":limit var not present in :in")
display(":limit var {} not present in :in", name)
}
}
}

View file

@ -13,10 +13,6 @@
#[macro_use]
extern crate error_chain;
#[cfg(test)]
#[macro_use]
extern crate maplit;
extern crate mentat_core;
extern crate mentat_query;
@ -35,18 +31,20 @@ use mentat_core::{
Schema,
TypedValue,
ValueType,
parse_query,
};
use mentat_core::counter::RcCounter;
use mentat_query::{
Element,
FindQuery,
FindSpec,
Limit,
Order,
ParsedQuery,
SrcVar,
Variable,
WhereClause,
};
pub use errors::{
@ -63,6 +61,7 @@ pub use clauses::{
pub use types::{
EmptyBecause,
FindQuery,
};
/// A convenience wrapper around things known in memory: the schema and caches.
@ -336,3 +335,69 @@ pub use types::{
TableAlias,
VariableColumn,
};
impl FindQuery {
pub fn simple(spec: FindSpec, where_clauses: Vec<WhereClause>) -> FindQuery {
FindQuery {
find_spec: spec,
default_source: SrcVar::DefaultSrc,
with: BTreeSet::default(),
in_vars: BTreeSet::default(),
in_sources: BTreeSet::default(),
limit: Limit::None,
where_clauses: where_clauses,
order: None,
}
}
pub fn from_parsed_query(parsed: ParsedQuery) -> Result<FindQuery> {
let in_vars = {
let mut set: BTreeSet<Variable> = BTreeSet::default();
for var in parsed.in_vars.into_iter() {
if !set.insert(var.clone()) {
bail!(ErrorKind::DuplicateVariableError(var.name(), ":in"));
}
}
set
};
let with = {
let mut set: BTreeSet<Variable> = BTreeSet::default();
for var in parsed.with.into_iter() {
if !set.insert(var.clone()) {
bail!(ErrorKind::DuplicateVariableError(var.name(), ":with"));
}
}
set
};
// Make sure that if we have `:limit ?x`, `?x` appears in `:in`.
if let Limit::Variable(ref v) = parsed.limit {
if !in_vars.contains(v) {
bail!(ErrorKind::UnknownLimitVar(v.name()));
}
}
Ok(FindQuery {
find_spec: parsed.find_spec,
default_source: parsed.default_source,
with,
in_vars,
in_sources: parsed.in_sources,
limit: parsed.limit,
where_clauses: parsed.where_clauses,
order: parsed.order,
})
}
}
pub fn parse_find_string(string: &str) -> Result<FindQuery> {
parse_query(string)
.map_err(|e| e.into())
.and_then(|parsed| FindQuery::from_parsed_query(parsed))
}

View file

@ -12,7 +12,6 @@ use std::collections::BTreeSet;
use std::fmt::{
Debug,
Formatter,
Result,
};
use mentat_core::{
@ -25,10 +24,13 @@ use mentat_core::{
use mentat_query::{
Direction,
FindSpec,
Keyword,
Limit,
Order,
SrcVar,
Variable,
WhereClause,
};
/// This enum models the fixed set of default tables we have -- two
@ -174,7 +176,7 @@ impl ColumnName for VariableColumn {
}
impl Debug for VariableColumn {
fn fmt(&self, f: &mut Formatter) -> Result {
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
match self {
// These should agree with VariableColumn::column_name.
&VariableColumn::Variable(ref v) => write!(f, "{}", v.as_str()),
@ -184,13 +186,13 @@ impl Debug for VariableColumn {
}
impl Debug for DatomsColumn {
fn fmt(&self, f: &mut Formatter) -> Result {
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl Debug for Column {
fn fmt(&self, f: &mut Formatter) -> Result {
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
match self {
&Column::Fixed(ref c) => c.fmt(f),
&Column::Fulltext(ref c) => c.fmt(f),
@ -217,7 +219,7 @@ impl ColumnName for FulltextColumn {
}
impl Debug for FulltextColumn {
fn fmt(&self, f: &mut Formatter) -> Result {
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
@ -251,7 +253,7 @@ impl ColumnName for TransactionsColumn {
}
impl Debug for TransactionsColumn {
fn fmt(&self, f: &mut Formatter) -> Result {
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
@ -264,7 +266,7 @@ pub type TableAlias = String;
pub struct SourceAlias(pub DatomsTable, pub TableAlias);
impl Debug for SourceAlias {
fn fmt(&self, f: &mut Formatter) -> Result {
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
write!(f, "SourceAlias({:?}, {})", self.0, self.1)
}
}
@ -274,7 +276,7 @@ impl Debug for SourceAlias {
pub struct QualifiedAlias(pub TableAlias, pub Column);
impl Debug for QualifiedAlias {
fn fmt(&self, f: &mut Formatter) -> Result {
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
write!(f, "{}.{:?}", self.0, self.1)
}
}
@ -691,6 +693,24 @@ impl Debug for EmptyBecause {
}
}
/// A `FindQuery` represents a valid query to the query algebrizer.
///
/// We split `FindQuery` from `ParsedQuery` because it's not easy to generalize over containers
/// (here, `Vec` and `BTreeSet`) in Rust.
#[allow(dead_code)]
#[derive(Debug, Eq, PartialEq)]
pub struct FindQuery {
pub find_spec: FindSpec,
pub default_source: SrcVar,
pub with: BTreeSet<Variable>,
pub in_vars: BTreeSet<Variable>,
pub in_sources: BTreeSet<SrcVar>,
pub limit: Limit,
pub where_clauses: Vec<WhereClause>,
pub order: Option<Vec<Order>>,
}
// Intermediate data structures for resolving patterns.
#[derive(Debug, Eq, PartialEq)]

View file

@ -96,10 +96,8 @@ pub(crate) fn validate_not_join(not_join: &NotJoin) -> Result<()> {
mod tests {
extern crate mentat_core;
extern crate mentat_query;
extern crate mentat_query_parser;
use self::mentat_query::{
FindQuery,
Keyword,
OrWhereClause,
Pattern,
@ -110,13 +108,12 @@ mod tests {
WhereClause,
};
use self::mentat_query_parser::parse_find_string;
use clauses::ident;
use super::{
validate_not_join,
validate_or_join,
use super::*;
use parse_find_string;
use types::{
FindQuery,
};
fn value_ident(ns: &str, name: &str) -> PatternValuePlace {
@ -212,7 +209,7 @@ mod tests {
(and [?artist :artist/type ?type]
[?type :artist/role :artist.role/parody]))]"#;
let parsed = parse_find_string(query).expect("expected successful parse");
let clauses = valid_or_join(parsed, UnifyVars::Explicit(btreeset!{Variable::from_valid_name("?artist")}));
let clauses = valid_or_join(parsed, UnifyVars::Explicit(::std::iter::once(Variable::from_valid_name("?artist")).collect()));
// Let's do some detailed parse checks.
let mut arms = clauses.into_iter();
@ -322,7 +319,7 @@ mod tests {
[?release :release/artists ?artist]
[?release :release/year 1970])]"#;
let parsed = parse_find_string(query).expect("expected successful parse");
let clauses = valid_not_join(parsed, UnifyVars::Explicit(btreeset!{Variable::from_valid_name("?artist")}));
let clauses = valid_not_join(parsed, UnifyVars::Explicit(::std::iter::once(Variable::from_valid_name("?artist")).collect()));
let release = PatternNonValuePlace::Variable(Variable::from_valid_name("?release"));
let artist = PatternValuePlace::Variable(Variable::from_valid_name("?artist"));

View file

@ -11,7 +11,6 @@
extern crate mentat_core;
extern crate mentat_query;
extern crate mentat_query_algebrizer;
extern crate mentat_query_parser;
mod utils;

View file

@ -11,7 +11,6 @@
extern crate mentat_core;
extern crate mentat_query;
extern crate mentat_query_algebrizer;
extern crate mentat_query_parser;
mod utils;

View file

@ -11,7 +11,6 @@
extern crate mentat_core;
extern crate mentat_query;
extern crate mentat_query_algebrizer;
extern crate mentat_query_parser;
mod utils;

View file

@ -11,7 +11,6 @@
extern crate mentat_core;
extern crate mentat_query;
extern crate mentat_query_algebrizer;
extern crate mentat_query_parser;
mod utils;
@ -43,22 +42,12 @@ fn prepopulated_schema() -> Schema {
#[test]
fn test_empty_known() {
let type_names = [
"boolean",
"long",
"double",
"string",
"keyword",
"uuid",
"instant",
"ref",
];
let schema = prepopulated_schema();
let known = Known::for_schema(&schema);
for known_type in type_names.iter() {
for required in type_names.iter() {
let q = format!("[:find ?e :where [?e :test/{} ?v] [({} ?v)]]",
known_type, required);
for known_type in ValueType::all_enums().iter() {
for required in ValueType::all_enums().iter() {
let q = format!("[:find ?e :where [?e :test/{} ?v] [(type ?v {})]]",
known_type.into_keyword().name(), required);
println!("Query: {}", q);
let cc = alg(known, &q);
// It should only be empty if the known type and our requirement differ.
@ -72,7 +61,7 @@ fn test_empty_known() {
fn test_multiple() {
let schema = prepopulated_schema();
let known = Known::for_schema(&schema);
let q = "[:find ?e :where [?e _ ?v] [(long ?v)] [(double ?v)]]";
let q = "[:find ?e :where [?e _ ?v] [(type ?v :db.type/long)] [(type ?v :db.type/double)]]";
let cc = alg(known, &q);
assert!(cc.empty_because.is_some());
}
@ -81,5 +70,5 @@ fn test_multiple() {
fn test_unbound() {
let schema = prepopulated_schema();
let known = Known::for_schema(&schema);
bails(known, "[:find ?e :where [(string ?e)]]");
bails(known, "[:find ?e :where [(type ?e :db.type/string)]]");
}

View file

@ -20,10 +20,6 @@ use mentat_core::{
ValueType,
};
use mentat_query_parser::{
parse_find_string,
};
use mentat_query::{
Keyword,
};
@ -35,6 +31,7 @@ use mentat_query_algebrizer::{
QueryInputs,
algebrize,
algebrize_with_inputs,
parse_find_string,
};
// Common utility functions used in multiple test files.

View file

@ -1,22 +0,0 @@
[package]
name = "mentat_query_parser"
version = "0.0.1"
workspace = ".."
[dependencies]
combine = "2.3.2"
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
maplit = "0.1"
matches = "0.1"
[dependencies.edn]
path = "../edn"
[dependencies.mentat_core]
path = "../core"
[dependencies.mentat_parser_utils]
path = "../parser-utils"
[dependencies.mentat_query]
path = "../query"

View file

@ -1,2 +0,0 @@
See <https://github.com/mozilla/mentat/wiki/Querying> for a description of
what's going on in this crate, as well as `query` and `query-executor`.

View file

@ -1,35 +0,0 @@
// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#![allow(unused_imports)]
#[macro_use]
extern crate maplit;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate matches;
extern crate edn;
#[macro_use]
extern crate mentat_parser_utils;
mod parse;
pub use parse::{
Error,
ErrorKind,
Result,
ResultExt,
parse_find_string,
};

File diff suppressed because it is too large Load diff

View file

@ -29,10 +29,6 @@ path = "../query-algebrizer"
[dependencies.mentat_query_pull]
path = "../query-pull"
# Only for tests.
[dev-dependencies.mentat_query_parser]
path = "../query-parser"
[dependencies.mentat_query_sql]
path = "../query-sql"

View file

@ -11,7 +11,6 @@
extern crate mentat_core;
extern crate mentat_query;
extern crate mentat_query_algebrizer;
extern crate mentat_query_parser;
extern crate mentat_query_projector;
use mentat_core::{
@ -21,10 +20,6 @@ use mentat_core::{
ValueType,
};
use mentat_query_parser::{
parse_find_string,
};
use mentat_query::{
Keyword,
};
@ -32,6 +27,7 @@ use mentat_query::{
use mentat_query_algebrizer::{
Known,
algebrize,
parse_find_string,
};
use mentat_query_projector::{

View file

@ -27,7 +27,3 @@ path = "../query-sql"
[dependencies.mentat_sql]
path = "../sql"
# Only for tests.
[dev-dependencies.mentat_query_parser]
path = "../query-parser"

View file

@ -17,7 +17,3 @@ path = "../query"
[dependencies.mentat_query_algebrizer]
path = "../query-algebrizer"
# Only for tests.
[dev-dependencies.mentat_query_parser]
path = "../query-parser"

View file

@ -18,10 +18,6 @@ path = "../query"
[dependencies.mentat_query_algebrizer]
path = "../query-algebrizer"
# Only for tests.
[dev-dependencies.mentat_query_parser]
path = "../query-parser"
[dependencies.mentat_query_projector]
path = "../query-projector"

View file

@ -11,7 +11,6 @@
extern crate mentat_core;
extern crate mentat_query;
extern crate mentat_query_algebrizer;
extern crate mentat_query_parser;
extern crate mentat_query_projector;
extern crate mentat_query_translator;
extern crate mentat_sql;
@ -34,12 +33,12 @@ use mentat_core::{
ValueType,
};
use mentat_query_parser::parse_find_string;
use mentat_query_algebrizer::{
Known,
QueryInputs,
algebrize,
algebrize_with_inputs,
parse_find_string,
};
use mentat_query_projector::{
@ -348,7 +347,7 @@ fn test_unknown_ident() {
fn test_type_required_long() {
let schema = Schema::default();
let query = r#"[:find ?x :where [?x _ ?e] [(long ?e)]]"#;
let query = r#"[:find ?x :where [?x _ ?e] [(type ?e :db.type/long)]]"#;
let SQLQuery { sql, args } = translate(&schema, query);
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
@ -363,7 +362,7 @@ fn test_type_required_long() {
fn test_type_required_double() {
let schema = Schema::default();
let query = r#"[:find ?x :where [?x _ ?e] [(double ?e)]]"#;
let query = r#"[:find ?x :where [?x _ ?e] [(type ?e :db.type/double)]]"#;
let SQLQuery { sql, args } = translate(&schema, query);
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
@ -378,7 +377,7 @@ fn test_type_required_double() {
fn test_type_required_boolean() {
let schema = Schema::default();
let query = r#"[:find ?x :where [?x _ ?e] [(boolean ?e)]]"#;
let query = r#"[:find ?x :where [?x _ ?e] [(type ?e :db.type/boolean)]]"#;
let SQLQuery { sql, args } = translate(&schema, query);
assert_eq!(sql, "SELECT DISTINCT `datoms00`.e AS `?x` \
@ -392,7 +391,7 @@ fn test_type_required_boolean() {
fn test_type_required_string() {
let schema = Schema::default();
let query = r#"[:find ?x :where [?x _ ?e] [(string ?e)]]"#;
let query = r#"[:find ?x :where [?x _ ?e] [(type ?e :db.type/string)]]"#;
let SQLQuery { sql, args } = translate(&schema, query);
// Note: strings should use `all_datoms` and not `datoms`.

File diff suppressed because it is too large Load diff

View file

@ -15,7 +15,7 @@
// The internal data format for transacting is required to encode the complexities of
// processing that format: temporary IDs, lookup refs, input spans, etc.
//
// See mentat_tx::entities::Entity and all of its child enums to see how complex this gets.
// See edn::entities::Entity and all of its child enums to see how complex this gets.
//
// A programmatic consumer doesn't want to build something that looks like:
//
@ -75,7 +75,7 @@ use mentat_db::internal_types::{
TypedValueOr,
};
use mentat_tx::entities::{
use edn::entities::{
OpType,
TempId,
};

View file

@ -24,7 +24,6 @@ use mentat_core::{
use mentat_db;
use mentat_query;
use mentat_query_algebrizer;
use mentat_query_parser;
use mentat_query_projector;
use mentat_query_pull;
use mentat_query_translator;
@ -46,7 +45,6 @@ error_chain! {
links {
DbError(mentat_db::Error, mentat_db::ErrorKind);
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::errors::Error, mentat_query_projector::errors::ErrorKind);
PullError(mentat_query_pull::errors::Error, mentat_query_pull::errors::ErrorKind);
TranslatorError(mentat_query_translator::Error, mentat_query_translator::ErrorKind);

View file

@ -25,13 +25,11 @@ extern crate mentat_core;
extern crate mentat_db;
extern crate mentat_query;
extern crate mentat_query_algebrizer;
extern crate mentat_query_parser;
extern crate mentat_query_projector;
extern crate mentat_query_pull;
extern crate mentat_query_translator;
extern crate mentat_sql;
extern crate mentat_tolstoy;
extern crate mentat_tx;
pub use mentat_core::{
Attribute,

View file

@ -14,18 +14,20 @@ use rusqlite::types::ToSql;
use std::rc::Rc;
use mentat_core::{
Binding,
Entid,
HasSchema,
KnownEntid,
Schema,
Binding,
TypedValue,
};
use mentat_query_algebrizer::{
AlgebraicQuery,
EmptyBecause,
FindQuery,
algebrize_with_inputs,
parse_find_string,
};
pub use mentat_query_algebrizer::{
@ -40,7 +42,6 @@ pub use mentat_query::{
use mentat_query::{
Element,
FindQuery,
FindSpec,
Pattern,
PatternNonValuePlace,
@ -48,10 +49,6 @@ use mentat_query::{
WhereClause,
};
use mentat_query_parser::{
parse_find_string,
};
use mentat_query_projector::{
ConstantProjector,
Projector,

View file

@ -124,11 +124,6 @@ mod test {
Store,
};
use mentat_core::{
DateTime,
Utc,
};
#[test]
fn test_scalar_query() {
let mut store = Store::open("").expect("store connection");

View file

@ -602,7 +602,7 @@ fn test_aggregates_type_handling() {
// You can't sum instants.
let r = store.q_once(r#"[:find (sum ?v) .
:where [_ _ ?v] [(instant ?v)]]"#,
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
None);
match r {
Result::Err(
@ -623,7 +623,7 @@ fn test_aggregates_type_handling() {
// But you can count them.
let r = store.q_once(r#"[:find (count ?v) .
:where [_ _ ?v] [(instant ?v)]]"#,
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
None)
.into_scalar_result()
.expect("results")
@ -634,7 +634,7 @@ fn test_aggregates_type_handling() {
// And you can min them, which returns an instant.
let r = store.q_once(r#"[:find (min ?v) .
:where [_ _ ?v] [(instant ?v)]]"#,
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
None)
.into_scalar_result()
.expect("results")
@ -644,7 +644,7 @@ fn test_aggregates_type_handling() {
assert_eq!(Binding::Scalar(TypedValue::Instant(earliest)), r);
let r = store.q_once(r#"[:find (sum ?v) .
:where [_ _ ?v] [(long ?v)]]"#,
:where [_ _ ?v] [(type ?v :db.type/long)]]"#,
None)
.into_scalar_result()
.expect("results")
@ -655,7 +655,7 @@ fn test_aggregates_type_handling() {
assert_eq!(Binding::Scalar(TypedValue::Long(total)), r);
let r = store.q_once(r#"[:find (avg ?v) .
:where [_ _ ?v] [(double ?v)]]"#,
:where [_ _ ?v] [(type ?v :db.type/double)]]"#,
None)
.into_scalar_result()
.expect("results")
@ -710,19 +710,8 @@ fn test_type_reqs() {
}
};
let type_names = &[
"boolean",
"long",
"double",
"string",
"keyword",
"uuid",
"instant",
"ref",
];
for name in type_names {
let q = format!("[:find [?v ...] :in ?e :where [?e _ ?v] [({} ?v)]]", name);
for value_type in ValueType::all_enums().iter() {
let q = format!("[:find [?v ...] :in ?e :where [?e _ ?v] [(type ?v {})]]", value_type.into_keyword());
let results = conn.q_once(&mut c, &q, QueryInputs::with_value_sequence(vec![
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),
]))
@ -746,7 +735,7 @@ fn test_type_reqs() {
let longs_query = r#"[:find [?v ...]
:order (asc ?v)
:in ?e
:where [?e _ ?v] [(long ?v)]]"#;
:where [?e _ ?v] [(type ?v :db.type/long)]]"#;
let res = conn.q_once(&mut c, longs_query, QueryInputs::with_value_sequence(vec![
(Variable::from_valid_name("?e"), TypedValue::Ref(entid)),

View file

@ -1,8 +0,0 @@
[package]
name = "mentat_tx"
version = "0.0.1"
workspace = ".."
[dependencies]
[dependencies.edn]
path = "../edn"

View file

@ -1 +0,0 @@
This sub-crate implements the core types used by the transaction processor.

View file

@ -1,13 +0,0 @@
// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#[allow(unused_imports)]
#[macro_use] extern crate edn;
pub use edn::entities;