1
0
Fork 0

preparation for conditions

This commit is contained in:
Jan Steemann 2015-01-25 10:40:13 +01:00
parent 8580d49aec
commit c49986d0e8
11 changed files with 267 additions and 78 deletions

View File

@ -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, AstNode* Ast::createNodeLet (char const* variableName,
@ -208,6 +208,27 @@ AstNode* Ast::createNodeLet (char const* variableName,
return node; 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 /// @brief create an AST filter node
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1882,15 +1903,17 @@ AstNode* Ast::optimizeReference (AstNode* node) {
AstNode* Ast::optimizeLet (AstNode* node) { AstNode* Ast::optimizeLet (AstNode* node) {
TRI_ASSERT(node != nullptr); TRI_ASSERT(node != nullptr);
TRI_ASSERT(node->type == NODE_TYPE_LET); TRI_ASSERT(node->type == NODE_TYPE_LET);
TRI_ASSERT(node->numMembers() == 2); TRI_ASSERT(node->numMembers() >= 2);
AstNode* variable = node->getMember(0); AstNode* variable = node->getMember(0);
AstNode* expression = node->getMember(1); AstNode* expression = node->getMember(1);
bool const hasCondition = (node->numMembers() > 2);
auto v = static_cast<Variable*>(variable->getData()); auto v = static_cast<Variable*>(variable->getData());
TRI_ASSERT(v != nullptr); TRI_ASSERT(v != nullptr);
if (expression->isConstant()) { if (! hasCondition && expression->isConstant()) {
// if the expression assigned to the LET variable is constant, we'll store // if the expression assigned to the LET variable is constant, we'll store
// a pointer to the const value in the variable // a pointer to the const value in the variable
// further optimizations can then use this pointer and optimize further, e.g. // further optimizations can then use this pointer and optimize further, e.g.

View File

@ -213,13 +213,21 @@ namespace triagens {
AstNode const*); AstNode const*);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST let node /// @brief create an AST let node, without an IF condition
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AstNode* createNodeLet (char const*, AstNode* createNodeLet (char const*,
AstNode const*, AstNode const*,
bool); 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 /// @brief create an AST filter node
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -2124,7 +2124,7 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) {
AqlItemBlock* cur = _buffer.front(); AqlItemBlock* cur = _buffer.front();
// get the thing we are looping over // 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 size_t sizeInVar = 0; // to shut up compiler
// get the size of the thing we are looping over // 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) { switch (inVarReg._type) {
case AqlValue::JSON: { case AqlValue::JSON: {
if (! inVarReg._json->isArray()) { if (! inVarReg._json->isArray()) {
throwListExpectedException(); throwArrayExpectedException();
} }
sizeInVar = inVarReg._json->size(); sizeInVar = inVarReg._json->size();
break; break;
@ -2160,11 +2160,11 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) {
} }
case AqlValue::SHAPED: { case AqlValue::SHAPED: {
throwListExpectedException(); throwArrayExpectedException();
} }
case AqlValue::EMPTY: { case AqlValue::EMPTY: {
throwListExpectedException(); throwArrayExpectedException();
} }
} }
@ -2253,7 +2253,7 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) {
switch (inVarReg._type) { switch (inVarReg._type) {
case AqlValue::JSON: { case AqlValue::JSON: {
if (! inVarReg._json->isArray()) { if (! inVarReg._json->isArray()) {
throwListExpectedException(); throwArrayExpectedException();
} }
sizeInVar = inVarReg._json->size(); sizeInVar = inVarReg._json->size();
break; break;
@ -2278,7 +2278,7 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) {
case AqlValue::SHAPED: case AqlValue::SHAPED:
case AqlValue::EMPTY: { 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 /// @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) { switch (inVarReg._type) {
case AqlValue::JSON: { case AqlValue::JSON: {
// FIXME: is this correct? What if the copy works, but the // 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); TRI_ASSERT(false);
// cannot be reached. function call above will always throw an exception // cannot be reached. function call above will always throw an exception
return AqlValue(); return AqlValue();
} }
void EnumerateListBlock::throwListExpectedException () { void EnumerateListBlock::throwArrayExpectedException () {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_ARRAY_EXPECTED, THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_ARRAY_EXPECTED,
TRI_errno_string(TRI_ERROR_QUERY_ARRAY_EXPECTED) + TRI_errno_string(TRI_ERROR_QUERY_ARRAY_EXPECTED) +
std::string(" as operand to FOR loop")); std::string(" as operand to FOR loop"));
@ -2359,6 +2359,8 @@ CalculationBlock::CalculationBlock (ExecutionEngine* engine,
_outReg(ExecutionNode::MaxRegisterId) { _outReg(ExecutionNode::MaxRegisterId) {
std::unordered_set<Variable*> inVars = _expression->variables(); std::unordered_set<Variable*> inVars = _expression->variables();
_inVars.reserve(inVars.size());
_inRegs.reserve(inVars.size());
for (auto it = inVars.begin(); it != inVars.end(); ++it) { for (auto it = inVars.begin(); it != inVars.end(); ++it) {
_inVars.push_back(*it); _inVars.push_back(*it);
@ -2381,6 +2383,13 @@ CalculationBlock::CalculationBlock (ExecutionEngine* engine,
TRI_ASSERT(it3 != en->getRegisterPlan()->varInfo.end()); TRI_ASSERT(it3 != en->getRegisterPlan()->varInfo.end());
_outReg = it3->second.registerId; _outReg = it3->second.registerId;
TRI_ASSERT(_outReg < ExecutionNode::MaxRegisterId); 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 () { CalculationBlock::~CalculationBlock () {
@ -2390,6 +2399,67 @@ int CalculationBlock::initialize () {
return ExecutionBlock::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<AqlValue>& data(result->getData());
std::vector<TRI_document_collection_t const*> docColls(result->getDocumentCollections());
RegisterId nrRegs = result->getNrRegs();
result->setDocumentCollection(_outReg, nullptr);
bool const hasCondition = (static_cast<CalculationNode const*>(_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 /// @brief doEvaluation, private helper to do the work
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -2397,39 +2467,26 @@ int CalculationBlock::initialize () {
void CalculationBlock::doEvaluation (AqlItemBlock* result) { void CalculationBlock::doEvaluation (AqlItemBlock* result) {
TRI_ASSERT(result != nullptr); TRI_ASSERT(result != nullptr);
size_t const n = result->size();
if (_isReference) { 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 // 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++) { // non-reference expression
// need not clone to avoid a copy, the AqlItemBlock's hash takes
// care of correct freeing: TRI_ASSERT(_expression != nullptr);
AqlValue a = result->getValue(i, _inRegs[0]);
try { if (! _expression->isV8()) {
result->setValue(i, _outReg, a); // an expression that does not require V8
} executeExpression(result);
catch (...) {
a.destroy();
throw;
}
}
} }
else { else {
vector<AqlValue>& data(result->getData());
vector<TRI_document_collection_t const*> 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() // must have a V8 context here to protect Expression::execute()
auto engine = _engine;
triagens::basics::ScopeGuard guard{ triagens::basics::ScopeGuard guard{
[&engine]() -> void { [&]() -> void {
engine->getQuery()->enterContext(); _engine->getQuery()->enterContext();
}, },
[&]() -> void { [&]() -> void {
// must invalidate the expression now as we might be called from // must invalidate the expression now as we might be called from
@ -2437,24 +2494,17 @@ void CalculationBlock::doEvaluation (AqlItemBlock* result) {
if (ExecutionEngine::isRunningInCluster()) { if (ExecutionEngine::isRunningInCluster()) {
_expression->invalidate(); _expression->invalidate();
} }
engine->getQuery()->exitContext(); _engine->getQuery()->exitContext();
} }
}; };
ISOLATE; ISOLATE;
v8::HandleScope scope(isolate); // do not delete this! v8::HandleScope scope(isolate); // do not delete this!
for (size_t i = 0; i < n; i++) { // do not merge the following function call with the same function call above!
// need to execute the expression // the V8 expression execution must happen in the scope that contains
TRI_document_collection_t const* myCollection = nullptr; // the V8 handle scope and the scope guard
AqlValue a = _expression->execute(_trx, docColls, data, nrRegs * i, _inVars, _inRegs, &myCollection); executeExpression(result);
try {
result->setValue(i, _outReg, a);
}
catch (...) {
a.destroy();
throw;
}
}
} }
} }

View File

@ -831,13 +831,13 @@ namespace triagens {
/// @brief create an AqlValue from the inVariable using the current _index /// @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 // --SECTION-- private variables
@ -898,13 +898,26 @@ namespace triagens {
int initialize () override; 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 /// @brief doEvaluation, private helper to do the work
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
private: void doEvaluation (AqlItemBlock*);
void doEvaluation (AqlItemBlock* result);
public: public:
@ -941,6 +954,12 @@ namespace triagens {
RegisterId _outReg; RegisterId _outReg;
////////////////////////////////////////////////////////////////////////////////
/// @brief condition variable register
////////////////////////////////////////////////////////////////////////////////
RegisterId _conditionReg;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not the expression is a simple variable reference /// @brief whether or not the expression is a simple variable reference
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -1645,6 +1645,7 @@ double LimitNode::estimateCost (size_t& nrItems) const {
CalculationNode::CalculationNode (ExecutionPlan* plan, CalculationNode::CalculationNode (ExecutionPlan* plan,
triagens::basics::Json const& base) triagens::basics::Json const& base)
: ExecutionNode(plan, base), : ExecutionNode(plan, base),
_conditionVariable(varFromJson(plan->getAst(), base, "conditionVariable")),
_outVariable(varFromJson(plan->getAst(), base, "outVariable")), _outVariable(varFromJson(plan->getAst(), base, "outVariable")),
_expression(new Expression(plan->getAst(), base)) { _expression(new Expression(plan->getAst(), base)) {
} }
@ -1666,6 +1667,10 @@ void CalculationNode::toJsonHelper (triagens::basics::Json& nodes,
("outVariable", _outVariable->toJson()) ("outVariable", _outVariable->toJson())
("canThrow", triagens::basics::Json(_expression->canThrow())); ("canThrow", triagens::basics::Json(_expression->canThrow()));
if (_conditionVariable != nullptr) {
json("conditionVariable", _conditionVariable->toJson());
}
// And add it: // And add it:
nodes(json); nodes(json);
} }
@ -1673,13 +1678,17 @@ void CalculationNode::toJsonHelper (triagens::basics::Json& nodes,
ExecutionNode* CalculationNode::clone (ExecutionPlan* plan, ExecutionNode* CalculationNode::clone (ExecutionPlan* plan,
bool withDependencies, bool withDependencies,
bool withProperties) const { bool withProperties) const {
auto conditionVariable = _conditionVariable;
auto outVariable = _outVariable; auto outVariable = _outVariable;
if (withProperties) { if (withProperties) {
if (_conditionVariable != nullptr) {
conditionVariable = plan->getAst()->variables()->createVariable(conditionVariable);
}
outVariable = plan->getAst()->variables()->createVariable(outVariable); 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); CloneHelper(c, plan, withDependencies, withProperties);

View File

@ -1398,8 +1398,10 @@ namespace triagens {
CalculationNode (ExecutionPlan* plan, CalculationNode (ExecutionPlan* plan,
size_t id, size_t id,
Expression* expr, Expression* expr,
Variable const* conditionVariable,
Variable const* outVariable) Variable const* outVariable)
: ExecutionNode(plan, id), : ExecutionNode(plan, id),
_conditionVariable(conditionVariable),
_outVariable(outVariable), _outVariable(outVariable),
_expression(expr) { _expression(expr) {
@ -1407,6 +1409,17 @@ namespace triagens {
TRI_ASSERT(_outVariable != nullptr); 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); CalculationNode (ExecutionPlan*, triagens::basics::Json const& base);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1477,6 +1490,11 @@ namespace triagens {
for (auto vv : vars) { for (auto vv : vars) {
v.emplace_back(vv); v.emplace_back(vv);
} }
if (_conditionVariable != nullptr) {
v.emplace_back(_conditionVariable);
}
return v; return v;
} }
@ -1504,6 +1522,12 @@ namespace triagens {
private: private:
////////////////////////////////////////////////////////////////////////////////
/// @brief an optional condition variable for the calculation
////////////////////////////////////////////////////////////////////////////////
Variable const* _conditionVariable;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief output variable to write to /// @brief output variable to write to
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -419,11 +419,22 @@ ExecutionNode* ExecutionPlan::fromNodeFilter (ExecutionNode* previous,
ExecutionNode* ExecutionPlan::fromNodeLet (ExecutionNode* previous, ExecutionNode* ExecutionPlan::fromNodeLet (ExecutionNode* previous,
AstNode const* node) { AstNode const* node) {
TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_LET); 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* variable = node->getMember(0);
AstNode const* expression = node->getMember(1); 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*>(variable->getData()); auto v = static_cast<Variable*>(variable->getData());
ExecutionNode* en = nullptr; ExecutionNode* en = nullptr;
@ -464,11 +475,11 @@ ExecutionNode* ExecutionPlan::fromNodeLet (ExecutionNode* previous,
// otherwise fall-through to normal behavior // 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<AstNode*>(expression)); auto expr = new Expression(_ast, const_cast<AstNode*>(expression));
try { try {
en = registerNode(new CalculationNode(this, nextId(), expr, v)); en = registerNode(new CalculationNode(this, nextId(), expr, conditionVariable, v));
} }
catch (...) { catch (...) {
// prevent memleak // prevent memleak

View File

@ -142,7 +142,7 @@ AqlValue Expression::execute (triagens::arango::AqlTransaction* trx,
ISOLATE; ISOLATE;
// Dump the expression in question // Dump the expression in question
// std::cout << triagens::basics::Json(TRI_UNKNOWN_MEM_ZONE, _node->toJson(TRI_UNKNOWN_MEM_ZONE, true)).toString()<< "\n"; // 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) { catch (triagens::arango::Exception& ex) {
if (_ast->query()->verboseErrors()) { if (_ast->query()->verboseErrors()) {
@ -314,9 +314,10 @@ void Expression::analyzeExpression () {
_attributes = std::move(Ast::getReferencedAttributes(_node, isSafeForOptimization)); _attributes = std::move(Ast::getReferencedAttributes(_node, isSafeForOptimization));
if (! 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(); _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) { else if (_type == V8) {
// generate a V8 expression // generate a V8 expression
_func = _executor->generateExpression(_node); _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; _built = true;

View File

@ -59,7 +59,7 @@ namespace triagens {
class Expression { class Expression {
enum ExpressionType { enum ExpressionType : uint32_t {
UNPROCESSED, UNPROCESSED,
JSON, JSON,
V8, V8,
@ -173,13 +173,35 @@ namespace triagens {
/// @brief check whether this is a simple expression /// @brief check whether this is a simple expression
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool isSimple () { inline bool isSimple () {
if (_type == UNPROCESSED) { if (_type == UNPROCESSED) {
analyzeExpression(); analyzeExpression();
} }
return _type == SIMPLE; 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, /// @brief check whether this is an attribute access of any degree (e.g. a.b,
/// a.b.c, ...) /// a.b.c, ...)
@ -353,4 +375,3 @@ namespace triagens {
// mode: outline-minor // mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End: // End:

View File

@ -33,6 +33,7 @@
#include "Aql/Variable.h" #include "Aql/Variable.h"
#include "Basics/json.h" #include "Basics/json.h"
#include "V8/v8-conv.h" #include "V8/v8-conv.h"
#include "V8/v8-utils.h"
using namespace triagens::aql; using namespace triagens::aql;
@ -71,7 +72,6 @@ V8Expression::~V8Expression () {
AqlValue V8Expression::execute (v8::Isolate* isolate, AqlValue V8Expression::execute (v8::Isolate* isolate,
Query* query, Query* query,
triagens::arango::AqlTransaction* trx, triagens::arango::AqlTransaction* trx,
std::unordered_map<Variable const*, std::unordered_set<std::string>> const& attributes,
std::vector<TRI_document_collection_t const*>& docColls, std::vector<TRI_document_collection_t const*>& docColls,
std::vector<AqlValue>& argv, std::vector<AqlValue>& argv,
size_t startPos, size_t startPos,
@ -80,7 +80,7 @@ AqlValue V8Expression::execute (v8::Isolate* isolate,
size_t const n = vars.size(); size_t const n = vars.size();
TRI_ASSERT_EXPENSIVE(regs.size() == n); // assert same vector length TRI_ASSERT_EXPENSIVE(regs.size() == n); // assert same vector length
bool const attributesPresent = ! attributes.empty(); bool const hasRestrictions = ! _attributeRestrictions.empty();
v8::Handle<v8::Object> values = v8::Object::New(isolate); v8::Handle<v8::Object> values = v8::Object::New(isolate);
@ -90,11 +90,11 @@ AqlValue V8Expression::execute (v8::Isolate* isolate,
TRI_ASSERT_EXPENSIVE(! argv[reg].isEmpty()); 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 // 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 // build a partial object
values->ForceSet(TRI_V8_STD_STRING(varname), argv[startPos + reg].toV8Partial(isolate, trx, (*it).second, docColls[reg])); values->ForceSet(TRI_V8_STD_STRING(varname), argv[startPos + reg].toV8Partial(isolate, trx, (*it).second, docColls[reg]));
continue; continue;
@ -121,11 +121,10 @@ AqlValue V8Expression::execute (v8::Isolate* isolate,
v8::TryCatch tryCatch; v8::TryCatch tryCatch;
auto func = v8::Local<v8::Function>::New(isolate, _func); auto func = v8::Local<v8::Function>::New(isolate, _func);
v8::Handle<v8::Value> result = func->Call(func, 1, args); v8::Handle<v8::Value> result = func->Call(func, 1, args);
v8g->_query = old; v8g->_query = old;
Executor::HandleV8Error(tryCatch, result); Executor::HandleV8Error(tryCatch, result);
// no exception was thrown if we get here // no exception was thrown if we get here
@ -138,6 +137,7 @@ AqlValue V8Expression::execute (v8::Isolate* isolate,
else { else {
// expression had a result. convert it to JSON // expression had a result. convert it to JSON
json = TRI_ObjectToJson(isolate, result); json = TRI_ObjectToJson(isolate, result);
// json = TRI_SimplifiedObjectToJson(isolate, result);
} }
if (json == nullptr) { if (json == nullptr) {
@ -162,4 +162,4 @@ AqlValue V8Expression::execute (v8::Isolate* isolate,
// Local Variables: // Local Variables:
// mode: outline-minor // mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: // End

View File

@ -68,6 +68,18 @@ namespace triagens {
// --SECTION-- public methods // --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<Variable const*, std::unordered_set<std::string>> const& attributeRestrictions) {
_attributeRestrictions = attributeRestrictions;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief execute the expression /// @brief execute the expression
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -75,7 +87,6 @@ namespace triagens {
AqlValue execute (v8::Isolate* isolate, AqlValue execute (v8::Isolate* isolate,
Query* query, Query* query,
triagens::arango::AqlTransaction*, triagens::arango::AqlTransaction*,
std::unordered_map<Variable const*, std::unordered_set<std::string>> const&,
std::vector<TRI_document_collection_t const*>&, std::vector<TRI_document_collection_t const*>&,
std::vector<AqlValue>&, std::vector<AqlValue>&,
size_t, size_t,
@ -98,6 +109,12 @@ namespace triagens {
v8::Persistent<v8::Function> _func; v8::Persistent<v8::Function> _func;
////////////////////////////////////////////////////////////////////////////////
/// @brief restrictions for creating the input values
////////////////////////////////////////////////////////////////////////////////
std::unordered_map<Variable const*, std::unordered_set<std::string>> _attributeRestrictions;
}; };
} }