diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index e13237386d..c0f1b89404 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -438,11 +438,11 @@ void IndexRangeNode::toJsonHelper (triagens::basics::Json& nodes, } // Now put info about vocbase and cid in there - json("database", Json(_vocbase->_name)) + /*json("database", Json(_vocbase->_name)) ("collection", Json(_collection->name)) ("outVariable", _outVariable->toJson()) ("index", _index->index()->json(_index->index())) - ("ranges", ranges); + ("ranges", ranges);*/ // And add it: nodes(json); diff --git a/arangod/Aql/ExecutionNode.h b/arangod/Aql/ExecutionNode.h index 1479559b26..60d15327b0 100644 --- a/arangod/Aql/ExecutionNode.h +++ b/arangod/Aql/ExecutionNode.h @@ -178,6 +178,24 @@ namespace triagens { return _dependencies; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief replace a dependency, returns true if the pointer was found and +/// replaced, please note that this does not delete oldNode! +//////////////////////////////////////////////////////////////////////////////// + + bool replaceDependency (ExecutionNode* oldNode, ExecutionNode* newNode) { + auto it = _dependencies.begin(); + + while (it != _dependencies.end()) { + if (*it == oldNode) { + *it = newNode; + return true; + } + ++it; + } + return false; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief remove a dependency, returns true if the pointer was found and /// removed, please note that this does not delete ep! @@ -545,6 +563,56 @@ namespace triagens { return v; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief get indexes with fields or nullptr if none exist +//////////////////////////////////////////////////////////////////////////////// + + vector getIndexes (vector attrs) const { + vector out; + TRI_document_collection_t* document = _collection->documentCollection(); + size_t const n = document->_allIndexes._length; + + for (size_t i = 0; i < n; ++i) { + TRI_index_t* idx = static_cast(document->_allIndexes._buffer[i]); + + size_t seen = 0; + for (size_t j = 0; j < idx->_fields._length; j++) { + bool found = false; + for (size_t k = 0; k < attrs.size(); k++){ + if(std::string(idx->_fields._buffer[j]) == attrs[k]) { + found = true; + break; + } + } + if (found) { + seen++; + } + else { + break; + } + } + + if (((idx->_type == TRI_IDX_TYPE_HASH_INDEX) && seen == idx->_fields._length ) + || ((idx->_type == TRI_IDX_TYPE_SKIPLIST_INDEX) && seen > 0 )) { + // all fields equal + out.push_back(idx); + } + } + return out; + } + + TRI_vocbase_t* vocbase () const { + return _vocbase; + } + + Collection* collection () const { + return _collection; + } + + Variable const* outVariable () const { + return _outVariable; + } + // ----------------------------------------------------------------------------- // --SECTION-- private variables // ----------------------------------------------------------------------------- @@ -555,7 +623,7 @@ namespace triagens { /// @brief the database //////////////////////////////////////////////////////////////////////////////// - TRI_vocbase_t* _vocbase; + TRI_vocbase_t* _vocbase; //////////////////////////////////////////////////////////////////////////////// /// @brief collection @@ -567,7 +635,7 @@ namespace triagens { /// @brief output variable //////////////////////////////////////////////////////////////////////////////// - Variable const* _outVariable; + Variable const* _outVariable; }; @@ -798,28 +866,46 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con ~RangesInfo(){} - RangeInfo* find(std::string name) const { - auto it = _ranges.find(name); + RangeInfo* find(std::string var, std::string name) const { + auto it1 = _ranges.find(var); + if (it1 == _ranges.end()) { + return nullptr; + } + auto it2 = it1->second.find(name); + if (it2 == it1->second.end()) { + return nullptr; + } + return (*it2).second; + } + + std::unordered_map* find(std::string var) { + auto it = _ranges.find(var); if (it == _ranges.end()) { return nullptr; } - return (*it).second; + //std::unordered_map* out = &((*it).second); + //return out; + return &((*it).second); } - - /*void insert (std::string name, RangeInfo* range) { - if( find(name) == nullptr ){ - _ranges.insert(make_pair(name, range)); - } - }*/ - - void insert (std::string name, RangeInfoBound* low, RangeInfoBound* high){ - auto oldRange = find(name); - auto newRange = new RangeInfo(low, high); - int cmp; - if( oldRange == nullptr ){ + void insert (std::string var, std::string name, + RangeInfoBound* low, RangeInfoBound* high) { + auto oldMap = find(var); + auto newRange = new RangeInfo(low, high); + + if (oldMap == nullptr) { // TODO add exception . . . - _ranges.insert(make_pair(name, newRange)); + auto newMap = std::unordered_map(); + newMap.insert(make_pair(name, newRange)); + _ranges.insert(std::make_pair(var, newMap)); + return; + } + + auto oldRange = find(var, name); //TODO improve + + if (oldRange == nullptr) { + // TODO add exception . . . + oldMap->insert(make_pair(name, newRange)); return; } @@ -837,7 +923,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con // check the new range bounds are valid if( oldRange->_low != nullptr && oldRange->_high != nullptr){ - cmp = TRI_CompareValuesJson(oldRange->_low->_bound.json(), + int cmp = TRI_CompareValuesJson(oldRange->_low->_bound.json(), oldRange->_high->_bound.json()); if (cmp == 1 || (cmp == 0 && !(oldRange->_low->_include == true && oldRange->_high->_include == true ))){ @@ -852,7 +938,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con return _ranges.size(); } - Json toJson () const { + /*Json toJson () const { Json list(Json::List); for (auto x : _ranges) { Json item(Json::Array); @@ -865,10 +951,10 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con std::string toString() const { return this->toJson().toString(); - } + }*/ private: - std::unordered_map _ranges; + std::unordered_map> _ranges; }; //////////////////////////////////////////////////////////////////////////////// @@ -891,7 +977,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con TRI_vocbase_t* vocbase, Collection* collection, Variable const* outVariable, - Index* index, + TRI_index_t* index, vector* ranges) : ExecutionNode(id), _vocbase(vocbase), @@ -984,7 +1070,7 @@ static int CompareRangeInfoBound (RangeInfoBound const* left, RangeInfoBound con /// @brief the index //////////////////////////////////////////////////////////////////////////////// - Index* _index; + TRI_index_t* _index; //////////////////////////////////////////////////////////////////////////////// /// @brief the range info diff --git a/arangod/Aql/ExecutionPlan.cpp b/arangod/Aql/ExecutionPlan.cpp index 282a4ac90c..efc04aba2a 100644 --- a/arangod/Aql/ExecutionPlan.cpp +++ b/arangod/Aql/ExecutionPlan.cpp @@ -51,12 +51,10 @@ using JsonHelper = triagens::basics::JsonHelper; //////////////////////////////////////////////////////////////////////////////// ExecutionPlan::ExecutionPlan () - : _nodes(), - _ids(), + : _ids(), _root(nullptr), _nextId(0) { - _nodes.reserve(8); } //////////////////////////////////////////////////////////////////////////////// @@ -64,8 +62,8 @@ ExecutionPlan::ExecutionPlan () //////////////////////////////////////////////////////////////////////////////// ExecutionPlan::~ExecutionPlan () { - for (auto it = _nodes.begin(); it != _nodes.end(); ++it) { - delete (*it); + for (auto x : _ids){ + delete x.second; } } @@ -189,23 +187,36 @@ ModificationOptions ExecutionPlan::createOptions (AstNode const* node) { } //////////////////////////////////////////////////////////////////////////////// -/// @brief add a node to the plan, will delete node if addition fails +/// @brief register a node with the plan, will delete node if addition fails //////////////////////////////////////////////////////////////////////////////// -ExecutionNode* ExecutionPlan::addNode (ExecutionNode* node) { +ExecutionNode* ExecutionPlan::registerNode (ExecutionNode* node) { TRI_ASSERT(node != nullptr); TRI_ASSERT(node->id() > 0); try { - _nodes.push_back(node); _ids.insert(std::make_pair(node->id(), node)); - - return node; } catch (...) { delete node; throw; } + return node; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief unregister a node to the plan +//////////////////////////////////////////////////////////////////////////////// + +void ExecutionPlan::unregisterNode (ExecutionNode* node) { + TRI_ASSERT(node != nullptr); + TRI_ASSERT(node->id() > 0); + + auto it = _ids.find(node->id()); + TRI_ASSERT(it != _ids.end()); + TRI_ASSERT(it->second == node); + + _ids.erase(it); } //////////////////////////////////////////////////////////////////////////////// @@ -224,9 +235,10 @@ CalculationNode* ExecutionPlan::createTemporaryCalculation (Ast const* ast, try { auto en = new CalculationNode(nextId(), expr, out); - addNode(reinterpret_cast(en)); + registerNode(reinterpret_cast(en)); return en; } + catch (...) { // prevent memleak delete expr; @@ -285,20 +297,20 @@ ExecutionNode* ExecutionPlan::fromNodeFor (Ast const* ast, if (collection == nullptr) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "no collection for EnumerateCollection"); } - en = addNode(new EnumerateCollectionNode(nextId(), ast->query()->vocbase(), collection, v)); + en = registerNode(new EnumerateCollectionNode(nextId(), ast->query()->vocbase(), collection, v)); } else if (expression->type == NODE_TYPE_REFERENCE) { // second operand is already a variable auto inVariable = static_cast(expression->getData()); TRI_ASSERT(inVariable != nullptr); - en = addNode(new EnumerateListNode(nextId(), inVariable, v)); + en = registerNode(new EnumerateListNode(nextId(), inVariable, v)); } else { // second operand is some misc. expression auto calc = createTemporaryCalculation(ast, expression); calc->addDependency(previous); - en = addNode(new EnumerateListNode(nextId(), calc->outVariable(), v)); + en = registerNode(new EnumerateListNode(nextId(), calc->outVariable(), v)); previous = calc; } @@ -325,14 +337,14 @@ ExecutionNode* ExecutionPlan::fromNodeFilter (Ast const* ast, // operand is already a variable auto v = static_cast(expression->getData()); TRI_ASSERT(v != nullptr); - en = addNode(new FilterNode(nextId(), v)); + en = registerNode(new FilterNode(nextId(), v)); } else { // operand is some misc expression auto calc = createTemporaryCalculation(ast, expression); calc->addDependency(previous); - en = addNode(new FilterNode(nextId(), calc->outVariable())); + en = registerNode(new FilterNode(nextId(), calc->outVariable())); previous = calc; } @@ -366,14 +378,14 @@ ExecutionNode* ExecutionPlan::fromNodeLet (Ast const* ast, THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } - en = addNode(new SubqueryNode(nextId(), subquery, v)); + en = registerNode(new SubqueryNode(nextId(), subquery, v)); } else { // operand is some misc expression, including references to other variables auto expr = new Expression(ast->query()->executor(), const_cast(expression)); try { - en = addNode(new CalculationNode(nextId(), expr, v)); + en = registerNode(new CalculationNode(nextId(), expr, v)); } catch (...) { // prevent memleak @@ -441,7 +453,7 @@ ExecutionNode* ExecutionPlan::fromNodeSort (Ast const* ast, previous = (*it); } - auto en = addNode(new SortNode(nextId(), elements)); + auto en = registerNode(new SortNode(nextId(), elements)); return addDependency(previous, en); } @@ -501,7 +513,7 @@ ExecutionNode* ExecutionPlan::fromNodeCollect (Ast const* ast, } // inject a sort node for all expressions / variables that we just picked up... - auto sort = addNode(new SortNode(nextId(), sortElements)); + auto sort = registerNode(new SortNode(nextId(), sortElements)); sort->addDependency(previous); previous = sort; @@ -514,7 +526,7 @@ ExecutionNode* ExecutionPlan::fromNodeCollect (Ast const* ast, outVariable = static_cast(v->getData()); } - auto en = addNode(new AggregateNode(nextId(), aggregateVariables, outVariable, ast->variables()->variables(false))); + auto en = registerNode(new AggregateNode(nextId(), aggregateVariables, outVariable, ast->variables()->variables(false))); return addDependency(previous, en); } @@ -535,7 +547,7 @@ ExecutionNode* ExecutionPlan::fromNodeLimit (Ast const* ast, TRI_ASSERT(offset->type == NODE_TYPE_VALUE); TRI_ASSERT(count->type == NODE_TYPE_VALUE); - auto en = addNode(new LimitNode(nextId(), static_cast(offset->getIntValue()), static_cast(count->getIntValue()))); + auto en = registerNode(new LimitNode(nextId(), static_cast(offset->getIntValue()), static_cast(count->getIntValue()))); return addDependency(previous, en); } @@ -558,13 +570,13 @@ ExecutionNode* ExecutionPlan::fromNodeReturn (Ast const* ast, // operand is already a variable auto v = static_cast(expression->getData()); TRI_ASSERT(v != nullptr); - en = addNode(new ReturnNode(nextId(), v)); + en = registerNode(new ReturnNode(nextId(), v)); } else { // operand is some misc expression auto calc = createTemporaryCalculation(ast, expression); calc->addDependency(previous); - en = addNode(new ReturnNode(nextId(), calc->outVariable())); + en = registerNode(new ReturnNode(nextId(), calc->outVariable())); previous = calc; } @@ -597,13 +609,13 @@ ExecutionNode* ExecutionPlan::fromNodeRemove (Ast const* ast, // operand is already a variable auto v = static_cast(expression->getData()); TRI_ASSERT(v != nullptr); - en = addNode(new RemoveNode(nextId(), ast->query()->vocbase(), collection, options, v, nullptr)); + en = registerNode(new RemoveNode(nextId(), ast->query()->vocbase(), collection, options, v, nullptr)); } else { // operand is some misc expression auto calc = createTemporaryCalculation(ast, expression); calc->addDependency(previous); - en = addNode(new RemoveNode(nextId(), ast->query()->vocbase(), collection, options, calc->outVariable(), nullptr)); + en = registerNode(new RemoveNode(nextId(), ast->query()->vocbase(), collection, options, calc->outVariable(), nullptr)); previous = calc; } @@ -631,13 +643,13 @@ ExecutionNode* ExecutionPlan::fromNodeInsert (Ast const* ast, // operand is already a variable auto v = static_cast(expression->getData()); TRI_ASSERT(v != nullptr); - en = addNode(new InsertNode(nextId(), ast->query()->vocbase(), collection, options, v, nullptr)); + en = registerNode(new InsertNode(nextId(), ast->query()->vocbase(), collection, options, v, nullptr)); } else { // operand is some misc expression auto calc = createTemporaryCalculation(ast, expression); calc->addDependency(previous); - en = addNode(new InsertNode(nextId(), ast->query()->vocbase(), collection, options, calc->outVariable(), nullptr)); + en = registerNode(new InsertNode(nextId(), ast->query()->vocbase(), collection, options, calc->outVariable(), nullptr)); previous = calc; } @@ -683,13 +695,13 @@ ExecutionNode* ExecutionPlan::fromNodeUpdate (Ast const* ast, // document operand is already a variable auto v = static_cast(docExpression->getData()); TRI_ASSERT(v != nullptr); - en = addNode(new UpdateNode(nextId(), ast->query()->vocbase(), collection, options, v, keyVariable, nullptr)); + en = registerNode(new UpdateNode(nextId(), ast->query()->vocbase(), collection, options, v, keyVariable, nullptr)); } else { // document operand is some misc expression auto calc = createTemporaryCalculation(ast, docExpression); calc->addDependency(previous); - en = addNode(new UpdateNode(nextId(), ast->query()->vocbase(), collection, options, calc->outVariable(), keyVariable, nullptr)); + en = registerNode(new UpdateNode(nextId(), ast->query()->vocbase(), collection, options, calc->outVariable(), keyVariable, nullptr)); previous = calc; } @@ -735,13 +747,13 @@ ExecutionNode* ExecutionPlan::fromNodeReplace (Ast const* ast, // operand is already a variable auto v = static_cast(docExpression->getData()); TRI_ASSERT(v != nullptr); - en = addNode(new ReplaceNode(nextId(), ast->query()->vocbase(), collection, options, v, keyVariable, nullptr)); + en = registerNode(new ReplaceNode(nextId(), ast->query()->vocbase(), collection, options, v, keyVariable, nullptr)); } else { // operand is some misc expression auto calc = createTemporaryCalculation(ast, docExpression); calc->addDependency(previous); - en = addNode(new ReplaceNode(nextId(), ast->query()->vocbase(), collection, options, calc->outVariable(), keyVariable, nullptr)); + en = registerNode(new ReplaceNode(nextId(), ast->query()->vocbase(), collection, options, calc->outVariable(), keyVariable, nullptr)); previous = calc; } @@ -756,7 +768,7 @@ ExecutionNode* ExecutionPlan::fromNode (Ast const* ast, AstNode const* node) { TRI_ASSERT(node != nullptr); - ExecutionNode* en = addNode(new SingletonNode(nextId())); + ExecutionNode* en = registerNode(new SingletonNode(nextId())); size_t const n = node->numMembers(); @@ -936,7 +948,7 @@ void ExecutionPlan::findVarUsage () { } //////////////////////////////////////////////////////////////////////////////// -/// @brief helper struct for removeNodes +/// @brief helper struct for unlinkNodes //////////////////////////////////////////////////////////////////////////////// struct NodeRemover : public WalkerWorker { @@ -963,6 +975,7 @@ struct NodeRemover : public WalkerWorker { } else { auto dep = en->getDependencies(); + //TODO make exception safe parents.back()->removeDependency(en); if (dep.size() == 1) { parents.back()->addDependency(dep[0]); @@ -977,29 +990,87 @@ struct NodeRemover : public WalkerWorker { } void after (ExecutionNode* en) { + _plan->unregisterNode(en); parents.pop_back(); } }; //////////////////////////////////////////////////////////////////////////////// -/// @brief removeNodes, note that this does not delete the removed +/// @brief unlinkNodes, note that this does not delete the removed /// nodes and that one cannot remove the root node of the plan. //////////////////////////////////////////////////////////////////////////////// -void ExecutionPlan::removeNodes (std::unordered_set& toRemove) { +void ExecutionPlan::unlinkNodes (std::unordered_set& toRemove) { NodeRemover remover(this, toRemove); root()->walk(&remover); } //////////////////////////////////////////////////////////////////////////////// -/// @brief removeNode, note that this does not delete the removed +/// @brief unlinkNode, note that this does not delete the removed /// node and that one cannot remove the root node of the plan. //////////////////////////////////////////////////////////////////////////////// -void ExecutionPlan::removeNode (ExecutionNode* node) { - std::unordered_set toRemove; - toRemove.insert(node); - return removeNodes(toRemove); +void ExecutionPlan::unlinkNode (ExecutionNode* node) { + std::unordered_set toUnlink; + toUnlink.insert(node); + return unlinkNodes(toUnlink); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief replaceNode, note that must be registered with the plan +/// before this method is called, also this does not delete the old +/// node and that one cannot replace the root node of the plan. +//////////////////////////////////////////////////////////////////////////////// + +void ExecutionPlan::replaceNode (ExecutionNode* oldNode, ExecutionNode* newNode, + ExecutionNode* oldNodeParent) { + TRI_ASSERT(oldNode->id() != newNode->id()); + TRI_ASSERT(newNode->getDependencies().size() ==0); + TRI_ASSERT(oldNode != _root); + + std::vector deps = oldNode->getDependencies(); + + for (auto x : deps) { + newNode->addDependency(x); + } + + if(!oldNodeParent->replaceDependency(oldNode, newNode)){ + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, + "Could not replace dependencies of an old node."); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clone the plan by recursively cloning starting from the root +//////////////////////////////////////////////////////////////////////////////// + +class CloneNodeAdder : public WalkerWorker { + ExecutionPlan* _plan; + + public: + + CloneNodeAdder (ExecutionPlan* plan) : _plan(plan){} + + ~CloneNodeAdder (){} + + void before (ExecutionNode* node){ + _plan->registerNode(node); + } +}; + +ExecutionPlan* ExecutionPlan::clone (){ + auto plan = new ExecutionPlan(); + try { + plan->_root = _root->clone(); + CloneNodeAdder adder(plan); + plan->_root->walk(&adder); + plan->findVarUsage(); + return plan; + } + catch (...) { + delete plan; + throw; + } } //////////////////////////////////////////////////////////////////////////////// @@ -1029,7 +1100,7 @@ ExecutionNode* ExecutionPlan::fromJson (Ast* ast, ret = ExecutionNode::fromJsonFactory(ast, oneJsonNode); - addNode(ret); + registerNode(ret); TRI_ASSERT(ret != nullptr); diff --git a/arangod/Aql/ExecutionPlan.h b/arangod/Aql/ExecutionPlan.h index 6a0eb5d67e..346f796f15 100644 --- a/arangod/Aql/ExecutionPlan.h +++ b/arangod/Aql/ExecutionPlan.h @@ -146,18 +146,43 @@ namespace triagens { void findVarUsage (); //////////////////////////////////////////////////////////////////////////////// -/// @brief removeNodes, note that this does not delete the removed +/// @brief unlinkNodes, note that this does not delete the removed /// nodes and that one cannot remove the root node of the plan. //////////////////////////////////////////////////////////////////////////////// - void removeNodes (std::unordered_set& toRemove); + void unlinkNodes (std::unordered_set& toUnlink); //////////////////////////////////////////////////////////////////////////////// -/// @brief removeNode, note that this does not delete the removed +/// @brief unlinkNode, note that this does not delete the removed /// node and that one cannot remove the root node of the plan. //////////////////////////////////////////////////////////////////////////////// - void removeNode (ExecutionNode*); + void unlinkNode (ExecutionNode*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief add a node to the plan, will delete node if addition fails +//////////////////////////////////////////////////////////////////////////////// + + ExecutionNode* registerNode (ExecutionNode*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief unregister a node to the plan +//////////////////////////////////////////////////////////////////////////////// + + void unregisterNode (ExecutionNode* node); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief replace oldNode with newNode and fix dependencies +//////////////////////////////////////////////////////////////////////////////// + + void replaceNode (ExecutionNode* oldNode, ExecutionNode* newNode, + ExecutionNode* oldNodeParent); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clone the plan by recursively cloning starting from the root +//////////////////////////////////////////////////////////////////////////////// + + ExecutionPlan* clone (); // ----------------------------------------------------------------------------- // --SECTION-- private methods @@ -171,11 +196,6 @@ namespace triagens { ModificationOptions createOptions (AstNode const*); -//////////////////////////////////////////////////////////////////////////////// -/// @brief add a node to the plan, will delete node if addition fails -//////////////////////////////////////////////////////////////////////////////// - - ExecutionNode* addNode (ExecutionNode*); //////////////////////////////////////////////////////////////////////////////// /// @brief creates a calculation node for an arbitrary expression @@ -295,12 +315,6 @@ namespace triagens { private: -//////////////////////////////////////////////////////////////////////////////// -/// @brief all nodes registered, used for memory management -//////////////////////////////////////////////////////////////////////////////// - - std::vector _nodes; - //////////////////////////////////////////////////////////////////////////////// /// @brief map from node id to the actual node //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Index.h b/arangod/Aql/Index.h index aa1239d9a2..81c577ea96 100644 --- a/arangod/Aql/Index.h +++ b/arangod/Aql/Index.h @@ -79,8 +79,6 @@ namespace triagens { return this->index()->_type; } - -// anything else?? // ----------------------------------------------------------------------------- // --SECTION-- public variables // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/Optimizer.cpp b/arangod/Aql/Optimizer.cpp index ae2a4f4d86..d8ac52f291 100644 --- a/arangod/Aql/Optimizer.cpp +++ b/arangod/Aql/Optimizer.cpp @@ -42,7 +42,7 @@ Optimizer::Optimizer () { // List all the rules in the system here: // try to find a filter after an enumerate collection and find an index . . . - // registerRule (useIndexRange, 999); + registerRule (useIndexRange, 999); // remove filters from the query that are not necessary at all // filters that are always true will be removed entirely diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index 4384810085..fcf98b18ca 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -50,7 +50,7 @@ int triagens::aql::removeUnnecessaryFiltersRule (Optimizer* opt, bool& keep) { keep = true; // plan will always be kept - std::unordered_set toRemove; + std::unordered_set toUnlink; std::vector nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::FILTER, true); for (auto n : nodes) { @@ -84,21 +84,20 @@ int triagens::aql::removeUnnecessaryFiltersRule (Optimizer* opt, if (root->toBoolean()) { // filter is always true // remove filter node and merge with following node - toRemove.insert(n); + toUnlink.insert(n); } else { // filter is always false // TODO: insert a NoResults node below it //auto noResultsNode = plan->registerNode(new NoResultsNode(plan->nextId())); //TRI_ASSERT(noResultsNode != nullptr); - } } - if (! toRemove.empty()) { - std::cout << "Removing " << toRemove.size() << " unnecessary " + if (! toUnlink.empty()) { + std::cout << "Removing " << toUnlink.size() << " unnecessary " "nodes..." << std::endl; - plan->removeNodes(toRemove); + plan->unlinkNodes(toUnlink); } return TRI_ERROR_NO_ERROR; @@ -171,7 +170,7 @@ std::cout << "LOOKING AT NODE OF TYPE: " << current->getTypeString() << "\n"; // no shared variables found. we can move the calculation up the dependency chain // first, delete the calculation from the plan - plan->removeNode(n); + plan->unlinkNode(n); // fiddle dependencies of calculation node n->removeDependencies(); @@ -208,7 +207,7 @@ int triagens::aql::removeUnnecessaryCalculationsRule (Optimizer* opt, keep = true; std::vector nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::CALCULATION, true); - std::unordered_set toRemove; + std::unordered_set toUnlink; for (auto n : nodes) { auto nn = static_cast(n); @@ -225,14 +224,14 @@ int triagens::aql::removeUnnecessaryCalculationsRule (Optimizer* opt, // The variable whose value is calculated here is not used at // all further down the pipeline! We remove the whole // calculation node, - toRemove.insert(n); + toUnlink.insert(n); } } - if (! toRemove.empty()) { - std::cout << "Removing " << toRemove.size() << " unnecessary " + if (! toUnlink.empty()) { + std::cout << "Removing " << toUnlink.size() << " unnecessary " "CalculationNodes..." << std::endl; - plan->removeNodes(toRemove); + plan->unlinkNodes(toUnlink); } return TRI_ERROR_NO_ERROR; @@ -246,10 +245,14 @@ class CalculationNodeFinder : public WalkerWorker { RangesInfo* _ranges; ExecutionPlan* _plan; Variable const* _var; + Optimizer::PlanList _out; + ExecutionNode* _prev; + + //EnumerateCollectionNode const* _enumColl; public: - CalculationNodeFinder (ExecutionPlan* plan, Variable const * var) - : _plan(plan), _var(var){ + CalculationNodeFinder (ExecutionPlan* plan, Variable const * var, Optimizer::PlanList& out) + : _plan(plan), _var(var), _out(out), _prev(nullptr){ _ranges = new RangesInfo(); }; @@ -259,29 +262,73 @@ class CalculationNodeFinder : public WalkerWorker { TRI_ASSERT(outvar.size() == 1); if(outvar[0]->id == _var->id){ auto node = static_cast(en); - std::string name; - buildRangeInfo(node->expression()->node(), name); + std::string attr; + std::string enumCollVar; + buildRangeInfo(node->expression()->node(), enumCollVar, attr); } } + else if (en->getType() == triagens::aql::ExecutionNode::ENUMERATE_COLLECTION) { + auto node = static_cast(en); + auto var = node->getVariablesSetHere()[0]; // should only be 1 + auto map = _ranges->find(var->name); // check if we have any ranges with this var + + if (map != nullptr) { + // check the first components of against indexes of . . . + // FIXME does this need to be done like this? Couldn't we keep track + // earlier? + std::vector attrs; + std::vector rangeInfo; + for (auto x : *map){ + attrs.push_back(x.first); + rangeInfo.push_back(x.second); + } + std::vector idxs = node->getIndexes(attrs); + + //use make one new plan for every index in that replaces the + //enumerate collection node with a RangeIndexNode . . . + for (auto idx: idxs) { + std::cout << "FOUND INDEX!\n"; + auto newPlan = _plan->clone(); + ExecutionNode* newNode = nullptr; + try{ + newNode = new IndexRangeNode( newPlan->nextId(), node->vocbase(), + node->collection(), node->outVariable(), idx, &rangeInfo); + newPlan->registerNode(newNode); + } + catch (...) { + if (newNode != nullptr) { + delete newNode; + } + delete newPlan; + throw; + } + newPlan->replaceNode(newPlan->getNodeById(node->id()), newNode, + newPlan->getNodeById(_prev->id())); + std::cout << newPlan->root()->toJson().toString() << "\n"; + _out.push_back(newPlan); + } + } + } + _prev = en; } - void buildRangeInfo (AstNode const* node, std::string& name){ + void buildRangeInfo (AstNode const* node, std::string& enumCollVar, std::string& attr){ if(node->type == NODE_TYPE_REFERENCE){ - auto var = static_cast(node->getData()); - auto setter = _plan->getVarSetBy(var->id); + auto x = static_cast(node->getData()); + auto setter = _plan->getVarSetBy(x->id); if( setter != nullptr && - setter->getType() == triagens::aql::ExecutionNode::ENUMERATE_COLLECTION){ - name = var->name; + setter->getType() == triagens::aql::ExecutionNode::ENUMERATE_COLLECTION){ + enumCollVar = x->name; } return; } if(node->type == NODE_TYPE_ATTRIBUTE_ACCESS){ char const* attributeName = node->getStringValue(); - buildRangeInfo(node->getMember(0), name); - if(!name.empty()){ - name.push_back('.'); - name.append(attributeName); + buildRangeInfo(node->getMember(0), enumCollVar, attr); + if(!enumCollVar.empty()){ + attr.append(attributeName); + attr.push_back('.'); } } @@ -303,14 +350,13 @@ class CalculationNodeFinder : public WalkerWorker { } if(val != nullptr){ - buildRangeInfo(nextNode, name); - if(!name.empty()){ - _ranges->insert(name, new RangeInfoBound(val, true), - new RangeInfoBound(val, true)); + buildRangeInfo(nextNode, enumCollVar, attr); + if(!enumCollVar.empty()){ + _ranges->insert(enumCollVar, attr.substr(0, attr.size()-1), + new RangeInfoBound(val, true), new RangeInfoBound(val, true)); } } - - std::cout << _ranges->toString() << "\n"; + //std::cout << _ranges->toString() << "\n"; } if(node->type == NODE_TYPE_OPERATOR_BINARY_LT || @@ -355,21 +401,22 @@ class CalculationNodeFinder : public WalkerWorker { } if(low != nullptr || high != nullptr){ - buildRangeInfo(nextNode, name); - if(!name.empty()){ - _ranges->insert(name, low, high); + buildRangeInfo(nextNode, enumCollVar, attr); + if(!enumCollVar.empty()){ + _ranges->insert(enumCollVar, attr.substr(0, attr.size()-1), low, high); } } - std::cout << _ranges->toString() << "\n"; + //std::cout << _ranges->toString() << "\n"; } if(node->type == NODE_TYPE_OPERATOR_BINARY_AND){ - buildRangeInfo(node->getMember(0), name); - buildRangeInfo(node->getMember(1), name); - std::cout << _ranges->toString() << "\n"; + attr = ""; + buildRangeInfo(node->getMember(0), enumCollVar, attr); + attr = ""; + buildRangeInfo(node->getMember(1), enumCollVar, attr); + //std::cout << _ranges->toString() << "\n"; } } - }; //////////////////////////////////////////////////////////////////////////////// @@ -380,7 +427,7 @@ int triagens::aql::useIndexRange (Optimizer* opt, ExecutionPlan* plan, Optimizer::PlanList& out, bool& keep) { - + keep = true; std::vector nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::FILTER, true); @@ -388,7 +435,7 @@ int triagens::aql::useIndexRange (Optimizer* opt, auto nn = static_cast(n); auto invars = nn->getVariablesUsedHere(); TRI_ASSERT(invars.size() == 1); - CalculationNodeFinder finder(plan, invars[0]); + CalculationNodeFinder finder(plan, invars[0], out); nn->walk(&finder); } diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index 5b58a18eee..356e0e1c72 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -246,7 +246,7 @@ QueryResult Query::execute () { opt.createPlans(plan); // Now plan and all derived plans belong to the // optimizer plan = opt.stealBest(); // Now we own the best one again - + TRI_ASSERT(plan != nullptr); triagens::basics::Json json(triagens::basics::Json::List); triagens::basics::Json stats;