mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:triAGENS/ArangoDB
This commit is contained in:
commit
1364ea8019
|
@ -0,0 +1,12 @@
|
||||||
|
> curl --data @- -X POST --dump - http://localhost:8529/_api/explain
|
||||||
|
{ "query" : "FOR u IN users FILTER u.name == @name LIMIT 2 RETURN u.n" }
|
||||||
|
|
||||||
|
HTTP/1.1 400 Bad Request
|
||||||
|
content-type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"errorNum": 1551,
|
||||||
|
"errorMessage": "no value specified for declared bind parameter 'name'",
|
||||||
|
"error": true,
|
||||||
|
"code": 400
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
> curl --data @- -X POST --dump - http://localhost:8529/_api/explain
|
||||||
|
{ "query" : "FOR u IN users FILTER u.id == @id LIMIT 2 RETURN u.name", "bindVars": { "id" : 3 } }
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
content-type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"plan": [
|
||||||
|
{ "id": 1,
|
||||||
|
"level": 1,
|
||||||
|
"type": "for",
|
||||||
|
"resultVariable": "u",
|
||||||
|
"expression": {
|
||||||
|
"type": "collection",
|
||||||
|
"value": "users",
|
||||||
|
"extra": {
|
||||||
|
"accessType": "index",
|
||||||
|
"index": {
|
||||||
|
"id": "1392880787389/1425650910275",
|
||||||
|
"type": "hash",
|
||||||
|
"attributes": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"level": 1,
|
||||||
|
"type": "filter",
|
||||||
|
"expression": {
|
||||||
|
"type": "expression",
|
||||||
|
"value": "u.id == 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"level": 1,
|
||||||
|
"type": "limit",
|
||||||
|
"offset": 0,
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"level": 1,
|
||||||
|
"type": "limit",
|
||||||
|
"offset": 0,
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"level": 1,
|
||||||
|
"type": "return",
|
||||||
|
"expression": {
|
||||||
|
"type": "expression",
|
||||||
|
"value": "u.name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"error": false,
|
||||||
|
"code": 200
|
||||||
|
}
|
|
@ -641,7 +641,7 @@ INPUT = \
|
||||||
./lib/Rest \
|
./lib/Rest \
|
||||||
./lib/ShapedJson \
|
./lib/ShapedJson \
|
||||||
./lib/UserManager \
|
./lib/UserManager \
|
||||||
./lib/V8 \
|
./lib/V8
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
||||||
|
|
|
@ -641,7 +641,7 @@ INPUT = \
|
||||||
./lib/Rest \
|
./lib/Rest \
|
||||||
./lib/ShapedJson \
|
./lib/ShapedJson \
|
||||||
./lib/UserManager \
|
./lib/UserManager \
|
||||||
./lib/V8 \
|
./lib/V8
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
||||||
|
|
|
@ -631,7 +631,8 @@ INPUT = \
|
||||||
./arangod/Ahuacatl \
|
./arangod/Ahuacatl \
|
||||||
./arangod/RestHandler \
|
./arangod/RestHandler \
|
||||||
./arangod/RestServer \
|
./arangod/RestServer \
|
||||||
./arangod/VocBase
|
./arangod/V8Server \
|
||||||
|
./arangod/VocBase \
|
||||||
./lib/Admin \
|
./lib/Admin \
|
||||||
./lib/ApplicationServer \
|
./lib/ApplicationServer \
|
||||||
./lib/Basics \
|
./lib/Basics \
|
||||||
|
@ -640,7 +641,7 @@ INPUT = \
|
||||||
./lib/Rest \
|
./lib/Rest \
|
||||||
./lib/ShapedJson \
|
./lib/ShapedJson \
|
||||||
./lib/UserManager \
|
./lib/UserManager \
|
||||||
./lib/V8 \
|
./lib/V8
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
||||||
|
@ -671,9 +672,9 @@ RECURSIVE = YES
|
||||||
# subdirectory from a directory tree whose root is specified with the INPUT tag.
|
# subdirectory from a directory tree whose root is specified with the INPUT tag.
|
||||||
|
|
||||||
EXCLUDE = \
|
EXCLUDE = \
|
||||||
./V8/v8-json.h \
|
./lib/V8/v8-json.h \
|
||||||
./V8/v8-json.cpp \
|
./lib/V8/v8-json.cpp \
|
||||||
./Ahuacatl/ahuacatl-tokens.c
|
./arangod/Ahuacatl/ahuacatl-tokens.c
|
||||||
|
|
||||||
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
|
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
|
||||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||||
|
|
|
@ -631,7 +631,8 @@ INPUT = \
|
||||||
@srcdir@/arangod/Ahuacatl \
|
@srcdir@/arangod/Ahuacatl \
|
||||||
@srcdir@/arangod/RestHandler \
|
@srcdir@/arangod/RestHandler \
|
||||||
@srcdir@/arangod/RestServer \
|
@srcdir@/arangod/RestServer \
|
||||||
@srcdir@/arangod/VocBase
|
@srcdir@/arangod/V8Server \
|
||||||
|
@srcdir@/arangod/VocBase \
|
||||||
@srcdir@/lib/Admin \
|
@srcdir@/lib/Admin \
|
||||||
@srcdir@/lib/ApplicationServer \
|
@srcdir@/lib/ApplicationServer \
|
||||||
@srcdir@/lib/Basics \
|
@srcdir@/lib/Basics \
|
||||||
|
@ -640,7 +641,7 @@ INPUT = \
|
||||||
@srcdir@/lib/Rest \
|
@srcdir@/lib/Rest \
|
||||||
@srcdir@/lib/ShapedJson \
|
@srcdir@/lib/ShapedJson \
|
||||||
@srcdir@/lib/UserManager \
|
@srcdir@/lib/UserManager \
|
||||||
@srcdir@/lib/V8 \
|
@srcdir@/lib/V8
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
||||||
|
@ -671,9 +672,9 @@ RECURSIVE = YES
|
||||||
# subdirectory from a directory tree whose root is specified with the INPUT tag.
|
# subdirectory from a directory tree whose root is specified with the INPUT tag.
|
||||||
|
|
||||||
EXCLUDE = \
|
EXCLUDE = \
|
||||||
@srcdir@/V8/v8-json.h \
|
@srcdir@/lib/V8/v8-json.h \
|
||||||
@srcdir@/V8/v8-json.cpp \
|
@srcdir@/lib/V8/v8-json.cpp \
|
||||||
@srcdir@/Ahuacatl/ahuacatl-tokens.c
|
@srcdir@/arangod/Ahuacatl/ahuacatl-tokens.c
|
||||||
|
|
||||||
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
|
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
|
||||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||||
|
|
|
@ -54,6 +54,7 @@ DOXYGEN = \
|
||||||
Doxygen/js/actions/system/api-collection.c \
|
Doxygen/js/actions/system/api-collection.c \
|
||||||
Doxygen/js/actions/system/api-cursor.c \
|
Doxygen/js/actions/system/api-cursor.c \
|
||||||
Doxygen/js/actions/system/api-edges.c \
|
Doxygen/js/actions/system/api-edges.c \
|
||||||
|
Doxygen/js/actions/system/api-explain.c \
|
||||||
Doxygen/js/actions/system/api-index.c \
|
Doxygen/js/actions/system/api-index.c \
|
||||||
Doxygen/js/actions/system/api-query.c \
|
Doxygen/js/actions/system/api-query.c \
|
||||||
Doxygen/js/actions/system/api-simple.c \
|
Doxygen/js/actions/system/api-simple.c \
|
||||||
|
@ -90,6 +91,7 @@ WIKI = \
|
||||||
HttpCursor \
|
HttpCursor \
|
||||||
HttpIndex \
|
HttpIndex \
|
||||||
HttpInterface \
|
HttpInterface \
|
||||||
|
HttpQueries \
|
||||||
HttpSimple \
|
HttpSimple \
|
||||||
HttpSystem \
|
HttpSystem \
|
||||||
ImplementorManual \
|
ImplementorManual \
|
||||||
|
|
|
@ -1107,6 +1107,7 @@ DOXYGEN = \
|
||||||
Doxygen/js/actions/system/api-collection.c \
|
Doxygen/js/actions/system/api-collection.c \
|
||||||
Doxygen/js/actions/system/api-cursor.c \
|
Doxygen/js/actions/system/api-cursor.c \
|
||||||
Doxygen/js/actions/system/api-edges.c \
|
Doxygen/js/actions/system/api-edges.c \
|
||||||
|
Doxygen/js/actions/system/api-explain.c \
|
||||||
Doxygen/js/actions/system/api-index.c \
|
Doxygen/js/actions/system/api-index.c \
|
||||||
Doxygen/js/actions/system/api-query.c \
|
Doxygen/js/actions/system/api-query.c \
|
||||||
Doxygen/js/actions/system/api-simple.c \
|
Doxygen/js/actions/system/api-simple.c \
|
||||||
|
@ -1143,6 +1144,7 @@ WIKI = \
|
||||||
HttpCursor \
|
HttpCursor \
|
||||||
HttpIndex \
|
HttpIndex \
|
||||||
HttpInterface \
|
HttpInterface \
|
||||||
|
HttpQueries \
|
||||||
HttpSimple \
|
HttpSimple \
|
||||||
HttpSystem \
|
HttpSystem \
|
||||||
ImplementorManual \
|
ImplementorManual \
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
require 'rspec'
|
||||||
|
require './arangodb.rb'
|
||||||
|
|
||||||
|
describe ArangoDB do
|
||||||
|
api = "/_api/explain"
|
||||||
|
prefix = "api-explain"
|
||||||
|
|
||||||
|
context "dealing with explain:" do
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## error handling
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
context "error handling:" do
|
||||||
|
it "returns an error if body is missing" do
|
||||||
|
cmd = api
|
||||||
|
doc = ArangoDB.log_post("#{prefix}-missing-body", cmd)
|
||||||
|
|
||||||
|
doc.code.should eq(400)
|
||||||
|
doc.headers['content-type'].should eq("application/json")
|
||||||
|
doc.parsed_response['error'].should eq(true)
|
||||||
|
doc.parsed_response['code'].should eq(400)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error if collection is unknown" do
|
||||||
|
cmd = api
|
||||||
|
body = "{ \"query\" : \"FOR u IN unknowncollection LIMIT 2 RETURN u.n\" }"
|
||||||
|
doc = ArangoDB.log_post("#{prefix}-unknown-collection", cmd, :body => body)
|
||||||
|
|
||||||
|
doc.code.should eq(400)
|
||||||
|
doc.headers['content-type'].should eq("application/json")
|
||||||
|
doc.parsed_response['error'].should eq(true)
|
||||||
|
doc.parsed_response['code'].should eq(400)
|
||||||
|
doc.parsed_response['errorNum'].should eq(1520)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error if bind variables are missing completely" do
|
||||||
|
cmd = api
|
||||||
|
body = "{ \"query\" : \"FOR u IN [1,2] FILTER u.id == @id RETURN 1\" }"
|
||||||
|
doc = ArangoDB.log_post("#{prefix}-missing-bind-variables-completely", cmd, :body => body)
|
||||||
|
|
||||||
|
doc.code.should eq(400)
|
||||||
|
doc.headers['content-type'].should eq("application/json")
|
||||||
|
doc.parsed_response['error'].should eq(true)
|
||||||
|
doc.parsed_response['code'].should eq(400)
|
||||||
|
doc.parsed_response['errorNum'].should eq(1551)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error if bind variables are required but empty" do
|
||||||
|
cmd = api
|
||||||
|
body = "{ \"query\" : \"FOR u IN [1,2] FILTER u.id == @id RETURN 1\", \"bindVars\" : { } }"
|
||||||
|
doc = ArangoDB.log_post("#{prefix}-missing-bind-variables-empty", cmd, :body => body)
|
||||||
|
|
||||||
|
doc.code.should eq(400)
|
||||||
|
doc.headers['content-type'].should eq("application/json")
|
||||||
|
doc.parsed_response['error'].should eq(true)
|
||||||
|
doc.parsed_response['code'].should eq(400)
|
||||||
|
doc.parsed_response['errorNum'].should eq(1551)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error if bind variables are missing" do
|
||||||
|
cmd = api
|
||||||
|
body = "{ \"query\" : \"FOR u IN [1,2] FILTER u.id == @id RETURN 1\", \"bindVars\" : { \"id2\" : 1 } }"
|
||||||
|
doc = ArangoDB.log_post("#{prefix}-missing-bind-variables-wrong", cmd, :body => body)
|
||||||
|
|
||||||
|
doc.code.should eq(400)
|
||||||
|
doc.headers['content-type'].should eq("application/json")
|
||||||
|
doc.parsed_response['error'].should eq(true)
|
||||||
|
doc.parsed_response['code'].should eq(400)
|
||||||
|
doc.parsed_response['errorNum'].should eq(1551)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error if query contains a parse error" do
|
||||||
|
cmd = api
|
||||||
|
body = "{ \"query\" : \"FOR u IN \" }"
|
||||||
|
doc = ArangoDB.log_post("#{prefix}-parse-error", cmd, :body => body)
|
||||||
|
|
||||||
|
doc.code.should eq(400)
|
||||||
|
doc.headers['content-type'].should eq("application/json")
|
||||||
|
doc.parsed_response['error'].should eq(true)
|
||||||
|
doc.parsed_response['code'].should eq(400)
|
||||||
|
doc.parsed_response['errorNum'].should eq(1501)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## explaining
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
context "explaining queries:" do
|
||||||
|
it "explains a simple query" do
|
||||||
|
cmd = api
|
||||||
|
body = "{ \"query\" : \"FOR u IN [1,2] RETURN u\" }"
|
||||||
|
doc = ArangoDB.log_post("#{prefix}-query-simple", cmd, :body => body)
|
||||||
|
|
||||||
|
doc.code.should eq(200)
|
||||||
|
doc.headers['content-type'].should eq("application/json")
|
||||||
|
doc.parsed_response['error'].should eq(false)
|
||||||
|
doc.parsed_response['code'].should eq(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,5 +11,6 @@ rspec --format d \
|
||||||
api-index-spec.rb \
|
api-index-spec.rb \
|
||||||
api-index-hash-spec.rb \
|
api-index-hash-spec.rb \
|
||||||
api-index-skiplist-spec.rb \
|
api-index-skiplist-spec.rb \
|
||||||
|
api-explain-spec.rb \
|
||||||
api-cursor-spec.rb \
|
api-cursor-spec.rb \
|
||||||
api-simple-spec.rb
|
api-simple-spec.rb
|
||||||
|
|
|
@ -47,10 +47,6 @@
|
||||||
|
|
||||||
static char* AccessName (const TRI_aql_access_e type) {
|
static char* AccessName (const TRI_aql_access_e type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TRI_AQL_ACCESS_ALL:
|
|
||||||
return "all";
|
|
||||||
case TRI_AQL_ACCESS_REFERENCE:
|
|
||||||
return "reference";
|
|
||||||
case TRI_AQL_ACCESS_IMPOSSIBLE:
|
case TRI_AQL_ACCESS_IMPOSSIBLE:
|
||||||
return "impossible";
|
return "impossible";
|
||||||
case TRI_AQL_ACCESS_EXACT:
|
case TRI_AQL_ACCESS_EXACT:
|
||||||
|
@ -61,6 +57,12 @@ static char* AccessName (const TRI_aql_access_e type) {
|
||||||
return "single range";
|
return "single range";
|
||||||
case TRI_AQL_ACCESS_RANGE_DOUBLE:
|
case TRI_AQL_ACCESS_RANGE_DOUBLE:
|
||||||
return "double range";
|
return "double range";
|
||||||
|
case TRI_AQL_ACCESS_REFERENCE_EXACT:
|
||||||
|
return "eq reference";
|
||||||
|
case TRI_AQL_ACCESS_REFERENCE_RANGE:
|
||||||
|
return "range reference";
|
||||||
|
case TRI_AQL_ACCESS_ALL:
|
||||||
|
return "all";
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
|
@ -121,13 +123,8 @@ static void FreeAccessMembers (TRI_aql_field_access_t* const fieldAccess) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TRI_AQL_ACCESS_REFERENCE: {
|
case TRI_AQL_ACCESS_REFERENCE_EXACT:
|
||||||
if (fieldAccess->_value._value) {
|
case TRI_AQL_ACCESS_REFERENCE_RANGE:
|
||||||
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TRI_AQL_ACCESS_ALL:
|
case TRI_AQL_ACCESS_ALL:
|
||||||
case TRI_AQL_ACCESS_IMPOSSIBLE: {
|
case TRI_AQL_ACCESS_IMPOSSIBLE: {
|
||||||
// nada
|
// nada
|
||||||
|
@ -213,23 +210,6 @@ static TRI_aql_field_access_t* MergeAndAll (TRI_aql_context_t* const context,
|
||||||
return rhs;
|
return rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief merge two access structures using a logical AND
|
|
||||||
///
|
|
||||||
/// left hand operand is a reference, so it will always be returned
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static TRI_aql_field_access_t* MergeAndReference (TRI_aql_context_t* const context,
|
|
||||||
TRI_aql_field_access_t* lhs,
|
|
||||||
TRI_aql_field_access_t* rhs) {
|
|
||||||
assert(lhs->_type == TRI_AQL_ACCESS_REFERENCE);
|
|
||||||
|
|
||||||
// reference always wins
|
|
||||||
TRI_FreeAccessAql(rhs);
|
|
||||||
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief merge two access structures using a logical AND
|
/// @brief merge two access structures using a logical AND
|
||||||
///
|
///
|
||||||
|
@ -336,6 +316,13 @@ static TRI_aql_field_access_t* MergeAndExact (TRI_aql_context_t* const context,
|
||||||
|
|
||||||
return rhs;
|
return rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_EXACT ||
|
||||||
|
rhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
// for simplicity, always return the const access
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -453,6 +440,13 @@ static TRI_aql_field_access_t* MergeAndList (TRI_aql_context_t* const context,
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_EXACT ||
|
||||||
|
rhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
// for simplicity, always return the const access
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -785,6 +779,13 @@ static TRI_aql_field_access_t* MergeAndRangeSingle (TRI_aql_context_t* const con
|
||||||
|
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_EXACT ||
|
||||||
|
rhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
// for simplicity, always return the const access
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -800,10 +801,132 @@ static TRI_aql_field_access_t* MergeAndRangeSingle (TRI_aql_context_t* const con
|
||||||
static TRI_aql_field_access_t* MergeAndRangeDouble (TRI_aql_context_t* const context,
|
static TRI_aql_field_access_t* MergeAndRangeDouble (TRI_aql_context_t* const context,
|
||||||
TRI_aql_field_access_t* lhs,
|
TRI_aql_field_access_t* lhs,
|
||||||
TRI_aql_field_access_t* rhs) {
|
TRI_aql_field_access_t* rhs) {
|
||||||
assert(false);
|
|
||||||
/* this should never be called. let's see if this assumption is true or not */
|
|
||||||
assert(lhs->_type == TRI_AQL_ACCESS_RANGE_DOUBLE);
|
assert(lhs->_type == TRI_AQL_ACCESS_RANGE_DOUBLE);
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_EXACT ||
|
||||||
|
rhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
// for simplicity, always return the const access
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
/* this should never be called. let's see if this assumption is true or not */
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief merge two access structures using a logical AND
|
||||||
|
///
|
||||||
|
/// left hand operand is a reference, so it will always be returned
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static TRI_aql_field_access_t* MergeAndReferenceExact (TRI_aql_context_t* const context,
|
||||||
|
TRI_aql_field_access_t* lhs,
|
||||||
|
TRI_aql_field_access_t* rhs) {
|
||||||
|
assert(lhs->_type == TRI_AQL_ACCESS_REFERENCE_EXACT);
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_EXACT) {
|
||||||
|
if (TRI_EqualString(lhs->_value._name,
|
||||||
|
rhs->_value._name)) {
|
||||||
|
// reference to the same variable/attribute, no special treatment here
|
||||||
|
// fall-through
|
||||||
|
}
|
||||||
|
|
||||||
|
// reference to different variable/attribute
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
if (TRI_EqualString(lhs->_value._name,
|
||||||
|
rhs->_value._referenceRange._name)) {
|
||||||
|
// reference to the same variable/attribute
|
||||||
|
|
||||||
|
if (rhs->_value._referenceRange._type == TRI_AQL_NODE_OPERATOR_BINARY_LT ||
|
||||||
|
rhs->_value._referenceRange._type == TRI_AQL_NODE_OPERATOR_BINARY_GT) {
|
||||||
|
// == && > or == && < => impossible
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
FreeAccessMembers(lhs);
|
||||||
|
lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE;
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// everything else results in lhs
|
||||||
|
// == && >= or == && <=
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reference to different variable/attribute, no special treatment here
|
||||||
|
}
|
||||||
|
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief merge two access structures using a logical AND
|
||||||
|
///
|
||||||
|
/// left hand operand is a reference, so it will always be returned
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static TRI_aql_field_access_t* MergeAndReferenceRange (TRI_aql_context_t* const context,
|
||||||
|
TRI_aql_field_access_t* lhs,
|
||||||
|
TRI_aql_field_access_t* rhs) {
|
||||||
|
assert(lhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE);
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
if (TRI_EqualString(lhs->_value._referenceRange._name,
|
||||||
|
rhs->_value._referenceRange._name)) {
|
||||||
|
// both references refer to the same variable, now compare their operators
|
||||||
|
TRI_aql_node_type_e lhsType = lhs->_value._referenceRange._type;
|
||||||
|
TRI_aql_node_type_e rhsType = rhs->_value._referenceRange._type;
|
||||||
|
bool possible = true;
|
||||||
|
|
||||||
|
if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_LT &&
|
||||||
|
(rhsType == TRI_AQL_NODE_OPERATOR_BINARY_GE || rhsType == TRI_AQL_NODE_OPERATOR_BINARY_GT)) {
|
||||||
|
// lhs < ref && (lhs >= ref || lhs > ref) => impossible
|
||||||
|
possible = false;
|
||||||
|
}
|
||||||
|
else if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_LE &&
|
||||||
|
rhsType == TRI_AQL_NODE_OPERATOR_BINARY_GT) {
|
||||||
|
// lhs <= ref && lhs > ref => impossible
|
||||||
|
possible = false;
|
||||||
|
}
|
||||||
|
else if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_GT &&
|
||||||
|
(rhsType == TRI_AQL_NODE_OPERATOR_BINARY_LE || rhsType == TRI_AQL_NODE_OPERATOR_BINARY_LT)) {
|
||||||
|
// lhs > ref && (lhs <= ref || lhs < ref) => impossible
|
||||||
|
possible = false;
|
||||||
|
}
|
||||||
|
else if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_GE &&
|
||||||
|
rhsType == TRI_AQL_NODE_OPERATOR_BINARY_LT) {
|
||||||
|
// lhs >= ref && lhs < ref => impossible
|
||||||
|
possible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!possible) {
|
||||||
|
// return the impossible range
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
FreeAccessMembers(lhs);
|
||||||
|
lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE;
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fall-through
|
||||||
|
}
|
||||||
|
|
||||||
|
// return either side (we pick lhs, but it does not matter)
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -840,35 +963,6 @@ static TRI_aql_field_access_t* MergeOrAll (TRI_aql_context_t* const context,
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief merge two access structures using a logical OR
|
|
||||||
///
|
|
||||||
/// left hand operand is reference access
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static TRI_aql_field_access_t* MergeOrReference (TRI_aql_context_t* const context,
|
|
||||||
TRI_aql_field_access_t* lhs,
|
|
||||||
TRI_aql_field_access_t* rhs) {
|
|
||||||
assert(lhs->_type == TRI_AQL_ACCESS_REFERENCE);
|
|
||||||
|
|
||||||
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE) {
|
|
||||||
// if rhs is also a reference, we can keep it if both refer to the same value
|
|
||||||
if (TRI_EqualString(lhs->_value._value->_value._string.data, rhs->_value._value->_value._string.data)) {
|
|
||||||
TRI_FreeAccessAql(rhs);
|
|
||||||
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for everything else, we have to use the ALL range unfortunately
|
|
||||||
|
|
||||||
TRI_FreeAccessAql(rhs);
|
|
||||||
FreeAccessMembers(lhs);
|
|
||||||
lhs->_type = TRI_AQL_ACCESS_ALL;
|
|
||||||
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief merge two access structures using a logical OR
|
/// @brief merge two access structures using a logical OR
|
||||||
///
|
///
|
||||||
|
@ -997,6 +1091,16 @@ static TRI_aql_field_access_t* MergeOrExact (TRI_aql_context_t* const context,
|
||||||
|
|
||||||
return rhs;
|
return rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_EXACT ||
|
||||||
|
rhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
// reference cannot be ORed with anything else
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
FreeAccessMembers(lhs);
|
||||||
|
lhs->_type = TRI_AQL_ACCESS_ALL;
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1158,9 +1262,145 @@ static TRI_aql_field_access_t* MergeOrRangeSingle (TRI_aql_context_t* const cont
|
||||||
static TRI_aql_field_access_t* MergeOrRangeDouble (TRI_aql_context_t* const context,
|
static TRI_aql_field_access_t* MergeOrRangeDouble (TRI_aql_context_t* const context,
|
||||||
TRI_aql_field_access_t* lhs,
|
TRI_aql_field_access_t* lhs,
|
||||||
TRI_aql_field_access_t* rhs) {
|
TRI_aql_field_access_t* rhs) {
|
||||||
|
assert(lhs->_type == TRI_AQL_ACCESS_RANGE_DOUBLE);
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_EXACT ||
|
||||||
|
rhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
// reference cannot be ORed with anything else
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
FreeAccessMembers(lhs);
|
||||||
|
lhs->_type = TRI_AQL_ACCESS_ALL;
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
/* this should never be called. let's see if this assumption is true or not */
|
/* this should never be called. let's see if this assumption is true or not */
|
||||||
assert(lhs->_type == TRI_AQL_ACCESS_RANGE_DOUBLE);
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief merge two access structures using a logical OR
|
||||||
|
///
|
||||||
|
/// left hand operand is reference access
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static TRI_aql_field_access_t* MergeOrReferenceExact (TRI_aql_context_t* const context,
|
||||||
|
TRI_aql_field_access_t* lhs,
|
||||||
|
TRI_aql_field_access_t* rhs) {
|
||||||
|
assert(lhs->_type == TRI_AQL_ACCESS_REFERENCE_EXACT);
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_EXACT) {
|
||||||
|
if (TRI_EqualString(lhs->_value._name,
|
||||||
|
rhs->_value._name)) {
|
||||||
|
// both references refer to the same variable
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
// fall-through
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
if (TRI_EqualString(lhs->_value._name,
|
||||||
|
rhs->_value._referenceRange._name)) {
|
||||||
|
// both references refer to the same variable
|
||||||
|
TRI_aql_node_type_e lhsType = lhs->_value._referenceRange._type;
|
||||||
|
TRI_aql_node_type_e rhsType = rhs->_value._referenceRange._type;
|
||||||
|
|
||||||
|
if (lhsType == rhsType) {
|
||||||
|
// same operation
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_LT && rhsType == TRI_AQL_NODE_OPERATOR_BINARY_LE) {
|
||||||
|
TRI_FreeAccessAql(lhs);
|
||||||
|
|
||||||
|
return rhs;
|
||||||
|
}
|
||||||
|
else if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_LE && rhsType == TRI_AQL_NODE_OPERATOR_BINARY_LT) {
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
else if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_GT && rhsType == TRI_AQL_NODE_OPERATOR_BINARY_GE) {
|
||||||
|
TRI_FreeAccessAql(lhs);
|
||||||
|
|
||||||
|
return rhs;
|
||||||
|
}
|
||||||
|
else if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_GE && rhsType == TRI_AQL_NODE_OPERATOR_BINARY_GT) {
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fall-through
|
||||||
|
}
|
||||||
|
|
||||||
|
// for everything else, we have to use the ALL range unfortunately
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
FreeAccessMembers(lhs);
|
||||||
|
lhs->_type = TRI_AQL_ACCESS_ALL;
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief merge two access structures using a logical OR
|
||||||
|
///
|
||||||
|
/// left hand operand is reference access
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static TRI_aql_field_access_t* MergeOrReferenceRange (TRI_aql_context_t* const context,
|
||||||
|
TRI_aql_field_access_t* lhs,
|
||||||
|
TRI_aql_field_access_t* rhs) {
|
||||||
|
assert(lhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE);
|
||||||
|
|
||||||
|
if (rhs->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
if (TRI_EqualString(lhs->_value._referenceRange._name,
|
||||||
|
rhs->_value._referenceRange._name)) {
|
||||||
|
// both references refer to the same variable
|
||||||
|
TRI_aql_node_type_e lhsType = lhs->_value._referenceRange._type;
|
||||||
|
TRI_aql_node_type_e rhsType = rhs->_value._referenceRange._type;
|
||||||
|
|
||||||
|
if (lhsType == rhsType) {
|
||||||
|
// same operation
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_LT && rhsType == TRI_AQL_NODE_OPERATOR_BINARY_LE) {
|
||||||
|
TRI_FreeAccessAql(lhs);
|
||||||
|
|
||||||
|
return rhs;
|
||||||
|
}
|
||||||
|
else if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_LE && rhsType == TRI_AQL_NODE_OPERATOR_BINARY_LT) {
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
else if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_GT && rhsType == TRI_AQL_NODE_OPERATOR_BINARY_GE) {
|
||||||
|
TRI_FreeAccessAql(lhs);
|
||||||
|
|
||||||
|
return rhs;
|
||||||
|
}
|
||||||
|
else if (lhsType == TRI_AQL_NODE_OPERATOR_BINARY_GE && rhsType == TRI_AQL_NODE_OPERATOR_BINARY_GT) {
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fall-through
|
||||||
|
}
|
||||||
|
|
||||||
|
// for everything else, we have to use the ALL range unfortunately
|
||||||
|
TRI_FreeAccessAql(rhs);
|
||||||
|
FreeAccessMembers(lhs);
|
||||||
|
lhs->_type = TRI_AQL_ACCESS_ALL;
|
||||||
|
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
@ -1190,10 +1430,6 @@ static TRI_aql_field_access_t* MergeAttributeAccessAnd (TRI_aql_context_t* const
|
||||||
switch (lhs->_type) {
|
switch (lhs->_type) {
|
||||||
case TRI_AQL_ACCESS_IMPOSSIBLE:
|
case TRI_AQL_ACCESS_IMPOSSIBLE:
|
||||||
return MergeAndImpossible(context, lhs, rhs);
|
return MergeAndImpossible(context, lhs, rhs);
|
||||||
case TRI_AQL_ACCESS_ALL:
|
|
||||||
return MergeAndAll(context, lhs, rhs);
|
|
||||||
case TRI_AQL_ACCESS_REFERENCE:
|
|
||||||
return MergeAndReference(context, lhs, rhs);
|
|
||||||
case TRI_AQL_ACCESS_EXACT:
|
case TRI_AQL_ACCESS_EXACT:
|
||||||
return MergeAndExact(context, lhs, rhs);
|
return MergeAndExact(context, lhs, rhs);
|
||||||
case TRI_AQL_ACCESS_LIST:
|
case TRI_AQL_ACCESS_LIST:
|
||||||
|
@ -1202,6 +1438,12 @@ static TRI_aql_field_access_t* MergeAttributeAccessAnd (TRI_aql_context_t* const
|
||||||
return MergeAndRangeSingle(context, lhs, rhs);
|
return MergeAndRangeSingle(context, lhs, rhs);
|
||||||
case TRI_AQL_ACCESS_RANGE_DOUBLE:
|
case TRI_AQL_ACCESS_RANGE_DOUBLE:
|
||||||
return MergeAndRangeDouble(context, lhs, rhs);
|
return MergeAndRangeDouble(context, lhs, rhs);
|
||||||
|
case TRI_AQL_ACCESS_REFERENCE_EXACT:
|
||||||
|
return MergeAndReferenceExact(context, lhs, rhs);
|
||||||
|
case TRI_AQL_ACCESS_REFERENCE_RANGE:
|
||||||
|
return MergeAndReferenceRange(context, lhs, rhs);
|
||||||
|
case TRI_AQL_ACCESS_ALL:
|
||||||
|
return MergeAndAll(context, lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
|
@ -1233,10 +1475,6 @@ static TRI_aql_field_access_t* MergeAttributeAccessOr (TRI_aql_context_t* const
|
||||||
switch (lhs->_type) {
|
switch (lhs->_type) {
|
||||||
case TRI_AQL_ACCESS_IMPOSSIBLE:
|
case TRI_AQL_ACCESS_IMPOSSIBLE:
|
||||||
return MergeOrImpossible(context, lhs, rhs);
|
return MergeOrImpossible(context, lhs, rhs);
|
||||||
case TRI_AQL_ACCESS_ALL:
|
|
||||||
return MergeOrAll(context, lhs, rhs);
|
|
||||||
case TRI_AQL_ACCESS_REFERENCE:
|
|
||||||
return MergeOrReference(context, lhs, rhs);
|
|
||||||
case TRI_AQL_ACCESS_EXACT:
|
case TRI_AQL_ACCESS_EXACT:
|
||||||
return MergeOrExact(context, lhs, rhs);
|
return MergeOrExact(context, lhs, rhs);
|
||||||
case TRI_AQL_ACCESS_LIST:
|
case TRI_AQL_ACCESS_LIST:
|
||||||
|
@ -1245,6 +1483,12 @@ static TRI_aql_field_access_t* MergeAttributeAccessOr (TRI_aql_context_t* const
|
||||||
return MergeOrRangeSingle(context, lhs, rhs);
|
return MergeOrRangeSingle(context, lhs, rhs);
|
||||||
case TRI_AQL_ACCESS_RANGE_DOUBLE:
|
case TRI_AQL_ACCESS_RANGE_DOUBLE:
|
||||||
return MergeOrRangeDouble(context, lhs, rhs);
|
return MergeOrRangeDouble(context, lhs, rhs);
|
||||||
|
case TRI_AQL_ACCESS_REFERENCE_EXACT:
|
||||||
|
return MergeOrReferenceExact(context, lhs, rhs);
|
||||||
|
case TRI_AQL_ACCESS_REFERENCE_RANGE:
|
||||||
|
return MergeOrReferenceRange(context, lhs, rhs);
|
||||||
|
case TRI_AQL_ACCESS_ALL:
|
||||||
|
return MergeOrAll(context, lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
|
@ -1536,6 +1780,22 @@ static TRI_aql_field_access_t* CreateAccessForNode (TRI_aql_context_t* const con
|
||||||
return fieldAccess;
|
return fieldAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node->_type == TRI_AQL_NODE_REFERENCE) {
|
||||||
|
// create the reference access
|
||||||
|
if (operator == TRI_AQL_NODE_OPERATOR_BINARY_EQ) {
|
||||||
|
fieldAccess->_type = TRI_AQL_ACCESS_REFERENCE_EXACT;
|
||||||
|
fieldAccess->_value._name = TRI_AQL_NODE_STRING(node);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fieldAccess->_type = TRI_AQL_ACCESS_REFERENCE_RANGE;
|
||||||
|
fieldAccess->_value._referenceRange._type = operator;
|
||||||
|
fieldAccess->_value._referenceRange._name = TRI_AQL_NODE_STRING(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// all other operation types require a value...
|
// all other operation types require a value...
|
||||||
value = TRI_NodeJsonAql(context, node);
|
value = TRI_NodeJsonAql(context, node);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
|
@ -1751,27 +2011,16 @@ static TRI_vector_pointer_t* ProcessNode (TRI_aql_context_t* const context,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS && TRI_IsConstantValueNodeAql(rhs)) {
|
if ((lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) &&
|
||||||
// collection.attribute operator value
|
(TRI_IsConstantValueNodeAql(rhs) || rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS)) {
|
||||||
|
// collection.attribute|reference operator value|reference
|
||||||
node1 = lhs;
|
node1 = lhs;
|
||||||
node2 = rhs;
|
node2 = rhs;
|
||||||
operator = node->_type;
|
operator = node->_type;
|
||||||
}
|
}
|
||||||
else if (rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS && TRI_IsConstantValueNodeAql(lhs)) {
|
else if ((rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) &&
|
||||||
// value operator collection.attribute
|
(TRI_IsConstantValueNodeAql(lhs) || lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS)) {
|
||||||
node1 = rhs;
|
// value|reference operator collection.attribute|reference
|
||||||
node2 = lhs;
|
|
||||||
operator = TRI_ReverseOperatorRelationalAql(node->_type);
|
|
||||||
assert(operator != TRI_AQL_NODE_NOP);
|
|
||||||
}
|
|
||||||
else if (lhs->_type == TRI_AQL_NODE_REFERENCE && TRI_IsConstantValueNodeAql(rhs)) {
|
|
||||||
// variable operator value
|
|
||||||
node1 = lhs;
|
|
||||||
node2 = rhs;
|
|
||||||
operator = node->_type;
|
|
||||||
}
|
|
||||||
else if (rhs->_type == TRI_AQL_NODE_REFERENCE && TRI_IsConstantValueNodeAql(lhs)) {
|
|
||||||
// value operator variable
|
|
||||||
node1 = rhs;
|
node1 = rhs;
|
||||||
node2 = lhs;
|
node2 = lhs;
|
||||||
operator = TRI_ReverseOperatorRelationalAql(node->_type);
|
operator = TRI_ReverseOperatorRelationalAql(node->_type);
|
||||||
|
@ -1783,11 +2032,12 @@ static TRI_vector_pointer_t* ProcessNode (TRI_aql_context_t* const context,
|
||||||
|
|
||||||
if (node2->_type != TRI_AQL_NODE_VALUE &&
|
if (node2->_type != TRI_AQL_NODE_VALUE &&
|
||||||
node2->_type != TRI_AQL_NODE_LIST &&
|
node2->_type != TRI_AQL_NODE_LIST &&
|
||||||
node2->_type != TRI_AQL_NODE_ARRAY) {
|
node2->_type != TRI_AQL_NODE_ARRAY &&
|
||||||
|
node2->_type != TRI_AQL_NODE_REFERENCE) {
|
||||||
// only the above types are supported
|
// only the above types are supported
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
field = GetAttributeName(context, node1);
|
field = GetAttributeName(context, node1);
|
||||||
|
|
||||||
if (field) {
|
if (field) {
|
||||||
|
@ -1960,8 +2210,14 @@ TRI_aql_field_access_t* TRI_CloneAccessAql (TRI_aql_context_t* const context,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TRI_AQL_ACCESS_REFERENCE: {
|
case TRI_AQL_ACCESS_REFERENCE_EXACT: {
|
||||||
// TODO
|
fieldAccess->_value._name = source->_value._name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TRI_AQL_ACCESS_REFERENCE_RANGE: {
|
||||||
|
fieldAccess->_value._referenceRange._type = source->_value._referenceRange._type;
|
||||||
|
fieldAccess->_value._referenceRange._name = source->_value._referenceRange._name;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,13 +68,14 @@ TRI_aql_logical_e;
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TRI_AQL_ACCESS_IMPOSSIBLE, // no value needs to be accessed (impossible range)
|
TRI_AQL_ACCESS_IMPOSSIBLE, // no value needs to be accessed (impossible range)
|
||||||
TRI_AQL_ACCESS_EXACT, // one value is accessed
|
TRI_AQL_ACCESS_EXACT, // one value is accessed
|
||||||
TRI_AQL_ACCESS_LIST, // a list of values is accessed
|
TRI_AQL_ACCESS_LIST, // a list of values is accessed
|
||||||
TRI_AQL_ACCESS_RANGE_SINGLE, // a range with one boundary is accessed
|
TRI_AQL_ACCESS_RANGE_SINGLE, // a range with one boundary is accessed
|
||||||
TRI_AQL_ACCESS_RANGE_DOUBLE, // a two bounded range is accessed
|
TRI_AQL_ACCESS_RANGE_DOUBLE, // a two bounded range is accessed
|
||||||
TRI_AQL_ACCESS_REFERENCE, // a reference can be used for access (a.x == b.x)
|
TRI_AQL_ACCESS_REFERENCE_EXACT, // a reference can be used for eq access (a.x == b.x)
|
||||||
TRI_AQL_ACCESS_ALL // all values must be accessed (full scan)
|
TRI_AQL_ACCESS_REFERENCE_RANGE, // a reference can be used for rahe access (a.x > b.x)
|
||||||
|
TRI_AQL_ACCESS_ALL // all values must be accessed (full scan)
|
||||||
}
|
}
|
||||||
TRI_aql_access_e;
|
TRI_aql_access_e;
|
||||||
|
|
||||||
|
@ -117,6 +118,12 @@ typedef struct TRI_aql_field_access_s {
|
||||||
TRI_aql_range_t _upper; // upper bound
|
TRI_aql_range_t _upper; // upper bound
|
||||||
}
|
}
|
||||||
_between; // used for TRI_AQL_ACCESS_RANGE_DOUBLE
|
_between; // used for TRI_AQL_ACCESS_RANGE_DOUBLE
|
||||||
|
char* _name; // used for TRI_AQL_ACCESS_REFERENCE_EXACT
|
||||||
|
struct {
|
||||||
|
char* _name;
|
||||||
|
TRI_aql_node_type_e _type;
|
||||||
|
}
|
||||||
|
_referenceRange; // used for TRI_AQL_ACCESS_REFERENCE_RANGE
|
||||||
}
|
}
|
||||||
_value;
|
_value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -653,6 +653,8 @@ static TRI_aql_codegen_register_t LookupSymbol (TRI_aql_codegen_js_t* const gene
|
||||||
const char* const name) {
|
const char* const name) {
|
||||||
size_t i = generator->_scopes._length;
|
size_t i = generator->_scopes._length;
|
||||||
|
|
||||||
|
assert(name);
|
||||||
|
|
||||||
// iterate from current scope to the top level scope
|
// iterate from current scope to the top level scope
|
||||||
while (i-- > 0) {
|
while (i-- > 0) {
|
||||||
TRI_aql_codegen_scope_t* scope = (TRI_aql_codegen_scope_t*) generator->_scopes._buffer[i];
|
TRI_aql_codegen_scope_t* scope = (TRI_aql_codegen_scope_t*) generator->_scopes._buffer[i];
|
||||||
|
@ -879,6 +881,10 @@ static void GeneratePrimaryAccess (TRI_aql_codegen_js_t* const generator,
|
||||||
assert(n == 1);
|
assert(n == 1);
|
||||||
|
|
||||||
fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, 0);
|
fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, 0);
|
||||||
|
|
||||||
|
assert(fieldAccess->_type == TRI_AQL_ACCESS_EXACT ||
|
||||||
|
fieldAccess->_type == TRI_AQL_ACCESS_LIST ||
|
||||||
|
fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE_EXACT);
|
||||||
|
|
||||||
if (fieldAccess->_type == TRI_AQL_ACCESS_LIST) {
|
if (fieldAccess->_type == TRI_AQL_ACCESS_LIST) {
|
||||||
ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_PRIMARY_LIST('");
|
ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_PRIMARY_LIST('");
|
||||||
|
@ -891,7 +897,12 @@ static void GeneratePrimaryAccess (TRI_aql_codegen_js_t* const generator,
|
||||||
ScopeOutput(generator, "', ");
|
ScopeOutput(generator, "', ");
|
||||||
ScopeOutputIndexId(generator, collection, idx);
|
ScopeOutputIndexId(generator, collection, idx);
|
||||||
ScopeOutput(generator, ", ");
|
ScopeOutput(generator, ", ");
|
||||||
ScopeOutputJson(generator, fieldAccess->_value._value);
|
if (fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE_EXACT) {
|
||||||
|
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._name));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ScopeOutputJson(generator, fieldAccess->_value._value);
|
||||||
|
}
|
||||||
ScopeOutput(generator, ")");
|
ScopeOutput(generator, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -937,7 +948,8 @@ static void GenerateHashAccess (TRI_aql_codegen_js_t* const generator,
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, i);
|
TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, i);
|
||||||
|
|
||||||
assert(fieldAccess->_type == TRI_AQL_ACCESS_EXACT);
|
assert(fieldAccess->_type == TRI_AQL_ACCESS_EXACT ||
|
||||||
|
fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE_EXACT);
|
||||||
|
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
ScopeOutput(generator, ", ");
|
ScopeOutput(generator, ", ");
|
||||||
|
@ -945,7 +957,12 @@ static void GenerateHashAccess (TRI_aql_codegen_js_t* const generator,
|
||||||
|
|
||||||
ScopeOutputQuoted2(generator, fieldAccess->_fullName + fieldAccess->_variableNameLength + 1);
|
ScopeOutputQuoted2(generator, fieldAccess->_fullName + fieldAccess->_variableNameLength + 1);
|
||||||
ScopeOutput(generator, " : ");
|
ScopeOutput(generator, " : ");
|
||||||
ScopeOutputJson(generator, fieldAccess->_value._value);
|
if (fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE_EXACT) {
|
||||||
|
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._name));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ScopeOutputJson(generator, fieldAccess->_value._value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopeOutput(generator, " })");
|
ScopeOutput(generator, " })");
|
||||||
|
@ -995,7 +1012,9 @@ static void GenerateSkiplistAccess (TRI_aql_codegen_js_t* const generator,
|
||||||
|
|
||||||
assert(fieldAccess->_type == TRI_AQL_ACCESS_EXACT ||
|
assert(fieldAccess->_type == TRI_AQL_ACCESS_EXACT ||
|
||||||
fieldAccess->_type == TRI_AQL_ACCESS_RANGE_SINGLE ||
|
fieldAccess->_type == TRI_AQL_ACCESS_RANGE_SINGLE ||
|
||||||
fieldAccess->_type == TRI_AQL_ACCESS_RANGE_DOUBLE);
|
fieldAccess->_type == TRI_AQL_ACCESS_RANGE_DOUBLE ||
|
||||||
|
fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE_EXACT ||
|
||||||
|
fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE_RANGE);
|
||||||
|
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
ScopeOutput(generator, ", ");
|
ScopeOutput(generator, ", ");
|
||||||
|
@ -1029,6 +1048,18 @@ static void GenerateSkiplistAccess (TRI_aql_codegen_js_t* const generator,
|
||||||
ScopeOutputJson(generator, fieldAccess->_value._between._upper._value);
|
ScopeOutputJson(generator, fieldAccess->_value._between._upper._value);
|
||||||
ScopeOutput(generator, " ] ");
|
ScopeOutput(generator, " ] ");
|
||||||
}
|
}
|
||||||
|
else if (fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE_EXACT) {
|
||||||
|
ScopeOutput(generator, " [ \"==\", ");
|
||||||
|
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._name));
|
||||||
|
ScopeOutput(generator, " ] ");
|
||||||
|
}
|
||||||
|
else if (fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
|
ScopeOutput(generator, " [ \"");
|
||||||
|
ScopeOutput(generator, TRI_RangeOperatorAql(fieldAccess->_value._singleRange._type));
|
||||||
|
ScopeOutput(generator, "\", ");
|
||||||
|
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._name));
|
||||||
|
ScopeOutput(generator, " ] ");
|
||||||
|
}
|
||||||
|
|
||||||
ScopeOutput(generator, " ] ");
|
ScopeOutput(generator, " ] ");
|
||||||
}
|
}
|
||||||
|
@ -1252,108 +1283,6 @@ static void ProcessCollectionHinted (TRI_aql_codegen_js_t* const generator,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
|
|
||||||
// TRI_vector_pointer_t* fieldAccesses = (TRI_vector_pointer_t*) TRI_AQL_NODE_DATA(node);
|
|
||||||
TRI_aql_collection_t* collection;
|
|
||||||
TRI_vector_pointer_t* availableIndexes;
|
|
||||||
TRI_aql_index_t* idx;
|
|
||||||
char* collectionName;
|
|
||||||
|
|
||||||
assert(nameNode);
|
|
||||||
collectionName = TRI_AQL_NODE_STRING(nameNode);
|
|
||||||
assert(collectionName);
|
|
||||||
|
|
||||||
collection = TRI_GetCollectionAql(generator->_context, collectionName);
|
|
||||||
if (collection == NULL) {
|
|
||||||
generator->_error = true;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
availableIndexes = &(((TRI_sim_collection_t*) collection->_collection->_collection)->_indexes);
|
|
||||||
if (availableIndexes == NULL) {
|
|
||||||
generator->_error = true;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
idx = TRI_DetermineIndexAql(generator->_context,
|
|
||||||
availableIndexes,
|
|
||||||
collectionName,
|
|
||||||
fieldAccesses);
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief generate code for collection access (hinted access)
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static void ProcessCollectionHinted (TRI_aql_codegen_js_t* const generator,
|
|
||||||
const TRI_aql_node_t* const node) {
|
|
||||||
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0);
|
|
||||||
TRI_vector_pointer_t* fieldAccesses = (TRI_vector_pointer_t*) TRI_AQL_NODE_DATA(node);
|
|
||||||
TRI_aql_collection_t* collection;
|
|
||||||
TRI_vector_pointer_t* availableIndexes;
|
|
||||||
TRI_aql_index_t* idx;
|
|
||||||
char* collectionName;
|
|
||||||
|
|
||||||
assert(nameNode);
|
|
||||||
collectionName = TRI_AQL_NODE_STRING(nameNode);
|
|
||||||
assert(collectionName);
|
|
||||||
|
|
||||||
collection = TRI_GetCollectionAql(generator->_context, collectionName);
|
|
||||||
if (collection == NULL) {
|
|
||||||
generator->_error = true;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
availableIndexes = &(((TRI_sim_collection_t*) collection->_collection->_collection)->_indexes);
|
|
||||||
if (availableIndexes == NULL) {
|
|
||||||
generator->_error = true;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
idx = TRI_DetermineIndexAql(generator->_context,
|
|
||||||
availableIndexes,
|
|
||||||
collectionName,
|
|
||||||
fieldAccesses);
|
|
||||||
|
|
||||||
if (idx == NULL) {
|
|
||||||
// no index can be used, proceed with normal (full table scan) access
|
|
||||||
ProcessCollectionFull(generator, node);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (idx->_idx->_type) {
|
|
||||||
case TRI_IDX_TYPE_GEO1_INDEX:
|
|
||||||
case TRI_IDX_TYPE_GEO2_INDEX:
|
|
||||||
case TRI_IDX_TYPE_PRIORITY_QUEUE_INDEX:
|
|
||||||
case TRI_IDX_TYPE_CAP_CONSTRAINT:
|
|
||||||
// these indexes are not yet supported
|
|
||||||
generator->_error = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TRI_IDX_TYPE_PRIMARY_INDEX:
|
|
||||||
GeneratePrimaryAccess(generator, idx, collection, collectionName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TRI_IDX_TYPE_HASH_INDEX:
|
|
||||||
GenerateHashAccess(generator, idx, collection, collectionName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TRI_IDX_TYPE_SKIPLIST_INDEX:
|
|
||||||
GenerateSkiplistAccess(generator, idx, collection, collectionName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRI_FreeIndexAql(idx);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief generate code for collection access
|
/// @brief generate code for collection access
|
||||||
|
@ -1361,7 +1290,6 @@ static void ProcessCollectionHinted (TRI_aql_codegen_js_t* const generator,
|
||||||
|
|
||||||
static void ProcessCollection (TRI_aql_codegen_js_t* const generator,
|
static void ProcessCollection (TRI_aql_codegen_js_t* const generator,
|
||||||
const TRI_aql_node_t* const node) {
|
const TRI_aql_node_t* const node) {
|
||||||
//TRI_vector_pointer_t* fieldAccesses = (TRI_vector_pointer_t*) (TRI_AQL_NODE_DATA(node));
|
|
||||||
TRI_aql_collection_hint_t* hint = (TRI_aql_collection_hint_t*) (TRI_AQL_NODE_DATA(node));
|
TRI_aql_collection_hint_t* hint = (TRI_aql_collection_hint_t*) (TRI_AQL_NODE_DATA(node));
|
||||||
|
|
||||||
assert(hint);
|
assert(hint);
|
||||||
|
|
|
@ -221,7 +221,8 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context,
|
||||||
|
|
||||||
if (idx->_type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
if (idx->_type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||||
if (candidate->_type != TRI_AQL_ACCESS_EXACT &&
|
if (candidate->_type != TRI_AQL_ACCESS_EXACT &&
|
||||||
candidate->_type != TRI_AQL_ACCESS_LIST) {
|
candidate->_type != TRI_AQL_ACCESS_LIST &&
|
||||||
|
candidate->_type != TRI_AQL_ACCESS_REFERENCE_EXACT) {
|
||||||
// wrong access type for primary index
|
// wrong access type for primary index
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -230,11 +231,12 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context,
|
||||||
}
|
}
|
||||||
else if (idx->_type == TRI_IDX_TYPE_HASH_INDEX) {
|
else if (idx->_type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||||
if (candidate->_type != TRI_AQL_ACCESS_EXACT &&
|
if (candidate->_type != TRI_AQL_ACCESS_EXACT &&
|
||||||
candidate->_type != TRI_AQL_ACCESS_LIST) {
|
candidate->_type != TRI_AQL_ACCESS_LIST &&
|
||||||
|
candidate->_type != TRI_AQL_ACCESS_REFERENCE_EXACT) {
|
||||||
// wrong access type for hash index
|
// wrong access type for hash index
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (candidate->_type == TRI_AQL_ACCESS_LIST && numIndexFields != 1) {
|
if (candidate->_type == TRI_AQL_ACCESS_LIST && numIndexFields != 1) {
|
||||||
// we found a list, but the index covers multiple attributes. that means we cannot use list access
|
// we found a list, but the index covers multiple attributes. that means we cannot use list access
|
||||||
continue;
|
continue;
|
||||||
|
@ -243,10 +245,15 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context,
|
||||||
TRI_PushBackVectorPointer(&matches, candidate);
|
TRI_PushBackVectorPointer(&matches, candidate);
|
||||||
}
|
}
|
||||||
else if (idx->_type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
else if (idx->_type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
||||||
|
bool candidateIsExact;
|
||||||
|
bool lastIsExact;
|
||||||
|
|
||||||
if (candidate->_type != TRI_AQL_ACCESS_EXACT &&
|
if (candidate->_type != TRI_AQL_ACCESS_EXACT &&
|
||||||
candidate->_type != TRI_AQL_ACCESS_LIST &&
|
candidate->_type != TRI_AQL_ACCESS_LIST &&
|
||||||
candidate->_type != TRI_AQL_ACCESS_RANGE_SINGLE &&
|
candidate->_type != TRI_AQL_ACCESS_RANGE_SINGLE &&
|
||||||
candidate->_type != TRI_AQL_ACCESS_RANGE_DOUBLE) {
|
candidate->_type != TRI_AQL_ACCESS_RANGE_DOUBLE &&
|
||||||
|
candidate->_type != TRI_AQL_ACCESS_REFERENCE_EXACT &&
|
||||||
|
candidate->_type != TRI_AQL_ACCESS_REFERENCE_RANGE) {
|
||||||
// wrong access type for skiplists
|
// wrong access type for skiplists
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -256,8 +263,11 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((candidate->_type == TRI_AQL_ACCESS_EXACT && lastType != TRI_AQL_ACCESS_EXACT) ||
|
candidateIsExact = (candidate->_type == TRI_AQL_ACCESS_EXACT || candidate->_type == TRI_AQL_ACCESS_REFERENCE_EXACT);
|
||||||
(candidate->_type != TRI_AQL_ACCESS_EXACT && lastType != TRI_AQL_ACCESS_EXACT)) {
|
lastIsExact = (lastType == TRI_AQL_ACCESS_EXACT || lastType == TRI_AQL_ACCESS_REFERENCE_EXACT);
|
||||||
|
|
||||||
|
if ((candidateIsExact && !lastIsExact) ||
|
||||||
|
(!candidateIsExact && !lastIsExact)) {
|
||||||
// if we already had a range query, we cannot check for equality after that
|
// if we already had a range query, we cannot check for equality after that
|
||||||
// if we already had a range query, we cannot check another range after that
|
// if we already had a range query, we cannot check another range after that
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -445,9 +445,10 @@ static TRI_aql_node_t* OptimiseReference (TRI_aql_statement_walker_t* const walk
|
||||||
TRI_aql_variable_t* variable;
|
TRI_aql_variable_t* variable;
|
||||||
TRI_aql_node_t* definingNode;
|
TRI_aql_node_t* definingNode;
|
||||||
char* variableName = (char*) TRI_AQL_NODE_STRING(node);
|
char* variableName = (char*) TRI_AQL_NODE_STRING(node);
|
||||||
|
size_t scopeCount; // ignored
|
||||||
|
|
||||||
assert(variableName);
|
assert(variableName);
|
||||||
variable = TRI_GetVariableStatementWalkerAql(walker, variableName);
|
variable = TRI_GetVariableStatementWalkerAql(walker, variableName, &scopeCount);
|
||||||
|
|
||||||
if (variable == NULL) {
|
if (variable == NULL) {
|
||||||
return node;
|
return node;
|
||||||
|
@ -634,13 +635,13 @@ static TRI_aql_node_t* OptimiseBinaryRelationalOperation (TRI_aql_context_t* con
|
||||||
func = "GREATER";
|
func = "GREATER";
|
||||||
}
|
}
|
||||||
else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_GE) {
|
else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_GE) {
|
||||||
func = "GREATER_EQUAL";
|
func = "GREATEREQUAL";
|
||||||
}
|
}
|
||||||
else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_LT) {
|
else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_LT) {
|
||||||
func = "LESS";
|
func = "LESS";
|
||||||
}
|
}
|
||||||
else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_LE) {
|
else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_LE) {
|
||||||
func = "LESS_EQUAL";
|
func = "LESSEQUAL";
|
||||||
}
|
}
|
||||||
else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_IN) {
|
else if (node->_type == TRI_AQL_NODE_OPERATOR_BINARY_IN) {
|
||||||
func = "IN";
|
func = "IN";
|
||||||
|
@ -904,6 +905,8 @@ static void PatchVariables (TRI_aql_statement_walker_t* const walker) {
|
||||||
TRI_aql_node_t* definingNode;
|
TRI_aql_node_t* definingNode;
|
||||||
TRI_aql_node_t* expressionNode;
|
TRI_aql_node_t* expressionNode;
|
||||||
char* variableName;
|
char* variableName;
|
||||||
|
size_t scopeCount;
|
||||||
|
bool isReference;
|
||||||
|
|
||||||
fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(ranges, i);
|
fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(ranges, i);
|
||||||
assert(fieldAccess);
|
assert(fieldAccess);
|
||||||
|
@ -917,13 +920,21 @@ static void PatchVariables (TRI_aql_statement_walker_t* const walker) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
variable = TRI_GetVariableStatementWalkerAql(walker, variableName);
|
isReference = (fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE_EXACT ||
|
||||||
|
fieldAccess->_type == TRI_AQL_ACCESS_REFERENCE_RANGE);
|
||||||
|
|
||||||
|
variable = TRI_GetVariableStatementWalkerAql(walker, variableName, &scopeCount);
|
||||||
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, variableName);
|
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, variableName);
|
||||||
|
|
||||||
if (variable == NULL) {
|
if (variable == NULL) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isReference && scopeCount > 0) {
|
||||||
|
// unfortunately, the referenced variable is in an outer scope, so we cannot use it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// note: we must not modify outer variables of subqueries
|
// note: we must not modify outer variables of subqueries
|
||||||
|
|
||||||
// get the node that defines the variable
|
// get the node that defines the variable
|
||||||
|
|
|
@ -229,9 +229,13 @@ TRI_vector_pointer_t* TRI_GetScopesStatementWalkerAql (TRI_aql_statement_walker_
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_t* const walker,
|
TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_t* const walker,
|
||||||
const char* const name) {
|
const char* const name,
|
||||||
|
size_t* scopeCount) {
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
|
// init scope counter to 0
|
||||||
|
*scopeCount = 0;
|
||||||
|
|
||||||
assert(name != NULL);
|
assert(name != NULL);
|
||||||
|
|
||||||
n = walker->_currentScopes._length;
|
n = walker->_currentScopes._length;
|
||||||
|
@ -251,6 +255,9 @@ TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_
|
||||||
// reached the outermost scope
|
// reached the outermost scope
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// increase the scope counter
|
||||||
|
(*scopeCount)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// variable not found
|
// variable not found
|
||||||
|
|
|
@ -121,7 +121,8 @@ TRI_vector_pointer_t* TRI_GetScopesStatementWalkerAql (TRI_aql_statement_walker_
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_t* const,
|
TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_t* const,
|
||||||
const char* const);
|
const char* const,
|
||||||
|
size_t*);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief create a statement walker
|
/// @brief create a statement walker
|
||||||
|
|
|
@ -162,13 +162,6 @@ void JavascriptDispatcherThread::run () {
|
||||||
|
|
||||||
DispatcherThread::run();
|
DispatcherThread::run();
|
||||||
|
|
||||||
// free memory for this thread
|
|
||||||
TRI_v8_global_t* v8g = (TRI_v8_global_t*) _isolate->GetData();
|
|
||||||
|
|
||||||
if (v8g) {
|
|
||||||
delete v8g;
|
|
||||||
}
|
|
||||||
|
|
||||||
_context->Exit();
|
_context->Exit();
|
||||||
_context.Dispose();
|
_context.Dispose();
|
||||||
|
|
||||||
|
@ -177,6 +170,13 @@ void JavascriptDispatcherThread::run () {
|
||||||
|
|
||||||
_isolate->Exit();
|
_isolate->Exit();
|
||||||
_isolate->Dispose();
|
_isolate->Dispose();
|
||||||
|
|
||||||
|
// free memory for this thread
|
||||||
|
TRI_v8_global_t* v8g = (TRI_v8_global_t*) _isolate->GetData();
|
||||||
|
|
||||||
|
if (v8g) {
|
||||||
|
delete v8g;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @page HttpCursor HTTP Interface for Cursors
|
/// @page HttpCursor HTTP Interface for AQL Query Cursors
|
||||||
///
|
///
|
||||||
/// This is an introduction to ArangoDB's Http interface for Queries. Results
|
/// This is an introduction to ArangoDB's Http interface for Queries. Results
|
||||||
/// of AQL and simple queries are returned as cursors in order to batch the
|
/// of AQL and simple queries are returned as cursors in order to batch the
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief over the wire protocol
|
||||||
|
///
|
||||||
|
/// @file
|
||||||
|
///
|
||||||
|
/// DISCLAIMER
|
||||||
|
///
|
||||||
|
/// Copyright 2012 triagens GmbH, Cologne, Germany
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||||
|
///
|
||||||
|
/// @author Jan Steemann
|
||||||
|
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @page HttpQueryTOC
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@ref HttpQueries
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@ref HttpExplainPost "POST /_api/explain"</li>
|
||||||
|
/// <li>@ref HttpQueryPost "POST /_api/query"</li>
|
||||||
|
/// </li>
|
||||||
|
/// </ul>
|
||||||
|
/// </li>
|
||||||
|
/// </ul>
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @page HttpQueries HTTP Interface for AQL Queries
|
||||||
|
///
|
||||||
|
/// ArangoDB has an Http interface to syntactically validate AQL queries.
|
||||||
|
/// Furthermore, it offers an Http interface to retrieve the execution plan for
|
||||||
|
/// any valid AQL query.
|
||||||
|
///
|
||||||
|
/// Both functionalities do not actually execute the supplied AQL query, but
|
||||||
|
/// only inspect it and return meta information about it.
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// @anchor HttpExplainPost
|
||||||
|
/// @copydetails JSF_POST_api_explain
|
||||||
|
///
|
||||||
|
/// @anchor HttpQueryPost
|
||||||
|
/// @copydetails JSF_POST_api_query
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Local Variables:
|
||||||
|
// mode: c++
|
||||||
|
// mode: outline-minor
|
||||||
|
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}\\)"
|
||||||
|
// End:
|
|
@ -494,16 +494,21 @@
|
||||||
///
|
///
|
||||||
/// Two documents operands are compared by checking attribute names and value. The
|
/// Two documents operands are compared by checking attribute names and value. The
|
||||||
/// attribute names are compared first. Before attribute names are compared, a
|
/// attribute names are compared first. Before attribute names are compared, a
|
||||||
/// combined list of all attribute names from both operands is created and sorted.
|
/// combined list of all attribute names from both operands is created and sorted
|
||||||
|
/// lexicographically.
|
||||||
/// This means that the order in which attributes are declared in a document is not
|
/// This means that the order in which attributes are declared in a document is not
|
||||||
/// relevant for comparing documents.
|
/// relevant when comparing two documents.
|
||||||
///
|
///
|
||||||
/// The combined list of attribute names is then traversed, and the respective
|
/// The combined and sorted list of attribute names is then traversed, and the
|
||||||
/// attributes from the two compared operands are then looked up. If one of the
|
/// respective attributes from the two compared operands are then looked up. If one
|
||||||
/// documents does not have an attribute with the sought name, its attribute value
|
/// of the documents does not have an attribute with the sought name, its attribute
|
||||||
/// for the comparison is considered to be @LIT{null}.
|
/// value is considered to be @LIT{null}.
|
||||||
/// If the attribute is found in both compared, documents, the usual data type and
|
/// Finally, the attribute value of both documents is compared using the beforementioned
|
||||||
/// data value comparison is performed as described before.
|
/// data type and value comparison.
|
||||||
|
/// The comparisons are performed for all document attributes until there is an
|
||||||
|
/// unambigious comparison result. If an unambigious comparison result is found, the
|
||||||
|
/// comparison is finished. If there is no unambigious comparison result, the two
|
||||||
|
/// compared documents are considered equal.
|
||||||
///
|
///
|
||||||
/// @verbinclude aqlcompareexamples2
|
/// @verbinclude aqlcompareexamples2
|
||||||
///
|
///
|
||||||
|
|
|
@ -51,8 +51,9 @@
|
||||||
/// @copydetails RestDocumentTOC
|
/// @copydetails RestDocumentTOC
|
||||||
/// @copydetails RestEdgeTOC
|
/// @copydetails RestEdgeTOC
|
||||||
/// </li>
|
/// </li>
|
||||||
/// <li>Light-Weight HTTP for Queries
|
/// <li>Light-Weight HTTP for Queries and Cursors
|
||||||
/// @copydetails HttpCursorTOC
|
/// @copydetails HttpCursorTOC
|
||||||
|
/// @copydetails HttpQueryTOC
|
||||||
/// @copydetails HttpSimpleTOC
|
/// @copydetails HttpSimpleTOC
|
||||||
/// @copydetails IndexCapHttpTOC
|
/// @copydetails IndexCapHttpTOC
|
||||||
/// @copydetails IndexGeoHttpTOC
|
/// @copydetails IndexGeoHttpTOC
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief query explain actions
|
||||||
|
///
|
||||||
|
/// @file
|
||||||
|
///
|
||||||
|
/// DISCLAIMER
|
||||||
|
///
|
||||||
|
/// Copyright 2012 triagens GmbH, Cologne, Germany
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||||
|
///
|
||||||
|
/// @author Jan Steemann
|
||||||
|
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @addtogroup ArangoAPI
|
||||||
|
/// @{
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- global variables
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var actions = require("actions");
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief explain a query and return information about it
|
||||||
|
///
|
||||||
|
/// @RESTHEADER{POST /_api/explain,explains a query}
|
||||||
|
///
|
||||||
|
/// @REST{POST /_api/explain}
|
||||||
|
///
|
||||||
|
/// To explain how a query would be executed (without actually executing it),
|
||||||
|
/// the query string can be passed to the server via an HTTP POST request.
|
||||||
|
///
|
||||||
|
/// These query string needs to be passed in the attribute @LIT{query} of a JSON
|
||||||
|
/// object as the body of the POST request. If the query references any bind
|
||||||
|
/// variables, these must also be passed in the attribute @LIT{bindVars}.
|
||||||
|
///
|
||||||
|
/// If the query is valid, the server will respond with @LIT{HTTP 200} and
|
||||||
|
/// return a list of the individual query execution steps in the @LIT{"plan"}
|
||||||
|
/// attribute of the response.
|
||||||
|
///
|
||||||
|
/// The server will respond with @LIT{HTTP 400} in case of a malformed request,
|
||||||
|
/// or if the query contains a parse error. The body of the response will
|
||||||
|
/// contain the error details embedded in a JSON object.
|
||||||
|
/// Omitting bind variables if the query references any will result also result
|
||||||
|
/// in an @LIT{HTTP 400} error.
|
||||||
|
///
|
||||||
|
/// @EXAMPLES
|
||||||
|
///
|
||||||
|
/// Valid query:
|
||||||
|
///
|
||||||
|
/// @verbinclude api-explain-valid
|
||||||
|
///
|
||||||
|
/// Invalid query:
|
||||||
|
///
|
||||||
|
/// @verbinclude api-explain-invalid
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function POST_api_explain (req, res) {
|
||||||
|
if (req.suffix.length != 0) {
|
||||||
|
actions.resultNotFound(req, res, actions.ERROR_HTTP_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = actions.getJsonBody(req, res);
|
||||||
|
|
||||||
|
if (json === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var result = AHUACATL_EXPLAIN(json.query, json.bindVars);
|
||||||
|
|
||||||
|
if (result instanceof ArangoError) {
|
||||||
|
actions.resultBad(req, res, result.errorNum, result.errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = { "plan" : result };
|
||||||
|
|
||||||
|
actions.resultOk(req, res, actions.HTTP_OK, result);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
actions.resultException(req, res, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- initialiser
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief explain gateway
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
actions.defineHttp({
|
||||||
|
url : "_api/explain",
|
||||||
|
context : "api",
|
||||||
|
|
||||||
|
callback : function (req, res) {
|
||||||
|
switch (req.requestType) {
|
||||||
|
case (actions.POST) :
|
||||||
|
POST_api_explain(req, res);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
actions.resultUnsupported(req, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @}
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Local Variables:
|
||||||
|
// mode: outline-minor
|
||||||
|
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
|
||||||
|
// End:
|
|
@ -313,6 +313,259 @@ function ahuacatlQuerySimpleTestSuite () {
|
||||||
assertEqual(expected, actual);
|
assertEqual(expected, actual);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter9 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("filter 1 == 0 filter 2 == 3 return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter10 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 filter a > 1 && a < 0 filter a > 2 && a < 1 return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter11 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 filter a == 1 && a == 2 && a == 3 return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter12 : function () {
|
||||||
|
var expected = [ 2 ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a == b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter13 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a != b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter14 : function () {
|
||||||
|
var expected = [ 2 ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a >= b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter15 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a > b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter16 : function () {
|
||||||
|
var expected = [ 2 ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a <= b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter17 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a < b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter18 : function () {
|
||||||
|
var expected = [ 2 ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a >= b filter a <= b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter19 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a >= b filter a < b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter20 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a >= b filter a > b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter21 : function () {
|
||||||
|
var expected = [ 2 ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a >= b filter a >= b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter22 : function () {
|
||||||
|
var expected = [ 2 ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a >= b filter a == b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter23 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a > b filter a <= b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter24 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a > b filter a < b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter25 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a > b filter a > b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter26 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a > b filter a >= b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter27 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a > b filter a == b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter28 : function () {
|
||||||
|
var expected = [ 2 ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a == b filter a <= b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter29 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a == b filter a < b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter30 : function () {
|
||||||
|
var expected = [ 2 ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a == b filter a >= b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a value from a filter
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testReturnFilter31 : function () {
|
||||||
|
var expected = [ ];
|
||||||
|
|
||||||
|
var actual = getQueryResults("let a = 1 let b = 1 filter a == b filter a > b return 2");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief sort and return
|
/// @brief sort and return
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Reference in New Issue