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,
|
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.
|
||||||
|
|
|
@ -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
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue