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/ShapedJson \
|
||||
./lib/UserManager \
|
||||
./lib/V8 \
|
||||
./lib/V8
|
||||
|
||||
# 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
|
||||
|
|
|
@ -641,7 +641,7 @@ INPUT = \
|
|||
./lib/Rest \
|
||||
./lib/ShapedJson \
|
||||
./lib/UserManager \
|
||||
./lib/V8 \
|
||||
./lib/V8
|
||||
|
||||
# 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
|
||||
|
|
|
@ -631,7 +631,8 @@ INPUT = \
|
|||
./arangod/Ahuacatl \
|
||||
./arangod/RestHandler \
|
||||
./arangod/RestServer \
|
||||
./arangod/VocBase
|
||||
./arangod/V8Server \
|
||||
./arangod/VocBase \
|
||||
./lib/Admin \
|
||||
./lib/ApplicationServer \
|
||||
./lib/Basics \
|
||||
|
@ -640,7 +641,7 @@ INPUT = \
|
|||
./lib/Rest \
|
||||
./lib/ShapedJson \
|
||||
./lib/UserManager \
|
||||
./lib/V8 \
|
||||
./lib/V8
|
||||
|
||||
# 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
|
||||
|
@ -671,9 +672,9 @@ RECURSIVE = YES
|
|||
# subdirectory from a directory tree whose root is specified with the INPUT tag.
|
||||
|
||||
EXCLUDE = \
|
||||
./V8/v8-json.h \
|
||||
./V8/v8-json.cpp \
|
||||
./Ahuacatl/ahuacatl-tokens.c
|
||||
./lib/V8/v8-json.h \
|
||||
./lib/V8/v8-json.cpp \
|
||||
./arangod/Ahuacatl/ahuacatl-tokens.c
|
||||
|
||||
# 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
|
||||
|
|
|
@ -631,7 +631,8 @@ INPUT = \
|
|||
@srcdir@/arangod/Ahuacatl \
|
||||
@srcdir@/arangod/RestHandler \
|
||||
@srcdir@/arangod/RestServer \
|
||||
@srcdir@/arangod/VocBase
|
||||
@srcdir@/arangod/V8Server \
|
||||
@srcdir@/arangod/VocBase \
|
||||
@srcdir@/lib/Admin \
|
||||
@srcdir@/lib/ApplicationServer \
|
||||
@srcdir@/lib/Basics \
|
||||
|
@ -640,7 +641,7 @@ INPUT = \
|
|||
@srcdir@/lib/Rest \
|
||||
@srcdir@/lib/ShapedJson \
|
||||
@srcdir@/lib/UserManager \
|
||||
@srcdir@/lib/V8 \
|
||||
@srcdir@/lib/V8
|
||||
|
||||
# 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
|
||||
|
@ -671,9 +672,9 @@ RECURSIVE = YES
|
|||
# subdirectory from a directory tree whose root is specified with the INPUT tag.
|
||||
|
||||
EXCLUDE = \
|
||||
@srcdir@/V8/v8-json.h \
|
||||
@srcdir@/V8/v8-json.cpp \
|
||||
@srcdir@/Ahuacatl/ahuacatl-tokens.c
|
||||
@srcdir@/lib/V8/v8-json.h \
|
||||
@srcdir@/lib/V8/v8-json.cpp \
|
||||
@srcdir@/arangod/Ahuacatl/ahuacatl-tokens.c
|
||||
|
||||
# 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
|
||||
|
|
|
@ -54,6 +54,7 @@ DOXYGEN = \
|
|||
Doxygen/js/actions/system/api-collection.c \
|
||||
Doxygen/js/actions/system/api-cursor.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-query.c \
|
||||
Doxygen/js/actions/system/api-simple.c \
|
||||
|
@ -90,6 +91,7 @@ WIKI = \
|
|||
HttpCursor \
|
||||
HttpIndex \
|
||||
HttpInterface \
|
||||
HttpQueries \
|
||||
HttpSimple \
|
||||
HttpSystem \
|
||||
ImplementorManual \
|
||||
|
|
|
@ -1107,6 +1107,7 @@ DOXYGEN = \
|
|||
Doxygen/js/actions/system/api-collection.c \
|
||||
Doxygen/js/actions/system/api-cursor.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-query.c \
|
||||
Doxygen/js/actions/system/api-simple.c \
|
||||
|
@ -1143,6 +1144,7 @@ WIKI = \
|
|||
HttpCursor \
|
||||
HttpIndex \
|
||||
HttpInterface \
|
||||
HttpQueries \
|
||||
HttpSimple \
|
||||
HttpSystem \
|
||||
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-hash-spec.rb \
|
||||
api-index-skiplist-spec.rb \
|
||||
api-explain-spec.rb \
|
||||
api-cursor-spec.rb \
|
||||
api-simple-spec.rb
|
||||
|
|
|
@ -47,10 +47,6 @@
|
|||
|
||||
static char* AccessName (const TRI_aql_access_e type) {
|
||||
switch (type) {
|
||||
case TRI_AQL_ACCESS_ALL:
|
||||
return "all";
|
||||
case TRI_AQL_ACCESS_REFERENCE:
|
||||
return "reference";
|
||||
case TRI_AQL_ACCESS_IMPOSSIBLE:
|
||||
return "impossible";
|
||||
case TRI_AQL_ACCESS_EXACT:
|
||||
|
@ -61,6 +57,12 @@ static char* AccessName (const TRI_aql_access_e type) {
|
|||
return "single range";
|
||||
case TRI_AQL_ACCESS_RANGE_DOUBLE:
|
||||
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);
|
||||
|
@ -121,13 +123,8 @@ static void FreeAccessMembers (TRI_aql_field_access_t* const fieldAccess) {
|
|||
break;
|
||||
}
|
||||
|
||||
case TRI_AQL_ACCESS_REFERENCE: {
|
||||
if (fieldAccess->_value._value) {
|
||||
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TRI_AQL_ACCESS_REFERENCE_EXACT:
|
||||
case TRI_AQL_ACCESS_REFERENCE_RANGE:
|
||||
case TRI_AQL_ACCESS_ALL:
|
||||
case TRI_AQL_ACCESS_IMPOSSIBLE: {
|
||||
// nada
|
||||
|
@ -213,23 +210,6 @@ static TRI_aql_field_access_t* MergeAndAll (TRI_aql_context_t* const context,
|
|||
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
|
||||
///
|
||||
|
@ -336,6 +316,13 @@ static TRI_aql_field_access_t* MergeAndExact (TRI_aql_context_t* const context,
|
|||
|
||||
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);
|
||||
return NULL;
|
||||
|
@ -453,6 +440,13 @@ static TRI_aql_field_access_t* MergeAndList (TRI_aql_context_t* const context,
|
|||
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);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -785,6 +779,13 @@ static TRI_aql_field_access_t* MergeAndRangeSingle (TRI_aql_context_t* const con
|
|||
|
||||
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);
|
||||
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,
|
||||
TRI_aql_field_access_t* lhs,
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -840,35 +963,6 @@ static TRI_aql_field_access_t* MergeOrAll (TRI_aql_context_t* const context,
|
|||
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
|
||||
///
|
||||
|
@ -997,6 +1091,16 @@ static TRI_aql_field_access_t* MergeOrExact (TRI_aql_context_t* const context,
|
|||
|
||||
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);
|
||||
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,
|
||||
TRI_aql_field_access_t* lhs,
|
||||
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);
|
||||
/* 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;
|
||||
}
|
||||
|
@ -1190,10 +1430,6 @@ static TRI_aql_field_access_t* MergeAttributeAccessAnd (TRI_aql_context_t* const
|
|||
switch (lhs->_type) {
|
||||
case TRI_AQL_ACCESS_IMPOSSIBLE:
|
||||
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:
|
||||
return MergeAndExact(context, lhs, rhs);
|
||||
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);
|
||||
case TRI_AQL_ACCESS_RANGE_DOUBLE:
|
||||
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);
|
||||
|
@ -1233,10 +1475,6 @@ static TRI_aql_field_access_t* MergeAttributeAccessOr (TRI_aql_context_t* const
|
|||
switch (lhs->_type) {
|
||||
case TRI_AQL_ACCESS_IMPOSSIBLE:
|
||||
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:
|
||||
return MergeOrExact(context, lhs, rhs);
|
||||
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);
|
||||
case TRI_AQL_ACCESS_RANGE_DOUBLE:
|
||||
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);
|
||||
|
@ -1536,6 +1780,22 @@ static TRI_aql_field_access_t* CreateAccessForNode (TRI_aql_context_t* const con
|
|||
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...
|
||||
value = TRI_NodeJsonAql(context, node);
|
||||
if (value == NULL) {
|
||||
|
@ -1751,27 +2011,16 @@ static TRI_vector_pointer_t* ProcessNode (TRI_aql_context_t* const context,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS && TRI_IsConstantValueNodeAql(rhs)) {
|
||||
// collection.attribute operator value
|
||||
if ((lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) &&
|
||||
(TRI_IsConstantValueNodeAql(rhs) || rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS)) {
|
||||
// collection.attribute|reference operator value|reference
|
||||
node1 = lhs;
|
||||
node2 = rhs;
|
||||
operator = node->_type;
|
||||
}
|
||||
else if (rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS && TRI_IsConstantValueNodeAql(lhs)) {
|
||||
// value operator collection.attribute
|
||||
node1 = rhs;
|
||||
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
|
||||
else if ((rhs->_type == TRI_AQL_NODE_REFERENCE || rhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS) &&
|
||||
(TRI_IsConstantValueNodeAql(lhs) || lhs->_type == TRI_AQL_NODE_REFERENCE || lhs->_type == TRI_AQL_NODE_ATTRIBUTE_ACCESS)) {
|
||||
// value|reference operator collection.attribute|reference
|
||||
node1 = rhs;
|
||||
node2 = lhs;
|
||||
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 &&
|
||||
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
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
field = GetAttributeName(context, node1);
|
||||
|
||||
if (field) {
|
||||
|
@ -1960,8 +2210,14 @@ TRI_aql_field_access_t* TRI_CloneAccessAql (TRI_aql_context_t* const context,
|
|||
break;
|
||||
}
|
||||
|
||||
case TRI_AQL_ACCESS_REFERENCE: {
|
||||
// TODO
|
||||
case TRI_AQL_ACCESS_REFERENCE_EXACT: {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,13 +68,14 @@ TRI_aql_logical_e;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef enum {
|
||||
TRI_AQL_ACCESS_IMPOSSIBLE, // no value needs to be accessed (impossible range)
|
||||
TRI_AQL_ACCESS_EXACT, // one value 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_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_ALL // all values must be accessed (full scan)
|
||||
TRI_AQL_ACCESS_IMPOSSIBLE, // no value needs to be accessed (impossible range)
|
||||
TRI_AQL_ACCESS_EXACT, // one value 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_DOUBLE, // a two bounded range is accessed
|
||||
TRI_AQL_ACCESS_REFERENCE_EXACT, // a reference can be used for eq access (a.x == b.x)
|
||||
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;
|
||||
|
||||
|
@ -117,6 +118,12 @@ typedef struct TRI_aql_field_access_s {
|
|||
TRI_aql_range_t _upper; // upper bound
|
||||
}
|
||||
_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;
|
||||
}
|
||||
|
|
|
@ -653,6 +653,8 @@ static TRI_aql_codegen_register_t LookupSymbol (TRI_aql_codegen_js_t* const gene
|
|||
const char* const name) {
|
||||
size_t i = generator->_scopes._length;
|
||||
|
||||
assert(name);
|
||||
|
||||
// iterate from current scope to the top level scope
|
||||
while (i-- > 0) {
|
||||
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);
|
||||
|
||||
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) {
|
||||
ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_PRIMARY_LIST('");
|
||||
|
@ -891,7 +897,12 @@ static void GeneratePrimaryAccess (TRI_aql_codegen_js_t* const generator,
|
|||
ScopeOutput(generator, "', ");
|
||||
ScopeOutputIndexId(generator, collection, idx);
|
||||
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, ")");
|
||||
}
|
||||
|
||||
|
@ -937,7 +948,8 @@ static void GenerateHashAccess (TRI_aql_codegen_js_t* const generator,
|
|||
for (i = 0; i < n; ++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) {
|
||||
ScopeOutput(generator, ", ");
|
||||
|
@ -945,7 +957,12 @@ static void GenerateHashAccess (TRI_aql_codegen_js_t* const generator,
|
|||
|
||||
ScopeOutputQuoted2(generator, fieldAccess->_fullName + fieldAccess->_variableNameLength + 1);
|
||||
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, " })");
|
||||
|
@ -995,7 +1012,9 @@ static void GenerateSkiplistAccess (TRI_aql_codegen_js_t* const generator,
|
|||
|
||||
assert(fieldAccess->_type == TRI_AQL_ACCESS_EXACT ||
|
||||
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) {
|
||||
ScopeOutput(generator, ", ");
|
||||
|
@ -1029,6 +1048,18 @@ static void GenerateSkiplistAccess (TRI_aql_codegen_js_t* const generator,
|
|||
ScopeOutputJson(generator, fieldAccess->_value._between._upper._value);
|
||||
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, " ] ");
|
||||
}
|
||||
|
@ -1252,108 +1283,6 @@ static void ProcessCollectionHinted (TRI_aql_codegen_js_t* const generator,
|
|||
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
|
||||
|
@ -1361,7 +1290,6 @@ static void ProcessCollectionHinted (TRI_aql_codegen_js_t* const generator,
|
|||
|
||||
static void ProcessCollection (TRI_aql_codegen_js_t* const generator,
|
||||
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));
|
||||
|
||||
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 (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
|
||||
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) {
|
||||
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
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
continue;
|
||||
|
@ -243,10 +245,15 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context,
|
|||
TRI_PushBackVectorPointer(&matches, candidate);
|
||||
}
|
||||
else if (idx->_type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
||||
bool candidateIsExact;
|
||||
bool lastIsExact;
|
||||
|
||||
if (candidate->_type != TRI_AQL_ACCESS_EXACT &&
|
||||
candidate->_type != TRI_AQL_ACCESS_LIST &&
|
||||
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
|
||||
continue;
|
||||
}
|
||||
|
@ -256,8 +263,11 @@ TRI_aql_index_t* TRI_DetermineIndexAql (TRI_aql_context_t* const context,
|
|||
continue;
|
||||
}
|
||||
|
||||
if ((candidate->_type == TRI_AQL_ACCESS_EXACT && lastType != TRI_AQL_ACCESS_EXACT) ||
|
||||
(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);
|
||||
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 another range after that
|
||||
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_node_t* definingNode;
|
||||
char* variableName = (char*) TRI_AQL_NODE_STRING(node);
|
||||
size_t scopeCount; // ignored
|
||||
|
||||
assert(variableName);
|
||||
variable = TRI_GetVariableStatementWalkerAql(walker, variableName);
|
||||
variable = TRI_GetVariableStatementWalkerAql(walker, variableName, &scopeCount);
|
||||
|
||||
if (variable == NULL) {
|
||||
return node;
|
||||
|
@ -634,13 +635,13 @@ static TRI_aql_node_t* OptimiseBinaryRelationalOperation (TRI_aql_context_t* con
|
|||
func = "GREATER";
|
||||
}
|
||||
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) {
|
||||
func = "LESS";
|
||||
}
|
||||
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) {
|
||||
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* expressionNode;
|
||||
char* variableName;
|
||||
size_t scopeCount;
|
||||
bool isReference;
|
||||
|
||||
fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(ranges, i);
|
||||
assert(fieldAccess);
|
||||
|
@ -917,13 +920,21 @@ static void PatchVariables (TRI_aql_statement_walker_t* const walker) {
|
|||
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);
|
||||
|
||||
if (variable == NULL) {
|
||||
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
|
||||
|
||||
// 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,
|
||||
const char* const name) {
|
||||
const char* const name,
|
||||
size_t* scopeCount) {
|
||||
size_t n;
|
||||
|
||||
// init scope counter to 0
|
||||
*scopeCount = 0;
|
||||
|
||||
assert(name != NULL);
|
||||
|
||||
n = walker->_currentScopes._length;
|
||||
|
@ -251,6 +255,9 @@ TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_
|
|||
// reached the outermost scope
|
||||
break;
|
||||
}
|
||||
|
||||
// increase the scope counter
|
||||
(*scopeCount)++;
|
||||
}
|
||||
|
||||
// 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,
|
||||
const char* const);
|
||||
const char* const,
|
||||
size_t*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a statement walker
|
||||
|
|
|
@ -162,13 +162,6 @@ void JavascriptDispatcherThread::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.Dispose();
|
||||
|
||||
|
@ -177,6 +170,13 @@ void JavascriptDispatcherThread::run () {
|
|||
|
||||
_isolate->Exit();
|
||||
_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
|
||||
/// 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
|
||||
/// 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
|
||||
/// relevant for comparing documents.
|
||||
/// relevant when comparing two documents.
|
||||
///
|
||||
/// The combined list of attribute names is then traversed, and the respective
|
||||
/// attributes from the two compared operands are then looked up. If one of the
|
||||
/// documents does not have an attribute with the sought name, its attribute value
|
||||
/// for the comparison is considered to be @LIT{null}.
|
||||
/// If the attribute is found in both compared, documents, the usual data type and
|
||||
/// data value comparison is performed as described before.
|
||||
/// The combined and sorted list of attribute names is then traversed, and the
|
||||
/// respective attributes from the two compared operands are then looked up. If one
|
||||
/// of the documents does not have an attribute with the sought name, its attribute
|
||||
/// value is considered to be @LIT{null}.
|
||||
/// Finally, the attribute value of both documents is compared using the beforementioned
|
||||
/// 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
|
||||
///
|
||||
|
|
|
@ -51,8 +51,9 @@
|
|||
/// @copydetails RestDocumentTOC
|
||||
/// @copydetails RestEdgeTOC
|
||||
/// </li>
|
||||
/// <li>Light-Weight HTTP for Queries
|
||||
/// <li>Light-Weight HTTP for Queries and Cursors
|
||||
/// @copydetails HttpCursorTOC
|
||||
/// @copydetails HttpQueryTOC
|
||||
/// @copydetails HttpSimpleTOC
|
||||
/// @copydetails IndexCapHttpTOC
|
||||
/// @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);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in New Issue