diff --git a/arangod/Aql/AqlItemBlock.cpp b/arangod/Aql/AqlItemBlock.cpp index eb70b6ea0c..dd9958c601 100644 --- a/arangod/Aql/AqlItemBlock.cpp +++ b/arangod/Aql/AqlItemBlock.cpp @@ -242,7 +242,7 @@ void AqlItemBlock::shrink (size_t nrItems) { /// necessary, using the reference count. //////////////////////////////////////////////////////////////////////////////// -void AqlItemBlock::clearRegisters (std::unordered_set& toClear) { +void AqlItemBlock::clearRegisters (std::unordered_set const& toClear) { for (auto reg : toClear) { for (size_t i = 0; i < _nrItems; i++) { AqlValue& a(_data[_nrRegs * i + reg]); diff --git a/arangod/Aql/AqlItemBlock.h b/arangod/Aql/AqlItemBlock.h index ef62f3afbc..1a7a7d62af 100644 --- a/arangod/Aql/AqlItemBlock.h +++ b/arangod/Aql/AqlItemBlock.h @@ -251,7 +251,7 @@ namespace triagens { /// necessary, using the reference count. //////////////////////////////////////////////////////////////////////////////// - void clearRegisters (std::unordered_set& toClear); + void clearRegisters (std::unordered_set const& toClear); //////////////////////////////////////////////////////////////////////////////// /// @brief slice/clone, this does a deep copy of all entries diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index b7aaa0a3ca..4f1a062c2f 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -120,8 +120,6 @@ ExecutionBlock::ExecutionBlock (ExecutionEngine* engine, : _engine(engine), _trx(engine->getTransaction()), _exeNode(ep), - _varOverview(nullptr), - _depth(0), _done(false) { } @@ -203,76 +201,6 @@ bool ExecutionBlock::walk (WalkerWorker* worker) { } -//////////////////////////////////////////////////////////////////////////////// -/// @brief static analysis -//////////////////////////////////////////////////////////////////////////////// - -struct StaticAnalysisDebugger : public WalkerWorker { - StaticAnalysisDebugger () : indent(0) {}; - ~StaticAnalysisDebugger () {}; - - int indent; - - bool enterSubquery (ExecutionBlock*, ExecutionBlock*) { - indent++; - return true; - } - - void leaveSubquery (ExecutionBlock*, ExecutionBlock*) { - indent--; - } - - void after (ExecutionBlock* eb) { - ExecutionNode const* ep = eb->getPlanNode(); - for (int i = 0; i < indent; i++) { - std::cout << " "; - } - std::cout << ep->getTypeString() << " "; - std::cout << "regsUsedHere: "; - for (auto v : ep->getVariablesUsedHere()) { - std::cout << eb->_varOverview->varInfo.find(v->id)->second.registerId - << " "; - } - std::cout << "regsSetHere: "; - for (auto v : ep->getVariablesSetHere()) { - std::cout << eb->_varOverview->varInfo.find(v->id)->second.registerId - << " "; - } - std::cout << "regsToClear: "; - for (auto r : eb->_regsToClear) { - std::cout << r << " "; - } - std::cout << std::endl; - } -}; - -void ExecutionBlock::staticAnalysis (ExecutionBlock* super) { - // The super is only for the case of subqueries. - shared_ptr v; - if (super == nullptr) { - v.reset(new VarOverview()); - } - else { - v.reset(new VarOverview(*(super->_varOverview), super->_depth)); - } - v->setSharedPtr(&v); - walk(v.get()); - // Now handle the subqueries: - for (auto s : v->subQueryBlocks) { - auto sq = static_cast(s); - sq->getSubquery()->staticAnalysis(s); - } - v->reset(); - - // Just for debugging: - /* - std::cout << std::endl; - StaticAnalysisDebugger debugger; - walk(&debugger); - std::cout << std::endl; - */ -} - //////////////////////////////////////////////////////////////////////////////// /// @brief initialize //////////////////////////////////////////////////////////////////////////////// @@ -366,7 +294,8 @@ void ExecutionBlock::inheritRegisters (AqlItemBlock const* src, RegisterId const n = src->getNrRegs(); for (RegisterId i = 0; i < n; i++) { - if (_regsToClear.find(i) == _regsToClear.end()) { + if (getPlanNode()->_regsToClear.find(i) == + getPlanNode()->_regsToClear.end()) { if (! src->getValue(row, i).isEmpty()) { AqlValue a = src->getValue(row, i).clone(); try { @@ -427,7 +356,7 @@ AqlItemBlock* ExecutionBlock::getSomeWithoutRegisterClearout ( void ExecutionBlock::clearRegisters (AqlItemBlock* result) { // Clear out registers not needed later on: if (result != nullptr) { - result->clearRegisters(_regsToClear); + result->clearRegisters(getPlanNode()->_regsToClear); } } @@ -627,7 +556,7 @@ int SingletonBlock::getOrSkipSome (size_t, // atLeast, } if(! skipping){ - result = new AqlItemBlock(1, _varOverview->nrRegs[_depth]); + result = new AqlItemBlock(1, getPlanNode()->getVarOverview()->nrRegs[getPlanNode()->getDepth()]); try { if (_inputRegisterValues != nullptr) { skipped++; @@ -752,7 +681,7 @@ AqlItemBlock* EnumerateCollectionBlock::getSome (size_t, // atLeast, size_t available = _documents.size() - _posInAllDocs; size_t toSend = (std::min)(atMost, available); - unique_ptr res(new AqlItemBlock(toSend, _varOverview->nrRegs[_depth])); + unique_ptr res(new AqlItemBlock(toSend, getPlanNode()->getVarOverview()->nrRegs[getPlanNode()->getDepth()])); // automatically freed if we throw TRI_ASSERT(curRegs <= res->getNrRegs()); @@ -773,7 +702,7 @@ AqlItemBlock* EnumerateCollectionBlock::getSome (size_t, // atLeast, } // The result is in the first variable of this depth, - // we do not need to do a lookup in _varOverview->varInfo, + // we do not need to do a lookup in getPlanNode()->_varOverview->varInfo, // but can just take cur->getNrRegs() as registerId: res->setValue(j, static_cast(curRegs), AqlValue(reinterpret_cast inVars = e->variables(); for (auto v : inVars) { inVarsCur.push_back(v); - auto it = _varOverview->varInfo.find(v->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + auto it = getPlanNode()->getVarOverview()->varInfo.find(v->id); + TRI_ASSERT(it != getPlanNode()->getVarOverview()->varInfo.end()); inRegsCur.push_back(it->second.registerId); } @@ -1108,7 +1037,7 @@ AqlItemBlock* IndexRangeBlock::getSome (size_t, // atLeast if (toSend > 0) { - res.reset(new AqlItemBlock(toSend, _varOverview->nrRegs[_depth])); + res.reset(new AqlItemBlock(toSend, getPlanNode()->getVarOverview()->nrRegs[getPlanNode()->getDepth()])); // automatically freed should we throw TRI_ASSERT(curRegs <= res->getNrRegs()); @@ -1130,7 +1059,7 @@ AqlItemBlock* IndexRangeBlock::getSome (size_t, // atLeast } // The result is in the first variable of this depth, - // we do not need to do a lookup in _varOverview->varInfo, + // we do not need to do a lookup in getPlanNode()->_varOverview->varInfo, // but can just take cur->getNrRegs() as registerId: res->setValue(j, static_cast(curRegs), AqlValue(reinterpret_cast(_exeNode); // get the inVariable register id . . . - // staticAnalysis has been run, so _varOverview is set up - auto it = _varOverview->varInfo.find(en->_inVariable->id); + // staticAnalysis has been run, so getPlanNode()->_varOverview is set up + auto it = getPlanNode()->getVarOverview()->varInfo.find(en->_inVariable->id); - if (it == _varOverview->varInfo.end()){ + if (it == getPlanNode()->getVarOverview()->varInfo.end()){ THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "variable not found"); } @@ -1668,7 +1597,7 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) { size_t toSend = (std::min)(atMost, sizeInVar - _index); // create the result - res.reset(new AqlItemBlock(toSend, _varOverview->nrRegs[_depth])); + res.reset(new AqlItemBlock(toSend, getPlanNode()->getVarOverview()->nrRegs[getPlanNode()->getDepth()])); inheritRegisters(cur, res.get(), _pos); @@ -1838,7 +1767,7 @@ int CalculationBlock::initialize () { return res; } - // We know that staticAnalysis has been run, so _varOverview is set up + // We know that staticAnalysis has been run, so getPlanNode()->_varOverview is set up auto en = static_cast(getPlanNode()); std::unordered_set inVars = _expression->variables(); @@ -1847,9 +1776,9 @@ int CalculationBlock::initialize () { for (auto it = inVars.begin(); it != inVars.end(); ++it) { _inVars.push_back(*it); - auto it2 = _varOverview->varInfo.find((*it)->id); + auto it2 = en->getVarOverview()->varInfo.find((*it)->id); - TRI_ASSERT(it2 != _varOverview->varInfo.end()); + TRI_ASSERT(it2 != en->getVarOverview()->varInfo.end()); _inRegs.push_back(it2->second.registerId); } @@ -1861,8 +1790,8 @@ int CalculationBlock::initialize () { TRI_ASSERT(_inRegs.size() == 1); } - auto it3 = _varOverview->varInfo.find(en->_outVariable->id); - TRI_ASSERT(it3 != _varOverview->varInfo.end()); + auto it3 = en->getVarOverview()->varInfo.find(en->_outVariable->id); + TRI_ASSERT(it3 != en->getVarOverview()->varInfo.end()); _outReg = it3->second.registerId; return TRI_ERROR_NO_ERROR; @@ -1940,12 +1869,12 @@ int SubqueryBlock::initialize () { return res; } - // We know that staticAnalysis has been run, so _varOverview is set up + // We know that staticAnalysis has been run, so getPlanNode()->_varOverview is set up auto en = static_cast(getPlanNode()); - auto it3 = _varOverview->varInfo.find(en->_outVariable->id); - TRI_ASSERT(it3 != _varOverview->varInfo.end()); + auto it3 = en->getVarOverview()->varInfo.find(en->_outVariable->id); + TRI_ASSERT(it3 != en->getVarOverview()->varInfo.end()); _outReg = it3->second.registerId; return getSubquery()->initialize(); @@ -2005,12 +1934,12 @@ int FilterBlock::initialize () { return res; } - // We know that staticAnalysis has been run, so _varOverview is set up + // We know that staticAnalysis has been run, so getPlanNode()->_varOverview is set up auto en = static_cast(getPlanNode()); - auto it = _varOverview->varInfo.find(en->_inVariable->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + auto it = en->getVarOverview()->varInfo.find(en->_inVariable->id); + TRI_ASSERT(it != en->getVarOverview()->varInfo.end()); _inReg = it->second.registerId; return TRI_ERROR_NO_ERROR; @@ -2189,18 +2118,18 @@ int AggregateBlock::initialize () { _variableNames.clear(); for (auto p : en->_aggregateVariables){ - //We know that staticAnalysis has been run, so _varOverview is set up - auto itOut = _varOverview->varInfo.find(p.first->id); - TRI_ASSERT(itOut != _varOverview->varInfo.end()); + // We know that staticAnalysis has been run, so getPlanNode()->_varOverview is set up + auto itOut = en->getVarOverview()->varInfo.find(p.first->id); + TRI_ASSERT(itOut != en->getVarOverview()->varInfo.end()); - auto itIn = _varOverview->varInfo.find(p.second->id); - TRI_ASSERT(itIn != _varOverview->varInfo.end()); + auto itIn = en->getVarOverview()->varInfo.find(p.second->id); + TRI_ASSERT(itIn != en->getVarOverview()->varInfo.end()); _aggregateRegisters.push_back(make_pair((*itOut).second.registerId, (*itIn).second.registerId)); } if (en->_outVariable != nullptr) { - auto it = _varOverview->varInfo.find(en->_outVariable->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + auto it = en->getVarOverview()->varInfo.find(en->_outVariable->id); + TRI_ASSERT(it != en->getVarOverview()->varInfo.end()); _groupRegister = (*it).second.registerId; TRI_ASSERT(_groupRegister > 0); @@ -2208,12 +2137,13 @@ int AggregateBlock::initialize () { // construct a mapping of all register ids to variable names // we need this mapping to generate the grouped output - for (size_t i = 0; i < _varOverview->varInfo.size(); ++i) { + for (size_t i = 0; i < en->getVarOverview()->varInfo.size(); ++i) { _variableNames.push_back(""); // initialize with some default value } // iterate over all our variables - for (auto it = _varOverview->varInfo.begin(); it != _varOverview->varInfo.end(); ++it) { + for (auto it = en->getVarOverview()->varInfo.begin(); + it != en->getVarOverview()->varInfo.end(); ++it) { // find variable in the global variable map auto itVar = en->_variableMap.find((*it).first); @@ -2253,7 +2183,7 @@ int AggregateBlock::getOrSkipSome (size_t atLeast, unique_ptr res; if(!skipping){ - res.reset(new AqlItemBlock(atMost, _varOverview->nrRegs[_depth])); + res.reset(new AqlItemBlock(atMost, getPlanNode()->getVarOverview()->nrRegs[getPlanNode()->getDepth()])); TRI_ASSERT(cur->getNrRegs() <= res->getNrRegs()); inheritRegisters(cur, res.get(), _pos); @@ -2421,9 +2351,9 @@ int SortBlock::initialize () { _sortRegisters.clear(); for( auto p: en->_elements){ - //We know that staticAnalysis has been run, so _varOverview is set up - auto it = _varOverview->varInfo.find(p.first->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + // We know that staticAnalysis has been run, so getPlanNode()->_varOverview is set up + auto it = en->getVarOverview()->varInfo.find(p.first->id); + TRI_ASSERT(it != en->getVarOverview()->varInfo.end()); _sortRegisters.push_back(make_pair(it->second.registerId, p.second)); } @@ -2713,8 +2643,8 @@ AqlItemBlock* ReturnBlock::getSome (size_t atLeast, // Let's steal the actual result and throw away the vars: auto ep = static_cast(getPlanNode()); - auto it = _varOverview->varInfo.find(ep->_inVariable->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + auto it = ep->getVarOverview()->varInfo.find(ep->_inVariable->id); + TRI_ASSERT(it != ep->getVarOverview()->varInfo.end()); RegisterId const registerId = it->second.registerId; AqlItemBlock* stripped = new AqlItemBlock(n, 1); @@ -2867,8 +2797,8 @@ RemoveBlock::~RemoveBlock () { void RemoveBlock::work (std::vector& blocks) { auto ep = static_cast(getPlanNode()); - auto it = _varOverview->varInfo.find(ep->_inVariable->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + auto it = ep->getVarOverview()->varInfo.find(ep->_inVariable->id); + TRI_ASSERT(it != ep->getVarOverview()->varInfo.end()); RegisterId const registerId = it->second.registerId; auto trxCollection = _trx->trxCollection(_collection->cid()); @@ -2944,8 +2874,8 @@ InsertBlock::~InsertBlock () { void InsertBlock::work (std::vector& blocks) { auto ep = static_cast(getPlanNode()); - auto it = _varOverview->varInfo.find(ep->_inVariable->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + auto it = ep->getVarOverview()->varInfo.find(ep->_inVariable->id); + TRI_ASSERT(it != ep->getVarOverview()->varInfo.end()); RegisterId const registerId = it->second.registerId; auto trxCollection = _trx->trxCollection(_collection->cid()); @@ -3056,16 +2986,16 @@ UpdateBlock::~UpdateBlock () { void UpdateBlock::work (std::vector& blocks) { auto ep = static_cast(getPlanNode()); - auto it = _varOverview->varInfo.find(ep->_inDocVariable->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + auto it = ep->getVarOverview()->varInfo.find(ep->_inDocVariable->id); + TRI_ASSERT(it != ep->getVarOverview()->varInfo.end()); RegisterId const docRegisterId = it->second.registerId; RegisterId keyRegisterId = 0; // default initialization bool const hasKeyVariable = (ep->_inKeyVariable != nullptr); if (hasKeyVariable) { - it = _varOverview->varInfo.find(ep->_inKeyVariable->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + it = ep->getVarOverview()->varInfo.find(ep->_inKeyVariable->id); + TRI_ASSERT(it != ep->getVarOverview()->varInfo.end()); keyRegisterId = it->second.registerId; } @@ -3174,16 +3104,16 @@ ReplaceBlock::~ReplaceBlock () { void ReplaceBlock::work (std::vector& blocks) { auto ep = static_cast(getPlanNode()); - auto it = _varOverview->varInfo.find(ep->_inDocVariable->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + auto it = ep->getVarOverview()->varInfo.find(ep->_inDocVariable->id); + TRI_ASSERT(it != ep->getVarOverview()->varInfo.end()); RegisterId const registerId = it->second.registerId; RegisterId keyRegisterId = 0; // default initialization bool const hasKeyVariable = (ep->_inKeyVariable != nullptr); if (hasKeyVariable) { - it = _varOverview->varInfo.find(ep->_inKeyVariable->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + it = ep->getVarOverview()->varInfo.find(ep->_inKeyVariable->id); + TRI_ASSERT(it != ep->getVarOverview()->varInfo.end()); keyRegisterId = it->second.registerId; } @@ -3245,201 +3175,6 @@ void ReplaceBlock::work (std::vector& blocks) { } } -// ----------------------------------------------------------------------------- -// --SECTION-- struct ExecutionBlock::VarOverview -// ----------------------------------------------------------------------------- - -// Copy constructor used for a subquery: -ExecutionBlock::VarOverview::VarOverview (VarOverview const& v, - unsigned int newdepth) - : varInfo(v.varInfo), - nrRegsHere(v.nrRegsHere), - nrRegs(v.nrRegs), - subQueryBlocks(), - depth(newdepth+1), - totalNrRegs(v.nrRegs[newdepth]), - me(nullptr) { - nrRegs.resize(depth); - nrRegsHere.resize(depth); - nrRegsHere.push_back(0); - nrRegs.push_back(nrRegs.back()); -} - -void ExecutionBlock::VarOverview::after (ExecutionBlock *eb) { - switch (eb->getPlanNode()->getType()) { - case ExecutionNode::ENUMERATE_COLLECTION: - case ExecutionNode::INDEX_RANGE: { - depth++; - nrRegsHere.push_back(1); - nrRegs.push_back(1 + nrRegs.back()); - auto ep = static_cast(eb->getPlanNode()); - TRI_ASSERT(ep != nullptr); - varInfo.insert(make_pair(ep->_outVariable->id, - VarInfo(depth, totalNrRegs))); - totalNrRegs++; - break; - } - case ExecutionNode::ENUMERATE_LIST: { - depth++; - nrRegsHere.push_back(1); - nrRegs.push_back(1 + nrRegs.back()); - auto ep = static_cast(eb->getPlanNode()); - TRI_ASSERT(ep != nullptr); - varInfo.insert(make_pair(ep->_outVariable->id, - VarInfo(depth, totalNrRegs))); - totalNrRegs++; - break; - } - case ExecutionNode::CALCULATION: { - nrRegsHere[depth]++; - nrRegs[depth]++; - auto ep = static_cast(eb->getPlanNode()); - TRI_ASSERT(ep != nullptr); - varInfo.insert(make_pair(ep->_outVariable->id, - VarInfo(depth, totalNrRegs))); - totalNrRegs++; - break; - } - - case ExecutionNode::SUBQUERY: { - nrRegsHere[depth]++; - nrRegs[depth]++; - auto ep = static_cast(eb->getPlanNode()); - TRI_ASSERT(ep != nullptr); - varInfo.insert(make_pair(ep->_outVariable->id, - VarInfo(depth, totalNrRegs))); - totalNrRegs++; - subQueryBlocks.push_back(eb); - break; - } - - case ExecutionNode::AGGREGATE: { - depth++; - nrRegsHere.push_back(0); - nrRegs.push_back(nrRegs.back()); - - auto ep = static_cast(eb->getPlanNode()); - for (auto p : ep->_aggregateVariables) { - // p is std::pair - // and the first is the to be assigned output variable - // for which we need to create a register in the current - // frame: - nrRegsHere[depth]++; - nrRegs[depth]++; - varInfo.insert(make_pair(p.first->id, - VarInfo(depth, totalNrRegs))); - totalNrRegs++; - } - if (ep->_outVariable != nullptr) { - nrRegsHere[depth]++; - nrRegs[depth]++; - varInfo.insert(make_pair(ep->_outVariable->id, - VarInfo(depth, totalNrRegs))); - totalNrRegs++; - } - break; - } - - case ExecutionNode::SORT: { - // sort sorts in place and does not produce new registers - break; - } - - case ExecutionNode::RETURN: { - // return is special. it produces a result but is the last step in the pipeline - break; - } - - case ExecutionNode::REMOVE: { - auto ep = static_cast(eb->getPlanNode()); - if (ep->_outVariable != nullptr) { - nrRegsHere[depth]++; - nrRegs[depth]++; - varInfo.insert(make_pair(ep->_outVariable->id, - VarInfo(depth, totalNrRegs))); - totalNrRegs++; - } - break; - } - - case ExecutionNode::INSERT: { - auto ep = static_cast(eb->getPlanNode()); - if (ep->_outVariable != nullptr) { - nrRegsHere[depth]++; - nrRegs[depth]++; - varInfo.insert(make_pair(ep->_outVariable->id, - VarInfo(depth, totalNrRegs))); - totalNrRegs++; - } - break; - } - - case ExecutionNode::UPDATE: { - auto ep = static_cast(eb->getPlanNode()); - if (ep->_outVariable != nullptr) { - nrRegsHere[depth]++; - nrRegs[depth]++; - varInfo.insert(make_pair(ep->_outVariable->id, - VarInfo(depth, totalNrRegs))); - totalNrRegs++; - } - break; - } - - case ExecutionNode::REPLACE: { - auto ep = static_cast(eb->getPlanNode()); - if (ep->_outVariable != nullptr) { - nrRegsHere[depth]++; - nrRegs[depth]++; - varInfo.insert(make_pair(ep->_outVariable->id, - VarInfo(depth, totalNrRegs))); - totalNrRegs++; - } - break; - } - - case ExecutionNode::SINGLETON: - case ExecutionNode::FILTER: - case ExecutionNode::LIMIT: - case ExecutionNode::SCATTER: - case ExecutionNode::GATHER: - case ExecutionNode::REMOTE: - case ExecutionNode::NORESULTS: { - // these node types do not produce any new registers - break; - } - - case ExecutionNode::ILLEGAL: { - THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "node type not implemented"); - } - } - - eb->_depth = depth; - eb->_varOverview = *me; - - // Now find out which registers ought to be erased after this node: - auto ep = eb->getPlanNode(); - if (ep->getType() != ExecutionNode::RETURN) { - // ReturnBlocks are special, since they return a single column anyway - std::unordered_set const& varsUsedLater = ep->getVarsUsedLater(); - std::vector const& varsUsedHere = ep->getVariablesUsedHere(); - - // We need to delete those variables that have been used here but are not - // used any more later: - std::unordered_set regsToClear; - for (auto v : varsUsedHere) { - auto it = varsUsedLater.find(v); - if (it == varsUsedLater.end()) { - auto it2 = varInfo.find(v->id); - TRI_ASSERT(it2 != varInfo.end()); - RegisterId r = it2->second.registerId; - regsToClear.insert(r); - } - } - eb->setRegsToClear(regsToClear); - } -} - // ----------------------------------------------------------------------------- // --SECTION-- class NoResultsBlock // ----------------------------------------------------------------------------- @@ -3499,9 +3234,9 @@ int GatherBlock::initialize () { _sortRegisters.clear(); for( auto p: en->_elements){ - // We know that staticAnalysis has been run, so _varOverview is set up - auto it = _varOverview->varInfo.find(p.first->id); - TRI_ASSERT(it != _varOverview->varInfo.end()); + // We know that staticAnalysis has been run, so getPlanNode()->_varOverview is set up + auto it = en->getVarOverview()->varInfo.find(p.first->id); + TRI_ASSERT(it != en->getVarOverview()->varInfo.end()); _sortRegisters.push_back(make_pair(it->second.registerId, p.second)); } } diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index 4c7cb78ab1..25362190f2 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -154,74 +154,10 @@ namespace triagens { return _dependencies.at(pos); } -//////////////////////////////////////////////////////////////////////////////// -/// @brief static analysis, walker class and information collector -//////////////////////////////////////////////////////////////////////////////// - - struct VarInfo { - unsigned int const depth; - RegisterId const registerId; - - VarInfo () = delete; - VarInfo (int depth, int registerId) - : depth(depth), registerId(registerId) { - } - }; - - struct VarOverview : public WalkerWorker { - // The following are collected for global usage in the ExecutionBlock: - - // map VariableIds to their depth and registerId: - std::unordered_map varInfo; - - // number of variables in the frame of the current depth: - std::vector nrRegsHere; - - // number of variables in this and all outer frames together, - // the entry with index i here is always the sum of all values - // in nrRegsHere from index 0 to i (inclusively) and the two - // have the same length: - std::vector nrRegs; - - // We collect the subquery blocks to deal with them at the end: - std::vector subQueryBlocks; - - // Local for the walk: - unsigned int depth; - unsigned int totalNrRegs; - - // This is used to tell all Blocks and share a pointer to ourselves - shared_ptr* me; - - VarOverview () - : depth(0), totalNrRegs(0), me(nullptr) { - nrRegsHere.push_back(0); - nrRegs.push_back(0); - }; - - void setSharedPtr (shared_ptr* shared) { - me = shared; - } - - // Copy constructor used for a subquery: - VarOverview (VarOverview const& v, unsigned int newdepth); - ~VarOverview () {}; - - virtual bool enterSubquery (ExecutionBlock*, - ExecutionBlock*) { - return false; // do not walk into subquery - } - - virtual void after (ExecutionBlock *eb); - - }; - //////////////////////////////////////////////////////////////////////////////// /// @brief Methods for execution /// Lifecycle is: /// CONSTRUCTOR -/// then the ExecutionEngine automatically calls -/// staticAnalysis() once, including subqueries /// then the ExecutionEngine automatically calls /// initialize() once, including subqueries /// possibly repeat many times: @@ -232,12 +168,6 @@ namespace triagens { /// DESTRUCTOR //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @brief static analysis -//////////////////////////////////////////////////////////////////////////////// - - void staticAnalysis (ExecutionBlock* super = nullptr); - //////////////////////////////////////////////////////////////////////////////// /// @brief initialize //////////////////////////////////////////////////////////////////////////////// @@ -351,14 +281,6 @@ namespace triagens { return _exeNode; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief set regs to be deleted -//////////////////////////////////////////////////////////////////////////////// - - void setRegsToClear (std::unordered_set& toClear) { - _regsToClear = toClear; - } - protected: //////////////////////////////////////////////////////////////////////////////// @@ -407,41 +329,18 @@ namespace triagens { std::deque _buffer; -//////////////////////////////////////////////////////////////////////////////// -/// @brief info about variables, filled in by staticAnalysis -//////////////////////////////////////////////////////////////////////////////// - -public: - std::shared_ptr _varOverview; - //////////////////////////////////////////////////////////////////////////////// /// @brief current working position in the first entry of _buffer //////////////////////////////////////////////////////////////////////////////// size_t _pos; -//////////////////////////////////////////////////////////////////////////////// -/// @brief depth of frames (number of FOR statements here or above) -//////////////////////////////////////////////////////////////////////////////// - - int _depth; // will be filled in by staticAnalysis - //////////////////////////////////////////////////////////////////////////////// /// @brief if this is set, we are done, this is reset to false by execute() //////////////////////////////////////////////////////////////////////////////// bool _done; -//////////////////////////////////////////////////////////////////////////////// -/// @brief the following contains the registers which should be cleared -/// just before this node hands on results. This is computed during -/// the static analysis for each node using the variable usage in the plan. -//////////////////////////////////////////////////////////////////////////////// - -public: - - std::unordered_set _regsToClear; - // ----------------------------------------------------------------------------- // --SECTION-- public variables // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/ExecutionEngine.cpp b/arangod/Aql/ExecutionEngine.cpp index 5339b7d692..40cea0f5b1 100644 --- a/arangod/Aql/ExecutionEngine.cpp +++ b/arangod/Aql/ExecutionEngine.cpp @@ -695,6 +695,7 @@ ExecutionEngine* ExecutionEngine::instanciateFromPlan (QueryRegistry* queryRegis if (! plan->varUsageComputed()) { plan->findVarUsage(); } + plan->staticAnalysis(); ExecutionBlock* root = nullptr; @@ -716,7 +717,6 @@ ExecutionEngine* ExecutionEngine::instanciateFromPlan (QueryRegistry* queryRegis } TRI_ASSERT(root != nullptr); - root->staticAnalysis(); root->initialize(); root->initializeCursor(nullptr, 0); diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index 24430786a3..d16e1acd46 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -429,6 +429,273 @@ triagens::basics::Json ExecutionNode::toJsonHelperGeneric (triagens::basics::Jso return json; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief static analysis debugger +//////////////////////////////////////////////////////////////////////////////// + +struct StaticAnalysisDebugger : public WalkerWorker { + StaticAnalysisDebugger () : indent(0) {}; + ~StaticAnalysisDebugger () {}; + + int indent; + + bool enterSubquery (ExecutionNode*, ExecutionNode*) { + indent++; + return true; + } + + void leaveSubquery (ExecutionNode*, ExecutionNode*) { + indent--; + } + + void after (ExecutionNode* ep) { + for (int i = 0; i < indent; i++) { + std::cout << " "; + } + std::cout << ep->getTypeString() << " "; + std::cout << "regsUsedHere: "; + for (auto v : ep->getVariablesUsedHere()) { + std::cout << ep->getVarOverview()->varInfo.find(v->id)->second.registerId + << " "; + } + std::cout << "regsSetHere: "; + for (auto v : ep->getVariablesSetHere()) { + std::cout << ep->getVarOverview()->varInfo.find(v->id)->second.registerId + << " "; + } + std::cout << "regsToClear: "; + for (auto r : ep->getRegsToClear()) { + std::cout << r << " "; + } + std::cout << std::endl; + } +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief staticAnalysis +//////////////////////////////////////////////////////////////////////////////// + +void ExecutionNode::staticAnalysis (ExecutionNode* super) { + // The super is only for the case of subqueries. + shared_ptr v; + if (super == nullptr) { + v.reset(new VarOverview()); + } + else { + v.reset(new VarOverview(*(super->_varOverview), super->_depth)); + } + v->setSharedPtr(&v); + walk(v.get()); + // Now handle the subqueries: + for (auto s : v->subQueryNodes) { + auto sq = static_cast(s); + sq->getSubquery()->staticAnalysis(s); + } + v->reset(); + + // Just for debugging: + /* + std::cout << std::endl; + StaticAnalysisDebugger debugger; + walk(&debugger); + std::cout << std::endl; + */ +} + +// ----------------------------------------------------------------------------- +// --SECTION-- struct ExecutionNode::VarOverview +// ----------------------------------------------------------------------------- + +// Copy constructor used for a subquery: +ExecutionNode::VarOverview::VarOverview (VarOverview const& v, + unsigned int newdepth) + : varInfo(v.varInfo), + nrRegsHere(v.nrRegsHere), + nrRegs(v.nrRegs), + subQueryNodes(), + depth(newdepth+1), + totalNrRegs(v.nrRegs[newdepth]), + me(nullptr) { + nrRegs.resize(depth); + nrRegsHere.resize(depth); + nrRegsHere.push_back(0); + nrRegs.push_back(nrRegs.back()); +} + +void ExecutionNode::VarOverview::after (ExecutionNode *en) { + switch (en->getType()) { + case ExecutionNode::ENUMERATE_COLLECTION: + case ExecutionNode::INDEX_RANGE: { + depth++; + nrRegsHere.push_back(1); + nrRegs.push_back(1 + nrRegs.back()); + auto ep = static_cast(en); + TRI_ASSERT(ep != nullptr); + varInfo.insert(make_pair(ep->_outVariable->id, + VarInfo(depth, totalNrRegs))); + totalNrRegs++; + break; + } + case ExecutionNode::ENUMERATE_LIST: { + depth++; + nrRegsHere.push_back(1); + nrRegs.push_back(1 + nrRegs.back()); + auto ep = static_cast(en); + TRI_ASSERT(ep != nullptr); + varInfo.insert(make_pair(ep->_outVariable->id, + VarInfo(depth, totalNrRegs))); + totalNrRegs++; + break; + } + case ExecutionNode::CALCULATION: { + nrRegsHere[depth]++; + nrRegs[depth]++; + auto ep = static_cast(en); + TRI_ASSERT(ep != nullptr); + varInfo.insert(make_pair(ep->_outVariable->id, + VarInfo(depth, totalNrRegs))); + totalNrRegs++; + break; + } + + case ExecutionNode::SUBQUERY: { + nrRegsHere[depth]++; + nrRegs[depth]++; + auto ep = static_cast(en); + TRI_ASSERT(ep != nullptr); + varInfo.insert(make_pair(ep->_outVariable->id, + VarInfo(depth, totalNrRegs))); + totalNrRegs++; + subQueryNodes.push_back(en); + break; + } + + case ExecutionNode::AGGREGATE: { + depth++; + nrRegsHere.push_back(0); + nrRegs.push_back(nrRegs.back()); + + auto ep = static_cast(en); + for (auto p : ep->_aggregateVariables) { + // p is std::pair + // and the first is the to be assigned output variable + // for which we need to create a register in the current + // frame: + nrRegsHere[depth]++; + nrRegs[depth]++; + varInfo.insert(make_pair(p.first->id, + VarInfo(depth, totalNrRegs))); + totalNrRegs++; + } + if (ep->_outVariable != nullptr) { + nrRegsHere[depth]++; + nrRegs[depth]++; + varInfo.insert(make_pair(ep->_outVariable->id, + VarInfo(depth, totalNrRegs))); + totalNrRegs++; + } + break; + } + + case ExecutionNode::SORT: { + // sort sorts in place and does not produce new registers + break; + } + + case ExecutionNode::RETURN: { + // return is special. it produces a result but is the last step in the pipeline + break; + } + + case ExecutionNode::REMOVE: { + auto ep = static_cast(en); + if (ep->_outVariable != nullptr) { + nrRegsHere[depth]++; + nrRegs[depth]++; + varInfo.insert(make_pair(ep->_outVariable->id, + VarInfo(depth, totalNrRegs))); + totalNrRegs++; + } + break; + } + + case ExecutionNode::INSERT: { + auto ep = static_cast(en); + if (ep->_outVariable != nullptr) { + nrRegsHere[depth]++; + nrRegs[depth]++; + varInfo.insert(make_pair(ep->_outVariable->id, + VarInfo(depth, totalNrRegs))); + totalNrRegs++; + } + break; + } + + case ExecutionNode::UPDATE: { + auto ep = static_cast(en); + if (ep->_outVariable != nullptr) { + nrRegsHere[depth]++; + nrRegs[depth]++; + varInfo.insert(make_pair(ep->_outVariable->id, + VarInfo(depth, totalNrRegs))); + totalNrRegs++; + } + break; + } + + case ExecutionNode::REPLACE: { + auto ep = static_cast(en); + if (ep->_outVariable != nullptr) { + nrRegsHere[depth]++; + nrRegs[depth]++; + varInfo.insert(make_pair(ep->_outVariable->id, + VarInfo(depth, totalNrRegs))); + totalNrRegs++; + } + break; + } + + case ExecutionNode::SINGLETON: + case ExecutionNode::FILTER: + case ExecutionNode::LIMIT: + case ExecutionNode::SCATTER: + case ExecutionNode::GATHER: + case ExecutionNode::REMOTE: + case ExecutionNode::NORESULTS: { + // these node types do not produce any new registers + break; + } + + case ExecutionNode::ILLEGAL: { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "node type not implemented"); + } + } + + en->_depth = depth; + en->_varOverview = *me; + + // Now find out which registers ought to be erased after this node: + if (en->getType() != ExecutionNode::RETURN) { + // ReturnNodes are special, since they return a single column anyway + std::unordered_set const& varsUsedLater = en->getVarsUsedLater(); + std::vector const& varsUsedHere = en->getVariablesUsedHere(); + + // We need to delete those variables that have been used here but are not + // used any more later: + std::unordered_set regsToClear; + for (auto v : varsUsedHere) { + auto it = varsUsedLater.find(v); + if (it == varsUsedLater.end()) { + auto it2 = varInfo.find(v->id); + TRI_ASSERT(it2 != varInfo.end()); + RegisterId r = it2->second.registerId; + regsToClear.insert(r); + } + } + en->setRegsToClear(regsToClear); + } +} + // ----------------------------------------------------------------------------- // --SECTION-- methods of SingletonNode // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/ExecutionNode.h b/arangod/Aql/ExecutionNode.h index 87c61e2ef6..25ac4cd3ef 100644 --- a/arangod/Aql/ExecutionNode.h +++ b/arangod/Aql/ExecutionNode.h @@ -75,6 +75,8 @@ namespace triagens { /// @brief node type //////////////////////////////////////////////////////////////////////////////// + friend class ExecutionBlock; + public: enum NodeType { @@ -113,7 +115,8 @@ namespace triagens { _estimatedCost(0.0), _estimatedCostSet(false), _varUsageValid(false), - _plan(plan) { + _plan(plan), + _depth(0) { } //////////////////////////////////////////////////////////////////////////////// @@ -496,6 +499,99 @@ namespace triagens { return false; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief static analysis, walker class and information collector +//////////////////////////////////////////////////////////////////////////////// + + struct VarInfo { + unsigned int const depth; + RegisterId const registerId; + + VarInfo () = delete; + VarInfo (int depth, int registerId) + : depth(depth), registerId(registerId) { + } + }; + + struct VarOverview : public WalkerWorker { + // The following are collected for global usage in the ExecutionBlock, + // although they are stored here in the node: + + // map VariableIds to their depth and registerId: + std::unordered_map varInfo; + + // number of variables in the frame of the current depth: + std::vector nrRegsHere; + + // number of variables in this and all outer frames together, + // the entry with index i here is always the sum of all values + // in nrRegsHere from index 0 to i (inclusively) and the two + // have the same length: + std::vector nrRegs; + + // We collect the subquery nodes to deal with them at the end: + std::vector subQueryNodes; + + // Local for the walk: + unsigned int depth; + unsigned int totalNrRegs; + + // This is used to tell all nodes and share a pointer to ourselves + shared_ptr* me; + + VarOverview () + : depth(0), totalNrRegs(0), me(nullptr) { + nrRegsHere.push_back(0); + nrRegs.push_back(0); + }; + + void setSharedPtr (shared_ptr* shared) { + me = shared; + } + + // Copy constructor used for a subquery: + VarOverview (VarOverview const& v, unsigned int newdepth); + ~VarOverview () {}; + + virtual bool enterSubquery (ExecutionNode*, + ExecutionNode*) { + return false; // do not walk into subquery + } + + virtual void after (ExecutionNode *eb); + + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief static analysis +//////////////////////////////////////////////////////////////////////////////// + + void staticAnalysis (ExecutionNode* super = nullptr); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get varOverview +//////////////////////////////////////////////////////////////////////////////// + + VarOverview const* getVarOverview () const { + return _varOverview.get(); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get depth +//////////////////////////////////////////////////////////////////////////////// + + int getDepth () const { + return _depth; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get registers to clear +//////////////////////////////////////////////////////////////////////////////// + + std::unordered_set const& getRegsToClear () const { + return _regsToClear; + } + // ----------------------------------------------------------------------------- // --SECTION-- protected functions // ----------------------------------------------------------------------------- @@ -528,6 +624,14 @@ namespace triagens { TRI_memory_zone_t*, bool) const; +//////////////////////////////////////////////////////////////////////////////// +/// @brief set regs to be deleted +//////////////////////////////////////////////////////////////////////////////// + + void setRegsToClear (std::unordered_set& toClear) { + _regsToClear = toClear; + } + // ----------------------------------------------------------------------------- // --SECTION-- protected variables // ----------------------------------------------------------------------------- @@ -589,6 +693,26 @@ namespace triagens { ExecutionPlan* _plan; +//////////////////////////////////////////////////////////////////////////////// +/// @brief info about variables, filled in by staticAnalysis +//////////////////////////////////////////////////////////////////////////////// + + std::shared_ptr _varOverview; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief depth of the current frame, will be filled in by staticAnalysis +//////////////////////////////////////////////////////////////////////////////// + + int _depth; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief the following contains the registers which should be cleared +/// just before this node hands on results. This is computed during +/// the static analysis for each node using the variable usage in the plan. +//////////////////////////////////////////////////////////////////////////////// + + std::unordered_set _regsToClear; + }; // ----------------------------------------------------------------------------- @@ -665,7 +789,7 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// class EnumerateCollectionNode : public ExecutionNode { - + friend class ExecutionNode; friend class ExecutionBlock; friend class EnumerateCollectionBlock; @@ -819,6 +943,7 @@ namespace triagens { class EnumerateListNode : public ExecutionNode { + friend class ExecutionNode; friend class ExecutionBlock; friend class EnumerateListBlock; friend class RedundantCalculationsReplacer; @@ -1178,6 +1303,7 @@ namespace triagens { class CalculationNode : public ExecutionNode { + friend class ExecutionNode; friend class ExecutionBlock; friend class CalculationBlock; friend class RedundantCalculationsReplacer; @@ -1323,6 +1449,7 @@ namespace triagens { class SubqueryNode : public ExecutionNode { + friend class ExecutionNode; friend class ExecutionBlock; friend class SubqueryBlock; @@ -1733,6 +1860,7 @@ namespace triagens { class AggregateNode : public ExecutionNode { + friend class ExecutionNode; friend class ExecutionBlock; friend class AggregateBlock; friend class RedundantCalculationsReplacer; @@ -2041,6 +2169,7 @@ namespace triagens { class RemoveNode : public ModificationNode { + friend class ExecutionNode; friend class ExecutionBlock; friend class RemoveBlock; @@ -2153,6 +2282,7 @@ namespace triagens { class InsertNode : public ModificationNode { + friend class ExecutionNode; friend class ExecutionBlock; friend class InsertBlock; @@ -2264,6 +2394,7 @@ namespace triagens { class UpdateNode : public ModificationNode { + friend class ExecutionNode; friend class ExecutionBlock; friend class UpdateBlock; @@ -2388,6 +2519,7 @@ namespace triagens { class ReplaceNode : public ModificationNode { + friend class ExecutionNode; friend class ExecutionBlock; friend class ReplaceBlock; diff --git a/arangod/Aql/ExecutionPlan.h b/arangod/Aql/ExecutionPlan.h index 11c84a424f..3a8b4c5cb3 100644 --- a/arangod/Aql/ExecutionPlan.h +++ b/arangod/Aql/ExecutionPlan.h @@ -95,6 +95,7 @@ namespace triagens { /// @brief create an execution plan identical to this one /// keep the memory of the plan on the query object specified. //////////////////////////////////////////////////////////////////////////////// + ExecutionPlan* clone(Query &onThatQuery); //////////////////////////////////////////////////////////////////////////////// @@ -229,6 +230,14 @@ namespace triagens { bool varUsageComputed (); +//////////////////////////////////////////////////////////////////////////////// +/// @brief static analysis +//////////////////////////////////////////////////////////////////////////////// + + void staticAnalysis () { + _root->staticAnalysis(); + } + //////////////////////////////////////////////////////////////////////////////// /// @brief unlinkNodes, note that this does not delete the removed /// nodes and that one cannot remove the root node of the plan.