Skip to content

Commit

Permalink
🚧 Add PredicateMergeRule
Browse files Browse the repository at this point in the history
  • Loading branch information
janetzki committed Sep 4, 2019
1 parent d2e7d86 commit 79fa9e1
Show file tree
Hide file tree
Showing 6 changed files with 396 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ set(
optimizer/strategy/insert_limit_in_exists_rule.hpp
optimizer/strategy/join_ordering_rule.cpp
optimizer/strategy/join_ordering_rule.hpp
optimizer/strategy/predicate_merge_rule.cpp
optimizer/strategy/predicate_merge_rule.hpp
optimizer/strategy/predicate_placement_rule.cpp
optimizer/strategy/predicate_placement_rule.hpp
optimizer/strategy/predicate_reordering_rule.cpp
Expand Down
3 changes: 3 additions & 0 deletions src/lib/optimizer/optimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "strategy/index_scan_rule.hpp"
#include "strategy/insert_limit_in_exists_rule.hpp"
#include "strategy/join_ordering_rule.hpp"
#include "strategy/predicate_merge_rule.hpp"
#include "strategy/predicate_placement_rule.hpp"
#include "strategy/predicate_reordering_rule.hpp"
#include "strategy/predicate_split_up_rule.hpp"
Expand Down Expand Up @@ -114,6 +115,8 @@ std::shared_ptr<Optimizer> Optimizer::create_default_optimizer() {

optimizer->add_rule(std::make_unique<IndexScanRule>());

optimizer->add_rule(std::make_unique<PredicateMergeRule>());

return optimizer;
}

Expand Down
91 changes: 91 additions & 0 deletions src/lib/optimizer/strategy/predicate_merge_rule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include "predicate_merge_rule.hpp"

#include "expression/expression_utils.hpp"
#include "expression/logical_expression.hpp"
#include "logical_query_plan/lqp_utils.hpp"
#include "logical_query_plan/union_node.hpp"

namespace opossum {

PredicateMergeRule::PredicateMergeRule(const bool split_disjunction) : _split_disjunction(split_disjunction) {}

bool PredicateMergeRule::_mergeConjunction(const std::shared_ptr<PredicateNode>& predicate_node) const {
const auto flat_conjunction = flatten_logical_expressions(predicate_node->predicate(), LogicalOperator::And);
if (flat_conjunction.size() <= 1) {
return false;
}

/**
* Split up PredicateNode with conjunctive chain (e.g., `PredicateNode(a AND b AND c)`) as its scan expression into
* multiple consecutive PredicateNodes (e.g. `PredicateNode(c) -> PredicateNode(b) -> PredicateNode(a)`).
*/
for (const auto& predicate_expression : flat_conjunction) {
const auto& new_predicate_node = PredicateNode::make(predicate_expression);
lqp_insert_node(predicate_node, LQPInputSide::Left, new_predicate_node);
_mergeDisjunction(new_predicate_node);
}
lqp_remove_node(predicate_node);

return true;
}

void PredicateMergeRule::_mergeDisjunction(const std::shared_ptr<PredicateNode>& predicate_node) const {
if (!_split_disjunction) {
return;
}

const auto flat_disjunction = flatten_logical_expressions(predicate_node->predicate(), LogicalOperator::Or);
if (flat_disjunction.size() <= 1) {
return;
}

/**
* Split up PredicateNode with disjunctive chain (e.g., `PredicateNode(a OR b OR c)`) as their scan expression into
* n-1 consecutive UnionNodes and n PredicateNodes.
*/
auto previous_union_node = UnionNode::make(UnionMode::Positions);
const auto left_input = predicate_node->left_input();
lqp_replace_node(predicate_node, previous_union_node);

auto new_predicate_node = PredicateNode::make(flat_disjunction[0], left_input);
previous_union_node->set_left_input(new_predicate_node);
_mergeConjunction(new_predicate_node);

new_predicate_node = PredicateNode::make(flat_disjunction[1], left_input);
previous_union_node->set_right_input(new_predicate_node);
_mergeConjunction(new_predicate_node);

for (auto disjunction_idx = size_t{2}; disjunction_idx < flat_disjunction.size(); ++disjunction_idx) {
const auto& predicate_expression = flat_disjunction[disjunction_idx];
auto next_union_node = UnionNode::make(UnionMode::Positions);
lqp_insert_node(previous_union_node, LQPInputSide::Right, next_union_node);

new_predicate_node = PredicateNode::make(predicate_expression, left_input);
next_union_node->set_right_input(new_predicate_node);
_mergeConjunction(new_predicate_node);

previous_union_node = next_union_node;
}
}

void PredicateMergeRule::apply_to(const std::shared_ptr<AbstractLQPNode>& root) const {
Assert(root->type == LQPNodeType::Root, "PredicateSplitUpRule needs root to hold onto");

auto predicate_nodes = std::vector<std::shared_ptr<PredicateNode>>{};
visit_lqp(root, [&](const auto& sub_node) {
if (const auto predicate_node = std::dynamic_pointer_cast<PredicateNode>(sub_node)) {
predicate_nodes.emplace_back(predicate_node);
}
return LQPVisitation::VisitInputs;
});

// _splitConjunction() and _splitDisjunction() split up logical expressions by calling each other recursively
for (const auto& predicate_node : predicate_nodes) {
if (!_mergeConjunction(predicate_node)) {
// If there is no conjunction at the top level, try to split disjunction first
_mergeDisjunction(predicate_node);
}
}
}

} // namespace opossum
42 changes: 42 additions & 0 deletions src/lib/optimizer/strategy/predicate_merge_rule.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include <vector>

#include "abstract_rule.hpp"
#include "logical_query_plan/predicate_node.hpp"

namespace opossum {

/**
* This rule turns PredicateNodes with (nested) conjunctions ("and) and disjunctions ("or")
* (e.g., `PredicateNode(a AND (b OR c))`) as their scan expression into an LQP of consecutive PredicateNodes (for the
* conjunctions) and UnionNodes (for the disjunctions).
*
* Doing so enables other Optimizer rules to process these PredicateNodes and split-up PredicateNodes might take a
* faster operator execution path.
*
* EXAMPLES:
* TPC-H query 19
* This rule makes `p_partkey = l_partkey` available as a join predicate and the predicates on `l_shipmode` and
* `l_shipinstruct` can be pulled below the join.
*
* TPC-DS query 35
* This rule splits up `EXISTS (...) OR EXISTS (...)` into two expressions that can later be rewritten into two
* semi-joins.
*/
class PredicateMergeRule : public AbstractRule {
public:
explicit PredicateMergeRule(const bool split_disjunction = true);
void apply_to(const std::shared_ptr<AbstractLQPNode>& root) const override;

private:
/**
* @return true if a conjunction was split up
*/
bool _mergeConjunction(const std::shared_ptr<PredicateNode>& predicate_node) const;
void _mergeDisjunction(const std::shared_ptr<PredicateNode>& predicate_node) const;

bool _split_disjunction;
};

} // namespace opossum
1 change: 1 addition & 0 deletions src/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ set(
optimizer/strategy/index_scan_rule_test.cpp
optimizer/strategy/insert_limit_in_exists_rule_test.cpp
optimizer/strategy/join_ordering_rule_test.cpp
optimizer/strategy/predicate_merge_rule_test.cpp
optimizer/strategy/predicate_placement_rule_test.cpp
optimizer/strategy/predicate_reordering_rule_test.cpp
optimizer/strategy/predicate_split_up_rule_test.cpp
Expand Down
Loading

0 comments on commit 79fa9e1

Please sign in to comment.