diff --git a/arangod/Aql/AstNode.cpp b/arangod/Aql/AstNode.cpp index 304c178a5e..4f2ed034ff 100644 --- a/arangod/Aql/AstNode.cpp +++ b/arangod/Aql/AstNode.cpp @@ -814,6 +814,31 @@ void AstNode::append (triagens::basics::StringBuffer* buffer) const { } } +//////////////////////////////////////////////////////////////////////////////// +/// @brief stringify a node +/// note that currently stringification is only supported for certain node types +//////////////////////////////////////////////////////////////////////////////// + +std::string AstNode::stringify () const { + switch (type) { + case NODE_TYPE_ATTRIBUTE_ACCESS: { + auto member = getMember(0); + return member->stringify() + std::string(".") + getStringValue(); + } + + case NODE_TYPE_REFERENCE: { + auto variable = static_cast(getData()); + TRI_ASSERT(variable != nullptr); + return variable->name; + } + + default: { + } + } + + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "stringification not supported for node type"); +} + // ----------------------------------------------------------------------------- // --SECTION-- private methods // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/AstNode.h b/arangod/Aql/AstNode.h index 04b9669985..4add133907 100644 --- a/arangod/Aql/AstNode.h +++ b/arangod/Aql/AstNode.h @@ -493,6 +493,13 @@ namespace triagens { void append (triagens::basics::StringBuffer*) const; +//////////////////////////////////////////////////////////////////////////////// +/// @brief stringify a node +/// note that currently stringification is only supported for certain node types +//////////////////////////////////////////////////////////////////////////////// + + std::string stringify () const; + // ----------------------------------------------------------------------------- // --SECTION-- private methods // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index 14e54e829b..b90299a95e 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -27,6 +27,7 @@ #include "Aql/ExecutionNode.h" #include "Aql/Collection.h" +#include "Aql/ExecutionPlan.h" #include "Aql/WalkerWorker.h" #include "Aql/Ast.h" @@ -70,7 +71,7 @@ std::unordered_map const ExecutionNode::TypeNames{ /// @brief returns the type name of the node //////////////////////////////////////////////////////////////////////////////// -const std::string& ExecutionNode::getTypeString () const { +std::string const& ExecutionNode::getTypeString () const { auto it = TypeNames.find(static_cast(getType())); if (it != TypeNames.end()) { return (*it).second; @@ -918,6 +919,44 @@ std::vector> SortNode::getCalcNodePairs () return findExp._myVars; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns all sort information +//////////////////////////////////////////////////////////////////////////////// + +SortInformation SortNode::getSortInformation (ExecutionPlan* plan) const { + SortInformation result; + + auto elements = getElements(); + for (auto it = elements.begin(); it != elements.end(); ++it) { + auto variable = (*it).first; + TRI_ASSERT(variable != nullptr); + auto setter = plan->getVarSetBy(variable->id); + + if (setter == nullptr) { + result.isValid = false; + break; + } + + if (setter->getType() == ExecutionNode::CALCULATION) { + // variable introduced by a calculation + auto expression = static_cast(setter)->expression(); + + if (! expression->isAttributeAccess() && + ! expression->isReference()) { + result.isComplex = true; + break; + } + + result.criteria.emplace_back(std::make_tuple(setter, expression->stringify(), (*it).second)); + } + else { + result.criteria.emplace_back(std::make_tuple(setter, variable->name, (*it).second)); + } + } + + return result; +} + // ----------------------------------------------------------------------------- // --SECTION-- methods of AggregateNode // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/ExecutionNode.h b/arangod/Aql/ExecutionNode.h index bcc5cf69d5..7913f82966 100644 --- a/arangod/Aql/ExecutionNode.h +++ b/arangod/Aql/ExecutionNode.h @@ -154,7 +154,7 @@ namespace triagens { /// @brief return the type name of the node //////////////////////////////////////////////////////////////////////////////// - const std::string &getTypeString () const; + std::string const& getTypeString () const; //////////////////////////////////////////////////////////////////////////////// /// @brief checks whether we know a type of this kind; throws exception if not. @@ -1413,6 +1413,20 @@ namespace triagens { }; +// ----------------------------------------------------------------------------- +// --SECTION-- struct SortInformation +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief this is an auxilliary struct for processed sort criteria information +//////////////////////////////////////////////////////////////////////////////// + + struct SortInformation { + std::vector> criteria; + bool isValid = true; + bool isComplex = false; + }; + // ----------------------------------------------------------------------------- // --SECTION-- class SortNode // ----------------------------------------------------------------------------- @@ -1493,10 +1507,16 @@ namespace triagens { /// @brief get Variables Used Here including ASC/DESC //////////////////////////////////////////////////////////////////////////////// - std::vector> getElements () { + std::vector> getElements () const { return _elements; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns all sort information +//////////////////////////////////////////////////////////////////////////////// + + SortInformation getSortInformation (ExecutionPlan*) const; + std::vector> getCalcNodePairs (); // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index 47c8e50a41..696b8f2268 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -347,14 +347,11 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, } //////////////////////////////////////////////////////////////////////////////// -/// @brief check whether this is a simple access to a variable with n attributes. +/// @brief check whether this is an attribute access of any degree (e.g. a.b, +/// a.b.c, ...) //////////////////////////////////////////////////////////////////////////////// -bool Expression::isMultipleAttributeAccess () const { - if (!isSimple()) { - return false; - } - +bool Expression::isAttributeAccess () const { auto expNode = _node; if (expNode->type != triagens::aql::NODE_TYPE_ATTRIBUTE_ACCESS) { @@ -362,15 +359,23 @@ bool Expression::isMultipleAttributeAccess () const { } while (expNode->type == triagens::aql::NODE_TYPE_ATTRIBUTE_ACCESS) { - expNode = expNode->getMember (0); + expNode = expNode->getMember(0); } return (expNode->type == triagens::aql::NODE_TYPE_REFERENCE); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief check whether this is a reference access +//////////////////////////////////////////////////////////////////////////////// + +bool Expression::isReference () const { + return (_node->type == triagens::aql::NODE_TYPE_REFERENCE); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief this gives you ("variable.access", "Reference") -/// call isSimpleAccessReference in advance to enshure no exceptions. +/// call isSimpleAccessReference in advance to ensure no exceptions. //////////////////////////////////////////////////////////////////////////////// std::pair Expression::getMultipleAttributes() const { if (!isSimple()) { @@ -411,6 +416,14 @@ std::pair Expression::getMultipleAttributes() const { } +//////////////////////////////////////////////////////////////////////////////// +/// @brief stringify an expression +/// note that currently stringification is only supported for certain node types +//////////////////////////////////////////////////////////////////////////////// + +std::string Expression::stringify () const { + return _node->stringify(); +} // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE diff --git a/arangod/Aql/Expression.h b/arangod/Aql/Expression.h index f4d4652237..41c8431e23 100644 --- a/arangod/Aql/Expression.h +++ b/arangod/Aql/Expression.h @@ -151,18 +151,32 @@ namespace triagens { } //////////////////////////////////////////////////////////////////////////////// -/// @brief check whether this is a simple access to a variable with n attributes. +/// @brief check whether this is an attribute access of any degree (e.g. a.b, +/// a.b.c, ...) //////////////////////////////////////////////////////////////////////////////// - bool isMultipleAttributeAccess () const; + bool isAttributeAccess () const; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief check whether this is only a reference access +//////////////////////////////////////////////////////////////////////////////// + + bool isReference () const; //////////////////////////////////////////////////////////////////////////////// /// @brief this gives you ("variable.access", "Reference") -/// call isSimpleAccessReference in advance to enshure no exceptions. +/// call isSimpleAccessReference in advance to ensure no exceptions. //////////////////////////////////////////////////////////////////////////////// std::pair getMultipleAttributes() const; +//////////////////////////////////////////////////////////////////////////////// +/// @brief stringify an expression +/// note that currently stringification is only supported for certain node types +//////////////////////////////////////////////////////////////////////////////// + + std::string stringify () const; + // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/Optimizer.cpp b/arangod/Aql/Optimizer.cpp index 07ecc37ea4..46f10c0512 100644 --- a/arangod/Aql/Optimizer.cpp +++ b/arangod/Aql/Optimizer.cpp @@ -170,10 +170,14 @@ void Optimizer::setupRules () { TRI_ASSERT(_rules.empty()); // List all the rules in the system here: + // lower level values mean earlier rule execution + // if two rules have the same level value, they will be executed in declaration order // try to find sort blocks which are superseeded by indexes registerRule("use-index-for-sort", useIndexForSort, 2000); - + + // remove redundant sort blocks + registerRule("remove-redundant-sorts", removeRedundantSorts, 1000); // try to find a filter after an enumerate collection and find an index . . . registerRule("use-index-range", useIndexRange, 999); diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index 82ca47db05..dd45558aa5 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -49,6 +49,38 @@ int triagens::aql::dummyRule (Optimizer*, return TRI_ERROR_NO_ERROR; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief remove redundant sorts +/// this rule modifies the plan in place: +/// - sorts that are covered by earlier sorts will be removed +//////////////////////////////////////////////////////////////////////////////// + +int triagens::aql::removeRedundantSorts (Optimizer* opt, + ExecutionPlan* plan, + int level, + Optimizer::PlanList& out) { + // should we enter subqueries?? + std::vector nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::SORT, true); + + for (auto n : nodes) { + auto sortInfo = static_cast(n)->getSortInformation(plan); + + if (sortInfo.isValid) { + /* + TODO: finalize + std::cout << "FOUND SORT:\n"; + for (auto it = sortInfo.criteria.begin(); it != sortInfo.criteria.end(); ++it) { + std::cout << "- " << std::get<1>(*it) << ", " << std::get<2>(*it) << "\n"; + } + */ + } + } + + out.push_back(plan, level); + + return TRI_ERROR_NO_ERROR; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief remove all unnecessary filters /// this rule modifies the plan in place: @@ -61,6 +93,7 @@ int triagens::aql::removeUnnecessaryFiltersRule (Optimizer* opt, int level, Optimizer::PlanList& out) { std::unordered_set toUnlink; + // should we enter subqueries?? std::vector nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::FILTER, true); for (auto n : nodes) { @@ -594,11 +627,11 @@ public: d->ASC = sortParams[n].second; d->calculationNodeID = sortParams[n].first->id(); - if (sortParams[n].first->getType() == EN::CALCULATION) { + if (sortParams[n].first->getType() == EN::CALCULATION) { auto cn = static_cast(sortParams[n].first); auto oneSortExpression = cn->expression(); - if (oneSortExpression->isMultipleAttributeAccess()) { + if (oneSortExpression->isAttributeAccess()) { auto simpleExpression = oneSortExpression->getMultipleAttributes(); d->variableName = simpleExpression.first; d->attributevec = simpleExpression.second; @@ -813,7 +846,6 @@ int triagens::aql::useIndexForSort (Optimizer* opt, ExecutionPlan* plan, int level, Optimizer::PlanList& out) { - std::cout << plan->toJson(TRI_UNKNOWN_MEM_ZONE, false).toString() << "\n"; std::vector nodes = plan->findNodesOfType(triagens::aql::ExecutionNode::SORT, true); for (auto n : nodes) { diff --git a/arangod/Aql/OptimizerRules.h b/arangod/Aql/OptimizerRules.h index 621579fb72..d1f6c35034 100644 --- a/arangod/Aql/OptimizerRules.h +++ b/arangod/Aql/OptimizerRules.h @@ -46,6 +46,14 @@ namespace triagens { int dummyRule (Optimizer*, ExecutionPlan*, int level, Optimizer::PlanList&); +//////////////////////////////////////////////////////////////////////////////// +/// @brief remove redundant sorts +/// this rule modifies the plan in place: +/// - sorts that are covered by earlier sorts will be removed +//////////////////////////////////////////////////////////////////////////////// + + int removeRedundantSorts (Optimizer*, ExecutionPlan*, int level, Optimizer::PlanList&); + //////////////////////////////////////////////////////////////////////////////// /// @brief remove all unnecessary filters /// this rule modifies the plan in place: