diff --git a/arangod/Aql/Ast.cpp b/arangod/Aql/Ast.cpp index 0c0ed343bb..8fcc742c91 100644 --- a/arangod/Aql/Ast.cpp +++ b/arangod/Aql/Ast.cpp @@ -189,7 +189,7 @@ AstNode* Ast::createNodeFor (char const* variableName, } //////////////////////////////////////////////////////////////////////////////// -/// @brief create an AST let node +/// @brief create an AST let node, without an IF condition //////////////////////////////////////////////////////////////////////////////// AstNode* Ast::createNodeLet (char const* variableName, @@ -208,6 +208,27 @@ AstNode* Ast::createNodeLet (char const* variableName, return node; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief create an AST let node, with an IF condition +//////////////////////////////////////////////////////////////////////////////// + +AstNode* Ast::createNodeLet (char const* variableName, + AstNode const* expression, + AstNode const* condition) { + if (variableName == nullptr) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } + + AstNode* node = createNode(NODE_TYPE_LET); + + AstNode* variable = createNodeVariable(variableName, true); + node->addMember(variable); + node->addMember(expression); + node->addMember(condition); + + return node; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief create an AST filter node //////////////////////////////////////////////////////////////////////////////// @@ -1882,15 +1903,17 @@ AstNode* Ast::optimizeReference (AstNode* node) { AstNode* Ast::optimizeLet (AstNode* node) { TRI_ASSERT(node != nullptr); TRI_ASSERT(node->type == NODE_TYPE_LET); - TRI_ASSERT(node->numMembers() == 2); + TRI_ASSERT(node->numMembers() >= 2); AstNode* variable = node->getMember(0); AstNode* expression = node->getMember(1); + + bool const hasCondition = (node->numMembers() > 2); auto v = static_cast(variable->getData()); TRI_ASSERT(v != nullptr); - if (expression->isConstant()) { + if (! hasCondition && 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. diff --git a/arangod/Aql/Ast.h b/arangod/Aql/Ast.h index 6ec46a0c73..047b61b4d8 100644 --- a/arangod/Aql/Ast.h +++ b/arangod/Aql/Ast.h @@ -213,13 +213,21 @@ namespace triagens { AstNode const*); //////////////////////////////////////////////////////////////////////////////// -/// @brief create an AST let node +/// @brief create an AST let node, without an IF condition //////////////////////////////////////////////////////////////////////////////// AstNode* createNodeLet (char const*, AstNode const*, bool); +//////////////////////////////////////////////////////////////////////////////// +/// @brief create an AST let node, with an IF condition +//////////////////////////////////////////////////////////////////////////////// + + AstNode* createNodeLet (char const*, + AstNode const*, + AstNode const*); + //////////////////////////////////////////////////////////////////////////////// /// @brief create an AST filter node //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 637ae3042f..0e661def53 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -2124,7 +2124,7 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) { AqlItemBlock* cur = _buffer.front(); // get the thing we are looping over - AqlValue inVarReg = cur->getValue(_pos, _inVarRegId); + AqlValue inVarReg = cur->getValueReference(_pos, _inVarRegId); size_t sizeInVar = 0; // to shut up compiler // get the size of the thing we are looping over @@ -2132,7 +2132,7 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) { switch (inVarReg._type) { case AqlValue::JSON: { if (! inVarReg._json->isArray()) { - throwListExpectedException(); + throwArrayExpectedException(); } sizeInVar = inVarReg._json->size(); break; @@ -2160,11 +2160,11 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) { } case AqlValue::SHAPED: { - throwListExpectedException(); + throwArrayExpectedException(); } case AqlValue::EMPTY: { - throwListExpectedException(); + throwArrayExpectedException(); } } @@ -2253,7 +2253,7 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) { switch (inVarReg._type) { case AqlValue::JSON: { if (! inVarReg._json->isArray()) { - throwListExpectedException(); + throwArrayExpectedException(); } sizeInVar = inVarReg._json->size(); break; @@ -2278,7 +2278,7 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) { case AqlValue::SHAPED: case AqlValue::EMPTY: { - throwListExpectedException(); + throwArrayExpectedException(); } } @@ -2305,7 +2305,7 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) { /// @brief create an AqlValue from the inVariable using the current _index //////////////////////////////////////////////////////////////////////////////// -AqlValue EnumerateListBlock::getAqlValue (AqlValue inVarReg) { +AqlValue EnumerateListBlock::getAqlValue (AqlValue const& inVarReg) { switch (inVarReg._type) { case AqlValue::JSON: { // FIXME: is this correct? What if the copy works, but the @@ -2333,14 +2333,14 @@ AqlValue EnumerateListBlock::getAqlValue (AqlValue inVarReg) { } } - throwListExpectedException(); + throwArrayExpectedException(); TRI_ASSERT(false); // cannot be reached. function call above will always throw an exception return AqlValue(); } -void EnumerateListBlock::throwListExpectedException () { +void EnumerateListBlock::throwArrayExpectedException () { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_ARRAY_EXPECTED, TRI_errno_string(TRI_ERROR_QUERY_ARRAY_EXPECTED) + std::string(" as operand to FOR loop")); @@ -2359,6 +2359,8 @@ CalculationBlock::CalculationBlock (ExecutionEngine* engine, _outReg(ExecutionNode::MaxRegisterId) { std::unordered_set inVars = _expression->variables(); + _inVars.reserve(inVars.size()); + _inRegs.reserve(inVars.size()); for (auto it = inVars.begin(); it != inVars.end(); ++it) { _inVars.push_back(*it); @@ -2381,6 +2383,13 @@ CalculationBlock::CalculationBlock (ExecutionEngine* engine, TRI_ASSERT(it3 != en->getRegisterPlan()->varInfo.end()); _outReg = it3->second.registerId; TRI_ASSERT(_outReg < ExecutionNode::MaxRegisterId); + + if (en->_conditionVariable != nullptr) { + auto it = en->getRegisterPlan()->varInfo.find(en->_conditionVariable->id); + TRI_ASSERT(it != en->getRegisterPlan()->varInfo.end()); + _conditionReg = it->second.registerId; + TRI_ASSERT(_conditionReg < ExecutionNode::MaxRegisterId); + } } CalculationBlock::~CalculationBlock () { @@ -2390,6 +2399,67 @@ int CalculationBlock::initialize () { return ExecutionBlock::initialize(); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief fill the target register in the item block with a reference to +/// another variable +//////////////////////////////////////////////////////////////////////////////// + +void CalculationBlock::fillBlockWithReference (AqlItemBlock* result) { + result->setDocumentCollection(_outReg, result->getDocumentCollection(_inRegs[0])); + + size_t const n = result->size(); + for (size_t i = 0; i < n; i++) { + // need not clone to avoid a copy, the AqlItemBlock's hash takes + // care of correct freeing: + AqlValue a = result->getValueReference(i, _inRegs[0]); + try { + result->setValue(i, _outReg, a); + } + catch (...) { + a.destroy(); + throw; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief shared code for executing a simple or a V8 expression +//////////////////////////////////////////////////////////////////////////////// + +void CalculationBlock::executeExpression (AqlItemBlock* result) { + std::vector& data(result->getData()); + std::vector docColls(result->getDocumentCollections()); + + RegisterId nrRegs = result->getNrRegs(); + result->setDocumentCollection(_outReg, nullptr); + + bool const hasCondition = (static_cast(_exeNode)->_conditionVariable != nullptr); + + size_t const n = result->size(); + for (size_t i = 0; i < n; i++) { + // check the condition variable (if any) + if (hasCondition) { + AqlValue conditionResult = result->getValueReference(i, _conditionReg); + + if (! conditionResult.isTrue()) { + result->setValue(i, _outReg, AqlValue(new Json(Json::Null))); + continue; + } + } + + // execute the expression + TRI_document_collection_t const* myCollection = nullptr; + AqlValue a = _expression->execute(_trx, docColls, data, nrRegs * i, _inVars, _inRegs, &myCollection); + try { + result->setValue(i, _outReg, a); + } + catch (...) { + a.destroy(); + throw; + } + } +} + //////////////////////////////////////////////////////////////////////////////// /// @brief doEvaluation, private helper to do the work //////////////////////////////////////////////////////////////////////////////// @@ -2397,39 +2467,26 @@ int CalculationBlock::initialize () { void CalculationBlock::doEvaluation (AqlItemBlock* result) { TRI_ASSERT(result != nullptr); - size_t const n = result->size(); if (_isReference) { - // the expression is a reference to a variable only. + // the calculation is a reference to a variable only. // no need to execute the expression at all - result->setDocumentCollection(_outReg, result->getDocumentCollection(_inRegs[0])); + fillBlockWithReference(result); + return; + } - for (size_t i = 0; i < n; i++) { - // need not clone to avoid a copy, the AqlItemBlock's hash takes - // care of correct freeing: - AqlValue a = result->getValue(i, _inRegs[0]); - try { - result->setValue(i, _outReg, a); - } - catch (...) { - a.destroy(); - throw; - } - } + // non-reference expression + + TRI_ASSERT(_expression != nullptr); + + if (! _expression->isV8()) { + // an expression that does not require V8 + executeExpression(result); } else { - vector& data(result->getData()); - vector docColls(result->getDocumentCollections()); - - RegisterId nrRegs = result->getNrRegs(); - result->setDocumentCollection(_outReg, nullptr); - - TRI_ASSERT(_expression != nullptr); - // must have a V8 context here to protect Expression::execute() - auto engine = _engine; triagens::basics::ScopeGuard guard{ - [&engine]() -> void { - engine->getQuery()->enterContext(); + [&]() -> void { + _engine->getQuery()->enterContext(); }, [&]() -> void { // must invalidate the expression now as we might be called from @@ -2437,24 +2494,17 @@ void CalculationBlock::doEvaluation (AqlItemBlock* result) { if (ExecutionEngine::isRunningInCluster()) { _expression->invalidate(); } - engine->getQuery()->exitContext(); + _engine->getQuery()->exitContext(); } }; + ISOLATE; v8::HandleScope scope(isolate); // do not delete this! - for (size_t i = 0; i < n; i++) { - // need to execute the expression - TRI_document_collection_t const* myCollection = nullptr; - AqlValue a = _expression->execute(_trx, docColls, data, nrRegs * i, _inVars, _inRegs, &myCollection); - try { - result->setValue(i, _outReg, a); - } - catch (...) { - a.destroy(); - throw; - } - } + // do not merge the following function call with the same function call above! + // the V8 expression execution must happen in the scope that contains + // the V8 handle scope and the scope guard + executeExpression(result); } } diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index 6a10caf190..11dc797129 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -831,13 +831,13 @@ namespace triagens { /// @brief create an AqlValue from the inVariable using the current _index //////////////////////////////////////////////////////////////////////////////// - AqlValue getAqlValue (AqlValue inVarReg); + AqlValue getAqlValue (AqlValue const&); //////////////////////////////////////////////////////////////////////////////// -/// @brief throws a "list expected" exception +/// @brief throws an "array expected" exception //////////////////////////////////////////////////////////////////////////////// - void throwListExpectedException (); + void throwArrayExpectedException (); // ----------------------------------------------------------------------------- // --SECTION-- private variables @@ -898,13 +898,26 @@ namespace triagens { int initialize () override; + private: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief fill the target register in the item block with a reference to +/// another variable +//////////////////////////////////////////////////////////////////////////////// + + void fillBlockWithReference (AqlItemBlock*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief shared code for executing a simple or a V8 expression +//////////////////////////////////////////////////////////////////////////////// + + void executeExpression (AqlItemBlock*); + //////////////////////////////////////////////////////////////////////////////// /// @brief doEvaluation, private helper to do the work //////////////////////////////////////////////////////////////////////////////// - private: - - void doEvaluation (AqlItemBlock* result); + void doEvaluation (AqlItemBlock*); public: @@ -941,6 +954,12 @@ namespace triagens { RegisterId _outReg; +//////////////////////////////////////////////////////////////////////////////// +/// @brief condition variable register +//////////////////////////////////////////////////////////////////////////////// + + RegisterId _conditionReg; + //////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the expression is a simple variable reference //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index 1eeef49d21..472d083789 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -1645,6 +1645,7 @@ double LimitNode::estimateCost (size_t& nrItems) const { CalculationNode::CalculationNode (ExecutionPlan* plan, triagens::basics::Json const& base) : ExecutionNode(plan, base), + _conditionVariable(varFromJson(plan->getAst(), base, "conditionVariable")), _outVariable(varFromJson(plan->getAst(), base, "outVariable")), _expression(new Expression(plan->getAst(), base)) { } @@ -1666,6 +1667,10 @@ void CalculationNode::toJsonHelper (triagens::basics::Json& nodes, ("outVariable", _outVariable->toJson()) ("canThrow", triagens::basics::Json(_expression->canThrow())); + if (_conditionVariable != nullptr) { + json("conditionVariable", _conditionVariable->toJson()); + } + // And add it: nodes(json); } @@ -1673,13 +1678,17 @@ void CalculationNode::toJsonHelper (triagens::basics::Json& nodes, ExecutionNode* CalculationNode::clone (ExecutionPlan* plan, bool withDependencies, bool withProperties) const { + auto conditionVariable = _conditionVariable; auto outVariable = _outVariable; if (withProperties) { + if (_conditionVariable != nullptr) { + conditionVariable = plan->getAst()->variables()->createVariable(conditionVariable); + } outVariable = plan->getAst()->variables()->createVariable(outVariable); } - auto c = new CalculationNode(plan, _id, _expression->clone(), - outVariable); + + auto c = new CalculationNode(plan, _id, _expression->clone(), conditionVariable, outVariable); CloneHelper(c, plan, withDependencies, withProperties); diff --git a/arangod/Aql/ExecutionNode.h b/arangod/Aql/ExecutionNode.h index c59911c9f2..d0a151fe3c 100644 --- a/arangod/Aql/ExecutionNode.h +++ b/arangod/Aql/ExecutionNode.h @@ -1398,8 +1398,10 @@ namespace triagens { CalculationNode (ExecutionPlan* plan, size_t id, Expression* expr, + Variable const* conditionVariable, Variable const* outVariable) : ExecutionNode(plan, id), + _conditionVariable(conditionVariable), _outVariable(outVariable), _expression(expr) { @@ -1407,6 +1409,17 @@ namespace triagens { TRI_ASSERT(_outVariable != nullptr); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructor +//////////////////////////////////////////////////////////////////////////////// + + CalculationNode (ExecutionPlan* plan, + size_t id, + Expression* expr, + Variable const* outVariable) + : CalculationNode(plan, id, expr, nullptr, outVariable) { + } + CalculationNode (ExecutionPlan*, triagens::basics::Json const& base); //////////////////////////////////////////////////////////////////////////////// @@ -1477,6 +1490,11 @@ namespace triagens { for (auto vv : vars) { v.emplace_back(vv); } + + if (_conditionVariable != nullptr) { + v.emplace_back(_conditionVariable); + } + return v; } @@ -1504,6 +1522,12 @@ namespace triagens { private: +//////////////////////////////////////////////////////////////////////////////// +/// @brief an optional condition variable for the calculation +//////////////////////////////////////////////////////////////////////////////// + + Variable const* _conditionVariable; + //////////////////////////////////////////////////////////////////////////////// /// @brief output variable to write to //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/ExecutionPlan.cpp b/arangod/Aql/ExecutionPlan.cpp index 94a5ae84ad..641fefc328 100644 --- a/arangod/Aql/ExecutionPlan.cpp +++ b/arangod/Aql/ExecutionPlan.cpp @@ -419,11 +419,22 @@ ExecutionNode* ExecutionPlan::fromNodeFilter (ExecutionNode* previous, ExecutionNode* ExecutionPlan::fromNodeLet (ExecutionNode* previous, AstNode const* node) { TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_LET); - TRI_ASSERT(node->numMembers() == 2); + TRI_ASSERT(node->numMembers() >= 2); AstNode const* variable = node->getMember(0); AstNode const* expression = node->getMember(1); + Variable const* conditionVariable = nullptr; + + if (node->numMembers() > 2) { + // a LET with an IF condition + auto condition = createTemporaryCalculation(node->getMember(2)); + condition->addDependency(previous); + previous = condition; + + conditionVariable = condition->outVariable(); + } + auto v = static_cast(variable->getData()); ExecutionNode* en = nullptr; @@ -464,11 +475,11 @@ ExecutionNode* ExecutionPlan::fromNodeLet (ExecutionNode* previous, // otherwise fall-through to normal behavior } - // operand is some misc expression, including references to other variables + // operand is some misc expression, potentially including references to other variables auto expr = new Expression(_ast, const_cast(expression)); try { - en = registerNode(new CalculationNode(this, nextId(), expr, v)); + en = registerNode(new CalculationNode(this, nextId(), expr, conditionVariable, v)); } catch (...) { // prevent memleak diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index af7a9dbc8a..7f20eb84e7 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -142,7 +142,7 @@ AqlValue Expression::execute (triagens::arango::AqlTransaction* trx, ISOLATE; // Dump the expression in question // std::cout << triagens::basics::Json(TRI_UNKNOWN_MEM_ZONE, _node->toJson(TRI_UNKNOWN_MEM_ZONE, true)).toString()<< "\n"; - return _func->execute(isolate, _ast->query(), trx, _attributes, docColls, argv, startPos, vars, regs); + return _func->execute(isolate, _ast->query(), trx, docColls, argv, startPos, vars, regs); } catch (triagens::arango::Exception& ex) { if (_ast->query()->verboseErrors()) { @@ -314,9 +314,10 @@ void Expression::analyzeExpression () { _attributes = std::move(Ast::getReferencedAttributes(_node, isSafeForOptimization)); if (! isSafeForOptimization) { - // unfortunately there are not only top-level attribute accesses but - // also other accesses, e.g. the index values or the whole value _attributes.clear(); + // unfortunately there are not only top-level attribute accesses but + // also other accesses, e.g. the index values or accesses of the whole value. + // for example, we cannot optimize LET x = a +1 or LET x = a[0], but LET x = a._key } } } @@ -345,6 +346,12 @@ void Expression::buildExpression () { else if (_type == V8) { // generate a V8 expression _func = _executor->generateExpression(_node); + + // optimizations for the generated function + if (_func != nullptr && ! _attributes.empty()) { + // pass which variables do not need to be fully constructed + _func->setAttributeRestrictions(_attributes); + } } _built = true; diff --git a/arangod/Aql/Expression.h b/arangod/Aql/Expression.h index 7ca027b578..bf7054b434 100644 --- a/arangod/Aql/Expression.h +++ b/arangod/Aql/Expression.h @@ -59,7 +59,7 @@ namespace triagens { class Expression { - enum ExpressionType { + enum ExpressionType : uint32_t { UNPROCESSED, JSON, V8, @@ -173,13 +173,35 @@ namespace triagens { /// @brief check whether this is a simple expression //////////////////////////////////////////////////////////////////////////////// - bool isSimple () { + inline bool isSimple () { if (_type == UNPROCESSED) { analyzeExpression(); } return _type == SIMPLE; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief check whether this is a JSON expression +//////////////////////////////////////////////////////////////////////////////// + + inline bool isJson () { + if (_type == UNPROCESSED) { + analyzeExpression(); + } + return _type == JSON; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief check whether this is a V8 expression +//////////////////////////////////////////////////////////////////////////////// + + inline bool isV8 () { + if (_type == UNPROCESSED) { + analyzeExpression(); + } + return _type == V8; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief check whether this is an attribute access of any degree (e.g. a.b, /// a.b.c, ...) @@ -353,4 +375,3 @@ namespace triagens { // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: - diff --git a/arangod/Aql/V8Expression.cpp b/arangod/Aql/V8Expression.cpp index ad7f966638..6cb7a32b8d 100644 --- a/arangod/Aql/V8Expression.cpp +++ b/arangod/Aql/V8Expression.cpp @@ -33,6 +33,7 @@ #include "Aql/Variable.h" #include "Basics/json.h" #include "V8/v8-conv.h" +#include "V8/v8-utils.h" using namespace triagens::aql; @@ -71,7 +72,6 @@ V8Expression::~V8Expression () { AqlValue V8Expression::execute (v8::Isolate* isolate, Query* query, triagens::arango::AqlTransaction* trx, - std::unordered_map> const& attributes, std::vector& docColls, std::vector& argv, size_t startPos, @@ -80,7 +80,7 @@ AqlValue V8Expression::execute (v8::Isolate* isolate, size_t const n = vars.size(); TRI_ASSERT_EXPENSIVE(regs.size() == n); // assert same vector length - bool const attributesPresent = ! attributes.empty(); + bool const hasRestrictions = ! _attributeRestrictions.empty(); v8::Handle values = v8::Object::New(isolate); @@ -90,11 +90,11 @@ AqlValue V8Expression::execute (v8::Isolate* isolate, TRI_ASSERT_EXPENSIVE(! argv[reg].isEmpty()); - if (attributesPresent && argv[startPos + reg]._type == AqlValue::JSON) { + if (hasRestrictions && argv[startPos + reg]._type == AqlValue::JSON) { // check if we can get away with constructing a partial JSON object - auto it = attributes.find(vars[i]); + auto it = _attributeRestrictions.find(vars[i]); - if (it != attributes.end()) { + if (it != _attributeRestrictions.end()) { // build a partial object values->ForceSet(TRI_V8_STD_STRING(varname), argv[startPos + reg].toV8Partial(isolate, trx, (*it).second, docColls[reg])); continue; @@ -121,11 +121,10 @@ AqlValue V8Expression::execute (v8::Isolate* isolate, v8::TryCatch tryCatch; auto func = v8::Local::New(isolate, _func); - v8::Handle result = func->Call(func, 1, args); v8g->_query = old; - + Executor::HandleV8Error(tryCatch, result); // no exception was thrown if we get here @@ -138,6 +137,7 @@ AqlValue V8Expression::execute (v8::Isolate* isolate, else { // expression had a result. convert it to JSON json = TRI_ObjectToJson(isolate, result); + // json = TRI_SimplifiedObjectToJson(isolate, result); } if (json == nullptr) { @@ -162,4 +162,4 @@ AqlValue V8Expression::execute (v8::Isolate* isolate, // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" -// End: +// End diff --git a/arangod/Aql/V8Expression.h b/arangod/Aql/V8Expression.h index 8906299cda..20b1d4b93d 100644 --- a/arangod/Aql/V8Expression.h +++ b/arangod/Aql/V8Expression.h @@ -68,6 +68,18 @@ namespace triagens { // --SECTION-- public methods // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief sets attribute restrictions. these prevent input variables to be +/// fully constructed as V8 objects (which can be very expensive), but limits +/// the objects to the actually used attributes only. +/// For example, the expression LET x = a.value + 1 will not build the full +/// object for "a", but only its "value" attribute +//////////////////////////////////////////////////////////////////////////////// + + void setAttributeRestrictions (std::unordered_map> const& attributeRestrictions) { + _attributeRestrictions = attributeRestrictions; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief execute the expression //////////////////////////////////////////////////////////////////////////////// @@ -75,7 +87,6 @@ namespace triagens { AqlValue execute (v8::Isolate* isolate, Query* query, triagens::arango::AqlTransaction*, - std::unordered_map> const&, std::vector&, std::vector&, size_t, @@ -98,6 +109,12 @@ namespace triagens { v8::Persistent _func; +//////////////////////////////////////////////////////////////////////////////// +/// @brief restrictions for creating the input values +//////////////////////////////////////////////////////////////////////////////// + + std::unordered_map> _attributeRestrictions; + }; }