2017-01-25 22:06:19 +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.
|
|
|
|
|
|
|
|
extern crate combine;
|
|
|
|
extern crate edn;
|
2017-02-03 02:32:00 +00:00
|
|
|
extern crate mentat_parser_utils;
|
2017-01-25 22:06:19 +00:00
|
|
|
extern crate mentat_query;
|
2018-01-29 22:29:16 +00:00
|
|
|
extern crate mentat_core;
|
2017-01-25 22:06:19 +00:00
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
use std; // To refer to std::result::Result.
|
|
|
|
|
2017-04-18 01:46:40 +00:00
|
|
|
use std::collections::BTreeSet;
|
|
|
|
|
2018-05-04 19:56:00 +00:00
|
|
|
use self::combine::{
|
|
|
|
eof,
|
|
|
|
look_ahead,
|
|
|
|
many,
|
|
|
|
many1,
|
|
|
|
optional,
|
|
|
|
parser,
|
|
|
|
satisfy,
|
|
|
|
satisfy_map,
|
|
|
|
Parser,
|
|
|
|
ParseResult,
|
|
|
|
Stream,
|
|
|
|
};
|
|
|
|
|
2017-04-19 23:16:19 +00:00
|
|
|
use self::combine::combinator::{any, choice, or, try};
|
2017-02-24 05:13:04 +00:00
|
|
|
|
2018-01-29 22:29:16 +00:00
|
|
|
use self::mentat_core::ValueType;
|
|
|
|
|
2018-05-31 19:13:28 +00:00
|
|
|
use errors::{
|
|
|
|
Error,
|
|
|
|
ErrorKind,
|
|
|
|
Result,
|
|
|
|
ResultExt,
|
|
|
|
};
|
|
|
|
|
2017-02-24 05:13:04 +00:00
|
|
|
use self::mentat_parser_utils::{
|
2017-05-04 20:40:41 +00:00
|
|
|
KeywordMapParser,
|
2017-02-24 05:13:04 +00:00
|
|
|
ResultParser,
|
|
|
|
ValueParseError,
|
|
|
|
};
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
use self::mentat_parser_utils::value_and_span::Stream as ValueStream;
|
|
|
|
use self::mentat_parser_utils::value_and_span::{
|
|
|
|
Item,
|
|
|
|
OfExactlyParsing,
|
2018-05-14 17:45:48 +00:00
|
|
|
forward_any_keyword,
|
|
|
|
forward_namespaced_keyword,
|
2017-04-06 17:06:28 +00:00
|
|
|
keyword_map,
|
|
|
|
list,
|
|
|
|
map,
|
|
|
|
seq,
|
|
|
|
vector,
|
|
|
|
};
|
|
|
|
|
2017-02-03 02:32:00 +00:00
|
|
|
use self::mentat_query::{
|
2018-03-12 22:18:50 +00:00
|
|
|
Aggregate,
|
2017-04-03 23:46:11 +00:00
|
|
|
Binding,
|
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
|
|
|
Direction,
|
2017-02-03 02:32:00 +00:00
|
|
|
Element,
|
|
|
|
FindQuery,
|
|
|
|
FindSpec,
|
2017-03-16 14:44:56 +00:00
|
|
|
FnArg,
|
2017-02-03 02:32:00 +00:00
|
|
|
FromValue,
|
2017-04-19 23:16:19 +00:00
|
|
|
Limit,
|
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
|
|
|
Order,
|
2017-03-23 20:10:44 +00:00
|
|
|
OrJoin,
|
|
|
|
OrWhereClause,
|
2018-05-13 21:15:36 +00:00
|
|
|
NamedPullAttribute,
|
2017-04-28 09:44:11 +00:00
|
|
|
NotJoin,
|
2017-02-03 02:32:00 +00:00
|
|
|
Pattern,
|
|
|
|
PatternNonValuePlace,
|
|
|
|
PatternValuePlace,
|
2017-03-16 14:44:56 +00:00
|
|
|
Predicate,
|
2018-05-04 19:56:00 +00:00
|
|
|
Pull,
|
|
|
|
PullAttributeSpec,
|
|
|
|
PullConcreteAttribute,
|
2017-11-30 23:02:07 +00:00
|
|
|
QueryFunction,
|
2017-02-03 02:32:00 +00:00
|
|
|
SrcVar,
|
2018-01-29 22:29:16 +00:00
|
|
|
TypeAnnotation,
|
2017-03-23 20:10:44 +00:00
|
|
|
UnifyVars,
|
2017-02-03 02:32:00 +00:00
|
|
|
Variable,
|
2017-04-03 23:46:11 +00:00
|
|
|
VariableOrPlaceholder,
|
2017-02-03 02:32:00 +00:00
|
|
|
WhereClause,
|
2017-04-03 23:46:11 +00:00
|
|
|
WhereFn,
|
2017-02-03 02:32:00 +00:00
|
|
|
};
|
|
|
|
|
2017-05-04 20:40:41 +00:00
|
|
|
pub struct Query<'a>(std::marker::PhantomData<&'a ()>);
|
2017-01-25 22:06:19 +00:00
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Query, variable, Variable, {
|
|
|
|
satisfy_map(Variable::from_value)
|
|
|
|
});
|
2017-01-25 22:06:19 +00:00
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Query, source_var, SrcVar, {
|
|
|
|
satisfy_map(SrcVar::from_value)
|
|
|
|
});
|
2017-01-25 22:06:19 +00:00
|
|
|
|
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: interning.
|
2017-11-30 23:02:07 +00:00
|
|
|
def_parser!(Query, query_function, QueryFunction, {
|
|
|
|
satisfy_map(QueryFunction::from_value)
|
2017-04-06 17:06:28 +00:00
|
|
|
});
|
2017-03-23 20:10:44 +00:00
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Query, fn_arg, FnArg, {
|
2017-04-19 18:10:24 +00:00
|
|
|
satisfy_map(FnArg::from_value).or(vector().of_exactly(many::<Vec<FnArg>, _>(Query::fn_arg())).map(FnArg::Vector))
|
2017-04-06 17:06:28 +00:00
|
|
|
});
|
2017-02-03 02:32:00 +00:00
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Query, arguments, Vec<FnArg>, {
|
|
|
|
(many::<Vec<FnArg>, _>(Query::fn_arg()))
|
2017-03-23 20:10:44 +00:00
|
|
|
});
|
|
|
|
|
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
|
|
|
def_parser!(Query, direction, Direction, {
|
2017-05-04 20:40:41 +00:00
|
|
|
satisfy_map(|v: &edn::ValueAndSpan| {
|
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
|
|
|
match v.inner {
|
|
|
|
edn::SpannedValue::PlainSymbol(ref s) => {
|
|
|
|
let name = s.0.as_str();
|
|
|
|
match name {
|
|
|
|
"asc" => Some(Direction::Ascending),
|
|
|
|
"desc" => Some(Direction::Descending),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
def_parser!(Query, order, Order, {
|
|
|
|
seq().of_exactly((Query::direction(), Query::variable()))
|
|
|
|
.map(|(d, v)| Order(d, v))
|
|
|
|
.or(Query::variable().map(|v| Order(Direction::Ascending, v)))
|
|
|
|
});
|
|
|
|
|
2018-03-12 22:18:50 +00:00
|
|
|
def_matches_plain_symbol!(Query, the, "the");
|
2018-05-04 19:56:00 +00:00
|
|
|
def_matches_plain_symbol!(Query, pull, "pull");
|
|
|
|
def_matches_plain_symbol!(Query, wildcard, "*");
|
2018-05-13 21:15:36 +00:00
|
|
|
def_matches_keyword!(Query, alias_as, "as");
|
2018-03-12 22:18:50 +00:00
|
|
|
|
2017-05-04 20:40:41 +00:00
|
|
|
pub struct Where<'a>(std::marker::PhantomData<&'a ()>);
|
2017-04-06 17:06:28 +00:00
|
|
|
|
|
|
|
def_parser!(Where, pattern_value_place, PatternValuePlace, {
|
|
|
|
satisfy_map(PatternValuePlace::from_value)
|
2017-03-23 20:10:44 +00:00
|
|
|
});
|
|
|
|
|
2017-04-19 23:16:19 +00:00
|
|
|
def_parser!(Query, natural_number, u64, {
|
2017-05-04 20:40:41 +00:00
|
|
|
any().and_then(|v: &edn::ValueAndSpan| {
|
2017-04-19 23:16:19 +00:00
|
|
|
match v.inner {
|
|
|
|
edn::SpannedValue::Integer(x) if (x > 0) => {
|
|
|
|
Ok(x as u64)
|
|
|
|
},
|
2017-05-04 20:40:41 +00:00
|
|
|
ref spanned => {
|
|
|
|
let e = Box::new(Error::from_kind(ErrorKind::InvalidLimit(spanned.clone().into())));
|
2017-04-19 23:16:19 +00:00
|
|
|
Err(combine::primitives::Error::Other(e))
|
|
|
|
},
|
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Where, pattern_non_value_place, PatternNonValuePlace, {
|
|
|
|
satisfy_map(PatternNonValuePlace::from_value)
|
2017-03-23 20:10:44 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_matches_plain_symbol!(Where, and, "and");
|
|
|
|
|
|
|
|
def_matches_plain_symbol!(Where, or, "or");
|
|
|
|
|
|
|
|
def_matches_plain_symbol!(Where, or_join, "or-join");
|
|
|
|
|
2017-04-28 09:44:11 +00:00
|
|
|
def_matches_plain_symbol!(Where, not, "not");
|
|
|
|
|
|
|
|
def_matches_plain_symbol!(Where, not_join, "not-join");
|
|
|
|
|
2017-06-02 22:36:12 +00:00
|
|
|
def_parser!(Where, rule_vars, BTreeSet<Variable>, {
|
2017-04-06 17:06:28 +00:00
|
|
|
seq()
|
2017-06-02 22:36:12 +00:00
|
|
|
.of_exactly(many1(Query::variable()).and_then(unique_vars))
|
2017-03-23 20:10:44 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Where, or_pattern_clause, OrWhereClause, {
|
|
|
|
Where::clause().map(|clause| OrWhereClause::Clause(clause))
|
2017-03-23 20:10:44 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Where, or_and_clause, OrWhereClause, {
|
|
|
|
seq()
|
|
|
|
.of_exactly(Where::and()
|
|
|
|
.with(many1(Where::clause()))
|
|
|
|
.map(OrWhereClause::And))
|
2017-03-23 20:10:44 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Where, or_where_clause, OrWhereClause, {
|
|
|
|
choice([Where::or_pattern_clause(), Where::or_and_clause()])
|
2017-03-23 20:10:44 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Where, or_clause, WhereClause, {
|
|
|
|
seq()
|
|
|
|
.of_exactly(Where::or()
|
|
|
|
.with(many1(Where::or_where_clause()))
|
|
|
|
.map(|clauses| {
|
2017-04-04 21:54:08 +00:00
|
|
|
WhereClause::OrJoin(OrJoin::new(UnifyVars::Implicit, clauses))
|
2017-04-06 17:06:28 +00:00
|
|
|
}))
|
2017-03-23 20:10:44 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Where, or_join_clause, WhereClause, {
|
|
|
|
seq()
|
|
|
|
.of_exactly(Where::or_join()
|
|
|
|
.with(Where::rule_vars())
|
|
|
|
.and(many1(Where::or_where_clause()))
|
|
|
|
.map(|(vars, clauses)| {
|
2017-04-04 21:54:08 +00:00
|
|
|
WhereClause::OrJoin(OrJoin::new(UnifyVars::Explicit(vars), clauses))
|
2017-04-06 17:06:28 +00:00
|
|
|
}))
|
2017-03-23 20:10:44 +00:00
|
|
|
});
|
|
|
|
|
2017-04-28 09:44:11 +00:00
|
|
|
def_parser!(Where, not_clause, WhereClause, {
|
|
|
|
seq()
|
|
|
|
.of_exactly(Where::not()
|
|
|
|
.with(many1(Where::clause()))
|
|
|
|
.map(|clauses| {
|
|
|
|
WhereClause::NotJoin(
|
|
|
|
NotJoin {
|
|
|
|
unify_vars: UnifyVars::Implicit,
|
|
|
|
clauses: clauses,
|
|
|
|
})
|
|
|
|
}))
|
|
|
|
});
|
|
|
|
|
|
|
|
def_parser!(Where, not_join_clause, WhereClause, {
|
|
|
|
seq()
|
|
|
|
.of_exactly(Where::not_join()
|
|
|
|
.with(Where::rule_vars())
|
|
|
|
.and(many1(Where::clause()))
|
|
|
|
.map(|(vars, clauses)| {
|
|
|
|
WhereClause::NotJoin(
|
|
|
|
NotJoin {
|
|
|
|
unify_vars: UnifyVars::Explicit(vars),
|
|
|
|
clauses: clauses,
|
|
|
|
})
|
|
|
|
}))
|
|
|
|
});
|
|
|
|
|
2017-11-30 23:02:07 +00:00
|
|
|
def_parser!(Query, func, (QueryFunction, Vec<FnArg>), {
|
|
|
|
(Query::query_function(), Query::arguments())
|
|
|
|
});
|
|
|
|
|
2018-03-12 22:18:50 +00:00
|
|
|
def_parser!(Query, aggregate, Aggregate, {
|
2018-05-04 19:56:00 +00:00
|
|
|
Query::func()
|
2018-03-12 22:18:50 +00:00
|
|
|
.map(|(func, args)| Aggregate {
|
|
|
|
func, args,
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
2018-05-13 21:15:36 +00:00
|
|
|
def_parser!(Query, pull_concrete_attribute_ident, PullConcreteAttribute, {
|
2018-05-14 17:45:48 +00:00
|
|
|
forward_namespaced_keyword()
|
|
|
|
.map(|k| PullConcreteAttribute::Ident(::std::rc::Rc::new(k.clone())))
|
2018-05-13 21:15:36 +00:00
|
|
|
});
|
|
|
|
|
2018-05-04 19:56:00 +00:00
|
|
|
def_parser!(Query, pull_concrete_attribute, PullAttributeSpec, {
|
2018-05-13 21:15:36 +00:00
|
|
|
(Query::pull_concrete_attribute_ident(),
|
2018-05-14 17:45:48 +00:00
|
|
|
optional(try(Query::alias_as()
|
|
|
|
.with(forward_any_keyword()
|
|
|
|
.map(|alias| ::std::rc::Rc::new(alias.clone()))))))
|
2018-05-13 21:15:36 +00:00
|
|
|
.map(|(attribute, alias)|
|
2018-05-04 19:56:00 +00:00
|
|
|
PullAttributeSpec::Attribute(
|
2018-05-13 21:15:36 +00:00
|
|
|
NamedPullAttribute {
|
|
|
|
attribute,
|
|
|
|
alias: alias,
|
|
|
|
}))
|
2018-05-04 19:56:00 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
def_parser!(Query, pull_wildcard_attribute, PullAttributeSpec, {
|
|
|
|
Query::wildcard().map(|_| PullAttributeSpec::Wildcard)
|
|
|
|
});
|
|
|
|
|
|
|
|
def_parser!(Query, pull_attribute, PullAttributeSpec, {
|
|
|
|
choice([
|
|
|
|
try(Query::pull_concrete_attribute()),
|
|
|
|
try(Query::pull_wildcard_attribute()),
|
|
|
|
// TODO: reversed keywords, entids (with aliases, presumably…).
|
|
|
|
])
|
|
|
|
});
|
|
|
|
|
|
|
|
// A wildcard can appear only once.
|
|
|
|
// If a wildcard appears, only map expressions can be present.
|
|
|
|
fn validate_attributes<'a, I>(attrs: I) -> std::result::Result<(), &'static str>
|
|
|
|
where I: IntoIterator<Item=&'a PullAttributeSpec> {
|
|
|
|
let mut wildcard_seen = false;
|
|
|
|
let mut non_map_or_wildcard_seen = false;
|
|
|
|
for attr in attrs {
|
|
|
|
match attr {
|
|
|
|
&PullAttributeSpec::Wildcard => {
|
|
|
|
if wildcard_seen {
|
|
|
|
return Err("duplicate wildcard pull attribute");
|
|
|
|
}
|
|
|
|
wildcard_seen = true;
|
|
|
|
if non_map_or_wildcard_seen {
|
|
|
|
return Err("wildcard with specified attributes");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// &PullAttributeSpec::LimitedAttribute(_, _) => {
|
|
|
|
&PullAttributeSpec::Attribute(_) => {
|
|
|
|
non_map_or_wildcard_seen = true;
|
|
|
|
if wildcard_seen {
|
|
|
|
return Err("wildcard with specified attributes");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// TODO: map form.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
def_parser!(Query, pull_attributes, Vec<PullAttributeSpec>, {
|
|
|
|
vector().of_exactly(many1(Query::pull_attribute()))
|
|
|
|
.and_then(|attrs: Vec<PullAttributeSpec>|
|
|
|
|
validate_attributes(&attrs)
|
|
|
|
.and(Ok(attrs))
|
|
|
|
.map_err(|e| combine::primitives::Error::Unexpected(e.into())))
|
|
|
|
});
|
|
|
|
|
2017-03-16 14:44:56 +00:00
|
|
|
/// A vector containing just a parenthesized filter expression.
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Where, pred, WhereClause, {
|
|
|
|
// Accept either a nested list or a nested vector here:
|
|
|
|
// `[(foo ?x ?y)]` or `[[foo ?x ?y]]`
|
|
|
|
vector()
|
|
|
|
.of_exactly(seq()
|
2017-11-30 23:02:07 +00:00
|
|
|
.of_exactly(Query::func()
|
2017-04-06 17:06:28 +00:00
|
|
|
.map(|(f, args)| {
|
|
|
|
WhereClause::Pred(
|
|
|
|
Predicate {
|
|
|
|
operator: f.0,
|
|
|
|
args: args,
|
|
|
|
})
|
|
|
|
})))
|
2017-03-16 14:44:56 +00:00
|
|
|
});
|
|
|
|
|
2018-01-29 22:29:16 +00:00
|
|
|
def_parser!(Query, type_anno_type, ValueType, {
|
|
|
|
satisfy_map(|v: &edn::ValueAndSpan| {
|
|
|
|
match v.inner {
|
|
|
|
edn::SpannedValue::PlainSymbol(ref s) => {
|
|
|
|
let name = s.0.as_str();
|
|
|
|
match 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
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
/// A type annotation.
|
|
|
|
def_parser!(Where, type_annotation, WhereClause, {
|
|
|
|
// Accept either a nested list or a nested vector here:
|
|
|
|
// `[(string ?x)]` or `[[string ?x]]`
|
|
|
|
vector()
|
|
|
|
.of_exactly(seq()
|
|
|
|
.of_exactly((Query::type_anno_type(), Query::variable())
|
|
|
|
.map(|(ty, var)| {
|
|
|
|
WhereClause::TypeAnnotation(
|
|
|
|
TypeAnnotation {
|
|
|
|
value_type: ty,
|
|
|
|
variable: var,
|
|
|
|
})
|
|
|
|
})))
|
|
|
|
});
|
|
|
|
|
2017-04-03 23:46:11 +00:00
|
|
|
/// A vector containing a parenthesized function expression and a binding.
|
|
|
|
def_parser!(Where, where_fn, WhereClause, {
|
|
|
|
// Accept either a nested list or a nested vector here:
|
|
|
|
// `[(foo ?x ?y) binding]` or `[[foo ?x ?y] binding]`
|
|
|
|
vector()
|
|
|
|
.of_exactly(
|
|
|
|
(seq().of_exactly(
|
2017-11-30 23:02:07 +00:00
|
|
|
Query::func()),
|
2017-04-03 23:46:11 +00:00
|
|
|
Bind::binding())
|
|
|
|
.map(|((f, args), binding)| {
|
|
|
|
WhereClause::WhereFn(
|
|
|
|
WhereFn {
|
|
|
|
operator: f.0,
|
|
|
|
args: args,
|
|
|
|
binding: binding,
|
|
|
|
})
|
|
|
|
}))
|
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Where, pattern, WhereClause, {
|
|
|
|
vector()
|
|
|
|
.of_exactly(
|
2017-02-03 02:32:00 +00:00
|
|
|
// While *technically* Datomic allows you to have a query like:
|
|
|
|
// [:find … :where [[?x]]]
|
2017-03-16 14:44:56 +00:00
|
|
|
// We don't -- we require at least e, a.
|
2017-04-06 17:06:28 +00:00
|
|
|
(optional(Query::source_var()), // src
|
|
|
|
Where::pattern_non_value_place(), // e
|
|
|
|
Where::pattern_non_value_place(), // a
|
|
|
|
optional(Where::pattern_value_place()), // v
|
|
|
|
optional(Where::pattern_non_value_place())) // tx
|
|
|
|
.and_then(|(src, e, a, v, tx)| {
|
|
|
|
let v = v.unwrap_or(PatternValuePlace::Placeholder);
|
|
|
|
let tx = tx.unwrap_or(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.
|
|
|
|
Pattern::new(src, e, a, v, tx)
|
|
|
|
.map(WhereClause::Pattern)
|
|
|
|
.ok_or(combine::primitives::Error::Expected("pattern".into()))
|
|
|
|
}))
|
2017-02-03 02:32:00 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Where, clause, WhereClause, {
|
|
|
|
choice([try(Where::pattern()),
|
2017-03-23 20:10:44 +00:00
|
|
|
// It's either
|
|
|
|
// (or-join [vars] clauses…)
|
|
|
|
// or
|
|
|
|
// (or clauses…)
|
|
|
|
// We don't yet handle source vars.
|
2017-04-06 17:06:28 +00:00
|
|
|
try(Where::or_join_clause()),
|
|
|
|
try(Where::or_clause()),
|
2017-04-28 09:44:11 +00:00
|
|
|
try(Where::not_join_clause()),
|
|
|
|
try(Where::not_clause()),
|
2017-04-06 17:06:28 +00:00
|
|
|
|
2018-01-29 22:29:16 +00:00
|
|
|
try(Where::type_annotation()),
|
2017-04-06 17:06:28 +00:00
|
|
|
try(Where::pred()),
|
2017-04-03 23:46:11 +00:00
|
|
|
try(Where::where_fn()),
|
2017-04-06 17:06:28 +00:00
|
|
|
])
|
2017-03-23 20:10:44 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Where, clauses, Vec<WhereClause>, {
|
2017-03-16 14:44:56 +00:00
|
|
|
// Right now we only support patterns and predicates. See #239 for more.
|
2017-03-23 20:10:44 +00:00
|
|
|
(many1::<Vec<WhereClause>, _>(Where::clause()))
|
2017-02-03 02:32:00 +00:00
|
|
|
});
|
|
|
|
|
2017-05-04 20:40:41 +00:00
|
|
|
pub struct Find<'a>(std::marker::PhantomData<&'a ()>);
|
2017-02-03 02:32:00 +00:00
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_matches_plain_symbol!(Find, period, ".");
|
2017-02-03 02:32:00 +00:00
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_matches_plain_symbol!(Find, ellipsis, "...");
|
2017-02-03 02:32:00 +00:00
|
|
|
|
2017-04-03 23:46:11 +00:00
|
|
|
def_matches_plain_symbol!(Find, placeholder, "_");
|
|
|
|
|
2018-03-12 22:18:50 +00:00
|
|
|
def_parser!(Find, variable_element, Element, {
|
2017-11-30 23:02:07 +00:00
|
|
|
Query::variable().map(Element::Variable)
|
|
|
|
});
|
|
|
|
|
2018-03-12 22:18:50 +00:00
|
|
|
def_parser!(Find, corresponding_element, Element, {
|
2018-05-04 19:56:00 +00:00
|
|
|
Query::the().with(Query::variable())
|
2018-03-12 22:18:50 +00:00
|
|
|
.map(Element::Corresponding)
|
|
|
|
});
|
|
|
|
|
|
|
|
def_parser!(Find, aggregate_element, Element, {
|
|
|
|
Query::aggregate().map(Element::Aggregate)
|
|
|
|
});
|
|
|
|
|
2018-05-04 19:56:00 +00:00
|
|
|
def_parser!(Find, pull_element, Element, {
|
|
|
|
Query::pull().with(Query::variable().and(Query::pull_attributes()))
|
|
|
|
.map(|(var, attrs)| Element::Pull(Pull { var: var, patterns: attrs }))
|
|
|
|
});
|
|
|
|
|
|
|
|
enum ElementType {
|
|
|
|
Corresponding,
|
|
|
|
Pull,
|
|
|
|
Aggregate,
|
|
|
|
}
|
|
|
|
|
|
|
|
def_parser!(Find, seq_elem, Element, {
|
|
|
|
let element_parser_for_type = |ty: ElementType| {
|
|
|
|
match ty {
|
|
|
|
ElementType::Corresponding => Find::corresponding_element(),
|
|
|
|
ElementType::Pull => Find::pull_element(),
|
|
|
|
ElementType::Aggregate => Find::aggregate_element(),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// This slightly tortured phrasing ensures that we don't consume
|
|
|
|
// when the first item in the list -- the function name -- doesn't
|
|
|
|
// match, but once we decide what the list is, we go ahead and
|
|
|
|
// commit to that branch.
|
|
|
|
seq().of_exactly(
|
|
|
|
// This comes first because otherwise (the ?x) will match as an aggregate.
|
|
|
|
look_ahead(Query::the()).map(|_| ElementType::Corresponding)
|
|
|
|
|
|
|
|
// Similarly, we have to parse pull before general functions.
|
|
|
|
.or(look_ahead(Query::pull()).map(|_| ElementType::Pull))
|
|
|
|
.or(look_ahead(Query::func()).map(|_| ElementType::Aggregate))
|
|
|
|
.then(element_parser_for_type))
|
|
|
|
});
|
|
|
|
|
2018-03-12 22:18:50 +00:00
|
|
|
def_parser!(Find, elem, Element, {
|
2018-05-04 19:56:00 +00:00
|
|
|
try(Find::variable_element())
|
|
|
|
.or(Find::seq_elem())
|
2018-03-12 22:18:50 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Find, find_scalar, FindSpec, {
|
2017-11-30 23:02:07 +00:00
|
|
|
Find::elem().skip(Find::period())
|
|
|
|
.map(FindSpec::FindScalar)
|
2017-02-03 02:32:00 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Find, find_coll, FindSpec, {
|
2017-11-30 23:02:07 +00:00
|
|
|
vector().of_exactly(Find::elem().skip(Find::ellipsis()))
|
|
|
|
.map(FindSpec::FindColl)
|
2017-02-03 02:32:00 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Find, elements, Vec<Element>, {
|
2017-11-30 23:02:07 +00:00
|
|
|
many1::<Vec<Element>, _>(Find::elem())
|
2017-02-03 02:32:00 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Find, find_rel, FindSpec, {
|
|
|
|
Find::elements().map(FindSpec::FindRel)
|
2017-02-03 02:32:00 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
def_parser!(Find, find_tuple, FindSpec, {
|
2017-11-30 23:02:07 +00:00
|
|
|
vector().of_exactly(Find::elements())
|
|
|
|
.map(FindSpec::FindTuple)
|
2017-02-03 02:32:00 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
/// Parse a stream of values into one of four find specs.
|
|
|
|
///
|
|
|
|
/// `:find` must be an array of plain var symbols (?foo), pull expressions, and aggregates. For now
|
|
|
|
/// we only support variables and the annotations necessary to declare which flavor of :find we
|
|
|
|
/// want:
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// `?x ?y ?z ` = FindRel
|
|
|
|
/// `[?x ...] ` = FindColl
|
|
|
|
/// `?x . ` = FindScalar
|
|
|
|
/// `[?x ?y ?z]` = FindTuple
|
|
|
|
def_parser!(Find, spec, FindSpec, {
|
|
|
|
// Any one of the four specs might apply, so we combine them with `choice`. Our parsers consume
|
|
|
|
// input, so we need to wrap them in `try` so that they operate independently.
|
|
|
|
choice::<[&mut Parser<Input = _, Output = FindSpec>; 4], _>
|
2017-02-03 02:32:00 +00:00
|
|
|
([&mut try(Find::find_scalar()),
|
|
|
|
&mut try(Find::find_coll()),
|
|
|
|
&mut try(Find::find_tuple()),
|
|
|
|
&mut try(Find::find_rel())])
|
|
|
|
});
|
|
|
|
|
2017-06-02 22:36:12 +00:00
|
|
|
fn unique_vars<T, E>(vars: Vec<Variable>) -> std::result::Result<BTreeSet<Variable>, combine::primitives::Error<T, E>> {
|
|
|
|
let given = vars.len();
|
|
|
|
let set: BTreeSet<Variable> = vars.into_iter().collect();
|
|
|
|
if given != set.len() {
|
|
|
|
// TODO: find out what the variable is!
|
|
|
|
let e = Box::new(Error::from_kind(ErrorKind::DuplicateVariableError));
|
|
|
|
Err(combine::primitives::Error::Other(e))
|
|
|
|
} else {
|
|
|
|
Ok(set)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-18 01:46:40 +00:00
|
|
|
def_parser!(Find, vars, BTreeSet<Variable>, {
|
2017-06-02 22:36:12 +00:00
|
|
|
many(Query::variable()).and_then(unique_vars)
|
2017-04-18 01:46:40 +00:00
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
/// This is awkward, but will do for now. We use `keyword_map()` to optionally accept vector find
|
|
|
|
/// queries, then we use `FindQueryPart` to collect parts that have heterogeneous types; and then we
|
|
|
|
/// construct a `FindQuery` from them.
|
|
|
|
def_parser!(Find, query, FindQuery, {
|
2017-05-04 20:40:41 +00:00
|
|
|
let find_map = keyword_map_of!(
|
|
|
|
("find", Find::spec()),
|
|
|
|
("in", Find::vars()),
|
|
|
|
("limit", Query::variable().map(Limit::Variable).or(Query::natural_number().map(Limit::Fixed))),
|
|
|
|
("order", many1(Query::order())),
|
|
|
|
("where", Where::clauses()),
|
|
|
|
("with", Find::vars()) // Note: no trailing comma allowed!
|
|
|
|
);
|
|
|
|
|
|
|
|
(or(keyword_map(), vector()))
|
|
|
|
.of_exactly(find_map)
|
|
|
|
.and_then(|(find_spec, in_vars, limit, order_clauses, where_clauses, with_vars) | -> std::result::Result<FindQuery, combine::primitives::Error<&edn::ValueAndSpan, &edn::ValueAndSpan>> {
|
|
|
|
let limit = limit.unwrap_or(Limit::None);
|
2017-04-19 23:16:19 +00:00
|
|
|
|
|
|
|
// Make sure that if we have `:limit ?x`, `?x` appears in `:in`.
|
|
|
|
let in_vars = in_vars.unwrap_or(BTreeSet::default());
|
|
|
|
if let Limit::Variable(ref v) = limit {
|
|
|
|
if !in_vars.contains(v) {
|
|
|
|
let e = Box::new(Error::from_kind(ErrorKind::UnknownLimitVar(v.name())));
|
|
|
|
return Err(combine::primitives::Error::Other(e));
|
2017-04-06 17:06:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(FindQuery {
|
|
|
|
default_source: SrcVar::DefaultSrc,
|
2017-11-30 23:02:07 +00:00
|
|
|
find_spec: find_spec.ok_or(combine::primitives::Error::Unexpected("expected :find".into()))?,
|
2017-04-18 01:46:40 +00:00
|
|
|
in_sources: BTreeSet::default(), // TODO
|
2017-04-19 23:16:19 +00:00
|
|
|
in_vars: in_vars,
|
|
|
|
limit: limit,
|
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
|
|
|
order: order_clauses,
|
2017-04-06 17:06:28 +00:00
|
|
|
where_clauses: where_clauses.ok_or(combine::primitives::Error::Unexpected("expected :where".into()))?,
|
2017-04-19 23:16:19 +00:00
|
|
|
with: with_vars.unwrap_or(BTreeSet::default()),
|
2017-04-06 17:06:28 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
2017-04-03 23:46:11 +00:00
|
|
|
pub struct Bind<'a>(std::marker::PhantomData<&'a ()>);
|
|
|
|
|
|
|
|
def_parser!(Bind, bind_scalar, Binding, {
|
|
|
|
Query::variable()
|
|
|
|
.skip(eof())
|
|
|
|
.map(|var| Binding::BindScalar(var))
|
|
|
|
});
|
|
|
|
|
|
|
|
def_parser!(Bind, variable_or_placeholder, VariableOrPlaceholder, {
|
|
|
|
Query::variable().map(VariableOrPlaceholder::Variable)
|
|
|
|
.or(Find::placeholder().map(|_| VariableOrPlaceholder::Placeholder))
|
|
|
|
});
|
|
|
|
|
|
|
|
def_parser!(Bind, bind_coll, Binding, {
|
|
|
|
vector()
|
|
|
|
.of_exactly(Query::variable()
|
|
|
|
.skip(Find::ellipsis()))
|
|
|
|
.map(Binding::BindColl)
|
|
|
|
});
|
|
|
|
|
|
|
|
def_parser!(Bind, bind_rel, Binding, {
|
|
|
|
vector().of_exactly(
|
|
|
|
vector().of_exactly(
|
|
|
|
many1::<Vec<VariableOrPlaceholder>, _>(Bind::variable_or_placeholder())
|
|
|
|
.map(Binding::BindRel)))
|
|
|
|
});
|
|
|
|
|
|
|
|
def_parser!(Bind, bind_tuple, Binding, {
|
|
|
|
vector().of_exactly(
|
|
|
|
many1::<Vec<VariableOrPlaceholder>, _>(Bind::variable_or_placeholder())
|
|
|
|
.map(Binding::BindTuple))
|
|
|
|
});
|
|
|
|
|
|
|
|
def_parser!(Bind, binding, Binding, {
|
|
|
|
// Any one of the four binding types might apply, so we combine them with `choice`. Our parsers
|
|
|
|
// consume input, so we need to wrap them in `try` so that they operate independently.
|
|
|
|
choice([try(Bind::bind_scalar()),
|
|
|
|
try(Bind::bind_coll()),
|
|
|
|
try(Bind::bind_tuple()),
|
|
|
|
try(Bind::bind_rel())])
|
|
|
|
});
|
|
|
|
|
2017-04-06 17:06:28 +00:00
|
|
|
pub fn parse_find_string(string: &str) -> Result<FindQuery> {
|
|
|
|
let expr = edn::parse::value(string)?;
|
|
|
|
Find::query()
|
2017-05-04 20:40:41 +00:00
|
|
|
.parse(expr.atom_stream())
|
2017-02-03 02:32:00 +00:00
|
|
|
.map(|x| x.0)
|
2017-05-04 20:40:41 +00:00
|
|
|
.map_err(|e| Error::from_kind(ErrorKind::FindParseError(e.into())))
|
2017-02-03 02:32:00 +00:00
|
|
|
}
|
|
|
|
|
2017-01-27 20:07:01 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
extern crate combine;
|
|
|
|
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-27 20:07:01 +00:00
|
|
|
use self::combine::Parser;
|
2017-02-08 11:19:16 +00:00
|
|
|
use self::edn::OrderedFloat;
|
2017-02-03 02:32:00 +00:00
|
|
|
use self::mentat_query::{
|
2017-04-03 23:46:11 +00:00
|
|
|
Binding,
|
2017-02-03 02:32:00 +00:00
|
|
|
Element,
|
|
|
|
FindSpec,
|
|
|
|
NonIntegerConstant,
|
|
|
|
Pattern,
|
|
|
|
PatternNonValuePlace,
|
|
|
|
PatternValuePlace,
|
|
|
|
SrcVar,
|
|
|
|
Variable,
|
2017-04-03 23:46:11 +00:00
|
|
|
VariableOrPlaceholder,
|
2017-02-03 02:32:00 +00:00
|
|
|
};
|
2017-01-27 20:07:01 +00:00
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
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
|
|
|
fn variable(x: edn::PlainSymbol) -> Variable {
|
|
|
|
Variable(Rc::new(x))
|
|
|
|
}
|
|
|
|
|
2018-05-11 16:52:17 +00:00
|
|
|
fn ident_kw(kw: edn::Keyword) -> PatternNonValuePlace {
|
2018-04-25 21:23:27 +00:00
|
|
|
PatternNonValuePlace::Ident(kw.into())
|
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
|
|
|
}
|
|
|
|
|
|
|
|
fn ident(ns: &str, name: &str) -> PatternNonValuePlace {
|
2018-05-11 16:52:17 +00:00
|
|
|
ident_kw(edn::Keyword::namespaced(ns, name))
|
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
|
|
|
}
|
|
|
|
|
2017-02-03 02:32:00 +00:00
|
|
|
#[test]
|
|
|
|
fn test_pattern_mixed() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let e = edn::PlainSymbol::plain("_");
|
|
|
|
let a = edn::Keyword::namespaced("foo", "bar");
|
2017-02-03 02:32:00 +00:00
|
|
|
let v = OrderedFloat(99.9);
|
2018-05-11 16:52:17 +00:00
|
|
|
let tx = edn::PlainSymbol::plain("?tx");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()),
|
2018-05-11 16:52:17 +00:00
|
|
|
edn::Value::Keyword(a.clone()),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::Value::Float(v.clone()),
|
|
|
|
edn::Value::PlainSymbol(tx.clone())));
|
2017-03-16 14:44:56 +00:00
|
|
|
assert_parses_to!(Where::pattern, input, WhereClause::Pattern(Pattern {
|
2017-02-03 02:32:00 +00:00
|
|
|
source: None,
|
|
|
|
entity: PatternNonValuePlace::Placeholder,
|
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: ident_kw(a),
|
2017-02-03 02:32:00 +00:00
|
|
|
value: PatternValuePlace::Constant(NonIntegerConstant::Float(v)),
|
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
|
|
|
tx: PatternNonValuePlace::Variable(variable(tx)),
|
2017-03-16 14:44:56 +00:00
|
|
|
}));
|
2017-02-03 02:32:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_pattern_vars() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let s = edn::PlainSymbol::plain("$x");
|
|
|
|
let e = edn::PlainSymbol::plain("?e");
|
|
|
|
let a = edn::PlainSymbol::plain("?a");
|
|
|
|
let v = edn::PlainSymbol::plain("?v");
|
|
|
|
let tx = edn::PlainSymbol::plain("?tx");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(s.clone()),
|
2017-02-03 02:32:00 +00:00
|
|
|
edn::Value::PlainSymbol(e.clone()),
|
|
|
|
edn::Value::PlainSymbol(a.clone()),
|
|
|
|
edn::Value::PlainSymbol(v.clone()),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::Value::PlainSymbol(tx.clone())));
|
2017-03-16 14:44:56 +00:00
|
|
|
assert_parses_to!(Where::pattern, input, WhereClause::Pattern(Pattern {
|
2017-02-03 02:32:00 +00:00
|
|
|
source: Some(SrcVar::NamedSrc("x".to_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
|
|
|
entity: PatternNonValuePlace::Variable(variable(e)),
|
|
|
|
attribute: PatternNonValuePlace::Variable(variable(a)),
|
|
|
|
value: PatternValuePlace::Variable(variable(v)),
|
|
|
|
tx: PatternNonValuePlace::Variable(variable(tx)),
|
2017-03-16 14:44:56 +00:00
|
|
|
}));
|
2017-02-03 02:32:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_pattern_reversed_invalid() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let e = edn::PlainSymbol::plain("_");
|
|
|
|
let a = edn::Keyword::namespaced("foo", "_bar");
|
2017-02-03 02:32:00 +00:00
|
|
|
let v = OrderedFloat(99.9);
|
2018-05-11 16:52:17 +00:00
|
|
|
let tx = edn::PlainSymbol::plain("?tx");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()),
|
2018-05-11 16:52:17 +00:00
|
|
|
edn::Value::Keyword(a.clone()),
|
2017-02-03 02:32:00 +00:00
|
|
|
edn::Value::Float(v.clone()),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::Value::PlainSymbol(tx.clone())));
|
2017-02-03 02:32:00 +00:00
|
|
|
|
2017-05-04 20:40:41 +00:00
|
|
|
let input = input.with_spans();
|
2017-02-03 02:32:00 +00:00
|
|
|
let mut par = Where::pattern();
|
2017-05-04 20:40:41 +00:00
|
|
|
let result = par.parse(input.atom_stream());
|
2017-02-03 02:32:00 +00:00
|
|
|
assert!(matches!(result, Err(_)), "Expected a parse error.");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_pattern_reversed() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let e = edn::PlainSymbol::plain("_");
|
|
|
|
let a = edn::Keyword::namespaced("foo", "_bar");
|
|
|
|
let v = edn::PlainSymbol::plain("?v");
|
|
|
|
let tx = edn::PlainSymbol::plain("?tx");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::Vector(vec!(edn::Value::PlainSymbol(e.clone()),
|
2018-05-11 16:52:17 +00:00
|
|
|
edn::Value::Keyword(a.clone()),
|
2017-02-03 02:32:00 +00:00
|
|
|
edn::Value::PlainSymbol(v.clone()),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::Value::PlainSymbol(tx.clone())));
|
2017-02-03 02:32:00 +00:00
|
|
|
|
|
|
|
// Note that the attribute is no longer reversed, and the entity and value have
|
|
|
|
// switched places.
|
2017-03-16 14:44:56 +00:00
|
|
|
assert_parses_to!(Where::pattern, input, WhereClause::Pattern(Pattern {
|
2017-02-03 02:32:00 +00:00
|
|
|
source: 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
|
|
|
entity: PatternNonValuePlace::Variable(variable(v)),
|
|
|
|
attribute: ident("foo", "bar"),
|
2017-02-03 02:32:00 +00:00
|
|
|
value: PatternValuePlace::Placeholder,
|
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
|
|
|
tx: PatternNonValuePlace::Variable(variable(tx)),
|
2017-03-16 14:44:56 +00:00
|
|
|
}));
|
2017-02-03 02:32:00 +00:00
|
|
|
}
|
|
|
|
|
2017-03-23 20:10:44 +00:00
|
|
|
#[test]
|
|
|
|
fn test_rule_vars() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let e = edn::PlainSymbol::plain("?e");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone())]);
|
2017-03-23 20:10:44 +00:00
|
|
|
assert_parses_to!(Where::rule_vars, input,
|
2017-06-02 22:36:12 +00:00
|
|
|
btreeset!{variable(e.clone())});
|
2017-03-23 20:10:44 +00:00
|
|
|
}
|
|
|
|
|
2017-04-18 20:01:26 +00:00
|
|
|
#[test]
|
|
|
|
fn test_repeated_vars() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let e = edn::PlainSymbol::plain("?e");
|
|
|
|
let f = edn::PlainSymbol::plain("?f");
|
2017-04-18 20:01:26 +00:00
|
|
|
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone()),
|
|
|
|
edn::Value::PlainSymbol(f.clone()),]);
|
2017-05-04 20:40:41 +00:00
|
|
|
assert_parses_to!(|| vector().of_exactly(Find::vars()), input,
|
2017-06-02 22:36:12 +00:00
|
|
|
btreeset!{variable(e.clone()), variable(f.clone())});
|
2017-04-18 20:01:26 +00:00
|
|
|
|
2018-05-11 16:52:17 +00:00
|
|
|
let g = edn::PlainSymbol::plain("?g");
|
2017-04-18 20:01:26 +00:00
|
|
|
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(g.clone()),
|
|
|
|
edn::Value::PlainSymbol(g.clone()),]);
|
|
|
|
|
2017-05-04 20:40:41 +00:00
|
|
|
let input = input.with_spans();
|
|
|
|
let mut par = vector().of_exactly(Find::vars());
|
|
|
|
let result = par.parse(input.atom_stream())
|
2017-04-18 20:01:26 +00:00
|
|
|
.map(|x| x.0)
|
|
|
|
.map_err(|e| if let Some(combine::primitives::Error::Other(x)) = e.errors.into_iter().next() {
|
|
|
|
// Pattern matching on boxes is rocket science until Rust Nightly features hit
|
|
|
|
// stable. ErrorKind isn't Clone, so convert to strings. We could pattern match
|
|
|
|
// for exact comparison here.
|
|
|
|
x.downcast::<Error>().ok().map(|e| e.to_string())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
});
|
|
|
|
assert_eq!(result, Err(Some("duplicates in variable list".to_string())));
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:10:44 +00:00
|
|
|
#[test]
|
|
|
|
fn test_or() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let oj = edn::PlainSymbol::plain("or");
|
|
|
|
let e = edn::PlainSymbol::plain("?e");
|
|
|
|
let a = edn::PlainSymbol::plain("?a");
|
|
|
|
let v = edn::PlainSymbol::plain("?v");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::List(
|
2017-03-23 20:10:44 +00:00
|
|
|
vec![edn::Value::PlainSymbol(oj),
|
|
|
|
edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone()),
|
|
|
|
edn::Value::PlainSymbol(a.clone()),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::Value::PlainSymbol(v.clone())])].into_iter().collect());
|
2017-03-23 20:10:44 +00:00
|
|
|
assert_parses_to!(Where::or_clause, input,
|
|
|
|
WhereClause::OrJoin(
|
2017-04-04 21:54:08 +00:00
|
|
|
OrJoin::new(UnifyVars::Implicit,
|
|
|
|
vec![OrWhereClause::Clause(
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
source: None,
|
|
|
|
entity: PatternNonValuePlace::Variable(variable(e)),
|
|
|
|
attribute: PatternNonValuePlace::Variable(variable(a)),
|
|
|
|
value: PatternValuePlace::Variable(variable(v)),
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
}))])));
|
2017-03-23 20:10:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_or_join() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let oj = edn::PlainSymbol::plain("or-join");
|
|
|
|
let e = edn::PlainSymbol::plain("?e");
|
|
|
|
let a = edn::PlainSymbol::plain("?a");
|
|
|
|
let v = edn::PlainSymbol::plain("?v");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::List(
|
2017-03-23 20:10:44 +00:00
|
|
|
vec![edn::Value::PlainSymbol(oj),
|
|
|
|
edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone())]),
|
|
|
|
edn::Value::Vector(vec![edn::Value::PlainSymbol(e.clone()),
|
|
|
|
edn::Value::PlainSymbol(a.clone()),
|
2017-04-06 17:06:28 +00:00
|
|
|
edn::Value::PlainSymbol(v.clone())])].into_iter().collect());
|
2017-03-23 20:10:44 +00:00
|
|
|
assert_parses_to!(Where::or_join_clause, input,
|
|
|
|
WhereClause::OrJoin(
|
2017-06-02 22:36:12 +00:00
|
|
|
OrJoin::new(UnifyVars::Explicit(btreeset!{variable(e.clone())}),
|
2017-04-04 21:54:08 +00:00
|
|
|
vec![OrWhereClause::Clause(
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
source: None,
|
|
|
|
entity: PatternNonValuePlace::Variable(variable(e)),
|
|
|
|
attribute: PatternNonValuePlace::Variable(variable(a)),
|
|
|
|
value: PatternValuePlace::Variable(variable(v)),
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
}))])));
|
2017-03-23 20:10:44 +00:00
|
|
|
}
|
|
|
|
|
2017-04-28 09:44:11 +00:00
|
|
|
#[test]
|
|
|
|
fn test_not() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let e = edn::PlainSymbol::plain("?e");
|
|
|
|
let a = edn::PlainSymbol::plain("?a");
|
|
|
|
let v = edn::PlainSymbol::plain("?v");
|
2017-04-28 09:44:11 +00:00
|
|
|
|
|
|
|
assert_edn_parses_to!(Where::not_clause,
|
|
|
|
"(not [?e ?a ?v])",
|
|
|
|
WhereClause::NotJoin(
|
|
|
|
NotJoin {
|
|
|
|
unify_vars: UnifyVars::Implicit,
|
|
|
|
clauses: vec![
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
source: None,
|
|
|
|
entity: PatternNonValuePlace::Variable(variable(e)),
|
|
|
|
attribute: PatternNonValuePlace::Variable(variable(a)),
|
|
|
|
value: PatternValuePlace::Variable(variable(v)),
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_not_join() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let e = edn::PlainSymbol::plain("?e");
|
|
|
|
let a = edn::PlainSymbol::plain("?a");
|
|
|
|
let v = edn::PlainSymbol::plain("?v");
|
2018-01-20 03:21:04 +00:00
|
|
|
|
2017-04-28 09:44:11 +00:00
|
|
|
assert_edn_parses_to!(Where::not_join_clause,
|
|
|
|
"(not-join [?e] [?e ?a ?v])",
|
|
|
|
WhereClause::NotJoin(
|
|
|
|
NotJoin {
|
2017-06-02 22:36:12 +00:00
|
|
|
unify_vars: UnifyVars::Explicit(btreeset!{variable(e.clone())}),
|
2017-04-28 09:44:11 +00:00
|
|
|
clauses: vec![WhereClause::Pattern(Pattern {
|
|
|
|
source: None,
|
|
|
|
entity: PatternNonValuePlace::Variable(variable(e)),
|
|
|
|
attribute: PatternNonValuePlace::Variable(variable(a)),
|
|
|
|
value: PatternValuePlace::Variable(variable(v)),
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2017-01-27 20:07:01 +00:00
|
|
|
#[test]
|
|
|
|
fn test_find_sp_variable() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let sym = edn::PlainSymbol::plain("?x");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone())]);
|
|
|
|
assert_parses_to!(|| vector().of_exactly(Query::variable()), input, variable(sym));
|
2017-01-27 20:07:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_find_scalar() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let sym = edn::PlainSymbol::plain("?x");
|
|
|
|
let period = edn::PlainSymbol::plain(".");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone()), edn::Value::PlainSymbol(period.clone())]);
|
|
|
|
assert_parses_to!(|| vector().of_exactly(Find::find_scalar()),
|
2017-01-27 20:07:01 +00:00
|
|
|
input,
|
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
|
|
|
FindSpec::FindScalar(Element::Variable(variable(sym))));
|
2017-01-27 20:07:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_find_coll() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let sym = edn::PlainSymbol::plain("?x");
|
|
|
|
let period = edn::PlainSymbol::plain("...");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(sym.clone()),
|
|
|
|
edn::Value::PlainSymbol(period.clone())]);
|
2017-02-03 02:32:00 +00:00
|
|
|
assert_parses_to!(Find::find_coll,
|
2017-01-27 20:07:01 +00:00
|
|
|
input,
|
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
|
|
|
FindSpec::FindColl(Element::Variable(variable(sym))));
|
2017-01-27 20:07:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_find_rel() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let vx = edn::PlainSymbol::plain("?x");
|
|
|
|
let vy = edn::PlainSymbol::plain("?y");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()), edn::Value::PlainSymbol(vy.clone())]);
|
|
|
|
assert_parses_to!(|| vector().of_exactly(Find::find_rel()),
|
2017-01-27 20:07:01 +00:00
|
|
|
input,
|
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
|
|
|
FindSpec::FindRel(vec![Element::Variable(variable(vx)),
|
|
|
|
Element::Variable(variable(vy))]));
|
2017-01-27 20:07:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_find_tuple() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let vx = edn::PlainSymbol::plain("?x");
|
|
|
|
let vy = edn::PlainSymbol::plain("?y");
|
2017-04-06 17:06:28 +00:00
|
|
|
let input = edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()),
|
|
|
|
edn::Value::PlainSymbol(vy.clone())]);
|
2017-02-03 02:32:00 +00:00
|
|
|
assert_parses_to!(Find::find_tuple,
|
2017-01-27 20:07:01 +00:00
|
|
|
input,
|
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
|
|
|
FindSpec::FindTuple(vec![Element::Variable(variable(vx)),
|
|
|
|
Element::Variable(variable(vy))]));
|
2017-01-27 20:07:01 +00:00
|
|
|
}
|
2017-04-19 23:16:19 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_natural_numbers() {
|
|
|
|
let text = edn::Value::Text("foo".to_string());
|
|
|
|
let neg = edn::Value::Integer(-10);
|
|
|
|
let zero = edn::Value::Integer(0);
|
|
|
|
let pos = edn::Value::Integer(5);
|
|
|
|
|
2017-05-04 20:40:41 +00:00
|
|
|
// This is terrible, but destructuring errors is frustrating.
|
|
|
|
let input = text.with_spans();
|
2017-04-19 23:16:19 +00:00
|
|
|
let mut par = Query::natural_number();
|
2017-05-04 20:40:41 +00:00
|
|
|
let x = par.parse(input.atom_stream()).err().expect("an error").errors;
|
2017-04-19 23:16:19 +00:00
|
|
|
let result = format!("{:?}", x);
|
2017-11-21 16:24:08 +00:00
|
|
|
assert_eq!(result, "[Other(Error(InvalidLimit(Text(\"foo\")), State { next_error: None, backtrace: None }))]");
|
2017-04-19 23:16:19 +00:00
|
|
|
|
2017-05-04 20:40:41 +00:00
|
|
|
let input = neg.with_spans();
|
2017-04-19 23:16:19 +00:00
|
|
|
let mut par = Query::natural_number();
|
2017-05-04 20:40:41 +00:00
|
|
|
let x = par.parse(input.atom_stream()).err().expect("an error").errors;
|
2017-04-19 23:16:19 +00:00
|
|
|
let result = format!("{:?}", x);
|
2017-11-21 16:24:08 +00:00
|
|
|
assert_eq!(result, "[Other(Error(InvalidLimit(Integer(-10)), State { next_error: None, backtrace: None }))]");
|
2017-04-19 23:16:19 +00:00
|
|
|
|
2017-05-04 20:40:41 +00:00
|
|
|
let input = zero.with_spans();
|
2017-04-19 23:16:19 +00:00
|
|
|
let mut par = Query::natural_number();
|
2017-05-04 20:40:41 +00:00
|
|
|
let x = par.parse(input.atom_stream()).err().expect("an error").errors;
|
2017-04-19 23:16:19 +00:00
|
|
|
let result = format!("{:?}", x);
|
2017-11-21 16:24:08 +00:00
|
|
|
assert_eq!(result, "[Other(Error(InvalidLimit(Integer(0)), State { next_error: None, backtrace: None }))]");
|
2017-04-19 23:16:19 +00:00
|
|
|
|
2017-05-04 20:40:41 +00:00
|
|
|
let input = pos.with_spans();
|
2017-04-19 23:16:19 +00:00
|
|
|
let mut par = Query::natural_number();
|
2017-05-04 20:40:41 +00:00
|
|
|
assert_eq!(None, par.parse(input.atom_stream()).err());
|
2017-04-19 23:16:19 +00:00
|
|
|
}
|
2017-04-19 18:10:24 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_fn_arg_collections() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let vx = edn::PlainSymbol::plain("?x");
|
|
|
|
let vy = edn::PlainSymbol::plain("?y");
|
2017-04-19 18:10:24 +00:00
|
|
|
let input = edn::Value::Vector(vec![edn::Value::Vector(vec![edn::Value::PlainSymbol(vx.clone()),
|
|
|
|
edn::Value::PlainSymbol(vy.clone())])]);
|
|
|
|
|
|
|
|
assert_parses_to!(|| vector().of_exactly(Query::fn_arg()),
|
|
|
|
input,
|
|
|
|
FnArg::Vector(vec![FnArg::Variable(variable(vx)),
|
|
|
|
FnArg::Variable(variable(vy)),
|
|
|
|
]));
|
|
|
|
}
|
2017-04-03 23:46:11 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bind_scalar() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let vx = edn::PlainSymbol::plain("?x");
|
2017-04-03 23:46:11 +00:00
|
|
|
assert_edn_parses_to!(|| list().of_exactly(Bind::binding()),
|
|
|
|
"(?x)",
|
|
|
|
Binding::BindScalar(variable(vx)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bind_coll() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let vx = edn::PlainSymbol::plain("?x");
|
2017-04-03 23:46:11 +00:00
|
|
|
assert_edn_parses_to!(|| list().of_exactly(Bind::binding()),
|
|
|
|
"([?x ...])",
|
|
|
|
Binding::BindColl(variable(vx)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bind_rel() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let vx = edn::PlainSymbol::plain("?x");
|
|
|
|
let vy = edn::PlainSymbol::plain("?y");
|
|
|
|
let vw = edn::PlainSymbol::plain("?w");
|
2017-04-03 23:46:11 +00:00
|
|
|
assert_edn_parses_to!(|| list().of_exactly(Bind::binding()),
|
|
|
|
"([[?x ?y _ ?w]])",
|
|
|
|
Binding::BindRel(vec![VariableOrPlaceholder::Variable(variable(vx)),
|
|
|
|
VariableOrPlaceholder::Variable(variable(vy)),
|
|
|
|
VariableOrPlaceholder::Placeholder,
|
|
|
|
VariableOrPlaceholder::Variable(variable(vw)),
|
|
|
|
]));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bind_tuple() {
|
2018-05-11 16:52:17 +00:00
|
|
|
let vx = edn::PlainSymbol::plain("?x");
|
|
|
|
let vy = edn::PlainSymbol::plain("?y");
|
|
|
|
let vw = edn::PlainSymbol::plain("?w");
|
2017-04-03 23:46:11 +00:00
|
|
|
assert_edn_parses_to!(|| list().of_exactly(Bind::binding()),
|
|
|
|
"([?x ?y _ ?w])",
|
|
|
|
Binding::BindTuple(vec![VariableOrPlaceholder::Variable(variable(vx)),
|
|
|
|
VariableOrPlaceholder::Variable(variable(vy)),
|
|
|
|
VariableOrPlaceholder::Placeholder,
|
|
|
|
VariableOrPlaceholder::Variable(variable(vw)),
|
|
|
|
]));
|
|
|
|
}
|
|
|
|
|
2018-03-12 22:18:50 +00:00
|
|
|
#[test]
|
|
|
|
fn test_the() {
|
2018-05-04 19:56:00 +00:00
|
|
|
assert_edn_parses_to!(Find::seq_elem,
|
2018-03-12 22:18:50 +00:00
|
|
|
"(the ?y)",
|
|
|
|
Element::Corresponding(Variable::from_valid_name("?y")));
|
|
|
|
assert_edn_parses_to!(Find::find_tuple,
|
|
|
|
"[(the ?x) ?y]",
|
|
|
|
FindSpec::FindTuple(vec![Element::Corresponding(Variable::from_valid_name("?x")),
|
|
|
|
Element::Variable(Variable::from_valid_name("?y"))]));
|
|
|
|
assert_edn_parses_to!(Find::spec,
|
|
|
|
"[(the ?x) ?y]",
|
|
|
|
FindSpec::FindTuple(vec![Element::Corresponding(Variable::from_valid_name("?x")),
|
|
|
|
Element::Variable(Variable::from_valid_name("?y"))]));
|
|
|
|
let expected_query =
|
|
|
|
FindQuery {
|
|
|
|
find_spec: FindSpec::FindTuple(vec![Element::Corresponding(Variable::from_valid_name("?x")),
|
|
|
|
Element::Variable(Variable::from_valid_name("?y"))]),
|
|
|
|
where_clauses: vec![
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
source: None,
|
|
|
|
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
|
|
|
|
attribute: PatternNonValuePlace::Placeholder,
|
|
|
|
value: PatternValuePlace::Variable(Variable::from_valid_name("?y")),
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})],
|
|
|
|
|
|
|
|
default_source: SrcVar::DefaultSrc,
|
|
|
|
with: Default::default(),
|
|
|
|
in_vars: Default::default(),
|
|
|
|
in_sources: Default::default(),
|
|
|
|
limit: Limit::None,
|
|
|
|
order: None,
|
|
|
|
};
|
|
|
|
assert_edn_parses_to!(Find::query,
|
|
|
|
"[:find [(the ?x) ?y]
|
|
|
|
:where [?x _ ?y]]",
|
|
|
|
expected_query);
|
2018-05-04 19:56:00 +00:00
|
|
|
|
|
|
|
// If we give a malformed pull expression, we don't fall through to aggregates.
|
|
|
|
assert_parse_failure_contains!(Find::elem,
|
|
|
|
"(pull x [])",
|
|
|
|
r#"errors: [Unexpected(Token(ValueAndSpan { inner: PlainSymbol(PlainSymbol("x")), span: Span(6, 7) })), Expected(Borrowed("variable"))]"#);
|
2018-03-12 22:18:50 +00:00
|
|
|
}
|
|
|
|
|
2017-04-03 23:46:11 +00:00
|
|
|
#[test]
|
|
|
|
fn test_where_fn() {
|
|
|
|
assert_edn_parses_to!(Where::where_fn,
|
|
|
|
"[(f ?x 1) ?y]",
|
|
|
|
WhereClause::WhereFn(WhereFn {
|
2018-05-11 16:52:17 +00:00
|
|
|
operator: edn::PlainSymbol::plain("f"),
|
2017-04-03 23:46:11 +00:00
|
|
|
args: vec![FnArg::Variable(Variable::from_valid_name("?x")),
|
|
|
|
FnArg::EntidOrInteger(1)],
|
|
|
|
binding: Binding::BindScalar(Variable::from_valid_name("?y")),
|
|
|
|
}));
|
|
|
|
|
|
|
|
assert_edn_parses_to!(Where::where_fn,
|
|
|
|
"[(f ?x) [?y ...]]",
|
|
|
|
WhereClause::WhereFn(WhereFn {
|
2018-05-11 16:52:17 +00:00
|
|
|
operator: edn::PlainSymbol::plain("f"),
|
2017-04-03 23:46:11 +00:00
|
|
|
args: vec![FnArg::Variable(Variable::from_valid_name("?x"))],
|
|
|
|
binding: Binding::BindColl(Variable::from_valid_name("?y")),
|
|
|
|
}));
|
|
|
|
|
|
|
|
assert_edn_parses_to!(Where::where_fn,
|
|
|
|
"[(f) [?y _]]",
|
|
|
|
WhereClause::WhereFn(WhereFn {
|
2018-05-11 16:52:17 +00:00
|
|
|
operator: edn::PlainSymbol::plain("f"),
|
2017-04-03 23:46:11 +00:00
|
|
|
args: vec![],
|
|
|
|
binding: Binding::BindTuple(vec![VariableOrPlaceholder::Variable(Variable::from_valid_name("?y")),
|
|
|
|
VariableOrPlaceholder::Placeholder]),
|
|
|
|
}));
|
|
|
|
|
|
|
|
assert_edn_parses_to!(Where::where_fn,
|
|
|
|
"[(f) [[_ ?y]]]",
|
|
|
|
WhereClause::WhereFn(WhereFn {
|
2018-05-11 16:52:17 +00:00
|
|
|
operator: edn::PlainSymbol::plain("f"),
|
2017-04-03 23:46:11 +00:00
|
|
|
args: vec![],
|
|
|
|
binding: Binding::BindRel(vec![VariableOrPlaceholder::Placeholder,
|
|
|
|
VariableOrPlaceholder::Variable(Variable::from_valid_name("?y"))]),
|
|
|
|
}));
|
|
|
|
}
|
2018-01-29 22:29:16 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_type_anno() {
|
|
|
|
assert_edn_parses_to!(Where::type_annotation,
|
|
|
|
"[(string ?x)]",
|
|
|
|
WhereClause::TypeAnnotation(TypeAnnotation {
|
|
|
|
value_type: ValueType::String,
|
|
|
|
variable: Variable::from_valid_name("?x"),
|
|
|
|
}));
|
|
|
|
assert_edn_parses_to!(Where::clause,
|
|
|
|
"[[long ?foo]]",
|
|
|
|
WhereClause::TypeAnnotation(TypeAnnotation {
|
|
|
|
value_type: ValueType::Long,
|
|
|
|
variable: Variable::from_valid_name("?foo"),
|
|
|
|
}));
|
|
|
|
|
|
|
|
}
|
2018-05-04 19:56:00 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_pull() {
|
|
|
|
assert_edn_parses_to!(Query::pull_attribute,
|
|
|
|
"*",
|
|
|
|
PullAttributeSpec::Wildcard);
|
|
|
|
assert_edn_parses_to!(Query::pull_attributes,
|
|
|
|
"[*]",
|
|
|
|
vec![PullAttributeSpec::Wildcard]);
|
|
|
|
assert_edn_parses_to!(Find::elem,
|
|
|
|
"(pull ?v [*])",
|
|
|
|
Element::Pull(Pull {
|
|
|
|
var: Variable::from_valid_name("?v"),
|
|
|
|
patterns: vec![PullAttributeSpec::Wildcard],
|
|
|
|
}));
|
|
|
|
|
2018-05-11 16:52:17 +00:00
|
|
|
let foo_bar = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "bar"));
|
|
|
|
let foo_baz = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "baz"));
|
2018-05-13 21:15:36 +00:00
|
|
|
let foo_horse = ::std::rc::Rc::new(edn::Keyword::namespaced("foo", "horse"));
|
2018-05-14 17:45:48 +00:00
|
|
|
let horse = ::std::rc::Rc::new(edn::Keyword::plain("horse"));
|
2018-05-04 19:56:00 +00:00
|
|
|
assert_edn_parses_to!(Query::pull_concrete_attribute,
|
|
|
|
":foo/bar",
|
|
|
|
PullAttributeSpec::Attribute(
|
2018-05-13 21:15:36 +00:00
|
|
|
PullConcreteAttribute::Ident(foo_bar.clone()).into()));
|
2018-05-04 19:56:00 +00:00
|
|
|
assert_edn_parses_to!(Query::pull_attribute,
|
|
|
|
":foo/bar",
|
|
|
|
PullAttributeSpec::Attribute(
|
2018-05-13 21:15:36 +00:00
|
|
|
PullConcreteAttribute::Ident(foo_bar.clone()).into()));
|
2018-05-04 19:56:00 +00:00
|
|
|
assert_edn_parses_to!(Find::elem,
|
2018-05-13 21:15:36 +00:00
|
|
|
"(pull ?v [:foo/bar :as :foo/horse, :foo/baz])",
|
2018-05-04 19:56:00 +00:00
|
|
|
Element::Pull(Pull {
|
|
|
|
var: Variable::from_valid_name("?v"),
|
|
|
|
patterns: vec![
|
|
|
|
PullAttributeSpec::Attribute(
|
2018-05-13 21:15:36 +00:00
|
|
|
NamedPullAttribute {
|
|
|
|
attribute: PullConcreteAttribute::Ident(foo_bar.clone()),
|
|
|
|
alias: Some(foo_horse),
|
|
|
|
}),
|
2018-05-04 19:56:00 +00:00
|
|
|
PullAttributeSpec::Attribute(
|
2018-05-14 17:45:48 +00:00
|
|
|
PullConcreteAttribute::Ident(foo_baz.clone()).into()),
|
|
|
|
],
|
|
|
|
}));
|
|
|
|
assert_edn_parses_to!(Find::elem,
|
|
|
|
"(pull ?v [:foo/bar :as :horse, :foo/baz])",
|
|
|
|
Element::Pull(Pull {
|
|
|
|
var: Variable::from_valid_name("?v"),
|
|
|
|
patterns: vec![
|
|
|
|
PullAttributeSpec::Attribute(
|
|
|
|
NamedPullAttribute {
|
|
|
|
attribute: PullConcreteAttribute::Ident(foo_bar.clone()),
|
|
|
|
alias: Some(horse),
|
|
|
|
}),
|
|
|
|
PullAttributeSpec::Attribute(
|
2018-05-13 21:15:36 +00:00
|
|
|
PullConcreteAttribute::Ident(foo_baz.clone()).into()),
|
2018-05-04 19:56:00 +00:00
|
|
|
],
|
|
|
|
}));
|
|
|
|
assert_parse_failure_contains!(Find::elem,
|
|
|
|
"(pull ?x [* :foo/bar])",
|
|
|
|
r#"errors: [Unexpected(Borrowed("wildcard with specified attributes"))]"#);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_query_with_pull() {
|
|
|
|
let q = "[:find ?x (pull ?x [:foo/bar]) :where [?x _ _]]";
|
|
|
|
let expected_query =
|
|
|
|
FindQuery {
|
|
|
|
find_spec: FindSpec::FindRel(vec![
|
|
|
|
Element::Variable(Variable::from_valid_name("?x")),
|
|
|
|
Element::Pull(Pull {
|
|
|
|
var: Variable::from_valid_name("?x"),
|
|
|
|
patterns: vec![
|
|
|
|
PullAttributeSpec::Attribute(
|
|
|
|
PullConcreteAttribute::Ident(
|
2018-05-11 16:52:17 +00:00
|
|
|
::std::rc::Rc::new(edn::Keyword::namespaced("foo", "bar"))
|
2018-05-13 21:15:36 +00:00
|
|
|
).into()
|
2018-05-04 19:56:00 +00:00
|
|
|
),
|
|
|
|
] })]),
|
|
|
|
where_clauses: vec![
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
source: None,
|
|
|
|
entity: PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
|
|
|
|
attribute: PatternNonValuePlace::Placeholder,
|
|
|
|
value: PatternValuePlace::Placeholder,
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})],
|
|
|
|
|
|
|
|
default_source: SrcVar::DefaultSrc,
|
|
|
|
with: Default::default(),
|
|
|
|
in_vars: Default::default(),
|
|
|
|
in_sources: Default::default(),
|
|
|
|
limit: Limit::None,
|
|
|
|
order: None,
|
|
|
|
};
|
|
|
|
assert_edn_parses_to!(Find::query,
|
|
|
|
q,
|
|
|
|
expected_query);
|
|
|
|
}
|
2017-01-25 22:06:19 +00:00
|
|
|
}
|