mirror of https://gitee.com/bigwinds/arangodb
added (still disabled) optimizer rule `inline-subqueries`
This commit is contained in:
parent
7f662bac09
commit
1ca11411fd
|
@ -394,6 +394,13 @@ void Optimizer::setupRules() {
|
|||
registerHiddenRule("specialize-collect", specializeCollectRule,
|
||||
specializeCollectRule_pass1, false);
|
||||
|
||||
// inline subqueries one level higher
|
||||
// rule not yet tested
|
||||
#if 0
|
||||
registerRule("inline-subqueries", inlineSubqueriesRule,
|
||||
inlineSubqueriesRule_pass1, true);
|
||||
#endif
|
||||
|
||||
// move calculations up the dependency chain (to pull them out of
|
||||
// inner loops etc.)
|
||||
registerRule("move-calculations-up", moveCalculationsUpRule,
|
||||
|
|
|
@ -95,6 +95,8 @@ class Optimizer {
|
|||
// determine the "right" type of CollectNode and
|
||||
// add a sort node for each COLLECT (may be removed later)
|
||||
specializeCollectRule_pass1 = 105,
|
||||
|
||||
inlineSubqueriesRule_pass1 = 106,
|
||||
|
||||
// split and-combined filters into multiple smaller filters
|
||||
splitFiltersRule_pass1 = 110,
|
||||
|
|
|
@ -901,6 +901,7 @@ void arangodb::aql::moveCalculationsUpRule(Optimizer* opt, ExecutionPlan* plan,
|
|||
|
||||
// first, unlink the calculation from the plan
|
||||
plan->unlinkNode(n);
|
||||
|
||||
// and re-insert into before the current node
|
||||
plan->insertDependency(current, n);
|
||||
modified = true;
|
||||
|
@ -1423,6 +1424,9 @@ class arangodb::aql::RedundantCalculationsReplacer final
|
|||
for (auto& variable : node->_groupVariables) {
|
||||
variable.second = Variable::replace(variable.second, _replacements);
|
||||
}
|
||||
for (auto& variable : node->_aggregateVariables) {
|
||||
variable.second.first = Variable::replace(variable.second.first, _replacements);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3538,11 +3542,136 @@ void arangodb::aql::mergeFilterIntoTraversalRule(Optimizer* opt,
|
|||
// These are all the end nodes where we start
|
||||
std::vector<ExecutionNode*> nodes(plan->findEndNodes(true));
|
||||
|
||||
bool planAltered = false;
|
||||
bool modified = false;
|
||||
for (auto const& n : nodes) {
|
||||
TraversalConditionFinder finder(plan, &planAltered);
|
||||
TraversalConditionFinder finder(plan, &modified);
|
||||
n->walk(&finder);
|
||||
}
|
||||
|
||||
opt->addPlan(plan, rule, planAltered);
|
||||
opt->addPlan(plan, rule, modified);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief pulls out simple subqueries and merges them with the level above
|
||||
///
|
||||
/// For example, if we have the input query
|
||||
///
|
||||
/// FOR x IN (
|
||||
/// FOR y IN collection FILTER y.value >= 5 RETURN y.test
|
||||
/// )
|
||||
/// RETURN x.a
|
||||
///
|
||||
/// then this rule will transform it into:
|
||||
///
|
||||
/// FOR tmp IN collection
|
||||
/// FILTER tmp.value >= 5
|
||||
/// LET x = tmp.test
|
||||
/// RETURN x.a
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void arangodb::aql::inlineSubqueriesRule(Optimizer* opt,
|
||||
ExecutionPlan* plan,
|
||||
Optimizer::Rule const* rule) {
|
||||
|
||||
std::vector<ExecutionNode*> nodes(
|
||||
plan->findNodesOfType(EN::SUBQUERY, true));
|
||||
|
||||
if (nodes.empty()) {
|
||||
opt->addPlan(plan, rule, false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
|
||||
for (auto const& n : nodes) {
|
||||
auto subqueryNode = static_cast<SubqueryNode const*>(n);
|
||||
|
||||
if (subqueryNode->isModificationQuery()) {
|
||||
// can't modify modifying subqueries
|
||||
continue;
|
||||
}
|
||||
|
||||
Variable const* out = subqueryNode->outVariable();
|
||||
TRI_ASSERT(out != nullptr);
|
||||
|
||||
auto current = n;
|
||||
// now check where the subquery is used
|
||||
while (current->hasParent()) {
|
||||
if (current->getType() == EN::ENUMERATE_LIST) {
|
||||
// we're only interested in FOR loops...
|
||||
auto listNode = static_cast<EnumerateListNode*>(current);
|
||||
|
||||
// ...that use our subquery as its input
|
||||
if (listNode->inVariable() == out) {
|
||||
// bingo!
|
||||
auto queryVariables = plan->getAst()->variables();
|
||||
std::vector<ExecutionNode*> subNodes(subqueryNode->getSubquery()->getDependencyChain(true));
|
||||
|
||||
TRI_ASSERT(! subNodes.empty());
|
||||
auto returnNode = static_cast<ReturnNode*>(subNodes[0]);
|
||||
TRI_ASSERT(returnNode->getType() == EN::RETURN);
|
||||
|
||||
modified = true;
|
||||
auto previous = n->getFirstDependency();
|
||||
auto insert = n->getFirstParent();
|
||||
TRI_ASSERT(insert != nullptr);
|
||||
|
||||
// unlink the original SubqueryNode
|
||||
plan->unlinkNode(n, false);
|
||||
|
||||
for (auto& it : subNodes) {
|
||||
// first unlink them all
|
||||
plan->unlinkNode(it, true);
|
||||
|
||||
if (it->getType() == EN::SINGLETON) {
|
||||
// reached the singleton node already. that means we can stop
|
||||
break;
|
||||
}
|
||||
|
||||
// and now insert them one level up
|
||||
if (it != returnNode) {
|
||||
// we skip over the subquery's return node. we don't need it anymore
|
||||
insert->removeDependencies();
|
||||
insert->addDependency(it);
|
||||
insert = it;
|
||||
|
||||
// additionally rename the variables from the subquery so they cannot
|
||||
// conflict with the ones from the top query
|
||||
for (auto const& variable : it->getVariablesSetHere()) {
|
||||
queryVariables->renameVariable(variable->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// link the top node in the subquery with the original plan
|
||||
if (previous != nullptr) {
|
||||
insert->addDependency(previous);
|
||||
}
|
||||
|
||||
// remove the list node from the plan
|
||||
plan->unlinkNode(listNode, false);
|
||||
|
||||
queryVariables->renameVariable(returnNode->inVariable()->id, listNode->outVariable()->name);
|
||||
|
||||
// finally replace the variables
|
||||
std::unordered_map<VariableId, Variable const*> replacements;
|
||||
replacements.emplace(listNode->outVariable()->id, returnNode->inVariable());
|
||||
RedundantCalculationsReplacer finder(replacements);
|
||||
plan->root()->walk(&finder);
|
||||
|
||||
current = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (current == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto const& parents = current->getParents();
|
||||
current = parents[0];
|
||||
}
|
||||
}
|
||||
|
||||
opt->addPlan(plan, rule, modified);
|
||||
}
|
||||
|
||||
|
|
|
@ -267,6 +267,12 @@ void patchUpdateStatementsRule(Optimizer*, ExecutionPlan*,
|
|||
void mergeFilterIntoTraversalRule(Optimizer* opt, ExecutionPlan* plan,
|
||||
Optimizer::Rule const* rule);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief moves simple subqueries one level higher
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void inlineSubqueriesRule(Optimizer*, ExecutionPlan*, Optimizer::Rule const*);
|
||||
|
||||
} // namespace aql
|
||||
} // namespace arangodb
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ struct Variable {
|
|||
/// @brief variable name
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string const name;
|
||||
std::string name;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief constant variable value (points to another AstNode)
|
||||
|
|
|
@ -162,6 +162,30 @@ Variable* VariableGenerator::createVariable(
|
|||
Variable* VariableGenerator::createTemporaryVariable() {
|
||||
return createVariable(nextName(), false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief renames a variable (assigns a temporary name)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* VariableGenerator::renameVariable(VariableId id) {
|
||||
return renameVariable(id, nextName());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief renames a variable (assigns the specified name
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* VariableGenerator::renameVariable(VariableId id, std::string const& name) {
|
||||
auto it = _variables.find(id);
|
||||
|
||||
if (it == _variables.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
(*it).second->name = name;
|
||||
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return a variable by id - this does not respect the scopes!
|
||||
|
@ -184,7 +208,7 @@ Variable* VariableGenerator::getVariable(VariableId id) const {
|
|||
std::string VariableGenerator::nextName() {
|
||||
// note: if the naming scheme is adjusted, it may be necessary to adjust
|
||||
// Variable::isUserDefined, too!
|
||||
return std::to_string(nextId()); // to_string: c++11
|
||||
return std::to_string(nextId());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -83,6 +83,18 @@ class VariableGenerator {
|
|||
|
||||
Variable* createTemporaryVariable();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief renames a variable (assigns a temporary name)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* renameVariable(VariableId);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief renames a variable (assigns the specified name)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* renameVariable(VariableId, std::string const&);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return a variable by id - this does not respect the scopes!
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in New Issue