//////////////////////////////////////////////////////////////////////////////// /// @brief Condition finder, used to build up the Condition object /// /// @file arangod/Aql/ConditionFinder.h /// /// DISCLAIMER /// /// Copyright 2010-2014 triagens GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Michael Hackstein /// @author Copyright 2015, ArangoDB GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "Aql/ConditionFinder.h" #include "Aql/ExecutionPlan.h" #include "Aql/IndexNode.h" #include "Aql/SortCondition.h" #include "Aql/SortNode.h" using namespace triagens::aql; using EN = triagens::aql::ExecutionNode; bool ConditionFinder::before (ExecutionNode* en) { if (! _variableDefinitions.empty() && en->canThrow()) { // we already found a FILTER and // something that can throw is not safe to optimize _filters.clear(); _sorts.clear(); return true; } switch (en->getType()) { case EN::ENUMERATE_LIST: case EN::AGGREGATE: case EN::SCATTER: case EN::DISTRIBUTE: case EN::GATHER: case EN::REMOTE: case EN::SUBQUERY: case EN::INDEX: case EN::INDEX_RANGE: case EN::INSERT: case EN::REMOVE: case EN::REPLACE: case EN::UPDATE: case EN::UPSERT: case EN::RETURN: // in these cases we simply ignore the intermediate nodes, note // that we have taken care of nodes that could throw exceptions // above. break; case EN::LIMIT: // LIMIT invalidates the sort expression we already found _sorts.clear(); break; case EN::SINGLETON: case EN::NORESULTS: case EN::ILLEGAL: // in all these cases we better abort return true; case EN::FILTER: { std::vector&& invars = en->getVariablesUsedHere(); TRI_ASSERT(invars.size() == 1); // register which variable is used in a FILTER _filters.emplace(invars[0]->id); break; } case EN::SORT: { // register which variables are used in a SORT if (_sorts.empty()) { for (auto& it : static_cast(en)->getElements()) { _sorts.emplace_back(std::make_pair((it.first)->id, it.second)); } } break; } case EN::CALCULATION: { auto outvars = en->getVariablesSetHere(); TRI_ASSERT(outvars.size() == 1); _variableDefinitions.emplace(outvars[0]->id, static_cast(en)->expression()->node()); break; } case EN::ENUMERATE_COLLECTION: { auto node = static_cast(en); if (_changes->find(node->id()) != _changes->end()) { // already optimized this node break; } auto const& varsValid = node->getVarsValid(); std::unordered_set varsUsed; std::unique_ptr condition(new Condition(_plan->getAst())); for (auto& it : _variableDefinitions) { if (_filters.find(it.first) != _filters.end()) { // a variable used in a FILTER // now check if all variables from the FILTER condition are still valid here Ast::getReferencedVariables(it.second, varsUsed); bool valid = true; for (auto& it2 : varsUsed) { if (varsValid.find(it2) == varsValid.end()) { valid = false; } } if (valid) { condition->andCombine(it.second); } } } condition->normalize(_plan); if (condition->isEmpty()) { // no filter conditions left break; } std::vector usedIndexes; SortCondition sortCondition(_sorts, _variableDefinitions); if (condition->findIndexes(node, usedIndexes, sortCondition)) { bool reverse = false; if (sortCondition.isUnidirectional()) { reverse = sortCondition.isDescending(); } TRI_ASSERT(! usedIndexes.empty()); std::cout << node->id() << " Number of indexes used: " << usedIndexes.size() << std::endl; // We either can find indexes for everything or findIndexes will clear out usedIndexes std::unique_ptr newNode(new IndexNode( _plan, _plan->nextId(), node->vocbase(), node->collection(), node->outVariable(), usedIndexes, condition.get(), reverse )); condition.release(); // We keep this nodes change _changes->emplace(node->id(), newNode.get()); newNode.release(); } break; } } return false; } bool ConditionFinder::enterSubquery (ExecutionNode* super, ExecutionNode* sub) { return false; } // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: