1
0
Fork 0

Merge branch 'aql2' of ssh://github.com/triAGENS/ArangoDB into aql2

Conflicts:
	arangod/Aql/OptimizerRules.cpp
This commit is contained in:
James 2014-08-21 12:53:05 +02:00
commit aa03c16be0
29 changed files with 1247 additions and 691 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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--\\|/// @\\}\\)"

View File

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

View File

@ -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--\\|/// @\\}\\)"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)) {
}
////////////////////////////////////////////////////////////////////////////////

View File

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

View File

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

View File

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

View File

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

View File

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

73
js/actions/api-aql2.js Normal file
View File

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

View File

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

59
scripts/jsonQueryToYaml Executable file
View File

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