1
0
Fork 0

Merge branch 'devel' of github.com:triAGENS/ArangoDB

This commit is contained in:
Frank Celler 2012-06-13 15:06:32 +02:00
commit 1364ea8019
24 changed files with 1110 additions and 245 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -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

View File

@ -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:

View File

@ -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
///

View File

@ -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

View File

@ -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:

View File

@ -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
////////////////////////////////////////////////////////////////////////////////