mirror of https://gitee.com/bigwinds/arangodb
preparation for conditions
This commit is contained in:
parent
8580d49aec
commit
c49986d0e8
|
@ -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*>(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.
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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<Variable*> 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<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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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<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()
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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*>(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<AstNode*>(expression));
|
||||
|
||||
try {
|
||||
en = registerNode(new CalculationNode(this, nextId(), expr, v));
|
||||
en = registerNode(new CalculationNode(this, nextId(), expr, conditionVariable, v));
|
||||
}
|
||||
catch (...) {
|
||||
// prevent memleak
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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<Variable const*, std::unordered_set<std::string>> const& attributes,
|
||||
std::vector<TRI_document_collection_t const*>& docColls,
|
||||
std::vector<AqlValue>& 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<v8::Object> 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<v8::Function>::New(isolate, _func);
|
||||
|
||||
v8::Handle<v8::Value> 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
|
||||
|
|
|
@ -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<Variable const*, std::unordered_set<std::string>> 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<Variable const*, std::unordered_set<std::string>> const&,
|
||||
std::vector<TRI_document_collection_t const*>&,
|
||||
std::vector<AqlValue>&,
|
||||
size_t,
|
||||
|
@ -98,6 +109,12 @@ namespace triagens {
|
|||
|
||||
v8::Persistent<v8::Function> _func;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief restrictions for creating the input values
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::unordered_map<Variable const*, std::unordered_set<std::string>> _attributeRestrictions;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue