1
0
Fork 0

added (still disabled) optimizer rule `inline-subqueries`

This commit is contained in:
jsteemann 2016-01-29 00:21:05 +01:00
parent 7f662bac09
commit 1ca11411fd
7 changed files with 185 additions and 5 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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);
}

View File

@ -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

View File

@ -97,7 +97,7 @@ struct Variable {
/// @brief variable name
//////////////////////////////////////////////////////////////////////////////
std::string const name;
std::string name;
//////////////////////////////////////////////////////////////////////////////
/// @brief constant variable value (points to another AstNode)

View File

@ -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());
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -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!
//////////////////////////////////////////////////////////////////////////////