Parse queries with rust-peg. r=rnewman,grisha (#728)
This commit is contained in:
commit
3d5ae797b2
56 changed files with 1856 additions and 2857 deletions
|
@ -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"
|
||||
|
||||
|
|
14
README.md
14
README.md
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -97,7 +97,7 @@ use mentat_sql::{
|
|||
SQLQuery,
|
||||
};
|
||||
|
||||
use mentat_tx::entities::{
|
||||
use edn::entities::{
|
||||
OpType,
|
||||
};
|
||||
|
||||
|
|
|
@ -1096,7 +1096,7 @@ mod tests {
|
|||
InternSet,
|
||||
};
|
||||
use mentat_core::util::Either::*;
|
||||
use mentat_tx::entities::{
|
||||
use edn::entities::{
|
||||
OpType,
|
||||
TempId,
|
||||
};
|
||||
|
|
|
@ -31,7 +31,9 @@ use mentat_core::{
|
|||
TypedValue,
|
||||
ValueType,
|
||||
};
|
||||
use mentat_tx::entities::{Entid};
|
||||
use edn::entities::{
|
||||
Entid,
|
||||
};
|
||||
use schema::{
|
||||
SchemaBuilding,
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::collections::{
|
|||
|
||||
use rusqlite;
|
||||
|
||||
use mentat_tx::entities::{
|
||||
use edn::entities::{
|
||||
TempId,
|
||||
};
|
||||
use mentat_core::{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -32,7 +32,7 @@ use mentat_core::{
|
|||
TypedValue,
|
||||
};
|
||||
|
||||
use mentat_tx::entities::{
|
||||
use edn::entities::{
|
||||
OpType,
|
||||
};
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ pub use self::mentat_core::{
|
|||
ValueType,
|
||||
};
|
||||
|
||||
use mentat_tx::entities::{
|
||||
use edn::entities::{
|
||||
EntityPlace,
|
||||
TempId,
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -23,7 +23,7 @@ use mentat_core::{
|
|||
TypedValue,
|
||||
};
|
||||
|
||||
use mentat_tx::entities::{
|
||||
use edn::entities::{
|
||||
OpType,
|
||||
};
|
||||
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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
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
107
edn/src/value_rc.rs
Normal 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>;
|
|
@ -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")),
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
extern crate mentat_core;
|
||||
extern crate mentat_query;
|
||||
extern crate mentat_query_algebrizer;
|
||||
extern crate mentat_query_parser;
|
||||
|
||||
mod utils;
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
extern crate mentat_core;
|
||||
extern crate mentat_query;
|
||||
extern crate mentat_query_algebrizer;
|
||||
extern crate mentat_query_parser;
|
||||
|
||||
mod utils;
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
extern crate mentat_core;
|
||||
extern crate mentat_query;
|
||||
extern crate mentat_query_algebrizer;
|
||||
extern crate mentat_query_parser;
|
||||
|
||||
mod utils;
|
||||
|
||||
|
|
|
@ -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)]]");
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
|
@ -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`.
|
|
@ -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
|
@ -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"
|
||||
|
||||
|
|
|
@ -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::{
|
||||
|
|
|
@ -27,7 +27,3 @@ path = "../query-sql"
|
|||
|
||||
[dependencies.mentat_sql]
|
||||
path = "../sql"
|
||||
|
||||
# Only for tests.
|
||||
[dev-dependencies.mentat_query_parser]
|
||||
path = "../query-parser"
|
||||
|
|
|
@ -17,7 +17,3 @@ path = "../query"
|
|||
|
||||
[dependencies.mentat_query_algebrizer]
|
||||
path = "../query-algebrizer"
|
||||
|
||||
# Only for tests.
|
||||
[dev-dependencies.mentat_query_parser]
|
||||
path = "../query-parser"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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`.
|
||||
|
|
1175
query/src/lib.rs
1175
query/src/lib.rs
File diff suppressed because it is too large
Load diff
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
[package]
|
||||
name = "mentat_tx"
|
||||
version = "0.0.1"
|
||||
workspace = ".."
|
||||
|
||||
[dependencies]
|
||||
[dependencies.edn]
|
||||
path = "../edn"
|
|
@ -1 +0,0 @@
|
|||
This sub-crate implements the core types used by the transaction processor.
|
|
@ -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;
|
Loading…
Reference in a new issue