mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'aql2' of ssh://github.com/triAGENS/ArangoDB into aql2
Conflicts: arangod/Aql/OptimizerRules.cpp
This commit is contained in:
commit
aa03c16be0
|
@ -157,6 +157,29 @@ bool AqlValue::isString () const {
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not the AqlValue contains a numeric value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool AqlValue::isNumber () const {
|
||||
switch (_type) {
|
||||
case JSON: {
|
||||
TRI_json_t const* json = _json->json();
|
||||
return TRI_IsNumberJson(json);
|
||||
}
|
||||
|
||||
case SHAPED:
|
||||
case DOCVEC:
|
||||
case RANGE:
|
||||
case EMPTY: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TRI_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not the AqlValue contains a list value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -303,21 +326,12 @@ v8::Handle<v8::Value> AqlValue::toV8 (AQL_TRANSACTION_V8* trx,
|
|||
TRI_ASSERT(_range != nullptr);
|
||||
|
||||
// allocate the buffer for the result
|
||||
int64_t const n = _range->_high - _range->_low + 1;
|
||||
size_t const n = _range->size();
|
||||
v8::Handle<v8::Array> result = v8::Array::New(static_cast<int>(n));
|
||||
|
||||
uint32_t j = 0; // output row count
|
||||
if (_range->_low <= _range->_high) {
|
||||
for (int64_t i = _range->_low; i <= _range->_high; ++i) {
|
||||
// is it safe to use a double here (precision loss)?
|
||||
result->Set(j++, v8::Number::New(static_cast<double>(i)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int64_t i = _range->_low; i >= _range->_high; --i) {
|
||||
// is it safe to use a double here (precision loss)?
|
||||
result->Set(j++, v8::Number::New(static_cast<double>(i)));
|
||||
}
|
||||
for (uint32_t i = 0; i < n; ++i) {
|
||||
// is it safe to use a double here (precision loss)?
|
||||
result->Set(i, v8::Number::New(_range->at(static_cast<size_t>(i))));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -409,12 +423,12 @@ Json AqlValue::toJson (AQL_TRANSACTION_V8* trx,
|
|||
TRI_ASSERT(_range != nullptr);
|
||||
|
||||
// allocate the buffer for the result
|
||||
int64_t const n = _range->_high - _range->_low + 1;
|
||||
Json json(Json::List, static_cast<size_t>(n));
|
||||
size_t const n = _range->size();
|
||||
Json json(Json::List, n);
|
||||
|
||||
for (int64_t i = _range->_low; i <= _range->_high; ++i) {
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
// is it safe to use a double here (precision loss)?
|
||||
json.add(Json(static_cast<double>(i)));
|
||||
json.add(Json(static_cast<double>(_range->at(i))));
|
||||
}
|
||||
|
||||
return json;
|
||||
|
@ -430,7 +444,7 @@ Json AqlValue::toJson (AQL_TRANSACTION_V8* trx,
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extract an attribute value from the AqlValue
|
||||
/// this will fail if the value is not an array
|
||||
/// this will return null if the value is not an array
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Json AqlValue::extractArrayMember (AQL_TRANSACTION_V8* trx,
|
||||
|
@ -518,6 +532,76 @@ Json AqlValue::extractArrayMember (AQL_TRANSACTION_V8* trx,
|
|||
return Json(Json::Null);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extract a value from a list AqlValue
|
||||
/// this will return null if the value is not a list
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Json AqlValue::extractListMember (AQL_TRANSACTION_V8* trx,
|
||||
TRI_document_collection_t const* document,
|
||||
int64_t position) const {
|
||||
switch (_type) {
|
||||
case JSON: {
|
||||
TRI_ASSERT(_json != nullptr);
|
||||
TRI_json_t const* json = _json->json();
|
||||
|
||||
if (TRI_IsListJson(json)) {
|
||||
size_t const length = TRI_LengthListJson(json);
|
||||
if (position < 0) {
|
||||
// a negative position is allowed
|
||||
position = static_cast<int64_t>(length) + position;
|
||||
}
|
||||
|
||||
if (position >= 0 && position < static_cast<int64_t>(length)) {
|
||||
// only look up the value if it is within list bounds
|
||||
TRI_json_t const* found = TRI_LookupListJson(json, static_cast<size_t>(position));
|
||||
|
||||
if (found != nullptr) {
|
||||
return Json(TRI_UNKNOWN_MEM_ZONE, TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, found));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// attribute does not exist or something went wrong - fall-through to returning null below
|
||||
return Json(Json::Null);
|
||||
}
|
||||
|
||||
case RANGE: {
|
||||
TRI_ASSERT(_range != nullptr);
|
||||
size_t const n = _range->size();
|
||||
size_t const p = static_cast<size_t>(position);
|
||||
|
||||
if (p < n) {
|
||||
return Json(static_cast<double>(_range->at(p)));
|
||||
}
|
||||
break; // fall-through to returning null
|
||||
}
|
||||
|
||||
case DOCVEC: {
|
||||
TRI_ASSERT(_vector != nullptr);
|
||||
size_t const p = static_cast<size_t>(position);
|
||||
|
||||
// calculate the result list length
|
||||
size_t totalSize = 0;
|
||||
for (auto it = _vector->begin(); it != _vector->end(); ++it) {
|
||||
if (p < totalSize + (*it)->size()) {
|
||||
// found the correct vector
|
||||
auto vecCollection = (*it)->getDocumentCollection(0);
|
||||
return (*it)->getValue(p - totalSize, 0).toJson(trx, vecCollection);
|
||||
}
|
||||
}
|
||||
break; // fall-through to returning null
|
||||
}
|
||||
|
||||
case SHAPED:
|
||||
case EMPTY: {
|
||||
break; // fall-through to returning null
|
||||
}
|
||||
}
|
||||
|
||||
return Json(Json::Null);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AqlValue from a vector of AqlItemBlock*s
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -156,6 +156,12 @@ namespace triagens {
|
|||
|
||||
bool isString () const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not the AqlValue contains a numeric value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool isNumber () const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not the AqlValue contains a list value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -198,13 +204,22 @@ namespace triagens {
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extract an attribute value from the AqlValue
|
||||
/// this will fail if the value is not an array
|
||||
/// this will return null if the value is not an array
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
triagens::basics::Json extractArrayMember (AQL_TRANSACTION_V8*,
|
||||
TRI_document_collection_t const*,
|
||||
char const*) const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extract a value from a list AqlValue
|
||||
/// this will return null if the value is not a list
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
triagens::basics::Json extractListMember (AQL_TRANSACTION_V8*,
|
||||
TRI_document_collection_t const*,
|
||||
int64_t) const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AqlValue from a vector of AqlItemBlock*s
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -166,8 +166,6 @@ AstNode* Ast::createNodeLet (char const* variableName,
|
|||
|
||||
AstNode* Ast::createNodeFilter (AstNode const* expression) {
|
||||
AstNode* node = createNode(NODE_TYPE_FILTER);
|
||||
node->setIntValue(static_cast<int64_t>(FILTER_UNKNOWN));
|
||||
|
||||
node->addMember(expression);
|
||||
|
||||
return node;
|
||||
|
@ -846,11 +844,6 @@ void Ast::optimize () {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// FILTER
|
||||
if (node->type == NODE_TYPE_FILTER) {
|
||||
return optimizeFilter(node);
|
||||
}
|
||||
|
||||
// unary operators
|
||||
if (node->type == NODE_TYPE_OPERATOR_UNARY_PLUS ||
|
||||
node->type == NODE_TYPE_OPERATOR_UNARY_MINUS) {
|
||||
|
@ -907,23 +900,14 @@ void Ast::optimize () {
|
|||
|
||||
// LET
|
||||
if (node->type == NODE_TYPE_LET) {
|
||||
return optimizeLet(node, *static_cast<int*>(data));
|
||||
return optimizeLet(node);
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
int pass;
|
||||
|
||||
// optimization pass 0
|
||||
pass = 0;
|
||||
_root = traverse(_root, func, &pass);
|
||||
|
||||
// optimization pass 1
|
||||
pass = 1;
|
||||
_root = traverse(_root, func, &pass);
|
||||
|
||||
optimizeRoot();
|
||||
// optimization
|
||||
_root = traverse(_root, func, nullptr);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -957,6 +941,14 @@ std::unordered_set<Variable*> Ast::getReferencedVariables (AstNode const* node)
|
|||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief add a node to the list of nodes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Ast::addNode (AstNode* node) {
|
||||
_nodes.push_back(node);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -988,43 +980,6 @@ AstNode* Ast::executeConstExpression (AstNode const* node) {
|
|||
return value;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes a FILTER node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* Ast::optimizeFilter (AstNode* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->type == NODE_TYPE_FILTER);
|
||||
TRI_ASSERT(node->numMembers() == 1);
|
||||
|
||||
if (node->getIntValue(true) != static_cast<int64_t>(FILTER_UNKNOWN)) {
|
||||
// already processed filter
|
||||
return node;
|
||||
}
|
||||
|
||||
AstNode* operand = node->getMember(0);
|
||||
if (! operand->isConstant()) {
|
||||
// unable to optimize non-constant expression
|
||||
return node;
|
||||
}
|
||||
|
||||
if (! operand->isBoolValue()) {
|
||||
_query->registerError(TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE);
|
||||
return node;
|
||||
}
|
||||
|
||||
if (operand->getBoolValue()) {
|
||||
// FILTER is always true
|
||||
node->setIntValue(static_cast<int64_t>(FILTER_TRUE));
|
||||
}
|
||||
else {
|
||||
// FILTER is always false
|
||||
node->setIntValue(static_cast<int64_t>(FILTER_FALSE));
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the unary operators + and -
|
||||
/// the unary plus will be converted into a simple value node if the operand of
|
||||
|
@ -1365,9 +1320,6 @@ AstNode* Ast::optimizeReference (AstNode* node) {
|
|||
|
||||
// constant propagation
|
||||
if (variable->constValue() == nullptr) {
|
||||
// note which variables we are reading
|
||||
variable->increaseReferenceCount();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -1405,8 +1357,7 @@ AstNode* Ast::optimizeRange (AstNode* node) {
|
|||
/// @brief optimizes the LET statement
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* Ast::optimizeLet (AstNode* node,
|
||||
int pass) {
|
||||
AstNode* Ast::optimizeLet (AstNode* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->type == NODE_TYPE_LET);
|
||||
TRI_ASSERT(node->numMembers() == 2);
|
||||
|
@ -1417,65 +1368,17 @@ AstNode* Ast::optimizeLet (AstNode* node,
|
|||
auto v = static_cast<Variable*>(variable->getData());
|
||||
TRI_ASSERT(v != nullptr);
|
||||
|
||||
if (pass == 0) {
|
||||
if (expression->isConstant()) {
|
||||
// if the expression assigned to the LET variable is constant, we'll store
|
||||
// a pointer to the const value in the variable
|
||||
// further optimizations can then use this pointer and optimize further, e.g.
|
||||
// LET a = 1 LET b = a + 1, c = b + a can be optimized to LET a = 1 LET b = 2 LET c = 4
|
||||
v->constValue(static_cast<void*>(expression));
|
||||
}
|
||||
}
|
||||
else if (pass == 1) {
|
||||
if (! v->isReferenceCounted() && false) {
|
||||
// this optimizes away the assignment of variables which are never read
|
||||
// (i.e. assigned-only variables). this is currently not free of side-effects:
|
||||
// for example, in the following query, the variable 'x' would be optimized
|
||||
// away, but actually the query should fail with an error:
|
||||
// LET x = SUM("foobar") RETURN 1
|
||||
// TODO: check whether the right-hand side of the assignment produces an
|
||||
// error and only throw away the variable if it is side-effects-free.
|
||||
|
||||
// TODO: also decrease the refcount of all variables used in the expression
|
||||
// (i.e. getReferencedVariables(expression)). this might produce further unused
|
||||
// variables
|
||||
|
||||
// TODO: COLLECT needs all variables in the scope if they do not appear directly
|
||||
// in any expression
|
||||
return createNodeNop();
|
||||
}
|
||||
}
|
||||
if (expression->isConstant()) {
|
||||
// if the expression assigned to the LET variable is constant, we'll store
|
||||
// a pointer to the const value in the variable
|
||||
// further optimizations can then use this pointer and optimize further, e.g.
|
||||
// LET a = 1 LET b = a + 1, c = b + a can be optimized to LET a = 1 LET b = 2 LET c = 4
|
||||
v->constValue(static_cast<void*>(expression));
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the top-level statements
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Ast::optimizeRoot() {
|
||||
TRI_ASSERT(_root != nullptr);
|
||||
TRI_ASSERT(_root->type == NODE_TYPE_ROOT);
|
||||
|
||||
/* not yet ready for prime time...
|
||||
size_t const n = _root->numMembers();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
AstNode* member = _root->getMember(i);
|
||||
|
||||
if (member == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: replace trampoline variables / expressions
|
||||
// TODO: detect common sub-expressions
|
||||
// TODO: remove always-true filters
|
||||
// TODO: remove blocks that contains always-false filters
|
||||
// TODO: pull up LET assignments
|
||||
// TODO: pull up FILTER
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST node from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1616,7 +1519,7 @@ AstNode* Ast::createNode (AstNodeType type) {
|
|||
auto node = new AstNode(type);
|
||||
|
||||
try {
|
||||
_nodes.push_back(node);
|
||||
addNode(node);
|
||||
}
|
||||
catch (...) {
|
||||
delete node;
|
||||
|
|
|
@ -58,12 +58,6 @@ namespace triagens {
|
|||
|
||||
class Ast {
|
||||
|
||||
enum FilterType {
|
||||
FILTER_UNKNOWN,
|
||||
FILTER_TRUE,
|
||||
FILTER_FALSE
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- constructors / destructors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -484,6 +478,12 @@ namespace triagens {
|
|||
|
||||
static std::unordered_set<Variable*> getReferencedVariables (AstNode const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief add a node to the list of nodes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void addNode (AstNode*);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -496,12 +496,6 @@ namespace triagens {
|
|||
|
||||
AstNode* executeConstExpression (AstNode const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes a FILTER node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* optimizeFilter (AstNode*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the unary operators + and -
|
||||
/// the unary plus will be converted into a simple value node if the operand of
|
||||
|
@ -562,14 +556,7 @@ namespace triagens {
|
|||
/// @brief optimizes the LET statement
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* optimizeLet (AstNode*,
|
||||
int);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the top-level statements
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void optimizeRoot ();
|
||||
AstNode* optimizeLet (AstNode*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST node from JSON
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Aql/AstNode.h"
|
||||
#include "Aql/Ast.h"
|
||||
#include "Aql/Function.h"
|
||||
#include "Aql/Scopes.h"
|
||||
#include "Aql/V8Executor.h"
|
||||
|
@ -113,136 +114,136 @@ AstNode::AstNode (AstNodeType type)
|
|||
TRI_InitVectorPointer(&members, TRI_UNKNOWN_MEM_ZONE);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create the node from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode::AstNode (triagens::aql::Query* Q,
|
||||
triagens::basics::Json const& json)
|
||||
: type(getNodeTypeFromJson(json))
|
||||
{
|
||||
AstNode::AstNode (Ast* ast,
|
||||
triagens::basics::Json const& json)
|
||||
: type(getNodeTypeFromJson(json)) {
|
||||
|
||||
auto query = ast->query();
|
||||
TRI_InitVectorPointer(&members, TRI_UNKNOWN_MEM_ZONE);
|
||||
|
||||
switch(type) {
|
||||
switch (type) {
|
||||
case NODE_TYPE_COLLECTION:
|
||||
case NODE_TYPE_PARAMETER:
|
||||
case NODE_TYPE_ATTRIBUTE_ACCESS:
|
||||
case NODE_TYPE_FCALL_USER:
|
||||
value.type = VALUE_TYPE_STRING;
|
||||
setStringValue(query->registerString(JsonHelper::getStringValue(json.json(),
|
||||
"name", ""),
|
||||
false));
|
||||
break;
|
||||
case NODE_TYPE_VALUE: {
|
||||
int vType = JsonHelper::getNumericValue<int>(json.json(), "vTypeID", 0);
|
||||
validateValueType(vType);
|
||||
value.type = static_cast<AstNodeValueType>(vType);
|
||||
|
||||
case NODE_TYPE_COLLECTION:
|
||||
case NODE_TYPE_PARAMETER:
|
||||
case NODE_TYPE_ATTRIBUTE_ACCESS:
|
||||
case NODE_TYPE_FCALL_USER:
|
||||
value.type = VALUE_TYPE_STRING;
|
||||
setStringValue(Q->registerString(JsonHelper::getStringValue(json.json(),
|
||||
"name", ""),
|
||||
false));
|
||||
break;
|
||||
case NODE_TYPE_VALUE: {
|
||||
int vType = JsonHelper::getNumericValue<int>(json.json(), "vTypeID", 0);
|
||||
validateValueType(vType);
|
||||
value.type = (AstNodeValueType) vType;
|
||||
switch (value.type) {
|
||||
case VALUE_TYPE_NULL:
|
||||
break;
|
||||
case VALUE_TYPE_BOOL:
|
||||
value.value._bool = JsonHelper::getBooleanValue(json.json(), "value", false);
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
setIntValue(JsonHelper::getNumericValue<int64_t>(json.json(), "value", 0));
|
||||
break;
|
||||
case VALUE_TYPE_DOUBLE:
|
||||
setDoubleValue(JsonHelper::getNumericValue<double>(json.json(), "value", 0.0));
|
||||
break;
|
||||
case VALUE_TYPE_STRING:
|
||||
setStringValue(query->registerString(JsonHelper::getStringValue(json.json(),
|
||||
"value", ""),
|
||||
false));
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_TYPE_VARIABLE: {
|
||||
auto variable = ast->variables()->createVariable(json);
|
||||
TRI_ASSERT(variable != nullptr);
|
||||
setData(variable);
|
||||
break;
|
||||
}
|
||||
case NODE_TYPE_REFERENCE: {
|
||||
auto variableId = JsonHelper::getNumericValue<VariableId>(json.json(), "id", 0);
|
||||
auto variable = ast->variables()->getVariable(variableId);
|
||||
|
||||
switch (value.type) {
|
||||
case VALUE_TYPE_NULL:
|
||||
/// todo? do we need to do anything?
|
||||
break;
|
||||
case VALUE_TYPE_BOOL:
|
||||
value.value._bool = JsonHelper::getBooleanValue(json.json(), "value", false);
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
setIntValue(JsonHelper::getNumericValue<int>(json.json(), "value", 0));
|
||||
break;
|
||||
case VALUE_TYPE_DOUBLE:
|
||||
setDoubleValue(JsonHelper::getNumericValue<double>(json.json(), "value", 0.0));
|
||||
break;
|
||||
case VALUE_TYPE_STRING:
|
||||
setStringValue(Q->registerString(JsonHelper::getStringValue(json.json(),
|
||||
"value", ""),
|
||||
false));
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
TRI_ASSERT(variable != nullptr);
|
||||
setData(variable);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_TYPE_VARIABLE:
|
||||
case NODE_TYPE_REFERENCE:
|
||||
/// auto varName=JsonHelper::getStringValue(json.json(), "name");
|
||||
// auto varId=JsonHelper::getStringValue(json.json(), "id");
|
||||
setData(Q->registerVar(new Variable(json)));
|
||||
break;
|
||||
case NODE_TYPE_FCALL: {
|
||||
setData(Q->executor()->getFunctionByName(JsonHelper::getStringValue(json.json(), "name", "")));
|
||||
break;
|
||||
}
|
||||
case NODE_TYPE_ARRAY_ELEMENT:
|
||||
case NODE_TYPE_ARRAY: {
|
||||
setStringValue(Q->registerString(JsonHelper::getStringValue(json.json(),
|
||||
"name", ""),
|
||||
false));
|
||||
/*
|
||||
Json subNodes = json.get("subNodes");
|
||||
if (subNodes.isList()) {
|
||||
int len = subNodes.size();
|
||||
for (int i = 0; i < len; i++) {
|
||||
Json subNode = subNodes.at(i);
|
||||
addMember (new AstNode(subNode));
|
||||
}
|
||||
case NODE_TYPE_FCALL: {
|
||||
setData(query->executor()->getFunctionByName(JsonHelper::getStringValue(json.json(), "name", "")));
|
||||
break;
|
||||
}
|
||||
case NODE_TYPE_ARRAY_ELEMENT: {
|
||||
setStringValue(query->registerString(JsonHelper::getStringValue(json.json(),
|
||||
"name", ""),
|
||||
false));
|
||||
break;
|
||||
}
|
||||
case NODE_TYPE_ARRAY:
|
||||
case NODE_TYPE_ROOT:
|
||||
case NODE_TYPE_FOR:
|
||||
case NODE_TYPE_LET:
|
||||
case NODE_TYPE_FILTER:
|
||||
case NODE_TYPE_RETURN:
|
||||
case NODE_TYPE_REMOVE:
|
||||
case NODE_TYPE_INSERT:
|
||||
case NODE_TYPE_UPDATE:
|
||||
case NODE_TYPE_REPLACE:
|
||||
case NODE_TYPE_COLLECT:
|
||||
case NODE_TYPE_SORT:
|
||||
case NODE_TYPE_SORT_ELEMENT:
|
||||
case NODE_TYPE_LIMIT:
|
||||
case NODE_TYPE_ASSIGN:
|
||||
case NODE_TYPE_OPERATOR_UNARY_PLUS:
|
||||
case NODE_TYPE_OPERATOR_UNARY_MINUS:
|
||||
case NODE_TYPE_OPERATOR_UNARY_NOT:
|
||||
case NODE_TYPE_OPERATOR_BINARY_AND:
|
||||
case NODE_TYPE_OPERATOR_BINARY_OR:
|
||||
case NODE_TYPE_OPERATOR_BINARY_PLUS:
|
||||
case NODE_TYPE_OPERATOR_BINARY_MINUS:
|
||||
case NODE_TYPE_OPERATOR_BINARY_TIMES:
|
||||
case NODE_TYPE_OPERATOR_BINARY_DIV:
|
||||
case NODE_TYPE_OPERATOR_BINARY_MOD:
|
||||
case NODE_TYPE_OPERATOR_BINARY_EQ:
|
||||
case NODE_TYPE_OPERATOR_BINARY_NE:
|
||||
case NODE_TYPE_OPERATOR_BINARY_LT:
|
||||
case NODE_TYPE_OPERATOR_BINARY_LE:
|
||||
case NODE_TYPE_OPERATOR_BINARY_GT:
|
||||
case NODE_TYPE_OPERATOR_BINARY_GE:
|
||||
case NODE_TYPE_OPERATOR_BINARY_IN:
|
||||
case NODE_TYPE_OPERATOR_TERNARY:
|
||||
case NODE_TYPE_SUBQUERY:
|
||||
case NODE_TYPE_BOUND_ATTRIBUTE_ACCESS:
|
||||
case NODE_TYPE_INDEXED_ACCESS:
|
||||
case NODE_TYPE_EXPAND:
|
||||
case NODE_TYPE_ITERATOR:
|
||||
case NODE_TYPE_LIST:
|
||||
case NODE_TYPE_RANGE:
|
||||
case NODE_TYPE_NOP:
|
||||
//TRI_ASSERT(false);
|
||||
//THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "json dserializer: node type not implemented.");
|
||||
break;
|
||||
}
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case NODE_TYPE_ROOT:
|
||||
case NODE_TYPE_FOR:
|
||||
case NODE_TYPE_LET:
|
||||
case NODE_TYPE_FILTER:
|
||||
case NODE_TYPE_RETURN:
|
||||
case NODE_TYPE_REMOVE:
|
||||
case NODE_TYPE_INSERT:
|
||||
case NODE_TYPE_UPDATE:
|
||||
case NODE_TYPE_REPLACE:
|
||||
case NODE_TYPE_COLLECT:
|
||||
case NODE_TYPE_SORT:
|
||||
case NODE_TYPE_SORT_ELEMENT:
|
||||
case NODE_TYPE_LIMIT:
|
||||
case NODE_TYPE_ASSIGN:
|
||||
case NODE_TYPE_OPERATOR_UNARY_PLUS:
|
||||
case NODE_TYPE_OPERATOR_UNARY_MINUS:
|
||||
case NODE_TYPE_OPERATOR_UNARY_NOT:
|
||||
case NODE_TYPE_OPERATOR_BINARY_AND:
|
||||
case NODE_TYPE_OPERATOR_BINARY_OR:
|
||||
case NODE_TYPE_OPERATOR_BINARY_PLUS:
|
||||
case NODE_TYPE_OPERATOR_BINARY_MINUS:
|
||||
case NODE_TYPE_OPERATOR_BINARY_TIMES:
|
||||
case NODE_TYPE_OPERATOR_BINARY_DIV:
|
||||
case NODE_TYPE_OPERATOR_BINARY_MOD:
|
||||
case NODE_TYPE_OPERATOR_BINARY_EQ:
|
||||
case NODE_TYPE_OPERATOR_BINARY_NE:
|
||||
case NODE_TYPE_OPERATOR_BINARY_LT:
|
||||
case NODE_TYPE_OPERATOR_BINARY_LE:
|
||||
case NODE_TYPE_OPERATOR_BINARY_GT:
|
||||
case NODE_TYPE_OPERATOR_BINARY_GE:
|
||||
case NODE_TYPE_OPERATOR_BINARY_IN:
|
||||
case NODE_TYPE_OPERATOR_TERNARY:
|
||||
case NODE_TYPE_SUBQUERY:
|
||||
case NODE_TYPE_BOUND_ATTRIBUTE_ACCESS:
|
||||
case NODE_TYPE_INDEXED_ACCESS:
|
||||
case NODE_TYPE_EXPAND:
|
||||
case NODE_TYPE_ITERATOR:
|
||||
case NODE_TYPE_LIST:
|
||||
case NODE_TYPE_RANGE:
|
||||
case NODE_TYPE_NOP:
|
||||
//TRI_ASSERT(false);
|
||||
//THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "json dserializer: node type not implemented.");
|
||||
break;
|
||||
}
|
||||
|
||||
Json subNodes = json.get("subNodes");
|
||||
if (subNodes.isList()) {
|
||||
int len = subNodes.size();
|
||||
for (int i = 0; i < len; i++) {
|
||||
size_t const len = subNodes.size();
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
Json subNode = subNodes.at(i);
|
||||
addMember (Q->registerNode(new AstNode(Q, subNode)));
|
||||
addMember(new AstNode(ast, subNode));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ast->addNode(this);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroy the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -251,12 +252,11 @@ AstNode::~AstNode () {
|
|||
TRI_DestroyVectorPointer(&members);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type name of a node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const std::string& AstNode::getTypeString () const {
|
||||
std::string const& AstNode::getTypeString () const {
|
||||
auto it = TypeNames.find(static_cast<int>(type));
|
||||
if (it != TypeNames.end()) {
|
||||
return (*it).second;
|
||||
|
@ -401,34 +401,24 @@ TRI_json_t* AstNode::toJson (TRI_memory_zone_t* zone) const {
|
|||
|
||||
if (type == NODE_TYPE_VALUE) {
|
||||
// dump value of "value" node
|
||||
auto v = toJsonValue(zone);
|
||||
|
||||
if (v == nullptr) {
|
||||
TRI_FreeJson(zone, node);
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
TRI_Insert3ArrayJson(zone, node, "value", v);
|
||||
TRI_Insert3ArrayJson(zone, node, "vType", TRI_CreateStringCopyJson(zone, getValueTypeString().c_str()));
|
||||
TRI_Insert3ArrayJson(zone, node, "vTypeID", TRI_CreateNumberJson(zone, static_cast<int>(value.type)));
|
||||
switch (value.type) {
|
||||
case VALUE_TYPE_NULL:
|
||||
TRI_Insert3ArrayJson(zone, node, "value", TRI_CreateStringCopyJson(zone, "null"));
|
||||
break;
|
||||
case VALUE_TYPE_BOOL:
|
||||
TRI_Insert3ArrayJson(zone, node, "value", TRI_CreateBooleanJson(zone, value.value._bool));
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
TRI_Insert3ArrayJson(zone, node, "value", TRI_CreateNumberJson(zone, static_cast<double>(value.value._int)));
|
||||
break;
|
||||
case VALUE_TYPE_DOUBLE:
|
||||
TRI_Insert3ArrayJson(zone, node, "value", TRI_CreateNumberJson(zone, value.value._double));
|
||||
break;
|
||||
case VALUE_TYPE_STRING:
|
||||
TRI_Insert3ArrayJson(zone, node, "value", TRI_CreateStringCopyJson(zone, value.value._string));
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_VARIABLE ||
|
||||
type == NODE_TYPE_REFERENCE) {
|
||||
auto variable = static_cast<Variable*>(getData());
|
||||
|
||||
TRI_ASSERT(variable != nullptr);
|
||||
|
||||
/// TODO: use variable.toJson()!!!
|
||||
TRI_Insert3ArrayJson(zone, node, "name", TRI_CreateStringCopyJson(zone, variable->name.c_str()));
|
||||
TRI_Insert3ArrayJson(zone, node, "id", TRI_CreateNumberJson(zone, static_cast<double>(variable->id)));
|
||||
|
@ -520,16 +510,14 @@ bool AstNode::toBoolean () const {
|
|||
bool AstNode::isSimple () const {
|
||||
if (type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
TRI_ASSERT(numMembers() == 1);
|
||||
|
||||
return getMember(0)->isSimple();
|
||||
}
|
||||
/*
|
||||
|
||||
if (type == NODE_TYPE_INDEXED_ACCESS) {
|
||||
TRI_ASSERT(numMembers() == 2);
|
||||
|
||||
return (getMember(0)->isSimple() && getMember(1)->isSimple());
|
||||
}
|
||||
*/
|
||||
|
||||
if (type == NODE_TYPE_REFERENCE) {
|
||||
return true;
|
||||
}
|
||||
|
@ -560,6 +548,10 @@ bool AstNode::isSimple () const {
|
|||
auto member = getMember(0);
|
||||
return member->isSimple();
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_VALUE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ namespace triagens {
|
|||
|
||||
namespace aql {
|
||||
|
||||
class Ast;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief enumeration of AST node value types
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -152,7 +154,8 @@ namespace triagens {
|
|||
|
||||
AstNode (AstNodeType);
|
||||
|
||||
AstNode (triagens::aql::Query* Q, triagens::basics::Json const& json);
|
||||
AstNode (Ast*,
|
||||
triagens::basics::Json const& json);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroy the node
|
||||
|
@ -170,15 +173,30 @@ namespace triagens {
|
|||
/// @brief return the type name of a node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const std::string& getTypeString () const;
|
||||
std::string const& getTypeString () const;
|
||||
|
||||
const std::string& getValueTypeString () const;
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the value type name of a node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string const& getValueTypeString () const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief checks whether we know a type of this kind; throws exception if not.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static void validateType (int type);
|
||||
static void validateValueType (int type);
|
||||
|
||||
static void validateType (int type);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief checks whether we know a value type of this kind;
|
||||
/// throws exception if not.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void validateValueType (int type);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief fetch a node's type from json
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AstNodeType getNodeTypeFromJson (triagens::basics::Json const& json);
|
||||
|
||||
|
@ -227,6 +245,14 @@ namespace triagens {
|
|||
return (type == NODE_TYPE_VALUE && value.type == VALUE_TYPE_BOOL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not a value node is of string type
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline bool isStringValue () const {
|
||||
return (type == NODE_TYPE_VALUE && value.type == VALUE_TYPE_STRING);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not a node is simple enough to be used in a simple
|
||||
/// expression
|
||||
|
|
|
@ -70,8 +70,8 @@ namespace triagens {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void add (std::string const& name,
|
||||
TRI_transaction_type_e accessType) {
|
||||
Collection* add (std::string const& name,
|
||||
TRI_transaction_type_e accessType) {
|
||||
// check if collection already is in our map
|
||||
auto it = _collections.find(name);
|
||||
|
||||
|
@ -84,6 +84,7 @@ namespace triagens {
|
|||
delete collection;
|
||||
throw;
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
else {
|
||||
// change access type from read to write
|
||||
|
@ -92,6 +93,7 @@ namespace triagens {
|
|||
(*it).second->accessType = TRI_TRANSACTION_WRITE;
|
||||
}
|
||||
}
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
std::vector<std::string> collectionNames () const {
|
||||
|
|
|
@ -1132,6 +1132,7 @@ AqlItemBlock* SubqueryBlock::getSome (size_t atLeast,
|
|||
|
||||
int FilterBlock::initialize () {
|
||||
int res = ExecutionBlock::initialize();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
}
|
||||
|
@ -1144,6 +1145,12 @@ int FilterBlock::initialize () {
|
|||
TRI_ASSERT(it != _varOverview->varInfo.end());
|
||||
_inReg = it->second.registerId;
|
||||
|
||||
if (en->_resultIsEmpty) {
|
||||
// we know that the filter will never produce any results
|
||||
// TODO: do we need to suck in (and free) data from all dependent nodes first??
|
||||
_done = true;
|
||||
}
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
|
@ -1188,6 +1195,7 @@ int FilterBlock::getOrSkipSome (size_t atLeast,
|
|||
size_t& skipped) {
|
||||
|
||||
TRI_ASSERT(result == nullptr && skipped == 0);
|
||||
|
||||
if (_done) {
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
@ -1208,7 +1216,7 @@ int FilterBlock::getOrSkipSome (size_t atLeast,
|
|||
AqlItemBlock* cur = _buffer.front();
|
||||
if (_chosen.size() - _pos + skipped > atMost) {
|
||||
// The current block of chosen ones is too large for atMost:
|
||||
if(!skipping){
|
||||
if(! skipping) {
|
||||
unique_ptr<AqlItemBlock> more(cur->slice(_chosen,
|
||||
_pos, _pos + (atMost - skipped)));
|
||||
collector.push_back(more.get());
|
||||
|
@ -1220,7 +1228,7 @@ int FilterBlock::getOrSkipSome (size_t atLeast,
|
|||
else if (_pos > 0 || _chosen.size() < cur->size()) {
|
||||
// The current block fits into our result, but it is already
|
||||
// half-eaten or needs to be copied anyway:
|
||||
if(!skipping){
|
||||
if (! skipping) {
|
||||
unique_ptr<AqlItemBlock> more(cur->steal(_chosen, _pos, _chosen.size()));
|
||||
collector.push_back(more.get());
|
||||
more.release();
|
||||
|
@ -1234,7 +1242,7 @@ int FilterBlock::getOrSkipSome (size_t atLeast,
|
|||
else {
|
||||
// The current block fits into our result and is fresh and
|
||||
// takes them all, so we can just hand it on:
|
||||
if(!skipping){
|
||||
if (! skipping) {
|
||||
collector.push_back(cur);
|
||||
}
|
||||
else {
|
||||
|
@ -1253,7 +1261,7 @@ int FilterBlock::getOrSkipSome (size_t atLeast,
|
|||
}
|
||||
throw;
|
||||
}
|
||||
if (!skipping) {
|
||||
if (! skipping) {
|
||||
if (collector.size() == 1) {
|
||||
result = collector[0];
|
||||
}
|
||||
|
@ -2506,6 +2514,29 @@ void ExecutionBlock::VarOverview::after (ExecutionBlock *eb) {
|
|||
eb->_varOverview = *me;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- class NoResultsBlock
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief initCursor, only call base
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int NoResultsBlock::initCursor (AqlItemBlock* items, size_t pos) {
|
||||
_done = true;
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
int NoResultsBlock::getOrSkipSome (size_t atLeast,
|
||||
size_t atMost,
|
||||
bool skipping,
|
||||
AqlItemBlock*& result,
|
||||
size_t& skipped) {
|
||||
|
||||
TRI_ASSERT(result == nullptr && skipped == 0);
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
||||
|
|
|
@ -1237,6 +1237,53 @@ namespace triagens {
|
|||
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- NoResultsBlock
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class NoResultsBlock : public ExecutionBlock {
|
||||
|
||||
public:
|
||||
|
||||
NoResultsBlock (AQL_TRANSACTION_V8* trx, SingletonNode const* ep)
|
||||
: ExecutionBlock(trx, ep) {
|
||||
}
|
||||
|
||||
~NoResultsBlock () {
|
||||
}
|
||||
|
||||
int initialize () {
|
||||
return ExecutionBlock::initialize();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief initCursor, store a copy of the register values coming from above
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int initCursor (AqlItemBlock* items, size_t pos);
|
||||
|
||||
bool hasMore () {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t count () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t remaining () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
int getOrSkipSome (size_t atLeast,
|
||||
size_t atMost,
|
||||
bool skipping,
|
||||
AqlItemBlock*& result,
|
||||
size_t& skipped);
|
||||
|
||||
};
|
||||
|
||||
} // namespace triagens::aql
|
||||
} // namespace triagens
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
using namespace triagens::basics;
|
||||
using namespace triagens::aql;
|
||||
|
||||
const static bool Optional = true;
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- static initialization
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -57,7 +58,8 @@ std::unordered_map<int, std::string const> const ExecutionNode::TypeNames{
|
|||
{ static_cast<int>(REMOVE), "RemoveNode" },
|
||||
{ static_cast<int>(INSERT), "InsertNode" },
|
||||
{ static_cast<int>(UPDATE), "UpdateNode" },
|
||||
{ static_cast<int>(REPLACE), "ReplaceNode" }
|
||||
{ static_cast<int>(REPLACE), "ReplaceNode" },
|
||||
{ static_cast<int>(NORESULTS), "NoResultsNode" }
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -84,30 +86,29 @@ void ExecutionNode::validateType (int type) {
|
|||
}
|
||||
}
|
||||
|
||||
ExecutionNode* ExecutionNode::fromJsonFactory (Ast const* ast,
|
||||
const Json &oneNode) {
|
||||
ExecutionNode* ExecutionNode::fromJsonFactory (Ast* ast,
|
||||
Json const& oneNode) {
|
||||
auto JsonString = oneNode.toString();
|
||||
|
||||
int nodeTypeID = JsonHelper::getNumericValue<int>(oneNode.json(), "typeID", 0);
|
||||
triagens::aql::Query* query = ast->query();
|
||||
validateType(nodeTypeID);
|
||||
|
||||
NodeType nodeType = (NodeType) nodeTypeID;
|
||||
switch (nodeType) {
|
||||
case SINGLETON:
|
||||
return new SingletonNode(query, oneNode);
|
||||
return new SingletonNode(ast, oneNode);
|
||||
case ENUMERATE_COLLECTION:
|
||||
return new EnumerateCollectionNode(query, oneNode);
|
||||
return new EnumerateCollectionNode(ast, oneNode);
|
||||
case ENUMERATE_LIST:
|
||||
return new EnumerateListNode(query, oneNode);
|
||||
return new EnumerateListNode(ast, oneNode);
|
||||
case FILTER:
|
||||
return new FilterNode(query, oneNode);
|
||||
return new FilterNode(ast, oneNode);
|
||||
case LIMIT:
|
||||
return new LimitNode(query, oneNode);
|
||||
return new LimitNode(ast, oneNode);
|
||||
case CALCULATION:
|
||||
return new CalculationNode(query, oneNode);
|
||||
return new CalculationNode(ast, oneNode);
|
||||
case SUBQUERY:
|
||||
return new SubqueryNode(ast, query, oneNode);
|
||||
return new SubqueryNode(ast, oneNode);
|
||||
case SORT: {
|
||||
Json jsonElements = oneNode.get("elements");
|
||||
if (! jsonElements.isList()){
|
||||
|
@ -119,24 +120,19 @@ ExecutionNode* ExecutionNode::fromJsonFactory (Ast const* ast,
|
|||
for (size_t i = 0; i < len; i++) {
|
||||
Json oneJsonElement = jsonElements.at(i);
|
||||
bool ascending = JsonHelper::getBooleanValue(oneJsonElement.json(), "ascending", false);
|
||||
Variable *v = query->registerVar(new Variable(oneJsonElement.get("inVariable")));
|
||||
Variable *v = varFromJson(ast, oneJsonElement, "inVariable");
|
||||
elements.push_back(std::make_pair(v, ascending));
|
||||
}
|
||||
|
||||
return new SortNode(query, oneNode, elements);
|
||||
return new SortNode(ast, oneNode, elements);
|
||||
}
|
||||
case AGGREGATE: {
|
||||
|
||||
Json outVariableJson = oneNode.get("outVariable");
|
||||
Variable *outVariable = nullptr;
|
||||
|
||||
if (!outVariableJson.isEmpty()) /* Optional... */
|
||||
outVariable = query->registerVar(new Variable(outVariableJson));
|
||||
|
||||
Variable *outVariable = varFromJson(ast, oneNode, "outVariable", Optional);
|
||||
|
||||
Json jsonAaggregates = oneNode.get("aggregates");
|
||||
if (!jsonAaggregates.isList()){
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "missing node type in valueTypeNames"); //// TODO
|
||||
if (! jsonAaggregates.isList()){
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "missing node type in valueTypeNames");
|
||||
}
|
||||
|
||||
int len = jsonAaggregates.size();
|
||||
|
@ -145,56 +141,60 @@ ExecutionNode* ExecutionNode::fromJsonFactory (Ast const* ast,
|
|||
aggregateVariables.reserve(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
Json oneJsonAggregate = jsonAaggregates.at(i);
|
||||
Variable* outVariable = query->registerVar(new Variable(oneJsonAggregate.get("outVariable")));
|
||||
Variable* inVariable = query->registerVar(new Variable(oneJsonAggregate.get("inVariable")));
|
||||
Variable* outVariable = varFromJson(ast, oneJsonAggregate, "outVariable");
|
||||
Variable* inVariable = varFromJson(ast, oneJsonAggregate, "inVariable");
|
||||
|
||||
aggregateVariables.push_back(std::make_pair(outVariable, inVariable));
|
||||
}
|
||||
|
||||
return new AggregateNode(query,
|
||||
return new AggregateNode(ast,
|
||||
oneNode,
|
||||
outVariable,
|
||||
ast->variables()->variables(false),
|
||||
aggregateVariables);
|
||||
}
|
||||
case INSERT:
|
||||
return new InsertNode(query, oneNode);
|
||||
return new InsertNode(ast, oneNode);
|
||||
case REMOVE:
|
||||
return new RemoveNode(query, oneNode);
|
||||
return new RemoveNode(ast, oneNode);
|
||||
case REPLACE:
|
||||
return new ReplaceNode(query, oneNode);
|
||||
return new ReplaceNode(ast, oneNode);
|
||||
case UPDATE:
|
||||
return new UpdateNode(query, oneNode);
|
||||
return new UpdateNode(ast, oneNode);
|
||||
case RETURN:
|
||||
return new ReturnNode(query, oneNode);
|
||||
return new ReturnNode(ast, oneNode);
|
||||
case NORESULTS:
|
||||
return new NoResultsNode(ast, oneNode);
|
||||
|
||||
case INTERSECTION:
|
||||
//return new (query, oneNode);
|
||||
case PROJECTION:
|
||||
//return new (query, oneNode);
|
||||
case LOOKUP_JOIN:
|
||||
//return new (query, oneNode);
|
||||
case MERGE_JOIN:
|
||||
//return new (query, oneNode);
|
||||
case LOOKUP_INDEX_UNIQUE:
|
||||
//return new (query, oneNode);
|
||||
case LOOKUP_INDEX_RANGE:
|
||||
//return new (query, oneNode);
|
||||
case LOOKUP_FULL_COLLECTION:
|
||||
//return new (query, oneNode);
|
||||
case CONCATENATION:
|
||||
//return new (query, oneNode);
|
||||
case INDEX_RANGE:
|
||||
//return new (query, oneNode);
|
||||
case MERGE:
|
||||
//return new (query, oneNode);
|
||||
case REMOTE:
|
||||
//return new (query, oneNode);
|
||||
// TODO: handle these types of nodes
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "unhandled node type");
|
||||
|
||||
case ILLEGAL:
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unhandled node type");
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "invalid node type");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an ExecutionNode from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ExecutionNode::ExecutionNode (triagens::basics::Json const& json)
|
||||
: ExecutionNode(JsonHelper::getNumericValue<size_t>(json.json(), "id", 0),
|
||||
JsonHelper::getNumericValue<double>(json.json(), "estimatedCost", 0.0)) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief toJson, export an ExecutionNode to JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -276,6 +276,31 @@ void ExecutionNode::walk (WalkerWorker<ExecutionNode>* worker) {
|
|||
// --SECTION-- protected methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief factory for (optional) variables from json.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* ExecutionNode::varFromJson (Ast* ast,
|
||||
triagens::basics::Json const& base,
|
||||
const char *variableName,
|
||||
bool optional) {
|
||||
Json variableJson = base.get(variableName);
|
||||
|
||||
if (variableJson.isEmpty()) {
|
||||
if (optional) {
|
||||
return nullptr;
|
||||
}
|
||||
else {
|
||||
std::string msg;
|
||||
msg += "Mandatory variable \"" + std::string(variableName) + "\"not found.";
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, msg.c_str());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ast->variables()->createVariable(variableJson);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief toJsonHelper, for a generic node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -301,48 +326,9 @@ Json ExecutionNode::toJsonHelperGeneric (triagens::basics::Json& nodes,
|
|||
if(this->_estimatedCost != 0){
|
||||
json("estimatedCost", Json(this->_estimatedCost));
|
||||
}
|
||||
/*
|
||||
if (_varUsageValid) {
|
||||
Json varsValid(Json::List, _varsValid.size());
|
||||
for (auto v : _varsValid) {
|
||||
varsValid(v->toJson());
|
||||
}
|
||||
json("varsValid", varsValid);
|
||||
Json varsUsedLater(Json::List, _varsUsedLater.size());
|
||||
for (auto v : _varsUsedLater) {
|
||||
varsUsedLater(v->toJson());
|
||||
}
|
||||
json("varsUsedLater", varsUsedLater);
|
||||
}
|
||||
*/
|
||||
return json;
|
||||
}
|
||||
|
||||
void ExecutionNode::fromJsonHelper (triagens::aql::Query* q, basics::Json const& base) {
|
||||
this->_estimatedCost = JsonHelper::getNumericValue<double>(base.json(), "estimatedCost", 0.0);
|
||||
/*
|
||||
Json varsUsedLaterJson = base.get("varsUsedLater");
|
||||
if (!varsUsedLaterJson.isEmpty()) {
|
||||
int len = varsUsedLaterJson.size();
|
||||
_varsUsedLater.reserve(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
_varsUsedLater.insert(q->registerVar(new Variable(varsUsedLaterJson.at(i))));
|
||||
}
|
||||
_varUsageValid = true;
|
||||
}
|
||||
|
||||
Json varsValidJson = base.get("varsValid");
|
||||
if (!varsValidJson.isEmpty()) {
|
||||
int len = varsValidJson.size();
|
||||
_varsValid.reserve(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
_varsValid.insert(q->registerVar(new Variable(varsUsedLaterJson.at(i))));
|
||||
}
|
||||
_varUsageValid = true;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- methods of SingletonNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -351,9 +337,8 @@ void ExecutionNode::fromJsonHelper (triagens::aql::Query* q, basics::Json const&
|
|||
/// @brief toJson, for SingletonNode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SingletonNode::SingletonNode (triagens::aql::Query* query, basics::Json const& base)
|
||||
SingletonNode::SingletonNode (Ast* ast, basics::Json const& base)
|
||||
: ExecutionNode(base) {
|
||||
fromJsonHelper(query, base);
|
||||
}
|
||||
|
||||
void SingletonNode::toJsonHelper (triagens::basics::Json& nodes,
|
||||
|
@ -371,12 +356,11 @@ void SingletonNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of EnumerateCollectionNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
EnumerateCollectionNode::EnumerateCollectionNode (triagens::aql::Query* q, basics::Json const& base)
|
||||
EnumerateCollectionNode::EnumerateCollectionNode (Ast* ast, basics::Json const& base)
|
||||
: ExecutionNode(base),
|
||||
_vocbase(q->vocbase()),
|
||||
_collection(q->collections()->get(JsonHelper::getStringValue(base.json(), "collection", ""))),
|
||||
_outVariable(q->registerVar(new Variable(base.get("outVariable")))) {
|
||||
fromJsonHelper(q, base);
|
||||
_vocbase(ast->query()->vocbase()),
|
||||
_collection(ast->query()->collections()->add(JsonHelper::getStringValue(base.json(), "collection", ""), TRI_TRANSACTION_READ)),
|
||||
_outVariable(varFromJson(ast, base, "outVariable")) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -404,11 +388,10 @@ void EnumerateCollectionNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of EnumerateListNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
EnumerateListNode::EnumerateListNode (triagens::aql::Query* q, basics::Json const& base)
|
||||
EnumerateListNode::EnumerateListNode (Ast* ast, basics::Json const& base)
|
||||
: ExecutionNode(base),
|
||||
_inVariable(q->registerVar(new Variable(base.get("inVariable")))),
|
||||
_outVariable(q->registerVar(new Variable(base.get("outVariable")))) {
|
||||
fromJsonHelper(q, base);
|
||||
_inVariable(varFromJson(ast, base, "inVariable")),
|
||||
_outVariable(varFromJson(ast, base, "outVariable")) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -464,11 +447,10 @@ void IndexRangeNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of LimitNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
LimitNode::LimitNode (triagens::aql::Query* query, basics::Json const& base)
|
||||
LimitNode::LimitNode (Ast* ast, basics::Json const& base)
|
||||
: ExecutionNode(base) {
|
||||
_offset = JsonHelper::getNumericValue<double>(base.json(), "offset", 0.0);
|
||||
_limit = JsonHelper::getNumericValue<double>(base.json(), "limit", 0.0);
|
||||
fromJsonHelper(query, base);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -493,12 +475,10 @@ void LimitNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of CalculationNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
CalculationNode::CalculationNode (triagens::aql::Query* q, basics::Json const& base)
|
||||
CalculationNode::CalculationNode (Ast* ast, basics::Json const& base)
|
||||
: ExecutionNode(base),
|
||||
_expression(new Expression(q, base)),
|
||||
_outVariable(q->registerVar(new Variable(base.get("outVariable")))) {
|
||||
//// _expression->fromJson(base, "expression")) -> list -> for schleife.
|
||||
fromJsonHelper(q, base);
|
||||
_expression(new Expression(ast, base)),
|
||||
_outVariable(varFromJson(ast, base, "outVariable")) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -525,13 +505,11 @@ void CalculationNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of SubqueryNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
SubqueryNode::SubqueryNode (Ast const* ast,
|
||||
triagens::aql::Query* q,
|
||||
SubqueryNode::SubqueryNode (Ast* ast,
|
||||
basics::Json const& base)
|
||||
: ExecutionNode(base),
|
||||
_subquery(nullptr),
|
||||
_outVariable(q->registerVar(new Variable(base.get("outVariable")))) {
|
||||
fromJsonHelper(q, base);
|
||||
_outVariable(varFromJson(ast, base, "outVariable")) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -619,10 +597,10 @@ std::vector<Variable const*> SubqueryNode::getVariablesUsedHere () {
|
|||
// --SECTION-- methods of FilterNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
FilterNode::FilterNode (triagens::aql::Query* q, basics::Json const& base)
|
||||
FilterNode::FilterNode (Ast* ast, basics::Json const& base)
|
||||
: ExecutionNode(base),
|
||||
_inVariable(q->registerVar(new Variable(base.get("inVariable")))) {
|
||||
fromJsonHelper(q, base);
|
||||
_inVariable(varFromJson(ast, base, "inVariable")),
|
||||
_resultIsEmpty(false) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -646,12 +624,11 @@ void FilterNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of SortNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
SortNode::SortNode (triagens::aql::Query* query,
|
||||
SortNode::SortNode (Ast* ast,
|
||||
basics::Json const& base,
|
||||
std::vector<std::pair<Variable const*, bool>> elements)
|
||||
: ExecutionNode(base),
|
||||
_elements(elements) {
|
||||
fromJsonHelper(query, base);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -681,7 +658,7 @@ void SortNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of AggregateNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
AggregateNode::AggregateNode (triagens::aql::Query* q,
|
||||
AggregateNode::AggregateNode (Ast* ast,
|
||||
basics::Json const& base,
|
||||
Variable const* outVariable,
|
||||
std::unordered_map<VariableId, std::string const> const& variableMap,
|
||||
|
@ -690,7 +667,6 @@ AggregateNode::AggregateNode (triagens::aql::Query* q,
|
|||
_aggregateVariables(aggregateVariables),
|
||||
_outVariable(outVariable),
|
||||
_variableMap(variableMap) {
|
||||
fromJsonHelper(q, base);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -726,10 +702,9 @@ void AggregateNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of ReturnNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ReturnNode::ReturnNode (triagens::aql::Query* q, basics::Json const& base)
|
||||
ReturnNode::ReturnNode (Ast* ast, basics::Json const& base)
|
||||
: ExecutionNode(base),
|
||||
_inVariable(q->registerVar(new Variable(base.get("inVariable")))) {
|
||||
fromJsonHelper(q, base);
|
||||
_inVariable(varFromJson(ast, base, "inVariable")) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -754,11 +729,11 @@ void ReturnNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- ModificationNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ModificationNode::ModificationNode (triagens::aql::Query* q,
|
||||
ModificationNode::ModificationNode (Ast* ast,
|
||||
basics::Json const& base)
|
||||
: ExecutionNode(base),
|
||||
_vocbase(q->vocbase()),
|
||||
_collection(q->collections()->get(JsonHelper::getStringValue(base.json(), "collection", ""))),
|
||||
_vocbase(ast->query()->vocbase()),
|
||||
_collection(ast->query()->collections()->add(JsonHelper::getStringValue(base.json(), "collection", ""), TRI_TRANSACTION_WRITE)),
|
||||
_options(base) {
|
||||
TRI_ASSERT(_vocbase != nullptr);
|
||||
TRI_ASSERT(_collection != nullptr);
|
||||
|
@ -768,11 +743,10 @@ ModificationNode::ModificationNode (triagens::aql::Query* q,
|
|||
// --SECTION-- methods of RemoveNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
RemoveNode::RemoveNode (triagens::aql::Query* q, basics::Json const& base)
|
||||
: ModificationNode(q, base),
|
||||
_inVariable(q->registerVar(new Variable(base.get("inVariable")))),
|
||||
_outVariable(base.get("outVariable").isEmpty() ? nullptr : q->registerVar(new Variable(base.get("outVariable")))) {
|
||||
fromJsonHelper(q, base);
|
||||
RemoveNode::RemoveNode (Ast* ast, basics::Json const& base)
|
||||
: ModificationNode(ast, base),
|
||||
_inVariable(varFromJson(ast, base, "inVariable")),
|
||||
_outVariable(varFromJson(ast, base, "outVariable", Optional)) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -805,11 +779,10 @@ void RemoveNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of InsertNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
InsertNode::InsertNode (triagens::aql::Query* q, basics::Json const& base)
|
||||
: ModificationNode(q, base),
|
||||
_inVariable(q->registerVar(new Variable(base.get("inVariable")))),
|
||||
_outVariable(base.get("outVariable").isEmpty() ? nullptr : q->registerVar(new Variable(base.get("outVariable")))) {
|
||||
fromJsonHelper(q, base);
|
||||
InsertNode::InsertNode (Ast* ast, basics::Json const& base)
|
||||
: ModificationNode(ast, base),
|
||||
_inVariable(varFromJson(ast, base, "inVariable")),
|
||||
_outVariable(varFromJson(ast, base, "outVariable", Optional)) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -842,12 +815,11 @@ void InsertNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of UpdateNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
UpdateNode::UpdateNode (triagens::aql::Query* q, basics::Json const& base)
|
||||
: ModificationNode(q, base),
|
||||
_inDocVariable(q->registerVar(new Variable(base.get("inDocVariable")))),
|
||||
_inKeyVariable(base.get("inKeyVariable").isEmpty() ? nullptr : q->registerVar(new Variable(base.get("inKeyVariable")))),
|
||||
_outVariable(base.get("outVariable").isEmpty() ? nullptr : q->registerVar(new Variable(base.get("outVariable")))) {
|
||||
fromJsonHelper(q, base);
|
||||
UpdateNode::UpdateNode (Ast* ast, basics::Json const& base)
|
||||
: ModificationNode(ast, base),
|
||||
_inDocVariable(varFromJson(ast, base, "inDocVariable")),
|
||||
_inKeyVariable(varFromJson(ast, base, "inKeyVariable", Optional)),
|
||||
_outVariable(varFromJson(ast, base, "outVariable", Optional)) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -885,12 +857,11 @@ void UpdateNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
// --SECTION-- methods of ReplaceNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ReplaceNode::ReplaceNode (triagens::aql::Query* q, basics::Json const& base)
|
||||
: ModificationNode(q, base),
|
||||
_inDocVariable(q->registerVar(new Variable(base.get("inDocVariable")))),
|
||||
_inKeyVariable(base.get("inKeyVariable").isEmpty() ? nullptr : q->registerVar(new Variable(base.get("inKeyVariable")))),
|
||||
_outVariable(base.get("outVariable").isEmpty() ? nullptr : q->registerVar(new Variable(base.get("outVariable")))) {
|
||||
fromJsonHelper(q, base);
|
||||
ReplaceNode::ReplaceNode (Ast* ast, basics::Json const& base)
|
||||
: ModificationNode(ast, base),
|
||||
_inDocVariable(varFromJson(ast, base, "inDocVariable")),
|
||||
_inKeyVariable(varFromJson(ast, base, "inKeyVariable", Optional)),
|
||||
_outVariable(varFromJson(ast, base, "outVariable", Optional)) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -924,6 +895,25 @@ void ReplaceNode::toJsonHelper (triagens::basics::Json& nodes,
|
|||
nodes(json);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- methods of NoResultsNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief toJson, for NoResultsNode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void NoResultsNode::toJsonHelper (triagens::basics::Json& nodes,
|
||||
TRI_memory_zone_t* zone) {
|
||||
Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method
|
||||
if (json.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// And add it:
|
||||
nodes(json);
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
||||
|
|
|
@ -47,8 +47,6 @@
|
|||
|
||||
using Json = triagens::basics::Json;
|
||||
|
||||
|
||||
|
||||
namespace triagens {
|
||||
namespace aql {
|
||||
|
||||
|
@ -94,25 +92,19 @@ namespace triagens {
|
|||
REMOVE = 23,
|
||||
REPLACE = 24,
|
||||
UPDATE = 25,
|
||||
RETURN = 26
|
||||
RETURN = 26,
|
||||
NORESULTS = 27
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- constructors / destructors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief factory from json.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static ExecutionNode* fromJsonFactory (Ast const* ast,
|
||||
basics::Json const& json);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief constructor using an id
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ExecutionNode (size_t id)
|
||||
ExecutionNode (size_t id, double cost = 0.0)
|
||||
: _id(id),
|
||||
_estimatedCost(0),
|
||||
_varUsageValid(false) {
|
||||
|
@ -122,9 +114,7 @@ namespace triagens {
|
|||
/// @brief constructor using a JSON struct
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ExecutionNode (triagens::basics::Json const& json)
|
||||
: ExecutionNode(triagens::basics::JsonHelper::getNumericValue<size_t>(json.json(), "id", 0)) {
|
||||
}
|
||||
ExecutionNode (triagens::basics::Json const& json);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destructor, free dependencies;
|
||||
|
@ -139,6 +129,13 @@ namespace triagens {
|
|||
|
||||
public:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief factory from json.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static ExecutionNode* fromJsonFactory (Ast* ast,
|
||||
basics::Json const& json);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the node's id
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -277,17 +274,6 @@ namespace triagens {
|
|||
|
||||
triagens::basics::Json toJson (TRI_memory_zone_t* zone = TRI_UNKNOWN_MEM_ZONE);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief toJsonHelper, for a generic node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void fromJsonHelper (triagens::aql::Query* query,
|
||||
basics::Json const& base);
|
||||
|
||||
triagens::basics::Json toJsonHelperGeneric (
|
||||
triagens::basics::Json& nodes,
|
||||
TRI_memory_zone_t* zone);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief toJson
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -363,6 +349,29 @@ namespace triagens {
|
|||
_varUsageValid = false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- protected functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
protected:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief factory for (optional) variables from json.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Variable* varFromJson (Ast*,
|
||||
triagens::basics::Json const& base,
|
||||
const char *variableName,
|
||||
bool optional = false);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief toJsonHelper, for a generic node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
triagens::basics::Json toJsonHelperGeneric (
|
||||
triagens::basics::Json& nodes,
|
||||
TRI_memory_zone_t* zone);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- protected variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -404,6 +413,7 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::unordered_set<Variable const*> _varsUsedLater;
|
||||
|
||||
std::unordered_set<Variable const*> _varsValid;
|
||||
|
||||
bool _varUsageValid;
|
||||
|
@ -424,7 +434,7 @@ namespace triagens {
|
|||
friend class SingletonBlock;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief constructor with a vocbase and a collection name
|
||||
/// @brief constructor with an id
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public:
|
||||
|
@ -433,7 +443,7 @@ namespace triagens {
|
|||
: ExecutionNode(id) {
|
||||
}
|
||||
|
||||
SingletonNode (triagens::aql::Query* query, basics::Json const& base);
|
||||
SingletonNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
|
@ -502,7 +512,7 @@ namespace triagens {
|
|||
TRI_ASSERT(_outVariable != nullptr);
|
||||
}
|
||||
|
||||
EnumerateCollectionNode (triagens::aql::Query* Q, basics::Json const& base);
|
||||
EnumerateCollectionNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
|
@ -656,7 +666,7 @@ namespace triagens {
|
|||
TRI_ASSERT(_outVariable != nullptr);
|
||||
}
|
||||
|
||||
EnumerateListNode (triagens::aql::Query* Q, basics::Json const& base);
|
||||
EnumerateListNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
|
@ -1097,7 +1107,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
_limit(limit) {
|
||||
}
|
||||
|
||||
LimitNode (triagens::aql::Query* query, basics::Json const& base);
|
||||
LimitNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
|
@ -1176,7 +1186,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
TRI_ASSERT(_outVariable != nullptr);
|
||||
}
|
||||
|
||||
CalculationNode (triagens::aql::Query* Q, basics::Json const& base);
|
||||
CalculationNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destructor
|
||||
|
@ -1301,12 +1311,12 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
|
||||
public:
|
||||
|
||||
SubqueryNode (Ast const* ast,
|
||||
triagens::aql::Query* Q,
|
||||
SubqueryNode (Ast*,
|
||||
basics::Json const& base);
|
||||
|
||||
SubqueryNode (size_t id,
|
||||
ExecutionNode* subquery, Variable const* outVariable)
|
||||
ExecutionNode* subquery,
|
||||
Variable const* outVariable)
|
||||
: ExecutionNode(id),
|
||||
_subquery(subquery),
|
||||
_outVariable(outVariable) {
|
||||
|
@ -1426,12 +1436,13 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
FilterNode (size_t id,
|
||||
Variable const* inVariable)
|
||||
: ExecutionNode(id),
|
||||
_inVariable(inVariable) {
|
||||
_inVariable(inVariable),
|
||||
_resultIsEmpty(false) {
|
||||
|
||||
TRI_ASSERT(_inVariable != nullptr);
|
||||
}
|
||||
|
||||
FilterNode (triagens::aql::Query* Q, basics::Json const& base);
|
||||
FilterNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
|
@ -1458,11 +1469,24 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
return static_cast<ExecutionNode*>(c);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief this is a hint that the filter will never let any data pass
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setEmptyResult () {
|
||||
_resultIsEmpty = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief the cost of a filter node is . . . FIXME
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
double estimateCost () {
|
||||
if (_resultIsEmpty) {
|
||||
// filter will not let any data through
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _dependencies.at(0)->getCost() * 0.105;
|
||||
//FIXME! 0.005 is the cost of doing the filter node under the
|
||||
//assumption that it returns 10% of the results of its dependency
|
||||
|
@ -1486,6 +1510,11 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
|
||||
Variable const* _inVariable;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief hint that is set to true if the filter will not let any data through
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool _resultIsEmpty;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -1513,7 +1542,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
_elements(elements) {
|
||||
}
|
||||
|
||||
SortNode (triagens::aql::Query* query,
|
||||
SortNode (Ast*,
|
||||
basics::Json const& base,
|
||||
std::vector<std::pair<Variable const*, bool>> elements);
|
||||
|
||||
|
@ -1609,7 +1638,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
// outVariable can be a nullptr
|
||||
}
|
||||
|
||||
AggregateNode (triagens::aql::Query* query,
|
||||
AggregateNode (Ast*,
|
||||
basics::Json const& base,
|
||||
Variable const* outVariable,
|
||||
std::unordered_map<VariableId, std::string const> const& variableMap,
|
||||
|
@ -1730,7 +1759,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
TRI_ASSERT(_inVariable != nullptr);
|
||||
}
|
||||
|
||||
ReturnNode (triagens::aql::Query* Q, basics::Json const& base);
|
||||
ReturnNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
|
@ -1821,7 +1850,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
TRI_ASSERT(_collection != nullptr);
|
||||
}
|
||||
|
||||
ModificationNode (triagens::aql::Query* q,
|
||||
ModificationNode (Ast*,
|
||||
basics::Json const& json);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -1884,7 +1913,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
// _outVariable might be a nullptr
|
||||
}
|
||||
|
||||
RemoveNode (triagens::aql::Query* Q, basics::Json const& base);
|
||||
RemoveNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
|
@ -1996,7 +2025,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
// _outVariable might be a nullptr
|
||||
}
|
||||
|
||||
InsertNode (triagens::aql::Query* Q, basics::Json const& base);
|
||||
InsertNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
|
@ -2110,7 +2139,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
// _outVariable might be a nullptr
|
||||
}
|
||||
|
||||
UpdateNode (triagens::aql::Query* Q, basics::Json const& base);
|
||||
UpdateNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
|
@ -2234,7 +2263,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
// _outVariable might be a nullptr
|
||||
}
|
||||
|
||||
ReplaceNode (triagens::aql::Query* Q, basics::Json const& base);
|
||||
ReplaceNode (Ast*, basics::Json const& base);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
|
@ -2322,6 +2351,67 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con
|
|||
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- class NoResultsNode
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief class NoResultsNode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class NoResultsNode : public ExecutionNode {
|
||||
|
||||
friend class ExecutionBlock;
|
||||
friend class NoResultsBlock;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief constructor with an id
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public:
|
||||
|
||||
NoResultsNode (size_t id)
|
||||
: ExecutionNode(id) {
|
||||
}
|
||||
|
||||
NoResultsNode (Ast*, basics::Json const& base)
|
||||
: ExecutionNode(base) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
return NORESULTS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief export to JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void toJsonHelper (triagens::basics::Json& nodes,
|
||||
TRI_memory_zone_t* zone = TRI_UNKNOWN_MEM_ZONE);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief clone ExecutionNode recursively
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual ExecutionNode* clone () const {
|
||||
auto c = new NoResultsNode(_id);
|
||||
cloneDependencies(c);
|
||||
return static_cast<ExecutionNode*>(c);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief the cost of a NoResults is 0
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
double estimateCost () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace triagens::aql
|
||||
} // namespace triagens
|
||||
|
|
|
@ -75,7 +75,7 @@ ExecutionPlan::~ExecutionPlan () {
|
|||
/// @brief create an execution plan from an AST
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ExecutionPlan* ExecutionPlan::instanciateFromAst (Ast const* ast) {
|
||||
ExecutionPlan* ExecutionPlan::instanciateFromAst (Ast* ast) {
|
||||
TRI_ASSERT(ast != nullptr);
|
||||
|
||||
auto root = ast->root();
|
||||
|
@ -87,17 +87,19 @@ ExecutionPlan* ExecutionPlan::instanciateFromAst (Ast const* ast) {
|
|||
try {
|
||||
plan->_root = plan->fromNode(ast, root);
|
||||
plan->findVarUsage();
|
||||
/*
|
||||
// just for debugging
|
||||
/*
|
||||
auto JsonPlan = plan->_root->toJson();
|
||||
auto JsonString = JsonPlan.toString();
|
||||
|
||||
std::cout << JsonString << "\n";
|
||||
auto otherPlan = ExecutionPlan::instanciateFromJson (ast,
|
||||
JsonPlan);
|
||||
auto otherJsonString = otherPlan->_root->toJson().toString();
|
||||
/// TRI_ASSERT(otherJsonString == JsonString);
|
||||
/// JsonHelper
|
||||
std::cout << otherJsonString << "\n";
|
||||
TRI_ASSERT(otherJsonString == JsonString);
|
||||
return otherPlan;
|
||||
*/
|
||||
//
|
||||
return plan;
|
||||
}
|
||||
catch (...) {
|
||||
|
@ -106,12 +108,16 @@ ExecutionPlan* ExecutionPlan::instanciateFromAst (Ast const* ast) {
|
|||
}
|
||||
}
|
||||
|
||||
ExecutionPlan* ExecutionPlan::instanciateFromJson (Ast const* ast,
|
||||
triagens::basics::Json const& Json) {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an execution plan from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ExecutionPlan* ExecutionPlan::instanciateFromJson (Ast* ast,
|
||||
triagens::basics::Json const& json) {
|
||||
auto plan = new ExecutionPlan();
|
||||
|
||||
try {
|
||||
plan->_root = plan->fromJson(ast, Json);
|
||||
plan->_root = plan->fromJson(ast, json);
|
||||
plan->findVarUsage();
|
||||
return plan;
|
||||
}
|
||||
|
@ -854,10 +860,13 @@ class NodeFinder : public WalkerWorker<ExecutionNode> {
|
|||
|
||||
std::vector<ExecutionNode*>& _out;
|
||||
|
||||
bool _enterSubqueries;
|
||||
|
||||
public:
|
||||
NodeFinder (ExecutionNode::NodeType lookingFor,
|
||||
std::vector<ExecutionNode*>& out)
|
||||
: _lookingFor(lookingFor), _out(out) {
|
||||
std::vector<ExecutionNode*>& out,
|
||||
bool enterSubqueries)
|
||||
: _lookingFor(lookingFor), _out(out), _enterSubqueries(enterSubqueries) {
|
||||
};
|
||||
|
||||
void before (ExecutionNode* en) {
|
||||
|
@ -866,13 +875,17 @@ class NodeFinder : public WalkerWorker<ExecutionNode> {
|
|||
}
|
||||
}
|
||||
|
||||
bool enterSubquery (ExecutionNode* super, ExecutionNode* sub) {
|
||||
return _enterSubqueries;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<ExecutionNode*> ExecutionPlan::findNodesOfType (
|
||||
ExecutionNode::NodeType type) {
|
||||
ExecutionNode::NodeType type,
|
||||
bool enterSubqueries) {
|
||||
|
||||
std::vector<ExecutionNode*> result;
|
||||
NodeFinder finder(type, result);
|
||||
NodeFinder finder(type, result, enterSubqueries);
|
||||
root()->walk(&finder);
|
||||
return result;
|
||||
}
|
||||
|
@ -1064,7 +1077,7 @@ ExecutionPlan* ExecutionPlan::clone (){
|
|||
/// @brief create a plan from the JSON provided
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ExecutionNode* ExecutionPlan::fromJson (Ast const* ast,
|
||||
ExecutionNode* ExecutionPlan::fromJson (Ast* ast,
|
||||
Json const& json) {
|
||||
ExecutionNode* ret = nullptr;
|
||||
Json nodes = json.get("nodes");
|
||||
|
|
|
@ -79,9 +79,13 @@ namespace triagens {
|
|||
/// @brief create an execution plan from an AST
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static ExecutionPlan* instanciateFromAst (Ast const*);
|
||||
static ExecutionPlan* instanciateFromAst (Ast*);
|
||||
|
||||
static ExecutionPlan* instanciateFromJson (Ast const* ast,
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an execution plan from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static ExecutionPlan* instanciateFromJson (Ast* ast,
|
||||
triagens::basics::Json const& Json);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -132,7 +136,8 @@ namespace triagens {
|
|||
/// @brief find nodes of a certain type
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::vector<ExecutionNode*> findNodesOfType (ExecutionNode::NodeType);
|
||||
std::vector<ExecutionNode*> findNodesOfType (ExecutionNode::NodeType,
|
||||
bool enterSubqueries);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief determine and set _varsUsedLater in all nodes
|
||||
|
@ -301,8 +306,9 @@ namespace triagens {
|
|||
ExecutionNode* fromNode (Ast const*,
|
||||
AstNode const*);
|
||||
|
||||
ExecutionNode* fromJson (Ast const* ast,
|
||||
ExecutionNode* fromJson (Ast*,
|
||||
triagens::basics::Json const& Json);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -65,10 +65,14 @@ Expression::Expression (V8Executor* executor,
|
|||
analyzeExpression();
|
||||
}
|
||||
|
||||
Expression::Expression (triagens::aql::Query* Q,
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an expression from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Expression::Expression (Ast* ast,
|
||||
triagens::basics::Json const& json)
|
||||
: _executor(Q->executor()),
|
||||
_node(Q->registerNode(new AstNode(Q, json.get("expression")))),
|
||||
: _executor(ast->query()->executor()),
|
||||
_node(new AstNode(ast, json.get("expression"))),
|
||||
_type(UNPROCESSED) {
|
||||
|
||||
TRI_ASSERT(_executor != nullptr);
|
||||
|
@ -189,6 +193,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
|
|||
std::vector<RegisterId> const& regs) {
|
||||
|
||||
if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
// array lookup, e.g. users.name
|
||||
TRI_ASSERT(node->numMembers() == 1);
|
||||
|
||||
auto member = node->getMember(0);
|
||||
|
@ -200,8 +205,14 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
|
|||
auto j = result.extractArrayMember(trx, myCollection, name);
|
||||
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
|
||||
}
|
||||
/*
|
||||
|
||||
else if (node->type == NODE_TYPE_INDEXED_ACCESS) {
|
||||
// list lookup, e.g. users[0]
|
||||
// note: it depends on the type of the value whether a list lookup or an array lookup is performed
|
||||
// for example, if the value is an array, then its elements might be access like this:
|
||||
// users['name'] or even users['0'] (as '0' is a valid attribute name, too)
|
||||
// if the value is a list, then string indexes might also be used and will be converted to integers, e.g.
|
||||
// users['0'] is the same as users[0], users['-2'] is the same as users[-2] etc.
|
||||
TRI_ASSERT(node->numMembers() == 2);
|
||||
|
||||
auto member = node->getMember(0);
|
||||
|
@ -210,10 +221,46 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
|
|||
TRI_document_collection_t const* myCollection = nullptr;
|
||||
AqlValue result = executeSimpleExpression(member, &myCollection, trx, docColls, argv, startPos, vars, regs);
|
||||
|
||||
auto j = result.extractListMember(trx, myCollection, name);
|
||||
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
|
||||
if (result.isList()) {
|
||||
if (index->isNumericValue()) {
|
||||
auto j = result.extractListMember(trx, myCollection, index->getIntValue());
|
||||
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
|
||||
}
|
||||
else if (index->isStringValue()) {
|
||||
char const* p = index->getStringValue();
|
||||
TRI_ASSERT(p != nullptr);
|
||||
|
||||
try {
|
||||
// stoll() might throw an exception if the string is not a number
|
||||
int64_t position = static_cast<int64_t>(std::stoll(p));
|
||||
auto j = result.extractListMember(trx, myCollection, position);
|
||||
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
|
||||
}
|
||||
catch (...) {
|
||||
// no number found.
|
||||
}
|
||||
}
|
||||
// fall-through to returning null
|
||||
}
|
||||
else if (result.isArray()) {
|
||||
if (index->isNumericValue()) {
|
||||
std::string const indexString = std::to_string(index->getIntValue());
|
||||
auto j = result.extractArrayMember(trx, myCollection, indexString.c_str());
|
||||
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
|
||||
}
|
||||
else if (index->isStringValue()) {
|
||||
char const* p = index->getStringValue();
|
||||
TRI_ASSERT(p != nullptr);
|
||||
|
||||
auto j = result.extractArrayMember(trx, myCollection, p);
|
||||
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
|
||||
}
|
||||
// fall-through to returning null
|
||||
}
|
||||
|
||||
return AqlValue(new Json(Json::Null));
|
||||
}
|
||||
*/
|
||||
|
||||
else if (node->type == NODE_TYPE_LIST) {
|
||||
auto list = new Json(Json::List);
|
||||
|
||||
|
@ -273,6 +320,16 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
|
|||
}
|
||||
// fall-through to exception
|
||||
}
|
||||
|
||||
else if (node->type == NODE_TYPE_VALUE) {
|
||||
auto j = node->toJsonValue(TRI_UNKNOWN_MEM_ZONE);
|
||||
|
||||
if (j == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j));
|
||||
}
|
||||
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unhandled type in simple expression");
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace triagens {
|
|||
|
||||
class AqlItemBlock;
|
||||
struct AqlValue;
|
||||
class Ast;
|
||||
struct Variable;
|
||||
class V8Executor;
|
||||
struct V8Expression;
|
||||
|
@ -76,7 +77,7 @@ namespace triagens {
|
|||
Expression (V8Executor*,
|
||||
AstNode const*);
|
||||
|
||||
Expression (triagens::aql::Query* Q,
|
||||
Expression (Ast*,
|
||||
triagens::basics::Json const &json);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -42,18 +42,19 @@ Optimizer::Optimizer () {
|
|||
// List all the rules in the system here:
|
||||
|
||||
// try to find a filter after an enumerate collection and find an index . . .
|
||||
registerRule (useIndexRange, 999);
|
||||
// registerRule (useIndexRange, 999);
|
||||
|
||||
// remove filters from the query that are not necessary at all
|
||||
// rule should be executed relatively early because it enables removal
|
||||
// of then-unused filter variables
|
||||
//registerRule(removeUnnecessaryFiltersRule, 10000);
|
||||
// filters that are always true will be removed entirely
|
||||
// filters that are always false will get a hint
|
||||
registerRule(removeUnnecessaryFiltersRule, 10000);
|
||||
|
||||
// move calculations up the dependency chain (to pull them out of inner loops etc.)
|
||||
// TODO: validate if this is really an optimization
|
||||
// registerRule(moveCalculationsUpRule, 1000);
|
||||
|
||||
//registerRule(removeUnnecessaryCalculationsRule, 999);
|
||||
// remove calculations that are never necessary
|
||||
registerRule(removeUnnecessaryCalculationsRule, 999);
|
||||
|
||||
// Now sort them by pass:
|
||||
std::stable_sort(_rules.begin(), _rules.end());
|
||||
|
@ -95,6 +96,9 @@ int Optimizer::createPlans (ExecutionPlan* plan) {
|
|||
while (oldPlans.size() > 0) {
|
||||
auto p = oldPlans.pop_front();
|
||||
try {
|
||||
// keep should have a default value so rules that forget to set it
|
||||
// have a deterministic behavior
|
||||
keep = true;
|
||||
res = r.func(this, p, newPlans, keep);
|
||||
if (keep) {
|
||||
nextOldPlans.push_back(p);
|
||||
|
|
|
@ -37,12 +37,11 @@ using Json = triagens::basics::Json;
|
|||
// --SECTION-- rules for the optimizer
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief remove all unnecessary filters
|
||||
/// filters that are always true are removed
|
||||
/// filters that are always false will be removed plus their dependent nodes
|
||||
/// this modifies the plan in place
|
||||
/// this rule modifies the plan in place:
|
||||
/// - filters that are always true are removed completely
|
||||
/// - filters that are always false will be removed plus their dependent nodes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int triagens::aql::removeUnnecessaryFiltersRule (Optimizer* opt,
|
||||
|
@ -50,12 +49,12 @@ int triagens::aql::removeUnnecessaryFiltersRule (Optimizer* opt,
|
|||
Optimizer::PlanList& out,
|
||||
bool& keep) {
|
||||
|
||||
keep = true;
|
||||
std::unordered_set<ExecutionNode*> toRemove;
|
||||
std::vector<ExecutionNode*> nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::FILTER);
|
||||
keep = true; // plan will always be kept
|
||||
std::unordered_set<ExecutionNode*> toUnlink;
|
||||
std::vector<ExecutionNode*> nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::FILTER, true);
|
||||
|
||||
for (auto n : nodes) {
|
||||
// filter has one input variable
|
||||
// filter nodes always have one input variable
|
||||
auto varsUsedHere = n->getVariablesUsedHere();
|
||||
TRI_ASSERT(varsUsedHere.size() == 1);
|
||||
|
||||
|
@ -63,74 +62,90 @@ int triagens::aql::removeUnnecessaryFiltersRule (Optimizer* opt,
|
|||
auto variable = varsUsedHere[0];
|
||||
auto setter = plan->getVarSetBy(variable->id);
|
||||
|
||||
if (setter != nullptr &&
|
||||
setter->getType() == triagens::aql::ExecutionNode::CALCULATION) {
|
||||
// if it was a CalculationNode, check its expression
|
||||
auto s = static_cast<CalculationNode*>(setter);
|
||||
auto root = s->expression()->node();
|
||||
if (setter == nullptr ||
|
||||
setter->getType() != triagens::aql::ExecutionNode::CALCULATION) {
|
||||
// filter variable was not introduced by a calculation.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (root->isConstant()) {
|
||||
// the expression is a constant value
|
||||
if (root->toBoolean()) {
|
||||
// filter is always true
|
||||
// remove filter node and merge with following node
|
||||
toRemove.insert(n);
|
||||
// filter variable was introduced a CalculationNode. now check the expression
|
||||
auto s = static_cast<CalculationNode*>(setter);
|
||||
auto root = s->expression()->node();
|
||||
|
||||
if (! root->isConstant()) {
|
||||
// filter expression can only be evaluated at runtime
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter expression is constant and thus cannot throw
|
||||
// we can now evaluate it safely
|
||||
TRI_ASSERT(! s->expression()->canThrow());
|
||||
|
||||
if (root->toBoolean()) {
|
||||
// filter is always true
|
||||
// remove filter node and merge with following node
|
||||
toUnlink.insert(n);
|
||||
}
|
||||
else {
|
||||
// filter is always false
|
||||
|
||||
|
||||
// get all dependent nodes of the filter node
|
||||
std::vector<ExecutionNode*> stack;
|
||||
stack.push_back(n);
|
||||
|
||||
bool canOptimize = true;
|
||||
|
||||
while (! stack.empty()) {
|
||||
// pop a node from the stack
|
||||
auto current = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
if (toUnlink.find(current) != toUnlink.end()) {
|
||||
// detected a cycle. TODO: decide whether a cycle is an error here or
|
||||
// if it is valid
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// filter is always false
|
||||
/* TODO
|
||||
// get all dependent nodes of the filter node
|
||||
std::vector<ExecutionNode*> stack;
|
||||
stack.push_back(n);
|
||||
|
||||
while (! stack.empty()) {
|
||||
// pop a node from the stack
|
||||
auto current = stack.back();
|
||||
stack.pop_back();
|
||||
if (current->getType() == triagens::aql::ExecutionNode::SINGLETON) {
|
||||
// stop at a singleton node
|
||||
break;
|
||||
}
|
||||
|
||||
if (current->getType() == triagens::aql::ExecutionNode::SUBQUERY) {
|
||||
// if we find a subquery, we abort optimizations.
|
||||
// TODO: peek into the subquery and check if it can throw an exception itself
|
||||
canOptimize = false;
|
||||
break;
|
||||
}
|
||||
|
||||
bool unlinkNode = true;
|
||||
|
||||
if (toRemove.find(current) != toRemove.end()) {
|
||||
// detected a cycle. TODO: decide whether a cycle is an error here or
|
||||
// if it is valid
|
||||
break;
|
||||
}
|
||||
|
||||
if (current->getType() == triagens::aql::ExecutionNode::SINGLETON) {
|
||||
// stop at a singleton node
|
||||
break;
|
||||
}
|
||||
|
||||
if (current->getType() == triagens::aql::ExecutionNode::CALCULATION) {
|
||||
auto c = static_cast<CalculationNode*>(current);
|
||||
if (c->expression()->node()->canThrow()) {
|
||||
// the calculation may throw an exception. we must not remove it
|
||||
// because its removal might change the query result
|
||||
unlinkNode = false;
|
||||
std::cout << "FOUND A CALCULATION THAT CAN THROW\n";
|
||||
}
|
||||
}
|
||||
|
||||
auto deps = current->getDependencies();
|
||||
for (auto it = deps.begin(); it != deps.end(); ++it) {
|
||||
stack.push_back((*it));
|
||||
}
|
||||
|
||||
if (unlinkNode) {
|
||||
std::cout << "REMOVING NODE " << current << " OF TYPE: " << current->getTypeString() << "\n";
|
||||
toRemove.insert(current);
|
||||
}
|
||||
if (current->getType() == triagens::aql::ExecutionNode::CALCULATION) {
|
||||
auto c = static_cast<CalculationNode*>(current);
|
||||
if (c->expression()->node()->canThrow()) {
|
||||
// the calculation may throw an exception. we must not remove it
|
||||
// because its removal might change the query result
|
||||
canOptimize = false;
|
||||
break;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
auto deps = current->getDependencies();
|
||||
for (auto it = deps.begin(); it != deps.end(); ++it) {
|
||||
stack.push_back((*it));
|
||||
}
|
||||
}
|
||||
|
||||
if (canOptimize) {
|
||||
// store a hint in the filter that it will never produce results
|
||||
static_cast<FilterNode*>(n)->setEmptyResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! toRemove.empty()) {
|
||||
std::cout << "Removing " << toRemove.size() << " unnecessary "
|
||||
if (! toUnlink.empty()) {
|
||||
std::cout << "Removing " << toUnlink.size() << " unnecessary "
|
||||
"nodes..." << std::endl;
|
||||
plan->unlinkNodes(toRemove);
|
||||
plan->unlinkNodes(toUnlink);
|
||||
}
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
|
@ -145,7 +160,7 @@ int triagens::aql::moveCalculationsUpRule (Optimizer* opt,
|
|||
ExecutionPlan* plan,
|
||||
Optimizer::PlanList& out,
|
||||
bool& keep) {
|
||||
std::vector<ExecutionNode*> nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::CALCULATION);
|
||||
std::vector<ExecutionNode*> nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::CALCULATION, true);
|
||||
|
||||
for (auto n : nodes) {
|
||||
auto nn = static_cast<CalculationNode*>(n);
|
||||
|
@ -159,6 +174,10 @@ int triagens::aql::moveCalculationsUpRule (Optimizer* opt,
|
|||
// (sorting is needed for intersection later)
|
||||
std::sort(neededVars.begin(), neededVars.end(), &Variable::Comparator);
|
||||
|
||||
for (auto it = neededVars.begin(); it != neededVars.end(); ++it) {
|
||||
std::cout << "VAR USED IN CALC: " << (*it)->name << "\n";
|
||||
}
|
||||
|
||||
std::vector<ExecutionNode*> stack;
|
||||
auto deps = n->getDependencies();
|
||||
|
||||
|
@ -170,6 +189,7 @@ int triagens::aql::moveCalculationsUpRule (Optimizer* opt,
|
|||
auto current = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
std::cout << "LOOKING AT NODE OF TYPE: " << current->getTypeString() << "\n";
|
||||
auto deps = current->getDependencies();
|
||||
|
||||
if (deps.size() != 1) {
|
||||
|
@ -225,17 +245,20 @@ int triagens::aql::moveCalculationsUpRule (Optimizer* opt,
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief remove CalculationNode(s) that are never needed
|
||||
/// this modifies an existing plan in place
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int triagens::aql::removeUnnecessaryCalculationsRule (Optimizer* opt,
|
||||
ExecutionPlan* plan,
|
||||
Optimizer::PlanList& out,
|
||||
bool& keep) {
|
||||
keep = true;
|
||||
std::vector<ExecutionNode*> nodes
|
||||
= plan->findNodesOfType(triagens::aql::ExecutionNode::CALCULATION);
|
||||
std::unordered_set<ExecutionNode*> toRemove;
|
||||
= plan->findNodesOfType(triagens::aql::ExecutionNode::CALCULATION, true);
|
||||
std::unordered_set<ExecutionNode*> toUnlink;
|
||||
for (auto n : nodes) {
|
||||
auto nn = static_cast<CalculationNode*>(n);
|
||||
|
||||
if (nn->expression()->canThrow()) {
|
||||
// If this node can throw, we must not optimize it away!
|
||||
continue;
|
||||
|
@ -249,20 +272,16 @@ int triagens::aql::removeUnnecessaryCalculationsRule (Optimizer* opt,
|
|||
// The variable whose value is calculated here is not used at
|
||||
// all further down the pipeline! We remove the whole
|
||||
// calculation node,
|
||||
toRemove.insert(n);
|
||||
toUnlink.insert(n);
|
||||
}
|
||||
}
|
||||
|
||||
if (! toRemove.empty()) {
|
||||
std::cout << "Removing " << toRemove.size() << " unnecessary "
|
||||
if (! toUnlink.empty()) {
|
||||
std::cout << "Removing " << toUnlink.size() << " unnecessary "
|
||||
"CalculationNodes..." << std::endl;
|
||||
plan->unlinkNodes(toRemove);
|
||||
out.push_back(plan);
|
||||
keep = false;
|
||||
}
|
||||
else {
|
||||
keep = true;
|
||||
plan->unlinkNodes(toUnlink);
|
||||
}
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
|
@ -460,7 +479,7 @@ int triagens::aql::useIndexRange (Optimizer* opt,
|
|||
bool& keep) {
|
||||
keep = true;
|
||||
std::vector<ExecutionNode*> nodes
|
||||
= plan->findNodesOfType(triagens::aql::ExecutionNode::FILTER);
|
||||
= plan->findNodesOfType(triagens::aql::ExecutionNode::FILTER, true);
|
||||
|
||||
for (auto n : nodes) {
|
||||
auto nn = static_cast<FilterNode*>(n);
|
||||
|
|
|
@ -61,6 +61,7 @@ Query::Query (TRI_vocbase_t* vocbase,
|
|||
_executor(nullptr),
|
||||
_queryString(queryString),
|
||||
_queryLength(queryLength),
|
||||
_queryJson(),
|
||||
_type(AQL_QUERY_READ),
|
||||
_bindParameters(bindParameters),
|
||||
_collections(vocbase),
|
||||
|
@ -71,6 +72,24 @@ Query::Query (TRI_vocbase_t* vocbase,
|
|||
_strings.reserve(32);
|
||||
}
|
||||
|
||||
Query::Query (TRI_vocbase_t* vocbase,
|
||||
triagens::basics::Json queryStruct,
|
||||
QueryType Type)
|
||||
: _vocbase(vocbase),
|
||||
_executor(nullptr),
|
||||
_queryString(nullptr),
|
||||
_queryLength(0),
|
||||
_queryJson(queryStruct),
|
||||
_type(Type),
|
||||
_bindParameters(nullptr),
|
||||
_collections(vocbase),
|
||||
_strings() {
|
||||
|
||||
TRI_ASSERT(_vocbase != nullptr);
|
||||
|
||||
_strings.reserve(32);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroys a query
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -174,24 +193,53 @@ void Query::registerError (int code,
|
|||
|
||||
QueryResult Query::execute () {
|
||||
try {
|
||||
ExecutionPlan* plan;
|
||||
Parser parser(this);
|
||||
parser.parse();
|
||||
|
||||
// put in bind parameters
|
||||
parser.ast()->injectBindParameters(_bindParameters);
|
||||
// optimize the ast
|
||||
parser.ast()->optimize();
|
||||
// std::cout << "AST: " << triagens::basics::JsonHelper::toString(parser.ast()->toJson(TRI_UNKNOWN_MEM_ZONE)) << "\n";
|
||||
|
||||
triagens::arango::AqlTransaction<triagens::arango::V8TransactionContext<true>> trx(_vocbase, _collections.collections());
|
||||
|
||||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return QueryResult(res, TRI_errno_string(res));
|
||||
if (_queryString != nullptr) {
|
||||
parser.parse();
|
||||
// put in bind parameters
|
||||
parser.ast()->injectBindParameters(_bindParameters);
|
||||
// optimize the ast
|
||||
parser.ast()->optimize();
|
||||
// std::cout << "AST: " << triagens::basics::JsonHelper::toString(parser.ast()->toJson(TRI_UNKNOWN_MEM_ZONE)) << "\n";
|
||||
}
|
||||
|
||||
auto plan = ExecutionPlan::instanciateFromAst(parser.ast());
|
||||
// create the transaction object, but do not start it yet
|
||||
AQL_TRANSACTION_V8 trx(_vocbase, _collections.collections());
|
||||
|
||||
if (_queryString != nullptr) {
|
||||
// we have an AST
|
||||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return QueryResult(res, TRI_errno_string(res));
|
||||
}
|
||||
|
||||
plan = ExecutionPlan::instanciateFromAst(parser.ast());
|
||||
if (plan == nullptr) {
|
||||
// oops
|
||||
return QueryResult(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// we have an execution plan in JSON format
|
||||
plan = ExecutionPlan::instanciateFromJson(parser.ast(), _queryJson);
|
||||
if (plan == nullptr) {
|
||||
// oops
|
||||
return QueryResult(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
// creating the plan may have produced some collections
|
||||
// we need to add them to the transaction now (otherwise the query will fail)
|
||||
trx.addCollections(_collections.collections());
|
||||
|
||||
int res = trx.begin();
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return QueryResult(res, TRI_errno_string(res));
|
||||
}
|
||||
}
|
||||
|
||||
// Run the query optimiser:
|
||||
triagens::aql::Optimizer opt;
|
||||
|
@ -333,10 +381,15 @@ char* Query::registerString (char const* p,
|
|||
return copy;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief register a string
|
||||
/// the string is freed when the query is destroyed
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
char* Query::registerString (std::string const& p,
|
||||
bool mustUnescape) {
|
||||
|
||||
return registerString (p.c_str(),p.length(), mustUnescape);
|
||||
return registerString(p.c_str(), p.length(), mustUnescape);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#define ARANGODB_AQL_QUERY_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/JsonHelper.h"
|
||||
#include "Aql/BindParameters.h"
|
||||
#include "Aql/Collections.h"
|
||||
#include "Aql/QueryResult.h"
|
||||
|
@ -45,6 +46,7 @@ namespace triagens {
|
|||
class Expression;
|
||||
struct Variable;
|
||||
struct AstNode;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -82,6 +84,10 @@ namespace triagens {
|
|||
size_t,
|
||||
struct TRI_json_s*);
|
||||
|
||||
Query (struct TRI_vocbase_s*,
|
||||
triagens::basics::Json queryStruct,
|
||||
QueryType Type);
|
||||
|
||||
~Query ();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -193,45 +199,14 @@ namespace triagens {
|
|||
size_t,
|
||||
bool);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief register a string
|
||||
/// the string is freed when the query is destroyed
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
char* registerString (std::string const&,
|
||||
bool);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief register an Expression
|
||||
/// the Expression is freed when the query is destroyed
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Expression* registerExp (Expression* x) {
|
||||
_expressions.push_back(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief register an AstNode
|
||||
/// the AstNode is freed when the query is destroyed
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* registerNode (AstNode* a) {
|
||||
_nodes.push_back(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief register a Variable
|
||||
/// the Variable is freed when the query is destroyed
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* registerVar (Variable* v) {
|
||||
_variables.push_back(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -262,6 +237,13 @@ namespace triagens {
|
|||
|
||||
size_t const _queryLength;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief query in a JSON structure
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
triagens::basics::Json const _queryJson;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief type of the query
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -285,12 +267,6 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::vector<char const*> _strings;
|
||||
|
||||
std::vector<Expression*> _expressions;
|
||||
|
||||
std::vector<AstNode*> _nodes;
|
||||
|
||||
std::vector<Variable*> _variables;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -58,6 +58,16 @@ namespace triagens {
|
|||
// e.g. 10..1
|
||||
return _low - _high + 1;
|
||||
}
|
||||
|
||||
int64_t at (size_t position) const {
|
||||
if (_low <= _high) {
|
||||
// e.g. 1..1, 1..10 etc.
|
||||
return _low + static_cast<int64_t>(position);
|
||||
}
|
||||
|
||||
// e.g. 10..1
|
||||
return _low - static_cast<int64_t>(position);
|
||||
}
|
||||
|
||||
int64_t const _low;
|
||||
int64_t const _high;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "Aql/Variable.h"
|
||||
#include "Basics/JsonHelper.h"
|
||||
|
||||
using namespace triagens::aql;
|
||||
using namespace triagens::basics;
|
||||
using Json = triagens::basics::Json;
|
||||
|
@ -45,16 +46,11 @@ Variable::Variable (std::string const& name,
|
|||
VariableId id)
|
||||
: name(name),
|
||||
value(nullptr),
|
||||
id(id),
|
||||
refCount(0) {
|
||||
id(id) {
|
||||
}
|
||||
|
||||
|
||||
Variable::Variable (Json const& json)
|
||||
: name(JsonHelper::getStringValue(json.json(), "name", "")),
|
||||
value(nullptr),
|
||||
id(JsonHelper::getNumericValue<double>(json.json(), "id", 0.0)),
|
||||
refCount(0) {
|
||||
: Variable(JsonHelper::getStringValue(json.json(), "name", ""), JsonHelper::getNumericValue<VariableId>(json.json(), "id", 0)) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -53,7 +53,6 @@ namespace triagens {
|
|||
|
||||
Variable (basics::Json const& json);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroy the variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -81,31 +80,6 @@ namespace triagens {
|
|||
return value;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not the variable is reference counted
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline bool isReferenceCounted () const {
|
||||
return (refCount > 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief increase the variable's reference counter
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline void increaseReferenceCount () {
|
||||
++refCount;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief decrease the variable's reference counter
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline void decreaseReferenceCount () {
|
||||
TRI_ASSERT(refCount > 0);
|
||||
--refCount;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not the variable is user-defined
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -162,12 +136,6 @@ namespace triagens {
|
|||
|
||||
VariableId const id;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief number of times the variable is used in the AST
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint32_t refCount;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -128,6 +128,32 @@ Variable* VariableGenerator::createVariable (std::string const& name,
|
|||
return variable;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate a variable from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* VariableGenerator::createVariable (triagens::basics::Json const& json) {
|
||||
auto variable = new Variable(json);
|
||||
|
||||
auto existing = getVariable(variable->id);
|
||||
if (existing != nullptr) {
|
||||
// variable already existed.
|
||||
delete variable;
|
||||
return existing;
|
||||
}
|
||||
|
||||
try {
|
||||
_variables.insert(std::make_pair(variable->id, variable));
|
||||
}
|
||||
catch (...) {
|
||||
// prevent memleak
|
||||
delete variable;
|
||||
throw;
|
||||
}
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate a temporary variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#define ARANGODB_AQL_VARIABLE_GENERATOR_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/JsonHelper.h"
|
||||
#include "Aql/Variable.h"
|
||||
|
||||
namespace triagens {
|
||||
|
@ -86,6 +87,12 @@ namespace triagens {
|
|||
Variable* createVariable (std::string const&,
|
||||
bool);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate a variable from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* createVariable (triagens::basics::Json const&);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate a temporary variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -81,6 +81,12 @@ namespace triagens {
|
|||
~AqlTransaction () {
|
||||
}
|
||||
|
||||
void addCollections (std::map<std::string, triagens::aql::Collection*>* collections) {
|
||||
for (auto it = collections->begin(); it != collections->end(); ++it) {
|
||||
processCollection((*it).second);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief add a collection to the transaction
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -861,6 +861,117 @@ static v8::Handle<v8::Value> JS_ParseAql (v8::Arguments const& argv) {
|
|||
/// @brief executes an AQL query
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static v8::Handle<v8::Value> JS_ExecuteAqlJson (v8::Arguments const& argv) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
TRI_vocbase_t* vocbase = GetContextVocBase();
|
||||
|
||||
if (vocbase == nullptr) {
|
||||
TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (argv.Length() < 1 || argv.Length() > 2) {
|
||||
TRI_V8_EXCEPTION_USAGE(scope, "AQL_EXECUTEJSON(<queryjson>, <options>)");
|
||||
}
|
||||
|
||||
// return number of total records in cursor?
|
||||
bool doCount = false;
|
||||
|
||||
// maximum number of results to return at once
|
||||
size_t batchSize = SIZE_MAX;
|
||||
|
||||
// ttl for cursor
|
||||
double ttl = 0.0;
|
||||
|
||||
// extra return values
|
||||
TRI_json_t* extra = nullptr;
|
||||
|
||||
if (! argv[0]->IsObject()) {
|
||||
TRI_V8_TYPE_ERROR(scope, "expecting object for <queryjson>");
|
||||
}
|
||||
TRI_json_t* queryjson = TRI_ObjectToJson(argv[0]);
|
||||
|
||||
if (argv.Length() > 1) {
|
||||
// we have options! yikes!
|
||||
if (! argv[1]->IsUndefined() && ! argv[1]->IsObject()) {
|
||||
TRI_V8_TYPE_ERROR(scope, "expecting object for <options>");
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> argValue = v8::Handle<v8::Object>::Cast(argv[1]);
|
||||
|
||||
v8::Handle<v8::String> optionName = v8::String::New("batchSize");
|
||||
if (argValue->Has(optionName)) {
|
||||
batchSize = static_cast<decltype(batchSize)>(TRI_ObjectToInt64(argValue->Get(optionName)));
|
||||
if (batchSize == 0) {
|
||||
TRI_V8_TYPE_ERROR(scope, "expecting non-zero value for <batchSize>");
|
||||
// well, this makes no sense
|
||||
}
|
||||
}
|
||||
|
||||
optionName = v8::String::New("count");
|
||||
if (argValue->Has(optionName)) {
|
||||
doCount = TRI_ObjectToBoolean(argValue->Get(optionName));
|
||||
}
|
||||
|
||||
optionName = v8::String::New("ttl");
|
||||
if (argValue->Has(optionName)) {
|
||||
ttl = TRI_ObjectToBoolean(argValue->Get(optionName));
|
||||
ttl = (ttl <= 0.0 ? 30.0 : ttl);
|
||||
}
|
||||
|
||||
optionName = v8::String::New("extra");
|
||||
if (argValue->Has(optionName)) {
|
||||
extra = TRI_ObjectToJson(argValue->Get(optionName));
|
||||
}
|
||||
}
|
||||
|
||||
triagens::aql::Query query(vocbase, Json(TRI_UNKNOWN_MEM_ZONE, queryjson), triagens::aql::AQL_QUERY_READ);
|
||||
|
||||
auto queryResult = query.execute();
|
||||
|
||||
if (queryResult.code != TRI_ERROR_NO_ERROR) {
|
||||
TRI_V8_EXCEPTION_FULL(scope, queryResult.code, queryResult.details);
|
||||
}
|
||||
|
||||
if (TRI_LengthListJson(queryResult.json) <= batchSize) {
|
||||
// return the array value as it is. this is a performance optimisation
|
||||
v8::Handle<v8::Object> result = v8::Object::New();
|
||||
if (queryResult.json != nullptr) {
|
||||
result->Set(TRI_V8_STRING("json"), TRI_ObjectJson(queryResult.json));
|
||||
}
|
||||
return scope.Close(result);
|
||||
}
|
||||
|
||||
TRI_general_cursor_result_t* cursorResult = TRI_CreateResultAql(queryResult.json);
|
||||
|
||||
if (cursorResult == nullptr){
|
||||
TRI_V8_EXCEPTION_MEMORY(scope);
|
||||
}
|
||||
|
||||
queryResult.json = nullptr;
|
||||
|
||||
TRI_general_cursor_t* cursor = TRI_CreateGeneralCursor(vocbase, cursorResult, doCount,
|
||||
batchSize, ttl, extra);
|
||||
|
||||
if (cursor == nullptr) {
|
||||
TRI_Free(TRI_UNKNOWN_MEM_ZONE, cursorResult);
|
||||
TRI_V8_EXCEPTION_MEMORY(scope);
|
||||
}
|
||||
TRI_ASSERT(cursor != nullptr);
|
||||
|
||||
v8::Handle<v8::Value> cursorObject = TRI_WrapGeneralCursor(cursor);
|
||||
|
||||
if (cursorObject.IsEmpty()) {
|
||||
TRI_V8_EXCEPTION_MEMORY(scope);
|
||||
}
|
||||
|
||||
return scope.Close(cursorObject);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief executes an AQL query
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static v8::Handle<v8::Value> JS_ExecuteAql (v8::Arguments const& argv) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
|
@ -981,6 +1092,7 @@ static v8::Handle<v8::Value> JS_ExecuteAql (v8::Arguments const& argv) {
|
|||
return scope.Close(cursorObject);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- AHUACATL
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -2560,6 +2672,7 @@ void TRI_InitV8VocBridge (v8::Handle<v8::Context> context,
|
|||
|
||||
// new AQL functions. not intended to be used directly by end users
|
||||
TRI_AddGlobalFunctionVocbase(context, "AQL_EXECUTE", JS_ExecuteAql, true);
|
||||
TRI_AddGlobalFunctionVocbase(context, "AQL_EXECUTEJSON", JS_ExecuteAqlJson, true);
|
||||
TRI_AddGlobalFunctionVocbase(context, "AQL_PARSE", JS_ParseAql, true);
|
||||
|
||||
TRI_InitV8replication(context, server, vocbase, loader, threadNumber, v8g);
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*global require,AQL_EXECUTEJSON */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief AQL user functions management
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014 ArangoDB 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 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var actions = require("org/arangodb/actions");
|
||||
|
||||
function post_api_executeJson (req, res) {
|
||||
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
|
||||
|
||||
if (json === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = AQL_EXECUTEJSON(json.tree, json.options);
|
||||
|
||||
actions.resultOk(req, res, actions.HTTP_OK, result);
|
||||
}
|
||||
|
||||
actions.defineHttp({
|
||||
url : "_api/aql2/execute-json",
|
||||
|
||||
callback : function (req, res) {
|
||||
try {
|
||||
switch (req.requestType) {
|
||||
case actions.POST:
|
||||
post_api_executeJson(req, res);
|
||||
break;
|
||||
|
||||
default:
|
||||
actions.resultUnsupported(req, res);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
actions.resultException(req, res, err, undefined, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
||||
// End:
|
|
@ -486,6 +486,8 @@ namespace triagens {
|
|||
: _zone(z), _json(j), _autofree(autofree) {
|
||||
}
|
||||
|
||||
explicit Json (TRI_json_t* j) = delete;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief "copy" constructor, note that in the AUTOFREE case this steals
|
||||
/// the structure from j to allow returning Json objects by value without
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import json
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
fn=''
|
||||
if len(sys.argv) > 1:
|
||||
fn = sys.argv[1]
|
||||
if len(fn) > 0:
|
||||
infile = open(fn, 'r')
|
||||
else:
|
||||
infile = sys.stdin
|
||||
|
||||
raw=infile.read()
|
||||
|
||||
#print raw
|
||||
instruct = json.loads(raw)
|
||||
|
||||
blacklist = ['typeID', 'vTypeID', 'varsUsedLater', 'varsValid']
|
||||
|
||||
def Filter(instruct, layer):
|
||||
|
||||
if type(instruct) == dict:
|
||||
out = {}
|
||||
#print "<- %s %d\n" % (instruct, layer)
|
||||
for oneKey in sorted(instruct): #.keys():
|
||||
if not oneKey in blacklist:
|
||||
if type(instruct[oneKey]) == dict:
|
||||
out[oneKey] = Filter(instruct[oneKey], layer + 1)
|
||||
elif type(instruct[oneKey]) == list:
|
||||
out[oneKey] = Filter(instruct[oneKey], layer + 1)
|
||||
else:
|
||||
out[oneKey] = instruct[oneKey]
|
||||
#else:
|
||||
#print "BLACKLIST %d\n" % (layer)
|
||||
#print "-> %s %d\n" % (out, layer)
|
||||
return out
|
||||
elif type(instruct) == list:
|
||||
out = []
|
||||
for item in instruct:
|
||||
if type(item) == dict:
|
||||
out.append(Filter(item, layer + 1))
|
||||
elif type(item) == list:
|
||||
out.append(Filter(item, layer + 1))
|
||||
else:
|
||||
out.append(item)
|
||||
return out
|
||||
else:
|
||||
return instruct
|
||||
|
||||
|
||||
|
||||
|
||||
filtered = Filter(instruct, 0)
|
||||
#print "---------"
|
||||
#print filtered
|
||||
# print json.dumps(instruct, indent=4, sort_keys=True)
|
||||
print yaml.safe_dump(filtered, default_flow_style=False)
|
Loading…
Reference in New Issue