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.
|
|
|
|
|
2017-06-02 22:36:12 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate maplit;
|
|
|
|
|
2017-01-25 22:06:19 +00:00
|
|
|
extern crate edn;
|
2017-04-19 23:16:19 +00:00
|
|
|
extern crate mentat_query;
|
|
|
|
extern crate mentat_query_parser;
|
2017-01-25 22:06:19 +00:00
|
|
|
|
2017-03-23 20:10:44 +00:00
|
|
|
use edn::{
|
2018-05-11 16:52:17 +00:00
|
|
|
Keyword,
|
2017-03-23 20:10:44 +00:00
|
|
|
PlainSymbol,
|
|
|
|
};
|
2017-01-25 22:06:19 +00:00
|
|
|
|
2017-03-16 14:44:56 +00:00
|
|
|
use mentat_query::{
|
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-03-16 14:44:56 +00:00
|
|
|
Element,
|
|
|
|
FindSpec,
|
|
|
|
FnArg,
|
2017-04-19 23:16:19 +00:00
|
|
|
Limit,
|
2017-04-29 03:11:55 +00:00
|
|
|
NonIntegerConstant,
|
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,
|
2017-03-16 14:44:56 +00:00
|
|
|
Pattern,
|
|
|
|
PatternNonValuePlace,
|
|
|
|
PatternValuePlace,
|
|
|
|
Predicate,
|
2017-03-23 20:10:44 +00:00
|
|
|
UnifyVars,
|
2017-03-16 14:44:56 +00:00
|
|
|
Variable,
|
|
|
|
WhereClause,
|
|
|
|
};
|
|
|
|
|
|
|
|
use mentat_query_parser::parse_find_string;
|
|
|
|
|
2017-01-25 22:06:19 +00:00
|
|
|
///! N.B., parsing a query can be done without reference to a DB.
|
|
|
|
///! Processing the parsed query into something we can work with
|
|
|
|
///! for planning involves interrogating the schema and idents in
|
|
|
|
///! the store.
|
|
|
|
///! See <https://github.com/mozilla/mentat/wiki/Querying> for more.
|
|
|
|
#[test]
|
2017-03-16 14:44:56 +00:00
|
|
|
fn can_parse_predicates() {
|
|
|
|
let s = "[:find [?x ...] :where [?x _ ?y] [(< ?y 10)]]";
|
|
|
|
let p = parse_find_string(s).unwrap();
|
2017-01-25 22:06:19 +00:00
|
|
|
|
2017-03-16 14:44:56 +00:00
|
|
|
assert_eq!(p.find_spec,
|
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::from_valid_name("?x"))));
|
2017-03-16 14:44:56 +00:00
|
|
|
assert_eq!(p.where_clauses,
|
|
|
|
vec![
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
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::from_valid_name("?x")),
|
2017-03-16 14:44:56 +00:00
|
|
|
attribute: 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
|
|
|
value: PatternValuePlace::Variable(Variable::from_valid_name("?y")),
|
2017-03-16 14:44:56 +00:00
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
}),
|
2018-05-11 16:52:17 +00:00
|
|
|
WhereClause::Pred(Predicate { operator: PlainSymbol::plain("<"), args: 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
|
|
|
FnArg::Variable(Variable::from_valid_name("?y")), FnArg::EntidOrInteger(10),
|
2017-03-16 14:44:56 +00:00
|
|
|
]}),
|
|
|
|
]);
|
2017-01-25 22:06:19 +00:00
|
|
|
}
|
2017-03-23 20:10:44 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_parse_simple_or() {
|
|
|
|
let s = "[:find ?x . :where (or [?x _ 10] [?x _ 15])]";
|
|
|
|
let p = parse_find_string(s).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(p.find_spec,
|
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::from_valid_name("?x"))));
|
2017-03-23 20:10:44 +00:00
|
|
|
assert_eq!(p.where_clauses,
|
|
|
|
vec![
|
2017-04-04 21:54:08 +00:00
|
|
|
WhereClause::OrJoin(OrJoin::new(
|
|
|
|
UnifyVars::Implicit,
|
|
|
|
vec![
|
2017-03-23 20:10:44 +00:00
|
|
|
OrWhereClause::Clause(
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
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::from_valid_name("?x")),
|
2017-03-23 20:10:44 +00:00
|
|
|
attribute: PatternNonValuePlace::Placeholder,
|
|
|
|
value: PatternValuePlace::EntidOrInteger(10),
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})),
|
|
|
|
OrWhereClause::Clause(
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
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::from_valid_name("?x")),
|
2017-03-23 20:10:44 +00:00
|
|
|
attribute: PatternNonValuePlace::Placeholder,
|
|
|
|
value: PatternValuePlace::EntidOrInteger(15),
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})),
|
|
|
|
],
|
2017-04-04 21:54:08 +00:00
|
|
|
)),
|
2017-03-23 20:10:44 +00:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_parse_unit_or_join() {
|
|
|
|
let s = "[:find ?x . :where (or-join [?x] [?x _ 15])]";
|
2017-04-06 17:06:28 +00:00
|
|
|
let p = parse_find_string(s).expect("to be able to parse find");
|
2017-03-23 20:10:44 +00:00
|
|
|
|
|
|
|
assert_eq!(p.find_spec,
|
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::from_valid_name("?x"))));
|
2017-03-23 20:10:44 +00:00
|
|
|
assert_eq!(p.where_clauses,
|
|
|
|
vec![
|
2017-04-04 21:54:08 +00:00
|
|
|
WhereClause::OrJoin(OrJoin::new(
|
2017-06-02 22:36:12 +00:00
|
|
|
UnifyVars::Explicit(btreeset!{Variable::from_valid_name("?x")}),
|
2017-04-04 21:54:08 +00:00
|
|
|
vec![
|
2017-03-23 20:10:44 +00:00
|
|
|
OrWhereClause::Clause(
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
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::from_valid_name("?x")),
|
2017-03-23 20:10:44 +00:00
|
|
|
attribute: PatternNonValuePlace::Placeholder,
|
|
|
|
value: PatternValuePlace::EntidOrInteger(15),
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})),
|
|
|
|
],
|
2017-04-04 21:54:08 +00:00
|
|
|
)),
|
2017-03-23 20:10:44 +00:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_parse_simple_or_join() {
|
2017-04-19 23:16:19 +00:00
|
|
|
let s = "[:find ?x . :where (or-join [?x] [?x _ 10] [?x _ -15])]";
|
2017-03-23 20:10:44 +00:00
|
|
|
let p = parse_find_string(s).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(p.find_spec,
|
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::from_valid_name("?x"))));
|
2017-03-23 20:10:44 +00:00
|
|
|
assert_eq!(p.where_clauses,
|
|
|
|
vec![
|
2017-04-04 21:54:08 +00:00
|
|
|
WhereClause::OrJoin(OrJoin::new(
|
2017-06-02 22:36:12 +00:00
|
|
|
UnifyVars::Explicit(btreeset!{Variable::from_valid_name("?x")}),
|
2017-04-04 21:54:08 +00:00
|
|
|
vec![
|
2017-03-23 20:10:44 +00:00
|
|
|
OrWhereClause::Clause(
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
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::from_valid_name("?x")),
|
2017-03-23 20:10:44 +00:00
|
|
|
attribute: PatternNonValuePlace::Placeholder,
|
|
|
|
value: PatternValuePlace::EntidOrInteger(10),
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})),
|
|
|
|
OrWhereClause::Clause(
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
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::from_valid_name("?x")),
|
2017-03-23 20:10:44 +00:00
|
|
|
attribute: PatternNonValuePlace::Placeholder,
|
2017-04-19 23:16:19 +00:00
|
|
|
value: PatternValuePlace::EntidOrInteger(-15),
|
2017-03-23 20:10:44 +00:00
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})),
|
|
|
|
],
|
2017-04-04 21:54:08 +00:00
|
|
|
)),
|
2017-03-23 20:10:44 +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
|
|
|
#[cfg(test)]
|
|
|
|
fn ident(ns: &str, name: &str) -> PatternNonValuePlace {
|
2018-05-11 16:52:17 +00:00
|
|
|
Keyword::namespaced(ns, name).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
|
|
|
}
|
|
|
|
|
2017-03-23 20:10:44 +00:00
|
|
|
#[test]
|
|
|
|
fn can_parse_simple_or_and_join() {
|
|
|
|
let s = "[:find ?x . :where (or [?x _ 10] (and (or [?x :foo/bar ?y] [?x :foo/baz ?y]) [(< ?y 1)]))]";
|
|
|
|
let p = parse_find_string(s).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(p.find_spec,
|
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::from_valid_name("?x"))));
|
2017-03-23 20:10:44 +00:00
|
|
|
assert_eq!(p.where_clauses,
|
|
|
|
vec![
|
2017-04-04 21:54:08 +00:00
|
|
|
WhereClause::OrJoin(OrJoin::new(
|
|
|
|
UnifyVars::Implicit,
|
|
|
|
vec![
|
2017-03-23 20:10:44 +00:00
|
|
|
OrWhereClause::Clause(
|
|
|
|
WhereClause::Pattern(Pattern {
|
|
|
|
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::from_valid_name("?x")),
|
2017-03-23 20:10:44 +00:00
|
|
|
attribute: PatternNonValuePlace::Placeholder,
|
|
|
|
value: PatternValuePlace::EntidOrInteger(10),
|
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})),
|
|
|
|
OrWhereClause::And(
|
|
|
|
vec![
|
2017-04-04 21:54:08 +00:00
|
|
|
WhereClause::OrJoin(OrJoin::new(
|
|
|
|
UnifyVars::Implicit,
|
|
|
|
vec![
|
2017-03-23 20:10:44 +00:00
|
|
|
OrWhereClause::Clause(WhereClause::Pattern(Pattern {
|
|
|
|
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::from_valid_name("?x")),
|
|
|
|
attribute: ident("foo", "bar"),
|
|
|
|
value: PatternValuePlace::Variable(Variable::from_valid_name("?y")),
|
2017-03-23 20:10:44 +00:00
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})),
|
|
|
|
OrWhereClause::Clause(WhereClause::Pattern(Pattern {
|
|
|
|
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::from_valid_name("?x")),
|
|
|
|
attribute: ident("foo", "baz"),
|
|
|
|
value: PatternValuePlace::Variable(Variable::from_valid_name("?y")),
|
2017-03-23 20:10:44 +00:00
|
|
|
tx: PatternNonValuePlace::Placeholder,
|
|
|
|
})),
|
|
|
|
],
|
2017-04-04 21:54:08 +00:00
|
|
|
)),
|
2017-03-23 20:10:44 +00:00
|
|
|
|
2018-05-11 16:52:17 +00:00
|
|
|
WhereClause::Pred(Predicate { operator: PlainSymbol::plain("<"), args: 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
|
|
|
FnArg::Variable(Variable::from_valid_name("?y")), FnArg::EntidOrInteger(1),
|
2017-03-23 20:10:44 +00:00
|
|
|
]}),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
],
|
2017-04-04 21:54:08 +00:00
|
|
|
)),
|
2017-03-23 20:10:44 +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
|
|
|
}
|
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
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_parse_order_by() {
|
|
|
|
let invalid = "[:find ?x :where [?x :foo/baz ?y] :order]";
|
|
|
|
assert!(parse_find_string(invalid).is_err());
|
|
|
|
|
|
|
|
// Defaults to ascending.
|
|
|
|
let default = "[:find ?x :where [?x :foo/baz ?y] :order ?y]";
|
|
|
|
assert_eq!(parse_find_string(default).unwrap().order,
|
|
|
|
Some(vec![Order(Direction::Ascending, Variable::from_valid_name("?y"))]));
|
|
|
|
|
|
|
|
let ascending = "[:find ?x :where [?x :foo/baz ?y] :order (asc ?y)]";
|
|
|
|
assert_eq!(parse_find_string(ascending).unwrap().order,
|
|
|
|
Some(vec![Order(Direction::Ascending, Variable::from_valid_name("?y"))]));
|
|
|
|
|
|
|
|
let descending = "[:find ?x :where [?x :foo/baz ?y] :order (desc ?y)]";
|
|
|
|
assert_eq!(parse_find_string(descending).unwrap().order,
|
|
|
|
Some(vec![Order(Direction::Descending, Variable::from_valid_name("?y"))]));
|
|
|
|
|
|
|
|
let mixed = "[:find ?x :where [?x :foo/baz ?y] :order (desc ?y) (asc ?x)]";
|
|
|
|
assert_eq!(parse_find_string(mixed).unwrap().order,
|
|
|
|
Some(vec![Order(Direction::Descending, Variable::from_valid_name("?y")),
|
|
|
|
Order(Direction::Ascending, Variable::from_valid_name("?x"))]));
|
|
|
|
}
|
2017-04-19 23:16:19 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_parse_limit() {
|
|
|
|
let invalid = "[:find ?x :where [?x :foo/baz ?y] :limit]";
|
|
|
|
assert!(parse_find_string(invalid).is_err());
|
|
|
|
|
|
|
|
let zero_invalid = "[:find ?x :where [?x :foo/baz ?y] :limit 00]";
|
|
|
|
assert!(parse_find_string(zero_invalid).is_err());
|
|
|
|
|
|
|
|
let none = "[:find ?x :where [?x :foo/baz ?y]]";
|
|
|
|
assert_eq!(parse_find_string(none).unwrap().limit,
|
|
|
|
Limit::None);
|
|
|
|
|
|
|
|
let one = "[:find ?x :where [?x :foo/baz ?y] :limit 1]";
|
|
|
|
assert_eq!(parse_find_string(one).unwrap().limit,
|
|
|
|
Limit::Fixed(1));
|
|
|
|
|
|
|
|
let onethousand = "[:find ?x :where [?x :foo/baz ?y] :limit 1000]";
|
|
|
|
assert_eq!(parse_find_string(onethousand).unwrap().limit,
|
|
|
|
Limit::Fixed(1000));
|
|
|
|
|
|
|
|
let variable_with_in = "[:find ?x :in ?limit :where [?x :foo/baz ?y] :limit ?limit]";
|
|
|
|
assert_eq!(parse_find_string(variable_with_in).unwrap().limit,
|
|
|
|
Limit::Variable(Variable::from_valid_name("?limit")));
|
|
|
|
|
|
|
|
let variable_with_in_used = "[:find ?x :in ?limit :where [?x :foo/baz ?limit] :limit ?limit]";
|
|
|
|
assert_eq!(parse_find_string(variable_with_in_used).unwrap().limit,
|
|
|
|
Limit::Variable(Variable::from_valid_name("?limit")));
|
|
|
|
|
|
|
|
let variable_without_in = "[:find ?x :where [?x :foo/baz ?y] :limit ?limit]";
|
|
|
|
assert!(parse_find_string(variable_without_in).is_err());
|
|
|
|
}
|
2017-04-29 03:11:55 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_parse_uuid() {
|
|
|
|
let expected = edn::Uuid::parse_str("4cb3f828-752d-497a-90c9-b1fd516d5644").expect("valid uuid");
|
|
|
|
let s = "[:find ?x :where [?x :foo/baz #uuid \"4cb3f828-752d-497a-90c9-b1fd516d5644\"]]";
|
|
|
|
assert_eq!(parse_find_string(s).expect("parsed").where_clauses.pop().expect("a where clause"),
|
|
|
|
WhereClause::Pattern(
|
|
|
|
Pattern::new(None,
|
|
|
|
PatternNonValuePlace::Variable(Variable::from_valid_name("?x")),
|
2018-05-11 16:52:17 +00:00
|
|
|
Keyword::namespaced("foo", "baz").into(),
|
2017-04-29 03:11:55 +00:00
|
|
|
PatternValuePlace::Constant(NonIntegerConstant::Uuid(expected)),
|
|
|
|
PatternNonValuePlace::Placeholder)
|
|
|
|
.expect("valid pattern")));
|
|
|
|
}
|