2017-01-04 13:09:12 +00:00
|
|
|
// 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.
|
|
|
|
|
2017-01-25 22:06:19 +00:00
|
|
|
///! This module defines some core types that support find expressions: sources,
|
|
|
|
///! variables, expressions, etc.
|
|
|
|
///! These are produced as 'fuel' by the query parser, consumed by the query
|
|
|
|
///! translator and executor.
|
|
|
|
///!
|
|
|
|
///! Many of these types are defined as simple structs that are little more than
|
|
|
|
///! a richer type alias: a variable, for example, is really just a fancy kind
|
|
|
|
///! of string.
|
|
|
|
///!
|
|
|
|
///! At some point in the future, we might consider reducing copying and memory
|
|
|
|
///! usage by recasting all of these string-holding structs and enums in terms
|
|
|
|
///! of string references, with those references being slices of some parsed
|
|
|
|
///! input query string, and valid for the lifetime of that string.
|
|
|
|
///!
|
|
|
|
///! For now, for the sake of simplicity, all of these strings are heap-allocated.
|
|
|
|
///!
|
|
|
|
///! Furthermore, we might cut out some of the chaff here: each time a 'tagged'
|
|
|
|
///! type is used within an enum, we have an opportunity to simplify and use the
|
|
|
|
///! inner type directly in conjunction with matching on the enum. Before diving
|
|
|
|
///! deeply into this it's worth recognizing that this loss of 'sovereignty' is
|
|
|
|
///! a tradeoff against well-typed function signatures and other such boundaries.
|
|
|
|
|
|
|
|
extern crate edn;
|
2017-02-10 00:59:35 +00:00
|
|
|
extern crate mentat_core;
|
2017-01-25 22:06:19 +00:00
|
|
|
|
2017-04-18 01:46:40 +00:00
|
|
|
use std::collections::{
|
|
|
|
BTreeSet,
|
2017-04-26 22:50:17 +00:00
|
|
|
HashSet,
|
2017-04-18 01:46:40 +00:00
|
|
|
};
|
|
|
|
|
2017-02-16 15:16:04 +00:00
|
|
|
use std::fmt;
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
use std::rc::Rc;
|
2017-04-18 01:46:40 +00:00
|
|
|
|
2017-04-29 03:11:55 +00:00
|
|
|
use edn::{
|
|
|
|
BigInt,
|
|
|
|
DateTime,
|
|
|
|
OrderedFloat,
|
|
|
|
Uuid,
|
2017-11-21 16:24:08 +00:00
|
|
|
Utc,
|
2017-04-29 03:11:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
pub use edn::{
|
|
|
|
NamespacedKeyword,
|
|
|
|
PlainSymbol,
|
|
|
|
};
|
2017-04-18 01:46:40 +00:00
|
|
|
|
|
|
|
use mentat_core::{
|
|
|
|
TypedValue,
|
|
|
|
};
|
2017-01-25 22:06:19 +00:00
|
|
|
|
|
|
|
pub type SrcVarName = String; // Do not include the required syntactic '$'.
|
|
|
|
|
2017-04-26 22:50:17 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
pub struct Variable(pub Rc<PlainSymbol>);
|
2017-01-25 22:06:19 +00:00
|
|
|
|
2017-03-29 21:11:00 +00:00
|
|
|
impl Variable {
|
|
|
|
pub fn as_str(&self) -> &str {
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
self.0.as_ref().0.as_str()
|
2017-03-29 21:11:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_string(&self) -> String {
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
self.0.as_ref().0.clone()
|
2017-03-29 21:11:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn name(&self) -> PlainSymbol {
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
self.0.as_ref().clone()
|
2017-03-29 21:11:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return a new `Variable`, assuming that the provided string is a valid name.
|
|
|
|
pub fn from_valid_name(name: &str) -> Variable {
|
|
|
|
let s = PlainSymbol::new(name);
|
|
|
|
assert!(s.is_var_symbol());
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
Variable(Rc::new(s))
|
2017-03-29 21:11:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 22:51:02 +00:00
|
|
|
pub trait FromValue<T> {
|
2017-05-04 20:40:41 +00:00
|
|
|
fn from_value(v: &edn::ValueAndSpan) -> Option<T>;
|
2017-02-01 22:51:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// If the provided EDN value is a PlainSymbol beginning with '?', return
|
|
|
|
/// it wrapped in a Variable. If not, return None.
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
/// TODO: intern strings. #398.
|
2017-02-01 22:51:02 +00:00
|
|
|
impl FromValue<Variable> for Variable {
|
2017-05-04 20:40:41 +00:00
|
|
|
fn from_value(v: &edn::ValueAndSpan) -> Option<Variable> {
|
2017-04-06 17:06:28 +00:00
|
|
|
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
2017-02-01 22:51:02 +00:00
|
|
|
Variable::from_symbol(s)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Variable {
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
pub fn from_rc(sym: Rc<PlainSymbol>) -> Option<Variable> {
|
2017-02-01 22:51:02 +00:00
|
|
|
if sym.is_var_symbol() {
|
|
|
|
Some(Variable(sym.clone()))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
|
|
|
|
/// TODO: intern strings. #398.
|
|
|
|
pub fn from_symbol(sym: &PlainSymbol) -> Option<Variable> {
|
|
|
|
if sym.is_var_symbol() {
|
|
|
|
Some(Variable(Rc::new(sym.clone())))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2017-02-16 15:16:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for Variable {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "var({})", self.0)
|
|
|
|
}
|
2017-02-01 22:51:02 +00:00
|
|
|
}
|
|
|
|
|
2017-03-16 14:44:56 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
pub struct PredicateFn(pub PlainSymbol);
|
|
|
|
|
|
|
|
impl FromValue<PredicateFn> for PredicateFn {
|
2017-05-04 20:40:41 +00:00
|
|
|
fn from_value(v: &edn::ValueAndSpan) -> Option<PredicateFn> {
|
2017-04-06 17:06:28 +00:00
|
|
|
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
2017-03-16 14:44:56 +00:00
|
|
|
PredicateFn::from_symbol(s)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PredicateFn {
|
|
|
|
pub fn from_symbol(sym: &PlainSymbol) -> Option<PredicateFn> {
|
|
|
|
// TODO: validate the acceptable set of function names.
|
|
|
|
Some(PredicateFn(sym.clone()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Implement :order. (#415) (#416) r=nalexander
This adds an `:order` keyword to `:find`.
If present, the results of the query will be an ordered set, rather than
an unordered set; rows will appear in an ordered defined by each
`:order` entry.
Each can be one of three things:
- A var, `?x`, meaning "order by ?x ascending".
- A pair, `(asc ?x)`, meaning "order by ?x ascending".
- A pair, `(desc ?x)`, meaning "order by ?x descending".
Values will be ordered in this sequence for asc, and in reverse for desc:
1. Entity IDs, in ascending numerical order.
2. Booleans, false then true.
3. Timestamps, in ascending numerical order.
4. Longs and doubles, intermixed, in ascending numerical order.
5. Strings, in ascending lexicographic order.
6. Keywords, in ascending lexicographic order, considering the entire
ns/name pair as a single string separated by '/'.
Subcommits:
Pre: make bound_value public.
Pre: generalize ErrorKind::UnboundVariable for use in order.
Part 1: parse (direction, var) pairs.
Part 2: parse :order clause into FindQuery.
Part 3: include order variables in algebrized query.
We add order variables to :with, so we can reuse its type tag projection
logic, and so that we can phrase ordering in terms of variables rather
than datoms columns.
Part 4: produce SQL for order clauses.
2017-04-14 23:10:56 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum Direction {
|
|
|
|
Ascending,
|
|
|
|
Descending,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An abstract declaration of ordering: direction and variable.
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct Order(pub Direction, pub Variable); // Future: Element instead of Variable?
|
|
|
|
|
2017-04-18 01:46:40 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
2017-01-25 22:06:19 +00:00
|
|
|
pub enum SrcVar {
|
|
|
|
DefaultSrc,
|
|
|
|
NamedSrc(SrcVarName),
|
|
|
|
}
|
|
|
|
|
2017-02-01 22:51:02 +00:00
|
|
|
impl FromValue<SrcVar> for SrcVar {
|
2017-05-04 20:40:41 +00:00
|
|
|
fn from_value(v: &edn::ValueAndSpan) -> Option<SrcVar> {
|
2017-04-06 17:06:28 +00:00
|
|
|
if let edn::SpannedValue::PlainSymbol(ref s) = v.inner {
|
2017-02-01 22:51:02 +00:00
|
|
|
SrcVar::from_symbol(s)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SrcVar {
|
|
|
|
pub fn from_symbol(sym: &PlainSymbol) -> Option<SrcVar> {
|
|
|
|
if sym.is_src_symbol() {
|
2017-04-05 22:30:22 +00:00
|
|
|
if sym.0 == "$" {
|
|
|
|
Some(SrcVar::DefaultSrc)
|
|
|
|
} else {
|
|
|
|
Some(SrcVar::NamedSrc(sym.plain_name().to_string()))
|
|
|
|
}
|
2017-02-01 22:51:02 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-25 22:06:19 +00:00
|
|
|
/// These are the scalar values representable in EDN.
|
2017-02-03 02:32:00 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2017-01-25 22:06:19 +00:00
|
|
|
pub enum NonIntegerConstant {
|
|
|
|
Boolean(bool),
|
|
|
|
BigInteger(BigInt),
|
|
|
|
Float(OrderedFloat<f64>),
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
Text(Rc<String>),
|
2017-11-21 16:24:08 +00:00
|
|
|
Instant(DateTime<Utc>),
|
2017-04-29 03:11:55 +00:00
|
|
|
Uuid(Uuid),
|
2017-01-25 22:06:19 +00:00
|
|
|
}
|
|
|
|
|
2017-02-10 00:59:35 +00:00
|
|
|
impl NonIntegerConstant {
|
|
|
|
pub fn into_typed_value(self) -> TypedValue {
|
|
|
|
match self {
|
|
|
|
NonIntegerConstant::BigInteger(_) => unimplemented!(), // TODO: #280.
|
|
|
|
NonIntegerConstant::Boolean(v) => TypedValue::Boolean(v),
|
|
|
|
NonIntegerConstant::Float(v) => TypedValue::Double(v),
|
|
|
|
NonIntegerConstant::Text(v) => TypedValue::String(v),
|
2017-04-29 03:11:55 +00:00
|
|
|
NonIntegerConstant::Instant(v) => TypedValue::Instant(v),
|
|
|
|
NonIntegerConstant::Uuid(v) => TypedValue::Uuid(v),
|
2017-02-10 00:59:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-16 14:44:56 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2017-01-25 22:06:19 +00:00
|
|
|
pub enum FnArg {
|
|
|
|
Variable(Variable),
|
|
|
|
SrcVar(SrcVar),
|
|
|
|
EntidOrInteger(i64),
|
2017-04-05 22:30:22 +00:00
|
|
|
IdentOrKeyword(NamespacedKeyword),
|
2017-01-25 22:06:19 +00:00
|
|
|
Constant(NonIntegerConstant),
|
2017-04-19 18:10:24 +00:00
|
|
|
// The collection values representable in EDN. There's no advantage to destructuring up front,
|
|
|
|
// since consumers will need to handle arbitrarily nested EDN themselves anyway.
|
|
|
|
Vector(Vec<FnArg>),
|
2017-01-25 22:06:19 +00:00
|
|
|
}
|
|
|
|
|
2017-03-16 14:44:56 +00:00
|
|
|
impl FromValue<FnArg> for FnArg {
|
2017-05-04 20:40:41 +00:00
|
|
|
fn from_value(v: &edn::ValueAndSpan) -> Option<FnArg> {
|
2017-04-05 22:30:22 +00:00
|
|
|
use edn::SpannedValue::*;
|
|
|
|
match v.inner {
|
|
|
|
Integer(x) =>
|
|
|
|
Some(FnArg::EntidOrInteger(x)),
|
|
|
|
PlainSymbol(ref x) if x.is_src_symbol() =>
|
|
|
|
SrcVar::from_symbol(x).map(FnArg::SrcVar),
|
|
|
|
PlainSymbol(ref x) if x.is_var_symbol() =>
|
|
|
|
Variable::from_symbol(x).map(FnArg::Variable),
|
|
|
|
PlainSymbol(_) => None,
|
|
|
|
NamespacedKeyword(ref x) =>
|
|
|
|
Some(FnArg::IdentOrKeyword(x.clone())),
|
|
|
|
Instant(x) =>
|
|
|
|
Some(FnArg::Constant(NonIntegerConstant::Instant(x))),
|
|
|
|
Uuid(x) =>
|
|
|
|
Some(FnArg::Constant(NonIntegerConstant::Uuid(x))),
|
|
|
|
Boolean(x) =>
|
|
|
|
Some(FnArg::Constant(NonIntegerConstant::Boolean(x))),
|
|
|
|
Float(x) =>
|
|
|
|
Some(FnArg::Constant(NonIntegerConstant::Float(x))),
|
|
|
|
BigInteger(ref x) =>
|
|
|
|
Some(FnArg::Constant(NonIntegerConstant::BigInteger(x.clone()))),
|
|
|
|
Text(ref x) =>
|
|
|
|
// TODO: intern strings. #398.
|
|
|
|
Some(FnArg::Constant(NonIntegerConstant::Text(Rc::new(x.clone())))),
|
|
|
|
Nil |
|
|
|
|
NamespacedSymbol(_) |
|
|
|
|
Keyword(_) |
|
|
|
|
Vector(_) |
|
|
|
|
List(_) |
|
|
|
|
Set(_) |
|
|
|
|
Map(_) => None,
|
|
|
|
}
|
2017-03-16 14:44:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-14 23:17:25 +00:00
|
|
|
impl FnArg {
|
|
|
|
pub fn as_variable(&self) -> Option<&Variable> {
|
|
|
|
match self {
|
|
|
|
&FnArg::Variable(ref v) => Some(v),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-25 22:06:19 +00:00
|
|
|
/// e, a, tx can't be values -- no strings, no floats -- and so
|
|
|
|
/// they can only be variables, entity IDs, ident keywords, or
|
|
|
|
/// placeholders.
|
|
|
|
/// This encoding allows us to represent integers that aren't
|
|
|
|
/// entity IDs. That'll get filtered out in the context of the
|
|
|
|
/// database.
|
2017-02-03 02:32:00 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2017-01-25 22:06:19 +00:00
|
|
|
pub enum PatternNonValuePlace {
|
|
|
|
Placeholder,
|
|
|
|
Variable(Variable),
|
2017-02-03 02:32:00 +00:00
|
|
|
Entid(i64), // Will always be +ve. See #190.
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
Ident(Rc<NamespacedKeyword>),
|
2017-01-25 22:06:19 +00:00
|
|
|
}
|
|
|
|
|
2017-02-03 02:32:00 +00:00
|
|
|
impl PatternNonValuePlace {
|
|
|
|
// I think we'll want move variants, so let's leave these here for now.
|
|
|
|
#[allow(dead_code)]
|
|
|
|
fn into_pattern_value_place(self) -> PatternValuePlace {
|
|
|
|
match self {
|
|
|
|
PatternNonValuePlace::Placeholder => PatternValuePlace::Placeholder,
|
|
|
|
PatternNonValuePlace::Variable(x) => PatternValuePlace::Variable(x),
|
|
|
|
PatternNonValuePlace::Entid(x) => PatternValuePlace::EntidOrInteger(x),
|
|
|
|
PatternNonValuePlace::Ident(x) => PatternValuePlace::IdentOrKeyword(x),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_pattern_value_place(&self) -> PatternValuePlace {
|
|
|
|
match *self {
|
|
|
|
PatternNonValuePlace::Placeholder => PatternValuePlace::Placeholder,
|
|
|
|
PatternNonValuePlace::Variable(ref x) => PatternValuePlace::Variable(x.clone()),
|
|
|
|
PatternNonValuePlace::Entid(x) => PatternValuePlace::EntidOrInteger(x),
|
|
|
|
PatternNonValuePlace::Ident(ref x) => PatternValuePlace::IdentOrKeyword(x.clone()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromValue<PatternNonValuePlace> for PatternNonValuePlace {
|
2017-05-04 20:40:41 +00:00
|
|
|
fn from_value(v: &edn::ValueAndSpan) -> Option<PatternNonValuePlace> {
|
2017-04-06 17:06:28 +00:00
|
|
|
match v.inner {
|
|
|
|
edn::SpannedValue::Integer(x) => if x >= 0 {
|
2017-02-03 02:32:00 +00:00
|
|
|
Some(PatternNonValuePlace::Entid(x))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::SpannedValue::PlainSymbol(ref x) => if x.0.as_str() == "_" {
|
2017-02-03 02:32:00 +00:00
|
|
|
Some(PatternNonValuePlace::Placeholder)
|
|
|
|
} else {
|
|
|
|
if let Some(v) = Variable::from_symbol(x) {
|
|
|
|
Some(PatternNonValuePlace::Variable(v))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
},
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::SpannedValue::NamespacedKeyword(ref x) =>
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
Some(PatternNonValuePlace::Ident(Rc::new(x.clone()))),
|
2017-02-03 02:32:00 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-04 00:52:03 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum IdentOrEntid {
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
Ident(Rc<NamespacedKeyword>),
|
2017-02-04 00:52:03 +00:00
|
|
|
Entid(i64),
|
|
|
|
}
|
|
|
|
|
2017-01-25 22:06:19 +00:00
|
|
|
/// The `v` part of a pattern can be much broader: it can represent
|
|
|
|
/// integers that aren't entity IDs (particularly negative integers),
|
|
|
|
/// strings, and all the rest. We group those under `Constant`.
|
2017-02-03 02:32:00 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2017-01-25 22:06:19 +00:00
|
|
|
pub enum PatternValuePlace {
|
|
|
|
Placeholder,
|
|
|
|
Variable(Variable),
|
|
|
|
EntidOrInteger(i64),
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
IdentOrKeyword(Rc<NamespacedKeyword>),
|
2017-01-25 22:06:19 +00:00
|
|
|
Constant(NonIntegerConstant),
|
|
|
|
}
|
|
|
|
|
2017-02-03 02:32:00 +00:00
|
|
|
impl FromValue<PatternValuePlace> for PatternValuePlace {
|
2017-05-04 20:40:41 +00:00
|
|
|
fn from_value(v: &edn::ValueAndSpan) -> Option<PatternValuePlace> {
|
2017-04-06 17:06:28 +00:00
|
|
|
match v.inner {
|
|
|
|
edn::SpannedValue::Integer(x) =>
|
2017-02-03 02:32:00 +00:00
|
|
|
Some(PatternValuePlace::EntidOrInteger(x)),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::SpannedValue::PlainSymbol(ref x) if x.0.as_str() == "_" =>
|
2017-02-03 02:32:00 +00:00
|
|
|
Some(PatternValuePlace::Placeholder),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::SpannedValue::PlainSymbol(ref x) =>
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
Variable::from_symbol(x).map(PatternValuePlace::Variable),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::SpannedValue::NamespacedKeyword(ref x) =>
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
Some(PatternValuePlace::IdentOrKeyword(Rc::new(x.clone()))),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::SpannedValue::Boolean(x) =>
|
2017-02-03 02:32:00 +00:00
|
|
|
Some(PatternValuePlace::Constant(NonIntegerConstant::Boolean(x))),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::SpannedValue::Float(x) =>
|
2017-02-03 02:32:00 +00:00
|
|
|
Some(PatternValuePlace::Constant(NonIntegerConstant::Float(x))),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::SpannedValue::BigInteger(ref x) =>
|
2017-02-03 02:32:00 +00:00
|
|
|
Some(PatternValuePlace::Constant(NonIntegerConstant::BigInteger(x.clone()))),
|
2017-04-29 03:11:55 +00:00
|
|
|
edn::SpannedValue::Instant(x) =>
|
|
|
|
Some(PatternValuePlace::Constant(NonIntegerConstant::Instant(x))),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::SpannedValue::Text(ref x) =>
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
// TODO: intern strings. #398.
|
|
|
|
Some(PatternValuePlace::Constant(NonIntegerConstant::Text(Rc::new(x.clone())))),
|
2017-04-29 03:11:55 +00:00
|
|
|
edn::SpannedValue::Uuid(ref u) =>
|
|
|
|
Some(PatternValuePlace::Constant(NonIntegerConstant::Uuid(u.clone()))),
|
|
|
|
|
|
|
|
// These don't appear in queries.
|
|
|
|
edn::SpannedValue::Nil => None,
|
|
|
|
edn::SpannedValue::NamespacedSymbol(_) => None,
|
|
|
|
edn::SpannedValue::Keyword(_) => None,
|
|
|
|
edn::SpannedValue::Map(_) => None,
|
|
|
|
edn::SpannedValue::List(_) => None,
|
|
|
|
edn::SpannedValue::Set(_) => None,
|
|
|
|
edn::SpannedValue::Vector(_) => None,
|
2017-02-03 02:32:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PatternValuePlace {
|
|
|
|
// I think we'll want move variants, so let's leave these here for now.
|
|
|
|
#[allow(dead_code)]
|
|
|
|
fn into_pattern_non_value_place(self) -> Option<PatternNonValuePlace> {
|
|
|
|
match self {
|
|
|
|
PatternValuePlace::Placeholder => Some(PatternNonValuePlace::Placeholder),
|
|
|
|
PatternValuePlace::Variable(x) => Some(PatternNonValuePlace::Variable(x)),
|
|
|
|
PatternValuePlace::EntidOrInteger(x) => if x >= 0 {
|
|
|
|
Some(PatternNonValuePlace::Entid(x))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
|
|
|
PatternValuePlace::IdentOrKeyword(x) => Some(PatternNonValuePlace::Ident(x)),
|
|
|
|
PatternValuePlace::Constant(_) => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_pattern_non_value_place(&self) -> Option<PatternNonValuePlace> {
|
|
|
|
match *self {
|
|
|
|
PatternValuePlace::Placeholder => Some(PatternNonValuePlace::Placeholder),
|
|
|
|
PatternValuePlace::Variable(ref x) => Some(PatternNonValuePlace::Variable(x.clone())),
|
|
|
|
PatternValuePlace::EntidOrInteger(x) => if x >= 0 {
|
|
|
|
Some(PatternNonValuePlace::Entid(x))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
|
|
|
PatternValuePlace::IdentOrKeyword(ref x) => Some(PatternNonValuePlace::Ident(x.clone())),
|
|
|
|
PatternValuePlace::Constant(_) => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-25 22:06:19 +00:00
|
|
|
/*
|
|
|
|
pub enum PullPattern {
|
|
|
|
Constant(Constant),
|
|
|
|
Variable(Variable),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Pull {
|
|
|
|
pub src: SrcVar,
|
|
|
|
pub var: Variable,
|
|
|
|
pub pattern: PullPattern, // Constant, variable, or plain variable.
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
pub struct Aggregate {
|
|
|
|
pub fn_name: String,
|
|
|
|
pub args: Vec<FnArg>,
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2017-04-19 23:16:19 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2017-01-25 22:06:19 +00:00
|
|
|
pub enum Element {
|
|
|
|
Variable(Variable),
|
|
|
|
// Aggregate(Aggregate), // TODO
|
|
|
|
// Pull(Pull), // TODO
|
|
|
|
}
|
|
|
|
|
2017-04-19 23:16:19 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum Limit {
|
|
|
|
None,
|
|
|
|
Fixed(u64),
|
|
|
|
Variable(Variable),
|
|
|
|
}
|
|
|
|
|
2017-01-25 22:06:19 +00:00
|
|
|
/// A definition of the first part of a find query: the
|
|
|
|
/// `[:find ?foo ?bar…]` bit.
|
|
|
|
///
|
|
|
|
/// There are four different kinds of find specs, allowing you to query for
|
|
|
|
/// a single value, a collection of values from different entities, a single
|
|
|
|
/// tuple (relation), or a collection of tuples.
|
|
|
|
///
|
|
|
|
/// Examples:
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # extern crate edn;
|
|
|
|
/// # extern crate mentat_query;
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
/// # use std::rc::Rc;
|
2017-01-25 22:06:19 +00:00
|
|
|
/// # use edn::PlainSymbol;
|
|
|
|
/// # use mentat_query::{Element, FindSpec, Variable};
|
|
|
|
///
|
|
|
|
/// # fn main() {
|
|
|
|
///
|
|
|
|
/// let elements = vec![
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
/// Element::Variable(Variable::from_valid_name("?foo")),
|
|
|
|
/// Element::Variable(Variable::from_valid_name("?bar")),
|
2017-01-25 22:06:19 +00:00
|
|
|
/// ];
|
|
|
|
/// let rel = FindSpec::FindRel(elements);
|
|
|
|
///
|
|
|
|
/// if let FindSpec::FindRel(elements) = rel {
|
|
|
|
/// assert_eq!(2, elements.len());
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
///
|
2017-04-19 23:16:19 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2017-01-25 22:06:19 +00:00
|
|
|
pub enum FindSpec {
|
|
|
|
/// Returns an array of arrays.
|
|
|
|
FindRel(Vec<Element>),
|
|
|
|
|
|
|
|
/// Returns an array of scalars, usually homogeneous.
|
|
|
|
/// This is equivalent to mapping over the results of a `FindRel`,
|
|
|
|
/// returning the first value of each.
|
|
|
|
FindColl(Element),
|
|
|
|
|
|
|
|
/// Returns a single tuple: a heterogeneous array of scalars. Equivalent to
|
|
|
|
/// taking the first result from a `FindRel`.
|
|
|
|
FindTuple(Vec<Element>),
|
|
|
|
|
|
|
|
/// Returns a single scalar value. Equivalent to taking the first result
|
|
|
|
/// from a `FindColl`.
|
|
|
|
FindScalar(Element),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true if the provided `FindSpec` returns at most one result.
|
2017-03-06 22:40:10 +00:00
|
|
|
impl FindSpec {
|
|
|
|
pub fn is_unit_limited(&self) -> bool {
|
|
|
|
use FindSpec::*;
|
|
|
|
match self {
|
|
|
|
&FindScalar(..) => true,
|
|
|
|
&FindTuple(..) => true,
|
|
|
|
&FindRel(..) => false,
|
|
|
|
&FindColl(..) => false,
|
|
|
|
}
|
2017-01-25 22:06:19 +00:00
|
|
|
}
|
|
|
|
|
2017-03-06 22:40:10 +00:00
|
|
|
pub fn expected_column_count(&self) -> usize {
|
|
|
|
use FindSpec::*;
|
|
|
|
match self {
|
|
|
|
&FindScalar(..) => 1,
|
|
|
|
&FindColl(..) => 1,
|
|
|
|
&FindTuple(ref elems) | &FindRel(ref elems) => elems.len(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Returns true if the provided `FindSpec` cares about distinct results.
|
|
|
|
///
|
|
|
|
/// I use the words "cares about" because find is generally defined in terms of producing distinct
|
|
|
|
/// results at the Datalog level.
|
|
|
|
///
|
|
|
|
/// Two of the find specs (scalar and tuple) produce only a single result. Those don't need to be
|
|
|
|
/// run with `SELECT DISTINCT`, because we're only consuming a single result. Those queries will be
|
|
|
|
/// run with `LIMIT 1`.
|
|
|
|
///
|
|
|
|
/// Additionally, some projections cannot produce duplicate results: `[:find (max ?x) …]`, for
|
|
|
|
/// example.
|
|
|
|
///
|
|
|
|
/// This function gives us the hook to add that logic when we're ready.
|
|
|
|
///
|
|
|
|
/// Beyond this, `DISTINCT` is not always needed. For example, in some kinds of accumulation or
|
|
|
|
/// sampling projections we might not need to do it at the SQL level because we're consuming into
|
|
|
|
/// a dupe-eliminating data structure like a Set, or we know that a particular query cannot produce
|
|
|
|
/// duplicate results.
|
|
|
|
pub fn requires_distinct(&self) -> bool {
|
|
|
|
!self.is_unit_limited()
|
|
|
|
}
|
2017-01-25 22:06:19 +00:00
|
|
|
}
|
|
|
|
|
2017-04-03 23:46:11 +00:00
|
|
|
// Datomic accepts variable or placeholder. DataScript accepts recursive bindings. Mentat sticks
|
|
|
|
// to the non-recursive form Datomic accepts, which is much simpler to process.
|
2017-04-26 22:50:17 +00:00
|
|
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
2017-04-03 23:46:11 +00:00
|
|
|
pub enum VariableOrPlaceholder {
|
|
|
|
Placeholder,
|
|
|
|
Variable(Variable),
|
|
|
|
}
|
|
|
|
|
2017-04-26 22:50:17 +00:00
|
|
|
impl VariableOrPlaceholder {
|
|
|
|
pub fn into_var(self) -> Option<Variable> {
|
|
|
|
match self {
|
|
|
|
VariableOrPlaceholder::Placeholder => None,
|
|
|
|
VariableOrPlaceholder::Variable(var) => Some(var),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn var(&self) -> Option<&Variable> {
|
|
|
|
match self {
|
|
|
|
&VariableOrPlaceholder::Placeholder => None,
|
|
|
|
&VariableOrPlaceholder::Variable(ref var) => Some(var),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-03 23:46:11 +00:00
|
|
|
#[derive(Clone,Debug,Eq,PartialEq)]
|
|
|
|
pub enum Binding {
|
2017-04-26 22:50:17 +00:00
|
|
|
BindScalar(Variable),
|
|
|
|
BindColl(Variable),
|
2017-04-03 23:46:11 +00:00
|
|
|
BindRel(Vec<VariableOrPlaceholder>),
|
2017-04-26 22:50:17 +00:00
|
|
|
BindTuple(Vec<VariableOrPlaceholder>),
|
|
|
|
}
|
2017-04-03 23:46:11 +00:00
|
|
|
|
2017-04-26 22:50:17 +00:00
|
|
|
impl Binding {
|
|
|
|
/// Return each variable or `None`, in order.
|
|
|
|
pub fn variables(&self) -> Vec<Option<Variable>> {
|
|
|
|
match self {
|
|
|
|
&Binding::BindScalar(ref var) | &Binding::BindColl(ref var) => vec![Some(var.clone())],
|
|
|
|
&Binding::BindRel(ref vars) | &Binding::BindTuple(ref vars) => vars.iter().map(|x| x.var().cloned()).collect(),
|
|
|
|
}
|
|
|
|
}
|
2017-04-03 23:46:11 +00:00
|
|
|
|
2017-04-26 22:50:17 +00:00
|
|
|
/// Return `true` if no variables are bound, i.e., all binding entries are placeholders.
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
&Binding::BindScalar(_) | &Binding::BindColl(_) => false,
|
|
|
|
&Binding::BindRel(ref vars) | &Binding::BindTuple(ref vars) => vars.iter().all(|x| x.var().is_none()),
|
|
|
|
}
|
|
|
|
}
|
2017-04-03 23:46:11 +00:00
|
|
|
|
2017-04-26 22:50:17 +00:00
|
|
|
/// Return `true` if no variable is bound twice, i.e., each binding entry is either a
|
|
|
|
/// placeholder or unique.
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// extern crate mentat_query;
|
|
|
|
/// use std::rc::Rc;
|
|
|
|
///
|
|
|
|
/// let v = mentat_query::Variable::from_valid_name("?foo");
|
|
|
|
/// let vv = mentat_query::VariableOrPlaceholder::Variable(v);
|
|
|
|
/// let p = mentat_query::VariableOrPlaceholder::Placeholder;
|
|
|
|
///
|
|
|
|
/// let e = mentat_query::Binding::BindTuple(vec![p.clone()]);
|
|
|
|
/// let b = mentat_query::Binding::BindTuple(vec![p.clone(), vv.clone()]);
|
|
|
|
/// let d = mentat_query::Binding::BindTuple(vec![vv.clone(), p, vv]);
|
|
|
|
/// assert!(b.is_valid()); // One var, one placeholder: OK.
|
|
|
|
/// assert!(!e.is_valid()); // Empty: not OK.
|
|
|
|
/// assert!(!d.is_valid()); // Duplicate var: not OK.
|
|
|
|
/// ```
|
|
|
|
pub fn is_valid(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
&Binding::BindScalar(_) | &Binding::BindColl(_) => true,
|
|
|
|
&Binding::BindRel(ref vars) | &Binding::BindTuple(ref vars) => {
|
|
|
|
let mut acc = HashSet::<Variable>::new();
|
|
|
|
for var in vars {
|
|
|
|
if let &VariableOrPlaceholder::Variable(ref var) = var {
|
|
|
|
if !acc.insert(var.clone()) {
|
|
|
|
// It's invalid if there was an equal var already present in the set --
|
|
|
|
// i.e., we have a duplicate var.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// We're not valid if every place is a placeholder!
|
|
|
|
!acc.is_empty()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-03 23:46:11 +00:00
|
|
|
}
|
|
|
|
|
2017-01-25 22:06:19 +00:00
|
|
|
// Note that the "implicit blank" rule applies.
|
|
|
|
// A pattern with a reversed attribute — :foo/_bar — is reversed
|
|
|
|
// at the point of parsing. These `Pattern` instances only represent
|
|
|
|
// one direction.
|
2017-02-03 02:32:00 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2017-01-25 22:06:19 +00:00
|
|
|
pub struct Pattern {
|
2017-02-03 02:32:00 +00:00
|
|
|
pub source: Option<SrcVar>,
|
|
|
|
pub entity: PatternNonValuePlace,
|
|
|
|
pub attribute: PatternNonValuePlace,
|
|
|
|
pub value: PatternValuePlace,
|
|
|
|
pub tx: PatternNonValuePlace,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Pattern {
|
|
|
|
pub fn new(src: Option<SrcVar>,
|
|
|
|
e: PatternNonValuePlace,
|
|
|
|
a: PatternNonValuePlace,
|
|
|
|
v: PatternValuePlace,
|
|
|
|
tx: PatternNonValuePlace) -> Option<Pattern> {
|
|
|
|
let aa = a.clone(); // Too tired of fighting borrow scope for now.
|
|
|
|
if let PatternNonValuePlace::Ident(ref k) = aa {
|
|
|
|
if k.is_backward() {
|
|
|
|
// e and v have different types; we must convert them.
|
|
|
|
// Not every parseable value is suitable for the entity field!
|
|
|
|
// As such, this is a failable constructor.
|
|
|
|
let e_v = e.to_pattern_value_place();
|
|
|
|
if let Some(v_e) = v.to_pattern_non_value_place() {
|
|
|
|
return Some(Pattern {
|
|
|
|
source: src,
|
|
|
|
entity: v_e,
|
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
|
|
|
attribute: PatternNonValuePlace::Ident(Rc::new(k.to_reversed())),
|
2017-02-03 02:32:00 +00:00
|
|
|
value: e_v,
|
|
|
|
tx: tx,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(Pattern {
|
|
|
|
source: src,
|
|
|
|
entity: e,
|
|
|
|
attribute: a,
|
|
|
|
value: v,
|
|
|
|
tx: tx,
|
|
|
|
})
|
|
|
|
}
|
2017-01-25 22:06:19 +00:00
|
|
|
}
|
|
|
|
|
2017-03-16 14:44:56 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct Predicate {
|
|
|
|
pub operator: PlainSymbol,
|
|
|
|
pub args: Vec<FnArg>,
|
|
|
|
}
|
2017-02-03 02:32:00 +00:00
|
|
|
|
2017-04-03 23:46:11 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct WhereFn {
|
|
|
|
pub operator: PlainSymbol,
|
|
|
|
pub args: Vec<FnArg>,
|
|
|
|
pub binding: Binding,
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:10:44 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum UnifyVars {
|
|
|
|
/// `Implicit` means the variables in an `or` or `not` are derived from the enclosed pattern.
|
|
|
|
/// DataScript regards these vars as 'free': these variables don't need to be bound by the
|
|
|
|
/// enclosing environment.
|
|
|
|
///
|
|
|
|
/// Datomic's documentation implies that all implicit variables are required:
|
|
|
|
///
|
|
|
|
/// > Datomic will attempt to push the or clause down until all necessary variables are bound,
|
|
|
|
/// > and will throw an exception if that is not possible.
|
|
|
|
///
|
|
|
|
/// but that would render top-level `or` expressions (as used in Datomic's own examples!)
|
|
|
|
/// impossible, so we assume that this is an error in the documentation.
|
|
|
|
///
|
|
|
|
/// All contained 'arms' in an `or` with implicit variables must bind the same vars.
|
|
|
|
Implicit,
|
|
|
|
|
|
|
|
/// `Explicit` means the variables in an `or-join` or `not-join` are explicitly listed,
|
|
|
|
/// specified with `required-vars` syntax.
|
|
|
|
///
|
|
|
|
/// DataScript parses these as free, but allows (incorrectly) the use of more complicated
|
|
|
|
/// `rule-vars` syntax.
|
|
|
|
///
|
|
|
|
/// Only the named variables will be unified with the enclosing query.
|
|
|
|
///
|
|
|
|
/// Every 'arm' in an `or-join` must mention the entire set of explicit vars.
|
2017-06-02 22:36:12 +00:00
|
|
|
Explicit(BTreeSet<Variable>),
|
2017-03-23 20:10:44 +00:00
|
|
|
}
|
|
|
|
|
2017-03-28 03:53:08 +00:00
|
|
|
impl WhereClause {
|
|
|
|
pub fn is_pattern(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
&WhereClause::Pattern(_) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:10:44 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum OrWhereClause {
|
|
|
|
Clause(WhereClause),
|
|
|
|
And(Vec<WhereClause>),
|
|
|
|
}
|
|
|
|
|
2017-03-28 03:53:08 +00:00
|
|
|
impl OrWhereClause {
|
|
|
|
pub fn is_pattern_or_patterns(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
&OrWhereClause::Clause(WhereClause::Pattern(_)) => true,
|
|
|
|
&OrWhereClause::And(ref clauses) => clauses.iter().all(|clause| clause.is_pattern()),
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:10:44 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct OrJoin {
|
|
|
|
pub unify_vars: UnifyVars,
|
|
|
|
pub clauses: Vec<OrWhereClause>,
|
2017-04-04 21:54:08 +00:00
|
|
|
|
|
|
|
/// Caches the result of `collect_mentioned_variables`.
|
|
|
|
mentioned_vars: Option<BTreeSet<Variable>>,
|
2017-03-23 20:10:44 +00:00
|
|
|
}
|
|
|
|
|
2017-04-28 09:44:11 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct NotJoin {
|
|
|
|
pub unify_vars: UnifyVars,
|
|
|
|
pub clauses: Vec<WhereClause>,
|
|
|
|
}
|
|
|
|
|
2017-01-25 22:06:19 +00:00
|
|
|
#[allow(dead_code)]
|
2017-02-03 02:32:00 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2017-01-25 22:06:19 +00:00
|
|
|
pub enum WhereClause {
|
2017-04-28 09:44:11 +00:00
|
|
|
NotJoin(NotJoin),
|
2017-03-23 20:10:44 +00:00
|
|
|
OrJoin(OrJoin),
|
2017-03-16 14:44:56 +00:00
|
|
|
Pred(Predicate),
|
2017-04-03 23:46:11 +00:00
|
|
|
WhereFn(WhereFn),
|
2017-01-25 22:06:19 +00:00
|
|
|
RuleExpr,
|
2017-02-03 02:32:00 +00:00
|
|
|
Pattern(Pattern),
|
2017-01-25 22:06:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
2017-02-03 02:32:00 +00:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct FindQuery {
|
|
|
|
pub find_spec: FindSpec,
|
|
|
|
pub default_source: SrcVar,
|
2017-04-18 01:46:40 +00:00
|
|
|
pub with: BTreeSet<Variable>,
|
|
|
|
pub in_vars: BTreeSet<Variable>,
|
|
|
|
pub in_sources: BTreeSet<SrcVar>,
|
2017-04-19 23:16:19 +00:00
|
|
|
pub limit: Limit,
|
2017-02-03 02:32:00 +00:00
|
|
|
pub where_clauses: Vec<WhereClause>,
|
Implement :order. (#415) (#416) r=nalexander
This adds an `:order` keyword to `:find`.
If present, the results of the query will be an ordered set, rather than
an unordered set; rows will appear in an ordered defined by each
`:order` entry.
Each can be one of three things:
- A var, `?x`, meaning "order by ?x ascending".
- A pair, `(asc ?x)`, meaning "order by ?x ascending".
- A pair, `(desc ?x)`, meaning "order by ?x descending".
Values will be ordered in this sequence for asc, and in reverse for desc:
1. Entity IDs, in ascending numerical order.
2. Booleans, false then true.
3. Timestamps, in ascending numerical order.
4. Longs and doubles, intermixed, in ascending numerical order.
5. Strings, in ascending lexicographic order.
6. Keywords, in ascending lexicographic order, considering the entire
ns/name pair as a single string separated by '/'.
Subcommits:
Pre: make bound_value public.
Pre: generalize ErrorKind::UnboundVariable for use in order.
Part 1: parse (direction, var) pairs.
Part 2: parse :order clause into FindQuery.
Part 3: include order variables in algebrized query.
We add order variables to :with, so we can reuse its type tag projection
logic, and so that we can phrase ordering in terms of variables rather
than datoms columns.
Part 4: produce SQL for order clauses.
2017-04-14 23:10:56 +00:00
|
|
|
pub order: Option<Vec<Order>>,
|
2017-01-25 22:06:19 +00:00
|
|
|
// TODO: in_rules;
|
|
|
|
}
|
2017-03-24 20:09:32 +00:00
|
|
|
|
2017-03-29 00:38:01 +00:00
|
|
|
impl OrJoin {
|
2017-04-04 21:54:08 +00:00
|
|
|
pub fn new(unify_vars: UnifyVars, clauses: Vec<OrWhereClause>) -> OrJoin {
|
|
|
|
OrJoin {
|
|
|
|
unify_vars: unify_vars,
|
|
|
|
clauses: clauses,
|
|
|
|
mentioned_vars: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-29 00:38:01 +00:00
|
|
|
/// Return true if either the `OrJoin` is `UnifyVars::Implicit`, or if
|
|
|
|
/// every variable mentioned inside the join is also mentioned in the `UnifyVars` list.
|
|
|
|
pub fn is_fully_unified(&self) -> bool {
|
|
|
|
match &self.unify_vars {
|
|
|
|
&UnifyVars::Implicit => true,
|
|
|
|
&UnifyVars::Explicit(ref vars) => {
|
|
|
|
// We know that the join list must be a subset of the vars in the pattern, or
|
|
|
|
// it would have failed validation. That allows us to simply compare counts here.
|
|
|
|
// TODO: in debug mode, do a full intersection, and verify that our count check
|
|
|
|
// returns the same results.
|
2017-04-04 21:54:08 +00:00
|
|
|
// Use the cached list if we have one.
|
|
|
|
if let Some(ref mentioned) = self.mentioned_vars {
|
|
|
|
vars.len() == mentioned.len()
|
|
|
|
} else {
|
|
|
|
vars.len() == self.collect_mentioned_variables().len()
|
|
|
|
}
|
2017-03-29 00:38:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-24 20:09:32 +00:00
|
|
|
pub trait ContainsVariables {
|
|
|
|
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>);
|
|
|
|
fn collect_mentioned_variables(&self) -> BTreeSet<Variable> {
|
|
|
|
let mut out = BTreeSet::new();
|
|
|
|
self.accumulate_mentioned_variables(&mut out);
|
|
|
|
out
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ContainsVariables for WhereClause {
|
|
|
|
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
|
|
|
use WhereClause::*;
|
|
|
|
match self {
|
|
|
|
&OrJoin(ref o) => o.accumulate_mentioned_variables(acc),
|
|
|
|
&Pred(ref p) => p.accumulate_mentioned_variables(acc),
|
|
|
|
&Pattern(ref p) => p.accumulate_mentioned_variables(acc),
|
2017-04-28 09:44:11 +00:00
|
|
|
&NotJoin(ref n) => n.accumulate_mentioned_variables(acc),
|
2017-04-03 23:46:11 +00:00
|
|
|
&WhereFn(ref f) => f.accumulate_mentioned_variables(acc),
|
2017-03-24 20:09:32 +00:00
|
|
|
&RuleExpr => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ContainsVariables for OrWhereClause {
|
|
|
|
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
|
|
|
use OrWhereClause::*;
|
|
|
|
match self {
|
|
|
|
&And(ref clauses) => for clause in clauses { clause.accumulate_mentioned_variables(acc) },
|
|
|
|
&Clause(ref clause) => clause.accumulate_mentioned_variables(acc),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ContainsVariables for OrJoin {
|
|
|
|
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
|
|
|
for clause in &self.clauses {
|
|
|
|
clause.accumulate_mentioned_variables(acc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-04 21:54:08 +00:00
|
|
|
impl OrJoin {
|
2017-04-11 17:31:31 +00:00
|
|
|
pub fn dismember(self) -> (Vec<OrWhereClause>, UnifyVars, BTreeSet<Variable>) {
|
2017-04-04 21:54:08 +00:00
|
|
|
let vars = match self.mentioned_vars {
|
|
|
|
Some(m) => m,
|
|
|
|
None => self.collect_mentioned_variables(),
|
|
|
|
};
|
2017-04-11 17:31:31 +00:00
|
|
|
(self.clauses, self.unify_vars, vars)
|
2017-04-04 21:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn mentioned_variables<'a>(&'a mut self) -> &'a BTreeSet<Variable> {
|
|
|
|
if self.mentioned_vars.is_none() {
|
|
|
|
let m = self.collect_mentioned_variables();
|
|
|
|
self.mentioned_vars = Some(m);
|
|
|
|
}
|
|
|
|
if let Some(ref mentioned) = self.mentioned_vars {
|
|
|
|
mentioned
|
|
|
|
} else {
|
|
|
|
panic!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-28 09:44:11 +00:00
|
|
|
impl ContainsVariables for NotJoin {
|
|
|
|
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
|
|
|
for clause in &self.clauses {
|
|
|
|
clause.accumulate_mentioned_variables(acc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-24 20:09:32 +00:00
|
|
|
impl ContainsVariables for Predicate {
|
|
|
|
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
|
|
|
for arg in &self.args {
|
|
|
|
if let &FnArg::Variable(ref v) = arg {
|
|
|
|
acc_ref(acc, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-03 23:46:11 +00:00
|
|
|
impl ContainsVariables for Binding {
|
|
|
|
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
|
|
|
match self {
|
|
|
|
&Binding::BindScalar(ref v) | &Binding::BindColl(ref v) => {
|
|
|
|
acc_ref(acc, v)
|
|
|
|
},
|
|
|
|
&Binding::BindRel(ref vs) | &Binding::BindTuple(ref vs) => {
|
|
|
|
for v in vs {
|
|
|
|
if let &VariableOrPlaceholder::Variable(ref v) = v {
|
|
|
|
acc_ref(acc, v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ContainsVariables for WhereFn {
|
|
|
|
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
|
|
|
for arg in &self.args {
|
|
|
|
if let &FnArg::Variable(ref v) = arg {
|
|
|
|
acc_ref(acc, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.binding.accumulate_mentioned_variables(acc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-24 20:09:32 +00:00
|
|
|
fn acc_ref<T: Clone + Ord>(acc: &mut BTreeSet<T>, v: &T) {
|
|
|
|
// Roll on, reference entries!
|
|
|
|
if !acc.contains(v) {
|
|
|
|
acc.insert(v.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ContainsVariables for Pattern {
|
|
|
|
fn accumulate_mentioned_variables(&self, acc: &mut BTreeSet<Variable>) {
|
|
|
|
if let PatternNonValuePlace::Variable(ref v) = self.entity {
|
|
|
|
acc_ref(acc, v)
|
|
|
|
}
|
|
|
|
if let PatternNonValuePlace::Variable(ref v) = self.attribute {
|
|
|
|
acc_ref(acc, v)
|
|
|
|
}
|
|
|
|
if let PatternValuePlace::Variable(ref v) = self.value {
|
|
|
|
acc_ref(acc, v)
|
|
|
|
}
|
|
|
|
if let PatternNonValuePlace::Variable(ref v) = self.tx {
|
|
|
|
acc_ref(acc, v)
|
|
|
|
}
|
|
|
|
}
|
2017-04-29 03:11:55 +00:00
|
|
|
}
|