2017-02-22 03:57:00 +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 mentat_core ;
extern crate mentat_query ;
extern crate mentat_query_algebrizer ;
extern crate mentat_query_parser ;
extern crate mentat_query_translator ;
extern crate mentat_sql ;
Use Rc for TypedValue, Variable, and query Ident keywords. (#395) r=nalexander
Part 1, core: use Rc for String and Keyword.
Part 2, query: use Rc for Variable.
Part 3, sql: use Rc for args in SQLiteQueryBuilder.
Part 4, query-algebrizer: use Rc.
Part 5, db: use Rc.
Part 6, query-parser: use Rc.
Part 7, query-projector: use Rc.
Part 8, query-translator: use Rc.
Part 9, top level: use Rc.
Part 10: intern Ident and IdentOrKeyword.
2017-03-29 20:18:17 +00:00
use std ::rc ::Rc ;
2017-04-19 23:16:19 +00:00
use mentat_query ::{
NamespacedKeyword ,
Variable ,
} ;
2017-02-22 03:57:00 +00:00
use mentat_core ::{
Attribute ,
Entid ,
Schema ,
2017-04-19 23:16:19 +00:00
TypedValue ,
2017-02-22 03:57:00 +00:00
ValueType ,
} ;
use mentat_query_parser ::parse_find_string ;
2017-04-19 23:16:19 +00:00
use mentat_query_algebrizer ::{
QueryInputs ,
algebrize ,
algebrize_with_inputs ,
} ;
2017-02-22 03:57:00 +00:00
use mentat_query_translator ::{
2017-03-06 22:40:10 +00:00
query_to_select ,
2017-02-22 03:57:00 +00:00
} ;
use mentat_sql ::SQLQuery ;
fn associate_ident ( schema : & mut Schema , i : NamespacedKeyword , e : Entid ) {
schema . entid_map . insert ( e , i . clone ( ) ) ;
schema . ident_map . insert ( i . clone ( ) , e ) ;
}
fn add_attribute ( schema : & mut Schema , e : Entid , a : Attribute ) {
schema . schema_map . insert ( e , a ) ;
}
2017-04-19 23:16:19 +00:00
fn translate_with_inputs ( schema : & Schema , query : & 'static str , inputs : QueryInputs ) -> SQLQuery {
let parsed = parse_find_string ( query ) . expect ( " parse failed " ) ;
let algebrized = algebrize_with_inputs ( schema , parsed , 0 , inputs ) . expect ( " algebrize failed " ) ;
2017-03-07 04:18:38 +00:00
let select = query_to_select ( algebrized ) ;
select . query . to_sql_query ( ) . unwrap ( )
}
2017-04-19 23:16:19 +00:00
fn translate ( schema : & Schema , query : & 'static str ) -> SQLQuery {
translate_with_inputs ( schema , query , QueryInputs ::default ( ) )
}
2017-04-04 21:54:08 +00:00
fn prepopulated_typed_schema ( foo_type : ValueType ) -> Schema {
2017-03-06 22:40:10 +00:00
let mut schema = Schema ::default ( ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " foo " , " bar " ) , 99 ) ;
add_attribute ( & mut schema , 99 , Attribute {
2017-04-04 21:54:08 +00:00
value_type : foo_type ,
2017-03-06 22:40:10 +00:00
.. Default ::default ( )
} ) ;
2017-03-22 21:02:00 +00:00
schema
}
2017-04-04 21:54:08 +00:00
fn prepopulated_schema ( ) -> Schema {
prepopulated_typed_schema ( ValueType ::String )
}
2017-04-29 03:11:55 +00:00
fn make_arg ( name : & 'static str , value : & 'static str ) -> ( String , Rc < mentat_sql ::Value > ) {
( name . to_string ( ) , Rc ::new ( mentat_sql ::Value ::Text ( value . 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
}
2017-03-22 21:02:00 +00:00
#[ test ]
fn test_scalar ( ) {
let schema = prepopulated_schema ( ) ;
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x . :where [?x :foo/bar "yyy"]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0 LIMIT 1 " ) ;
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
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " yyy " ) ] ) ;
2017-03-22 21:02:00 +00:00
}
#[ test ]
fn test_tuple ( ) {
let schema = prepopulated_schema ( ) ;
2017-04-19 23:16:19 +00:00
let query = r # "[:find [?x] :where [?x :foo/bar "yyy"]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0 LIMIT 1 " ) ;
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
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " yyy " ) ] ) ;
2017-03-22 21:02:00 +00:00
}
#[ test ]
fn test_coll ( ) {
let schema = prepopulated_schema ( ) ;
2017-03-06 22:40:10 +00:00
2017-04-19 23:16:19 +00:00
let query = r # "[:find [?x ...] :where [?x :foo/bar "yyy"]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0 " ) ;
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
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " yyy " ) ] ) ;
2017-03-06 22:40:10 +00:00
}
#[ test ]
fn test_rel ( ) {
2017-03-22 21:02:00 +00:00
let schema = prepopulated_schema ( ) ;
2017-02-22 03:57:00 +00:00
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :where [?x :foo/bar "yyy"]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0 " ) ;
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
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " yyy " ) ] ) ;
2017-02-22 03:57:00 +00:00
}
2017-03-07 00:27:13 +00:00
#[ test ]
fn test_limit ( ) {
2017-03-22 21:02:00 +00:00
let schema = prepopulated_schema ( ) ;
2017-03-07 00:27:13 +00:00
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :where [?x :foo/bar "yyy"] :limit 5]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0 LIMIT 5 " ) ;
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
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " yyy " ) ] ) ;
2017-03-07 00:27:13 +00:00
}
2017-03-07 04:18:38 +00:00
2017-04-19 23:16:19 +00:00
#[ test ]
fn test_unbound_variable_limit ( ) {
let schema = prepopulated_schema ( ) ;
// We don't know the value of the limit var, so we produce an escaped SQL variable to handle
// later input.
let query = r # "[:find ?x :in ?limit-is-9-great :where [?x :foo/bar "yyy"] :limit ?limit-is-9-great]"# ;
let SQLQuery { sql , args } = translate_with_inputs ( & schema , query , QueryInputs ::default ( ) ) ;
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` \
FROM ` datoms ` AS ` datoms00 ` \
WHERE ` datoms00 ` . a = 99 AND ` datoms00 ` . v = $v0 \
LIMIT $ilimit_is_9_great " );
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " yyy " ) ] ) ;
}
#[ test ]
fn test_bound_variable_limit ( ) {
let schema = prepopulated_schema ( ) ;
// We know the value of `?limit` at algebrizing time, so we substitute directly.
let query = r # "[:find ?x :in ?limit :where [?x :foo/bar "yyy"] :limit ?limit]"# ;
let inputs = QueryInputs ::with_value_sequence ( vec! [ ( Variable ::from_valid_name ( " ?limit " ) , TypedValue ::Long ( 92 ) ) ] ) ;
let SQLQuery { sql , args } = translate_with_inputs ( & schema , query , inputs ) ;
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0 LIMIT 92 " ) ;
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " yyy " ) ] ) ;
}
#[ test ]
fn test_bound_variable_limit_affects_distinct ( ) {
let schema = prepopulated_schema ( ) ;
// We know the value of `?limit` at algebrizing time, so we substitute directly.
// As it's `1`, we know we don't need `DISTINCT`!
let query = r # "[:find ?x :in ?limit :where [?x :foo/bar "yyy"] :limit ?limit]"# ;
let inputs = QueryInputs ::with_value_sequence ( vec! [ ( Variable ::from_valid_name ( " ?limit " ) , TypedValue ::Long ( 1 ) ) ] ) ;
let SQLQuery { sql , args } = translate_with_inputs ( & schema , query , inputs ) ;
assert_eq! ( sql , " SELECT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0 LIMIT 1 " ) ;
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " yyy " ) ] ) ;
}
#[ test ]
fn test_bound_variable_limit_affects_types ( ) {
let schema = prepopulated_schema ( ) ;
let query = r # "[:find ?x ?limit :in ?limit :where [?x _ ?limit] :limit ?limit]"# ;
let parsed = parse_find_string ( query ) . expect ( " parse failed " ) ;
let algebrized = algebrize ( & schema , parsed ) . expect ( " algebrize failed " ) ;
// The type is known.
assert_eq! ( Some ( ValueType ::Long ) ,
algebrized . cc . known_type ( & Variable ::from_valid_name ( " ?limit " ) ) ) ;
let select = query_to_select ( algebrized ) ;
let SQLQuery { sql , args } = select . query . to_sql_query ( ) . unwrap ( ) ;
// TODO: this query isn't actually correct -- we don't yet algebrize for variables that are
// specified in `:in` but not provided at algebrizing time. But it shows what we care about
// at the moment: we don't project a type column, because we know it's a Long.
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x`, `datoms00`.v AS `?limit` FROM `datoms` AS `datoms00` LIMIT $ilimit " ) ;
assert_eq! ( args , vec! [ ] ) ;
}
2017-03-07 04:18:38 +00:00
#[ test ]
fn test_unknown_attribute_keyword_value ( ) {
let schema = Schema ::default ( ) ;
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :where [?x _ :ab/yyy]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-07 04:18:38 +00:00
// Only match keywords, not strings: tag = 13.
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.v = $v0 AND `datoms00`.value_type_tag = 13 " ) ;
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
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " :ab/yyy " ) ] ) ;
2017-03-07 04:18:38 +00:00
}
#[ test ]
fn test_unknown_attribute_string_value ( ) {
let schema = Schema ::default ( ) ;
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :where [?x _ "horses"]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-07 04:18:38 +00:00
// We expect all_datoms because we're querying for a string. Magic, that.
// We don't want keywords etc., so tag = 10.
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `all_datoms00`.e AS `?x` FROM `all_datoms` AS `all_datoms00` WHERE `all_datoms00`.v = $v0 AND `all_datoms00`.value_type_tag = 10 " ) ;
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
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " horses " ) ] ) ;
2017-03-07 04:18:38 +00:00
}
#[ test ]
fn test_unknown_attribute_double_value ( ) {
let schema = Schema ::default ( ) ;
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :where [?x _ 9.95]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-07 04:18:38 +00:00
// In general, doubles _could_ be 1.0, which might match a boolean or a ref. Set tag = 5 to
// make sure we only match numbers.
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.v = 9.95 AND `datoms00`.value_type_tag = 5 " ) ;
2017-03-07 04:18:38 +00:00
assert_eq! ( args , vec! [ ] ) ;
}
#[ test ]
fn test_unknown_attribute_integer_value ( ) {
let schema = Schema ::default ( ) ;
let negative = r # "[:find ?x :where [?x _ -1]]"# ;
let zero = r # "[:find ?x :where [?x _ 0]]"# ;
let one = r # "[:find ?x :where [?x _ 1]]"# ;
let two = r # "[:find ?x :where [?x _ 2]]"# ;
// Can't match boolean; no need to filter it out.
2017-04-19 23:16:19 +00:00
let SQLQuery { sql , args } = translate ( & schema , negative ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.v = -1 " ) ;
2017-03-07 04:18:38 +00:00
assert_eq! ( args , vec! [ ] ) ;
// Excludes booleans.
2017-04-19 23:16:19 +00:00
let SQLQuery { sql , args } = translate ( & schema , zero ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE (`datoms00`.v = 0 AND `datoms00`.value_type_tag <> 1) " ) ;
2017-03-07 04:18:38 +00:00
assert_eq! ( args , vec! [ ] ) ;
// Excludes booleans.
2017-04-19 23:16:19 +00:00
let SQLQuery { sql , args } = translate ( & schema , one ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE (`datoms00`.v = 1 AND `datoms00`.value_type_tag <> 1) " ) ;
2017-03-07 04:18:38 +00:00
assert_eq! ( args , vec! [ ] ) ;
// Can't match boolean; no need to filter it out.
2017-04-19 23:16:19 +00:00
let SQLQuery { sql , args } = translate ( & schema , two ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.v = 2 " ) ;
2017-03-07 04:18:38 +00:00
assert_eq! ( args , vec! [ ] ) ;
}
#[ test ]
fn test_unknown_ident ( ) {
let schema = Schema ::default ( ) ;
let impossible = r # "[:find ?x :where [?x :db/ident :no/exist]]"# ;
let parsed = parse_find_string ( impossible ) . expect ( " parse failed " ) ;
2017-03-16 19:23:48 +00:00
let algebrized = algebrize ( & schema , parsed ) . expect ( " algebrize failed " ) ;
2017-03-07 04:18:38 +00:00
// This query cannot return results: the ident doesn't resolve for a ref-typed attribute.
assert! ( algebrized . is_known_empty ( ) ) ;
// If you insist…
let select = query_to_select ( algebrized ) ;
let sql = select . query . to_sql_query ( ) . unwrap ( ) . sql ;
assert_eq! ( " SELECT 1 LIMIT 0 " , sql ) ;
}
2017-03-16 19:23:48 +00:00
#[ test ]
fn test_numeric_less_than_unknown_attribute ( ) {
let schema = Schema ::default ( ) ;
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :where [?x _ ?y] [(< ?y 10)]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-16 19:23:48 +00:00
2017-03-20 14:11:32 +00:00
// Although we infer numericness from numeric predicates, we've already assigned a table to the
// first pattern, and so this is _still_ `all_datoms`.
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `all_datoms00`.e AS `?x` FROM `all_datoms` AS `all_datoms00` WHERE `all_datoms00`.v < 10 " ) ;
2017-03-16 19:23:48 +00:00
assert_eq! ( args , vec! [ ] ) ;
}
#[ test ]
fn test_numeric_gte_known_attribute ( ) {
2017-04-04 21:54:08 +00:00
let schema = prepopulated_typed_schema ( ValueType ::Double ) ;
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :where [?x :foo/bar ?y] [(>= ?y 12.9)]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v >= 12.9 " ) ;
2017-03-16 19:23:48 +00:00
assert_eq! ( args , vec! [ ] ) ;
}
#[ test ]
fn test_numeric_not_equals_known_attribute ( ) {
2017-04-04 21:54:08 +00:00
let schema = prepopulated_typed_schema ( ValueType ::Long ) ;
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x . :where [?x :foo/bar ?y] [(!= ?y 12)]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-03-22 21:02:00 +00:00
assert_eq! ( sql , " SELECT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v <> 12 LIMIT 1 " ) ;
2017-03-16 19:23:48 +00:00
assert_eq! ( args , vec! [ ] ) ;
2017-04-04 21:54:08 +00:00
}
#[ test ]
fn test_simple_or_join ( ) {
let mut schema = Schema ::default ( ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " url " ) , 97 ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " title " ) , 98 ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " description " ) , 99 ) ;
for x in 97 .. 100 {
add_attribute ( & mut schema , x , Attribute {
value_type : ValueType ::String ,
.. Default ::default ( )
} ) ;
}
2017-04-19 23:16:19 +00:00
let query = r #" [:find [?url ?description]
2017-04-04 21:54:08 +00:00
:where
( or - join [ ? page ]
[ ? page :page / url " http://foo.com/ " ]
[ ? page :page / title " Foo " ] )
[ ? page :page / url ? url ]
[ ? page :page / description ? description ] ] " #;
2017-04-19 23:16:19 +00:00
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-04-04 21:54:08 +00:00
assert_eq! ( sql , " SELECT `datoms01`.v AS `?url`, `datoms02`.v AS `?description` FROM `datoms` AS `datoms00`, `datoms` AS `datoms01`, `datoms` AS `datoms02` WHERE ((`datoms00`.a = 97 AND `datoms00`.v = $v0) OR (`datoms00`.a = 98 AND `datoms00`.v = $v1)) AND `datoms01`.a = 97 AND `datoms02`.a = 99 AND `datoms00`.e = `datoms01`.e AND `datoms00`.e = `datoms02`.e LIMIT 1 " ) ;
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " http://foo.com/ " ) , make_arg ( " $v1 " , " Foo " ) ] ) ;
}
2017-04-11 17:31:31 +00:00
#[ test ]
fn test_complex_or_join ( ) {
let mut schema = Schema ::default ( ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " save " ) , 95 ) ;
add_attribute ( & mut schema , 95 , Attribute {
value_type : ValueType ::Ref ,
.. Default ::default ( )
} ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " save " , " title " ) , 96 ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " url " ) , 97 ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " title " ) , 98 ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " description " ) , 99 ) ;
for x in 96 .. 100 {
add_attribute ( & mut schema , x , Attribute {
value_type : ValueType ::String ,
.. Default ::default ( )
} ) ;
}
2017-04-19 23:16:19 +00:00
let query = r #" [:find [?url ?description]
2017-04-11 17:31:31 +00:00
:where
( or - join [ ? page ]
[ ? page :page / url " http://foo.com/ " ]
[ ? page :page / title " Foo " ]
( and
[ ? page :page / save ? save ]
[ ? save :save / title " Foo " ] ) )
[ ? page :page / url ? url ]
[ ? page :page / description ? description ] ] " #;
2017-04-19 23:16:19 +00:00
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-04-11 17:31:31 +00:00
assert_eq! ( sql , " SELECT `datoms04`.v AS `?url`, \
` datoms05 ` . v AS ` ? description ` \
FROM ( SELECT ` datoms00 ` . e AS ` ? page ` \
FROM ` datoms ` AS ` datoms00 ` \
WHERE ` datoms00 ` . a = 97 \
AND ` datoms00 ` . v = $v0 \
UNION \
SELECT ` datoms01 ` . e AS ` ? page ` \
FROM ` datoms ` AS ` datoms01 ` \
WHERE ` datoms01 ` . a = 98 \
AND ` datoms01 ` . v = $v1 \
UNION \
SELECT ` datoms02 ` . e AS ` ? page ` \
FROM ` datoms ` AS ` datoms02 ` , \
` datoms ` AS ` datoms03 ` \
WHERE ` datoms02 ` . a = 95 \
AND ` datoms03 ` . a = 96 \
AND ` datoms03 ` . v = $v2 \
AND ` datoms02 ` . v = ` datoms03 ` . e ) AS ` c00 ` , \
` datoms ` AS ` datoms04 ` , \
` datoms ` AS ` datoms05 ` \
WHERE ` datoms04 ` . a = 97 \
AND ` datoms05 ` . a = 99 \
AND ` c00 ` . ` ? page ` = ` datoms04 ` . e \
AND ` c00 ` . ` ? page ` = ` datoms05 ` . e \
LIMIT 1 " );
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " http://foo.com/ " ) ,
make_arg ( " $v1 " , " Foo " ) ,
make_arg ( " $v2 " , " Foo " ) ] ) ;
}
#[ test ]
fn test_complex_or_join_type_projection ( ) {
let mut schema = Schema ::default ( ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " title " ) , 98 ) ;
add_attribute ( & mut schema , 98 , Attribute {
value_type : ValueType ::String ,
.. Default ::default ( )
} ) ;
2017-04-19 23:16:19 +00:00
let query = r #" [:find [?y]
2017-04-11 17:31:31 +00:00
:where
( or
[ 6 :page / title ? y ]
[ 5 _ ? y ] ) ] " #;
2017-04-19 23:16:19 +00:00
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-04-11 17:31:31 +00:00
assert_eq! ( sql , " SELECT `c00`.`?y` AS `?y`, \
` c00 ` . ` ? y_value_type_tag ` AS ` ? y_value_type_tag ` \
FROM ( SELECT ` datoms00 ` . v AS ` ? y ` , \
10 AS ` ? y_value_type_tag ` \
FROM ` datoms ` AS ` datoms00 ` \
WHERE ` datoms00 ` . e = 6 \
AND ` datoms00 ` . a = 98 \
UNION \
SELECT ` all_datoms01 ` . v AS ` ? y ` , \
` all_datoms01 ` . value_type_tag AS ` ? y_value_type_tag ` \
FROM ` all_datoms ` AS ` all_datoms01 ` \
WHERE ` all_datoms01 ` . e = 5 ) AS ` c00 ` \
LIMIT 1 " );
assert_eq! ( args , vec! [ ] ) ;
}
2017-04-17 16:23:55 +00:00
2017-04-28 09:44:11 +00:00
#[ test ]
fn test_not ( ) {
let mut schema = Schema ::default ( ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " url " ) , 97 ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " title " ) , 98 ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " bookmarked " ) , 99 ) ;
for x in 97 .. 99 {
add_attribute ( & mut schema , x , Attribute {
value_type : ValueType ::String ,
.. Default ::default ( )
} ) ;
}
add_attribute ( & mut schema , 99 , Attribute {
value_type : ValueType ::Boolean ,
.. Default ::default ( )
} ) ;
let query = r #" [:find ?title
:where [ ? page :page / title ? title ]
( not [ ? page :page / url " http://foo.com/ " ]
[ ? page :page / bookmarked true ] ) ] " #;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.v AS `?title` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 98 AND NOT EXISTS (SELECT 1 FROM `datoms` AS `datoms01`, `datoms` AS `datoms02` WHERE `datoms01`.a = 97 AND `datoms01`.v = $v0 AND `datoms02`.a = 99 AND `datoms02`.v = 1 AND `datoms00`.e = `datoms01`.e AND `datoms00`.e = `datoms02`.e) " ) ;
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " http://foo.com/ " ) ] ) ;
}
#[ test ]
fn test_not_join ( ) {
let mut schema = Schema ::default ( ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " url " ) , 97 ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " bookmarks " , " page " ) , 98 ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " bookmarks " , " date_created " ) , 99 ) ;
add_attribute ( & mut schema , 97 , Attribute {
value_type : ValueType ::String ,
.. Default ::default ( )
} ) ;
add_attribute ( & mut schema , 98 , Attribute {
value_type : ValueType ::Ref ,
.. Default ::default ( )
} ) ;
add_attribute ( & mut schema , 99 , Attribute {
value_type : ValueType ::String ,
.. Default ::default ( )
} ) ;
let query = r #" [:find ?url
:where [ ? url :page / url ]
( not - join [ ? url ]
[ ? page :bookmarks / page ? url ]
[ ? page :bookmarks / date_created " 4/4/2017 " ] ) ] " #;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?url` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 97 AND NOT EXISTS (SELECT 1 FROM `datoms` AS `datoms01`, `datoms` AS `datoms02` WHERE `datoms01`.a = 98 AND `datoms02`.a = 99 AND `datoms02`.v = $v0 AND `datoms01`.e = `datoms02`.e AND `datoms00`.e = `datoms01`.v) " ) ;
assert_eq! ( args , vec! [ make_arg ( " $v0 " , " 4/4/2017 " ) ] ) ;
}
2017-04-17 16:23:55 +00:00
#[ test ]
fn test_with_without_aggregate ( ) {
let schema = prepopulated_schema ( ) ;
// Known type.
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :with ?y :where [?x :foo/bar ?y]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-04-17 16:23:55 +00:00
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x`, `datoms00`.v AS `?y` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 " ) ;
assert_eq! ( args , vec! [ ] ) ;
// Unknown type.
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :with ?y :where [?x _ ?y]]"# ;
let SQLQuery { sql , args } = translate ( & schema , query ) ;
2017-04-17 16:23:55 +00:00
assert_eq! ( sql , " SELECT DISTINCT `all_datoms00`.e AS `?x`, `all_datoms00`.v AS `?y`, `all_datoms00`.value_type_tag AS `?y_value_type_tag` FROM `all_datoms` AS `all_datoms00` " ) ;
assert_eq! ( args , vec! [ ] ) ;
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 test_order_by ( ) {
let schema = prepopulated_schema ( ) ;
// Known type.
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :where [?x :foo/bar ?y] :order (desc ?y)]"# ;
let SQLQuery { sql , args } = translate ( & schema , 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
assert_eq! ( sql , " SELECT DISTINCT `datoms00`.e AS `?x`, `datoms00`.v AS `?y` \
FROM ` datoms ` AS ` datoms00 ` \
WHERE ` datoms00 ` . a = 99 \
ORDER BY ` ? y ` DESC " );
2017-04-17 20:29:40 +00:00
assert_eq! ( args , vec! [ ] ) ;
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
// Unknown type.
2017-04-19 23:16:19 +00:00
let query = r # "[:find ?x :with ?y :where [?x _ ?y] :order ?y ?x]"# ;
let SQLQuery { sql , args } = translate ( & schema , 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
assert_eq! ( sql , " SELECT DISTINCT `all_datoms00`.e AS `?x`, `all_datoms00`.v AS `?y`, \
` all_datoms00 ` . value_type_tag AS ` ? y_value_type_tag ` \
FROM ` all_datoms ` AS ` all_datoms00 ` \
ORDER BY ` ? y_value_type_tag ` ASC , ` ? y ` ASC , ` ? x ` ASC " );
2017-04-17 20:29:40 +00:00
assert_eq! ( args , vec! [ ] ) ;
2017-04-19 23:16:19 +00:00
}
2017-04-24 21:15:26 +00:00
#[ test ]
fn test_complex_nested_or_join_type_projection ( ) {
let mut schema = Schema ::default ( ) ;
associate_ident ( & mut schema , NamespacedKeyword ::new ( " page " , " title " ) , 98 ) ;
add_attribute ( & mut schema , 98 , Attribute {
value_type : ValueType ::String ,
.. Default ::default ( )
} ) ;
let input = r #" [:find [?y]
:where
( or
( or
[ _ :page / title ? y ] )
( or
[ _ :page / title ? y ] ) ) ] " #;
let SQLQuery { sql , args } = translate ( & schema , input ) ;
assert_eq! ( sql , " SELECT `c00`.`?y` AS `?y` \
FROM ( SELECT ` datoms00 ` . v AS ` ? y ` \
FROM ` datoms ` AS ` datoms00 ` \
WHERE ` datoms00 ` . a = 98 \
UNION \
SELECT ` datoms01 ` . v AS ` ? y ` \
FROM ` datoms ` AS ` datoms01 ` \
WHERE ` datoms01 ` . a = 98 ) \
AS ` c00 ` \
LIMIT 1 " );
assert_eq! ( args , vec! [ ] ) ;
2017-04-29 03:11:55 +00:00
}