1
0
Fork 0
arangodb/arangod/Aql/OptimizerRule.h

268 lines
8.7 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2017 ArangoDB 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 Jan Steemann
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_AQL_AQL_OPTIMIZER_RULE_H
#define ARANGOD_AQL_AQL_OPTIMIZER_RULE_H 1
#include "Basics/Common.h"
namespace arangodb {
namespace aql {
class ExecutionPlan;
class Optimizer;
struct OptimizerRule;
/// @brief type of an optimizer rule function, the function gets an
/// optimizer, an ExecutionPlan, and the current rule. it has
/// to append one or more plans to the resulting deque. This must
/// include the original plan if it ought to be kept. The rule has to
/// set the level of the appended plan to the largest level of rule
/// that ought to be considered as done to indicate which rule is to be
/// applied next.
typedef std::function<void(Optimizer*, std::unique_ptr<ExecutionPlan>, OptimizerRule const*)>
RuleFunction;
/// @brief type of an optimizer rule
struct OptimizerRule {
/// @brief optimizer rules
enum RuleLevel : int {
// List all the rules in the system here:
// lower level values mean earlier rule execution
// note that levels must be unique
initial = 100,
// "Pass 1": moving nodes "up" (potentially outside loops):
// ========================================================
// determine the "right" type of CollectNode and
// add a sort node for each COLLECT (may be removed later)
specializeCollectRule_pass1,
// remove legacy geo functions
removeLegacyGeoFunctions_pass1,
inlineSubqueriesRule_pass1,
// split and-combined filters into multiple smaller filters
splitFiltersRule_pass1,
// move calculations up the dependency chain (to pull them out of
// inner loops etc.)
moveCalculationsUpRule_pass1,
// move filters up the dependency chain (to make result sets as small
// as possible as early as possible)
moveFiltersUpRule_pass1,
// remove calculations that are repeatedly used in a query
removeRedundantCalculationsRule_pass1,
// "Pass 2": try to remove redundant or unnecessary nodes
// ======================================================
// remove filters from the query that are not necessary at all
// filters that are always true will be removed entirely
// filters that are always false will be replaced with a NoResults node
removeUnnecessaryFiltersRule_pass2,
// remove calculations that are never necessary
removeUnnecessaryCalculationsRule_pass2,
// remove redundant sort blocks
removeRedundantSortsRule_pass2,
// push limits into subqueries and simplify them
optimizeSubqueriesRule_pass2,
// "Pass 3": interchange EnumerateCollection nodes in all possible ways
// this is level 500, please never let new plans from higher
// levels go back to this or lower levels!
// ======================================================
interchangeAdjacentEnumerationsRule_pass3,
// "Pass 4": moving nodes "up" (potentially outside loops) (second try):
// ======================================================
// move calculations up the dependency chain (to pull them out of
// inner loops etc.)
moveCalculationsUpRule_pass4,
// move filters up the dependency chain (to make result sets as small
// as possible as early as possible)
moveFiltersUpRule_pass4,
/// "Pass 5": try to remove redundant or unnecessary nodes (second try)
// remove filters from the query that are not necessary at all
// filters that are always true will be removed entirely
// filters that are always false will be replaced with a NoResults node
// ======================================================
// remove redundant sort blocks
removeRedundantSortsRule_pass5,
// remove SORT RAND() if appropriate
removeSortRandRule_pass5,
// remove INTO for COLLECT if appropriate
removeCollectVariablesRule_pass5,
// propagate constant attributes in FILTERs
propagateConstantAttributesRule_pass5,
// remove unused out variables for data-modification queries
removeDataModificationOutVariablesRule_pass5,
/// "Pass 6": use indexes if possible for FILTER and/or SORT nodes
// ======================================================
// replace simple OR conditions with IN
replaceOrWithInRule_pass6,
// remove redundant OR conditions
removeRedundantOrRule_pass6,
// remove FILTER and SORT if there are geoindexes
applyGeoIndexRule_pass6,
// replace FULLTEXT with index
applyFulltextIndexRule_pass6,
useIndexesRule_pass6,
// try to remove filters covered by index ranges
removeFiltersCoveredByIndexRule_pass6,
removeUnnecessaryFiltersRule_pass6,
// try to find sort blocks which are superseeded by indexes
useIndexForSortRule_pass6,
// sort values used in IN comparisons of remaining filters
sortInValuesRule_pass6,
// merge filters into graph traversals
optimizeTraversalsRule_pass6,
// remove redundant filters statements
removeFiltersCoveredByTraversal_pass6,
// remove calculations that are redundant
// needs to run after filter removal
removeUnnecessaryCalculationsRule_pass6,
#ifdef USE_IRESEARCH
// move filters and sort conditions into views and remove them
handleViewsRule_pass6,
#endif
// remove now obsolete path variables
removeTraversalPathVariable_pass6,
prepareTraversalsRule_pass6,
/// Pass 9: push down calculations beyond FILTERs and LIMITs
moveCalculationsDownRule_pass9,
/// Pass 9: patch update statements
patchUpdateStatementsRule_pass9,
/// "Pass 10": final transformations for the cluster
// optimize queries in the cluster so that the entire query
// gets pushed to a single server
optimizeClusterSingleShardRule_pass10,
// make operations on sharded collections use distribute
distributeInClusterRule_pass10,
// try to find candidates for shard-local joins in the cluster
optimizeClusterJoinsRule_pass10,
// make operations on sharded collections use scatter / gather / remote
scatterInClusterRule_pass10,
#ifdef USE_IRESEARCH
// FIXME order-???
// make operations on sharded IResearch views use scatter / gather / remote
scatterIResearchViewInClusterRule_pass10,
#endif
// move FilterNodes & Calculation nodes in between
// scatter(remote) <-> gather(remote) so they're
// distributed to the cluster nodes.
distributeFilternCalcToClusterRule_pass10,
// move SortNodes into the distribution.
// adjust gathernode to also contain the sort criteria.
distributeSortToClusterRule_pass10,
// try to get rid of a RemoteNode->ScatterNode combination which has
// only a SingletonNode and possibly some CalculationNodes as dependencies
removeUnnecessaryRemoteScatterRule_pass10,
#ifdef USE_ENTERPRISE
// remove any superflous satellite collection joins...
// put it after Scatter rule because we would do
// the work twice otherwise
removeSatelliteJoinsRule_pass10,
#endif
// recognize that a RemoveNode can be moved to the shards
undistributeRemoveAfterEnumCollRule_pass10,
// push collect operations to the db servers
collectInClusterRule_pass10,
// try to restrict fragments to a single shard if possible
restrictToSingleShardRule_pass10,
// simplify an EnumerationCollectionNode that fetches an
// entire document to a projection of this document
reduceExtractionToProjectionRule_pass10,
};
std::string name;
RuleFunction func;
RuleLevel const level;
bool const canCreateAdditionalPlans;
bool const canBeDisabled;
bool const isHidden;
OptimizerRule() = delete;
OptimizerRule(std::string const& name, RuleFunction const& func, RuleLevel level,
bool canCreateAdditionalPlans, bool canBeDisabled, bool isHidden)
: name(name),
func(func),
level(level),
canCreateAdditionalPlans(canCreateAdditionalPlans),
canBeDisabled(canBeDisabled),
isHidden(isHidden) {}
};
} // namespace aql
} // namespace arangodb
#endif