1
0
Fork 0

Implement clear unneeded registers.

This commit is contained in:
Max Neunhoeffer 2014-08-22 17:00:53 +02:00
parent e5f09bfd87
commit 8fd6cd1184
9 changed files with 290 additions and 51 deletions

View File

@ -118,6 +118,34 @@ void AqlItemBlock::shrink (size_t nrItems) {
_nrItems = nrItems;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief clears out some columns (registers), this deletes the values if
/// necessary, using the reference count.
////////////////////////////////////////////////////////////////////////////////
void AqlItemBlock::clearRegisters (std::unordered_set<RegisterId>& toClear) {
for (auto reg : toClear) {
for (size_t i = 0; i < _nrItems; i++) {
AqlValue& a(_data[_nrRegs * i + reg]);
if (! a.isEmpty()) {
auto it = _valueCount.find(a);
if (it != _valueCount.end()) {
if (--it->second == 0) {
try {
_valueCount.erase(it);
}
catch (...) {
it->second++;
throw;
}
}
}
a.erase();
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief slice/clone, this does a deep copy of all entries
////////////////////////////////////////////////////////////////////////////////

View File

@ -239,6 +239,13 @@ namespace triagens {
void shrink (size_t nrItems);
////////////////////////////////////////////////////////////////////////////////
/// @brief clears out some columns (registers), this deletes the values if
/// necessary, using the reference count.
////////////////////////////////////////////////////////////////////////////////
void clearRegisters (std::unordered_set<RegisterId>& toClear);
////////////////////////////////////////////////////////////////////////////////
/// @brief slice/clone, this does a deep copy of all entries
////////////////////////////////////////////////////////////////////////////////

View File

@ -194,6 +194,45 @@ void ExecutionBlock::walk (WalkerWorker<ExecutionBlock>* worker) {
/// @brief static analysis
////////////////////////////////////////////////////////////////////////////////
struct StaticAnalysisDebugger : public WalkerWorker<ExecutionBlock> {
StaticAnalysisDebugger () : indent(0) {};
~StaticAnalysisDebugger () {};
int indent;
bool enterSubquery (ExecutionBlock* super, ExecutionBlock* sub) {
indent++;
return true;
}
void leaveSubquery (ExecutionBlock* super, ExecutionBlock* sub) {
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<VarOverview> v;
@ -211,6 +250,14 @@ void ExecutionBlock::staticAnalysis (ExecutionBlock* super) {
sq->getSubquery()->staticAnalysis(s);
}
v->reset();
// Just for debugging:
/*
std::cout << std::endl;
StaticAnalysisDebugger debugger;
walk(&debugger);
std::cout << std::endl;
*/
}
int ExecutionBlock::initialize () {
@ -250,19 +297,20 @@ void ExecutionBlock::inheritRegisters (AqlItemBlock const* src,
RegisterId const n = src->getNrRegs();
for (RegisterId i = 0; i < n; i++) {
if (! src->getValue(row, i).isEmpty()) {
AqlValue a = src->getValue(row, i).clone();
try {
dst->setValue(0, i, a);
}
catch (...) {
a.destroy();
throw;
if (_regsToClear.find(i) == _regsToClear.end()) {
if (! src->getValue(row, i).isEmpty()) {
AqlValue a = src->getValue(row, i).clone();
try {
dst->setValue(0, i, a);
}
catch (...) {
a.destroy();
throw;
}
}
// copy collection
dst->setDocumentCollection(i, src->getDocumentCollection(i));
}
// copy collection
dst->setDocumentCollection(i, src->getDocumentCollection(i));
}
}
@ -281,7 +329,14 @@ bool ExecutionBlock::getBlock (size_t atLeast, size_t atMost) {
return true;
}
AqlItemBlock* ExecutionBlock::getSome (size_t atLeast, size_t atMost) {
AqlItemBlock* ExecutionBlock::getSome(size_t atLeast, size_t atMost) {
std::unique_ptr<AqlItemBlock> result(getSomeWithoutRegisterClearout(atLeast, atMost));
clearRegisters(result.get());
return result.release();
}
AqlItemBlock* ExecutionBlock::getSomeWithoutRegisterClearout (
size_t atLeast, size_t atMost) {
TRI_ASSERT(0 < atLeast && atLeast <= atMost);
size_t skipped = 0;
AqlItemBlock* result = nullptr;
@ -292,6 +347,13 @@ AqlItemBlock* ExecutionBlock::getSome (size_t atLeast, size_t atMost) {
return result;
}
void ExecutionBlock::clearRegisters (AqlItemBlock* result) {
// Clear out registers not needed later on:
if (result != nullptr) {
result->clearRegisters(_regsToClear);
}
}
size_t ExecutionBlock::skipSome (size_t atLeast, size_t atMost) {
TRI_ASSERT(0 < atLeast && atLeast <= atMost);
size_t skipped = 0;
@ -656,6 +718,8 @@ AqlItemBlock* EnumerateCollectionBlock::getSome (size_t atLeast,
}
}
}
// Clear out registers no longer needed later:
clearRegisters(res.get());
return res.release();
}
@ -831,6 +895,8 @@ AqlItemBlock* IndexRangeBlock::getSome (size_t atLeast,
}
}
}
// Clear out registers no longer needed later:
clearRegisters(res.get());
return res.release();
}
@ -1048,6 +1114,9 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t atLeast, size_t atMost) {
}
while (res.get() == nullptr);
// Clear out registers no longer needed later:
clearRegisters(res.get());
return res.release();
}
@ -1249,13 +1318,16 @@ void CalculationBlock::doEvaluation (AqlItemBlock* result) {
AqlItemBlock* CalculationBlock::getSome (size_t atLeast,
size_t atMost) {
unique_ptr<AqlItemBlock> res(ExecutionBlock::getSome(atLeast, atMost));
unique_ptr<AqlItemBlock> res(ExecutionBlock::getSomeWithoutRegisterClearout(
atLeast, atMost));
if (res.get() == nullptr) {
return nullptr;
}
doEvaluation(res.get());
// Clear out registers no longer needed later:
clearRegisters(res.get());
return res.release();
}
@ -1282,7 +1354,8 @@ int SubqueryBlock::initialize () {
AqlItemBlock* SubqueryBlock::getSome (size_t atLeast,
size_t atMost) {
unique_ptr<AqlItemBlock> res(ExecutionBlock::getSome(atLeast, atMost));
unique_ptr<AqlItemBlock> res(ExecutionBlock::getSomeWithoutRegisterClearout(
atLeast, atMost));
if (res.get() == nullptr) {
return nullptr;
@ -1315,6 +1388,8 @@ AqlItemBlock* SubqueryBlock::getSome (size_t atLeast,
throw;
}
}
// Clear out registers no longer needed later:
clearRegisters(res.get());
return res.release();
}
@ -2029,7 +2104,7 @@ int LimitBlock::getOrSkipSome (size_t atLeast,
AqlItemBlock* ReturnBlock::getSome (size_t atLeast,
size_t atMost) {
auto res = ExecutionBlock::getSome(atLeast, atMost);
auto res = ExecutionBlock::getSomeWithoutRegisterClearout(atLeast, atMost);
if (res == nullptr) {
return res;
@ -2107,7 +2182,7 @@ AqlItemBlock* ModificationBlock::getSome (size_t atLeast,
// loop over input until it is exhausted
try {
while (true) {
auto res = ExecutionBlock::getSome(atLeast, atMost);
auto res = ExecutionBlock::getSomeWithoutRegisterClearout(atLeast, atMost);
if (res == nullptr) {
break;
@ -2707,6 +2782,28 @@ void ExecutionBlock::VarOverview::after (ExecutionBlock *eb) {
}
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<Variable const*> const& varsUsedLater = ep->getVarsUsedLater();
std::vector<Variable const*> 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<RegisterId> 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);
}
}
// -----------------------------------------------------------------------------

View File

@ -294,6 +294,27 @@ namespace triagens {
virtual AqlItemBlock* getSome (size_t atLeast, size_t atMost);
////////////////////////////////////////////////////////////////////////////////
/// @brief getSomeWithoutRegisterClearout, same as above, however, this
/// is the actual worker which does not clear out registers at the end
/// the idea is that somebody who wants to call the generic functionality
/// in a derived class but wants to modify the results before the register
/// cleanup can use this method, internal use only
////////////////////////////////////////////////////////////////////////////////
protected:
AqlItemBlock* getSomeWithoutRegisterClearout (size_t atLeast, size_t atMost);
////////////////////////////////////////////////////////////////////////////////
/// @brief clearRegisters, clears out registers holding values that are no
/// longer needed by later nodes
////////////////////////////////////////////////////////////////////////////////
void clearRegisters (AqlItemBlock* result);
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief getSome, skips some more items, semantic is as follows: not
/// more than atMost items may be skipped. The method tries to
@ -320,6 +341,14 @@ namespace triagens {
return _exeNode;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set regs to be deleted
////////////////////////////////////////////////////////////////////////////////
void setRegsToClear (std::unordered_set<RegisterId>& toClear) {
_regsToClear = toClear;
}
protected:
////////////////////////////////////////////////////////////////////////////////
@ -372,6 +401,7 @@ namespace triagens {
/// @brief info about variables, filled in by staticAnalysis
////////////////////////////////////////////////////////////////////////////////
public:
std::shared_ptr<VarOverview> _varOverview;
////////////////////////////////////////////////////////////////////////////////
@ -392,6 +422,16 @@ namespace triagens {
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<RegisterId> _regsToClear;
// -----------------------------------------------------------------------------
// --SECTION-- public variables
// -----------------------------------------------------------------------------

View File

@ -635,7 +635,7 @@ struct SubqueryVarUsageFinder : public WalkerWorker<ExecutionNode> {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
std::vector<Variable const*> SubqueryNode::getVariablesUsedHere () {
std::vector<Variable const*> SubqueryNode::getVariablesUsedHere () const {
SubqueryVarUsageFinder finder;
_subquery->walk(&finder);
@ -755,6 +755,50 @@ void AggregateNode::toJsonHelper (triagens::basics::Json& nodes,
nodes(json);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
struct UserVarFinder : public WalkerWorker<ExecutionNode> {
UserVarFinder () {};
~UserVarFinder () {};
std::vector<Variable const*> userVars;
bool enterSubquery (ExecutionNode* super, ExecutionNode* sub) {
return false;
}
void before (ExecutionNode* en) {
auto vars = en->getVariablesSetHere();
for (auto v : vars) {
if (v->isUserDefined()) {
userVars.push_back(v);
}
}
}
};
std::vector<Variable const*> AggregateNode::getVariablesUsedHere () const {
std::unordered_set<Variable const*> v;
for (auto p : _aggregateVariables) {
v.insert(p.second);
}
if (_outVariable != nullptr) {
// Here we have to find all user defined variables in this query
// amonst our dependencies:
UserVarFinder finder;
auto myselfasnonconst = const_cast<AggregateNode*>(this);
myselfasnonconst->walk(&finder);
for (auto x : finder.userVars) {
v.insert(x);
}
}
std::vector<Variable const*> vv;
for (auto x : v) {
vv.push_back(x);
}
return vv;
}
// -----------------------------------------------------------------------------
// --SECTION-- methods of ReturnNode
// -----------------------------------------------------------------------------

View File

@ -364,7 +364,7 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
virtual std::vector<Variable const*> getVariablesUsedHere () const {
return std::vector<Variable const*>();
}
@ -372,7 +372,7 @@ namespace triagens {
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
return std::vector<Variable const*>();
}
@ -388,7 +388,7 @@ namespace triagens {
/// @brief getVarsUsedLater
////////////////////////////////////////////////////////////////////////////////
std::unordered_set<Variable const*>& getVarsUsedLater () {
std::unordered_set<Variable const*> const& getVarsUsedLater () const {
TRI_ASSERT(_varUsageValid);
return _varsUsedLater;
}
@ -405,7 +405,7 @@ namespace triagens {
/// @brief getVarsValid
////////////////////////////////////////////////////////////////////////////////
std::unordered_set<Variable const*>& getVarsValid () {
std::unordered_set<Variable const*> const& getVarsValid () const {
TRI_ASSERT(_varUsageValid);
return _varsValid;
}
@ -648,7 +648,7 @@ namespace triagens {
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
std::vector<Variable const*> v;
v.push_back(_outVariable);
return v;
@ -770,7 +770,7 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
virtual std::vector<Variable const*> getVariablesUsedHere () const {
std::vector<Variable const*> v;
v.push_back(_inVariable);
return v;
@ -780,7 +780,7 @@ namespace triagens {
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
std::vector<Variable const*> v;
v.push_back(_outVariable);
return v;
@ -885,7 +885,7 @@ namespace triagens {
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
std::vector<Variable const*> v;
v.push_back(_outVariable);
return v;
@ -1110,7 +1110,7 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
virtual std::vector<Variable const*> getVariablesUsedHere () const {
std::unordered_set<Variable*> vars = _expression->variables();
std::vector<Variable const*> v;
for (auto vv : vars) {
@ -1123,7 +1123,7 @@ namespace triagens {
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
std::vector<Variable const*> v;
v.push_back(_outVariable);
return v;
@ -1248,13 +1248,13 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere ();
virtual std::vector<Variable const*> getVariablesUsedHere () const;
////////////////////////////////////////////////////////////////////////////////
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
std::vector<Variable const*> v;
v.push_back(_outVariable);
return v;
@ -1358,7 +1358,7 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
virtual std::vector<Variable const*> getVariablesUsedHere () const {
std::vector<Variable const*> v;
v.push_back(_inVariable);
return v;
@ -1442,7 +1442,7 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
virtual std::vector<Variable const*> getVariablesUsedHere () const {
std::vector<Variable const*> v;
for (auto p : _elements) {
v.push_back(p.first);
@ -1541,19 +1541,13 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
std::vector<Variable const*> v;
for (auto p : _aggregateVariables) {
v.push_back(p.second);
}
return v;
}
virtual std::vector<Variable const*> getVariablesUsedHere () const;
////////////////////////////////////////////////////////////////////////////////
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
std::vector<Variable const*> v;
for (auto p : _aggregateVariables) {
v.push_back(p.first);
@ -1658,7 +1652,7 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
virtual std::vector<Variable const*> getVariablesUsedHere () const {
std::vector<Variable const*> v;
v.push_back(_inVariable);
return v;
@ -1815,7 +1809,7 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
virtual std::vector<Variable const*> getVariablesUsedHere () const {
std::vector<Variable const*> v;
v.push_back(_inVariable);
return v;
@ -1825,7 +1819,7 @@ namespace triagens {
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
std::vector<Variable const*> v;
if (_outVariable != nullptr) {
v.push_back(_outVariable);
@ -1927,7 +1921,7 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
virtual std::vector<Variable const*> getVariablesUsedHere () const {
std::vector<Variable const*> v;
v.push_back(_inVariable);
return v;
@ -1937,7 +1931,7 @@ namespace triagens {
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
std::vector<Variable const*> v;
if (_outVariable != nullptr) {
v.push_back(_outVariable);
@ -2042,7 +2036,7 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
virtual std::vector<Variable const*> getVariablesUsedHere () const {
std::vector<Variable const*> v;
v.push_back(_inDocVariable);
@ -2056,7 +2050,7 @@ namespace triagens {
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
std::vector<Variable const*> v;
if (_outVariable != nullptr) {
v.push_back(_outVariable);
@ -2167,7 +2161,7 @@ namespace triagens {
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
virtual std::vector<Variable const*> getVariablesUsedHere () const {
std::vector<Variable const*> v;
v.push_back(_inDocVariable);
@ -2181,7 +2175,7 @@ namespace triagens {
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
virtual std::vector<Variable const*> getVariablesSetHere () const {
std::vector<Variable const*> v;
if (_outVariable != nullptr) {
v.push_back(_outVariable);

View File

@ -53,6 +53,7 @@ using JsonHelper = triagens::basics::JsonHelper;
ExecutionPlan::ExecutionPlan ()
: _ids(),
_root(nullptr),
_varUsageComputed(false),
_nextId(0) {
}
@ -997,6 +998,15 @@ void ExecutionPlan::findVarUsage () {
VarUsageFinder finder;
root()->walk(&finder);
_varSetBy = finder._varSetBy;
_varUsageComputed = true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief determine if the above are already set
////////////////////////////////////////////////////////////////////////////////
bool ExecutionPlan::varUsageComputed () {
return _varUsageComputed;
}
////////////////////////////////////////////////////////////////////////////////
@ -1033,6 +1043,7 @@ void ExecutionPlan::unlinkNode (ExecutionNode* node) {
node->removeDependency(x);
}
}
_varUsageComputed = false;
}
////////////////////////////////////////////////////////////////////////////////
@ -1060,6 +1071,7 @@ void ExecutionPlan::replaceNode (ExecutionNode* oldNode,
"Could not replace dependencies of an old node.");
}
}
_varUsageComputed = false;
}
////////////////////////////////////////////////////////////////////////////////
@ -1082,6 +1094,7 @@ void ExecutionPlan::insertDependency (ExecutionNode* oldNode,
newNode->removeDependencies();
newNode->addDependency(oldDeps[0]);
_varUsageComputed = false;
}
////////////////////////////////////////////////////////////////////////////////
@ -1109,7 +1122,9 @@ ExecutionPlan* ExecutionPlan::clone (){
plan->_nextId = _nextId;
CloneNodeAdder adder(plan);
plan->_root->walk(&adder);
plan->findVarUsage();
// plan->findVarUsage();
// Let's not do it here, because supposedly the plan is modified as
// the very next thing anyway!
return plan;
}
catch (...) {

View File

@ -155,11 +155,17 @@ namespace triagens {
void checkLinkage ();
////////////////////////////////////////////////////////////////////////////////
/// @brief determine and set _varsUsedLater in all nodes
/// @brief determine and set _varsUsedLater and _valid and _varSetBy
////////////////////////////////////////////////////////////////////////////////
void findVarUsage ();
////////////////////////////////////////////////////////////////////////////////
/// @brief determine if the above are already set
////////////////////////////////////////////////////////////////////////////////
bool varUsageComputed ();
////////////////////////////////////////////////////////////////////////////////
/// @brief unlinkNodes, note that this does not delete the removed
/// nodes and that one cannot remove the root node of the plan.
@ -358,6 +364,12 @@ namespace triagens {
std::unordered_map<VariableId, ExecutionNode*> _varSetBy;
////////////////////////////////////////////////////////////////////////////////
/// @brief flag to indicate whether the variable usage is computed
////////////////////////////////////////////////////////////////////////////////
bool _varUsageComputed;
////////////////////////////////////////////////////////////////////////////////
/// @brief auto-increment sequence for node ids
////////////////////////////////////////////////////////////////////////////////

View File

@ -89,7 +89,9 @@ int Optimizer::createPlans (ExecutionPlan* plan) {
// Find variable usage for all old plans now:
for (auto p : oldPlans.list) {
p->findVarUsage();
if (! p->varUsageComputed()) {
p->findVarUsage();
}
}
// For all rules: