4#include "IR/PhysicalPlanTable.hpp"
8#include <unordered_map>
16template<
typename T>
struct Match;
18struct ConstPhysOptVisitor;
30concept producer = std::is_base_of_v<Producer, T>;
33concept consumer = std::is_base_of_v<Consumer, T>;
56template<
typename...
Ts>
80template<logical_operator Op>
84requires (
sizeof...(Children) != 0)
and
85 (
sizeof...(Children) <= 2)
99template<std::size_t I,
typename... Ts>
105{
using type = std::tuple<const Op*>; };
107template<
typename Op,
typename...
Children>
121template<logical_operator Op>
124template<logical_operator Op>
127template<logical_operator Op>
137template<logical_operator Op>
141template<logical_operator Op>
145template<logical_operator Op>
149template<singleton_pattern T>
161 using base_t = std::function<void(
void)>;
168 :
base_t([parent_setup=
std::move(parent_setup), callback=
std::move(callback)](){
174 base_t::result_type
operator()()
const {
if (*
this) base_t::operator()(); }
181 using base_t = std::function<void(
void)>;
188 :
base_t([parent_teardown=
std::move(parent_teardown), callback=
std::move(callback)](){
194 base_t::result_type
operator()()
const {
if (*
this) base_t::operator()(); }
201 template<
typename>
friend struct Match;
205 double cost_ = std::numeric_limits<double>::infinity();
223 void dump(std::ostream &out)
const;
230 friend std::string
to_string(
const MatchBase &M) { std::ostringstream oss; oss << M;
return oss.str(); }
233 static std::ostream &
indent(std::ostream &out,
unsigned level) {
234 if (level) out <<
'\n' << std::string(2 * level - 2,
' ') <<
"` ";
237 virtual void print(std::ostream &out,
unsigned level = 0)
const = 0;
279 virtual void accept(PhysOptVisitor &v) = 0;
280 virtual void accept(ConstPhysOptVisitor &v)
const = 0;
285template<
typename PhysicalPlanTable>
290 using entry_type = PhysicalPlanTable::condition2entry_map_type::entry_type;
291 using cost_type = PhysicalPlanTable::condition2entry_map_type::entry_type::cost_type;
292 using children_type = std::vector<typename PhysicalPlanTable::condition2entry_map_type::const_iterator>;
314 typename PhysicalPlanTable::condition2entry_map_type::iterator it_best;
315 double min_cost = std::numeric_limits<double>::infinity();
316 for (
auto it =
table().back().begin(); it !=
table().
back().end(); ++it) {
317 if (
auto cost = it->entry.cost(); cost < min_cost) {
322 return it_best->entry;
328 return M_nothrow(plan.exclusive_shared_to_unique());
333 template<
typename PhysOp>
336 auto cost = PhysOp::cost(*match);
337 for (
const auto &child : children)
338 cost += child->entry.cost();
342 auto post_cond = PhysOp::post_condition_(*match);
343 if (post_cond.empty()) {
344 if (children.size() <= 1) {
346 post_cond = PhysOp::adapt_post_condition_(*match, children.empty() ? empty_cond
347 : children.front()->condition);
349 std::vector<std::reference_wrapper<const ConditionSet>> children_post_conditions;
350 children_post_conditions.reserve(children.size());
351 for (
const auto &child : children)
352 children_post_conditions.emplace_back(child->condition);
353 post_cond = PhysOp::adapt_post_conditions_(*match, std::move(children_post_conditions));
358 auto it = std::find_if(
table()[op.id()].begin(),
table()[op.id()].end(), [&post_cond](
const auto &e) {
359 return e.condition == post_cond;
363 if (it ==
table()[op.id()].end()) {
364 table()[op.id()].insert(std::move(post_cond),
entry_type(std::move(match), children, cost));
366 if (cost < it->entry.cost())
367 it->entry =
entry_type(std::move(match), children, cost);
375 using ConstPostOrderOperatorVisitor::operator();
376#define DECLARE(CLASS) \
377 void operator()(const CLASS &op) override { \
378 for (const auto &matcher : pattern_matchers_) \
379 matcher->matches(*this, op); \
384 void accept(PhysOptVisitor &v)
override;
385 void accept(ConstPhysOptVisitor &v)
const override;
388#define M_PHYS_OPT_LIST(X) \
389 X(PhysicalOptimizerImpl<ConcretePhysicalPlanTable>)
395#define DECLARE(CLASS) \
396 extern template struct m::CLASS;
407template<
typename Actual, val
id_pattern Pattern>
419 Actual::execute(M, std::move(setup), std::move(pipeline), std::move(teardown));
432 return Actual::pre_condition(child_idx, partial_inner_nodes);
449 return Actual::adapt_post_condition(M, post_cond_child);
455 std::vector<std::reference_wrapper<const ConditionSet>> &&post_cond_children)
457 return Actual::adapt_post_conditions(M, std::move(post_cond_children));
466 std::vector<std::reference_wrapper<const ConditionSet>>&&)
468 M_unreachable(
"for patterns with multiple children, either `PhysicalOperator::post_condition()` or this method "
469 "must be overwritten");
474 template<
typename It>
475 static std::unique_ptr<Match<Actual>>
477 std::vector<unsharable_shared_ptr<const MatchBase>> children_matches;
478 children_matches.reserve(children.size());
479 for (
const auto &child : children)
480 children_matches.push_back(child->entry.share_match());
481 return std::apply([children_matches=std::move(children_matches)](
auto...
args)
mutable {
482 return std::make_unique<Match<Actual>>(
args..., std::move(children_matches));
492template<
typename PhysOp, std::size_t Idx,
typename... PatternQueue>
495template<
typename PhysOp, std::size_t Idx,
typename Op,
typename... PatternQueue>
502 template<
typename PhysOpt,
typename... OpQueue>
505 std::vector<typename PhysOpt::children_type> current_children_list,
507 const OpQueue&... op_queue)
const
509 M_insist(
sizeof...(PatternQueue) ==
sizeof...(op_queue));
510 M_insist(std::all_of(current_children_list.cbegin(), current_children_list.cend(), [&](
auto ¤t_children){
511 return current_children.size() == current_children_list.front().size();
514 auto op = cast<const Op>(&op_);
521 std::get<Idx>(current_nodes) = op;
522 if constexpr (std::same_as<Op, Wildcard>) {
523 if (op->id() >= opt.table().size())
526 std::vector<typename PhysOpt::children_type::value_type> new_child_list;
527 for (
auto it = opt.table()[op->id()].cbegin(); it != opt.table()[op->id()].cend(); ++it) {
528 if (PhysOp::pre_condition_(
529 current_children_list.front().size(), current_nodes
530 ).implied_by(it->condition))
532 new_child_list.push_back(it);
535 if (new_child_list.empty())
538 std::vector<typename PhysOpt::children_type> _current_children_list;
539 for (
auto ¤t_children : current_children_list) {
540 for (
auto &new_child : new_child_list) {
541 auto &_current_children = _current_children_list.emplace_back(current_children);
542 _current_children.push_back(new_child);
545 current_children_list = std::move(_current_children_list);
547 std::vector<typename PhysOpt::children_type> new_children_list;
548 new_children_list.emplace_back();
549 for (std::size_t i = 0; i < op->children().size(); ++i) {
550 auto c = op->children()[i];
551 if (c->id() >= opt.table().size())
554 std::vector<typename PhysOpt::children_type::value_type> new_child_list;
555 for (
auto it = opt.table()[c->id()].cbegin(); it != opt.table()[c->id()].cend(); ++it) {
556 if (PhysOp::pre_condition_(
557 current_children_list.front().size() + i, current_nodes
558 ).implied_by(it->condition))
560 new_child_list.push_back(it);
563 if (new_child_list.empty())
566 std::vector<typename PhysOpt::children_type> _new_children_list;
567 for (
auto &new_children : new_children_list) {
568 for (
auto &new_child : new_child_list) {
569 auto &_new_children = _new_children_list.emplace_back(new_children);
570 _new_children.push_back(new_child);
573 new_children_list = std::move(_new_children_list);
576 std::vector<typename PhysOpt::children_type> _current_children_list;
577 for (
auto ¤t_children : current_children_list) {
578 for (
auto &new_children : new_children_list) {
579 auto &_current_children = _current_children_list.emplace_back(current_children);
580 _current_children.insert(_current_children.end(), new_children.begin(), new_children.end());
583 current_children_list = std::move(_current_children_list);
587 if (not PhysOp::pre_condition_(current_children_list.front().size(), current_nodes).implied_by(empty))
591 if constexpr (
sizeof...(PatternQueue) == 0) {
592 static_assert(Idx == std::tuple_size_v<get_nodes_t<pattern>> - 1);
594 std::apply([](
auto&&... ops) { (
M_insist(ops !=
nullptr), ...); }, current_nodes);
597 for (
auto ¤t_children : current_children_list) {
598 auto M = PhysOp::instantiate(current_nodes, current_children);
599 opt.template handle_match<PhysOp>(*std::get<0>(current_nodes), std::move(M), current_children);
604 m.matches(opt, current_nodes, std::move(current_children_list), op_queue...);
609template<
typename PhysOp, std::size_t Idx,
typename Op,
typename...
Children,
typename... PatternQueue>
616 template<
typename PhysOpt,
typename... OpQueue>
619 std::vector<typename PhysOpt::children_type> current_children_list,
621 const OpQueue&... op_queue)
const
623 M_insist(
sizeof...(PatternQueue) ==
sizeof...(op_queue));
625 auto op = cast<const Op>(&op_);
629 if (op->children().size() !=
sizeof...(Children))
633 std::get<Idx>(current_nodes) = op;
635 if constexpr (
sizeof...(Children) == 1) {
639 m.matches(opt, current_nodes, std::move(current_children_list), *op->child(0), op_queue...);
642 static_assert(
sizeof...(Children) == 2);
647 m.matches(opt, current_nodes, current_children_list, *op->child(0), *op->child(1), op_queue...);
651 m.matches(opt, current_nodes, std::move(current_children_list), *op->child(1), *op->child(0), op_queue...);
657template<
typename PhysOp,
typename PhysOpt>
664 std::vector<typename PhysOpt::children_type> current_children_list;
665 current_children_list.emplace_back();
667 m.matches(as<PhysOpt>(opt), current_nodes, std::move(current_children_list), op);
676template<
typename PhysOp>
680 [
this]<
typename PhysOpt>(PhysOpt&) {
#define M_PHYS_OPT_LIST(X)
#define M_DECLARE_VISITOR(VISITOR_NAME, BASE_CLASS, CLASS_LIST)
Defines a visitor VISITOR_NAME to visit the class hierarchy rooted in BASE_CLASS and with subclasses ...
#define M_unreachable(MSG)
typename get_singleton_operator< T >::type get_singleton_operator_t
typename get_nodes< T >::type get_nodes_t
std::function< void(void)> pipeline_t
std::tuple_element_t< I, std::tuple< Ts... > > ith_type_of
auto visit(Callable &&callable, Base &obj, m::tag< Callable > &&=m::tag< Callable >())
Generic implementation to visit a class hierarchy, with similar syntax as std::visit.
virtual const Operator & get_matched_root() const =0
Returns the matched logical root operator for physical operators.
friend std::string to_string(const MatchBase &M)
virtual void execute(setup_t setup, pipeline_t pipeline, teardown_t teardown) const =0
Executes this physical operator match.
virtual void print(std::ostream &out, unsigned level=0) const =0
static std::ostream & indent(std::ostream &out, unsigned level)
friend std::ostream & operator<<(std::ostream &out, const MatchBase &M)
void cost(double new_cost)
An Operator represents an operation in a query plan.
virtual void assign_post_order_ids(std::size_t start_id=0UL) const
Assigns IDs to the operator tree rooted in this in post-order starting with ID start_id.
std::size_t id() const
Returns the ID of this.
friend struct pattern_matcher_recursive
static ConditionSet adapt_post_conditions_(const Match< Actual > &M, std::vector< std::reference_wrapper< const ConditionSet > > &&post_cond_children)
Returns the adapted post-condition of this physical operator given the match M and the former post-co...
static void execute(const Match< Actual > &M, setup_t setup, pipeline_t pipeline, teardown_t teardown)
Executes this physical operator given the match M and three callbacks: Setup for some initializations...
static ConditionSet post_condition(const Match< Actual > &)
Overwrite this to implement a custom post-condition.
static double cost(const Match< Actual > &M)
Returns the cost of this physical operator given the match M.
static ConditionSet adapt_post_conditions(const Match< Actual > &, std::vector< std::reference_wrapper< const ConditionSet > > &&)
Overwrite this to implement custom adaptation of multiple post-conditions.
static ConditionSet adapt_post_condition_(const Match< Actual > &M, const ConditionSet &post_cond_child)
Returns the adapted post-condition of this physical operator given the match M and the former post-co...
static ConditionSet pre_condition_(std::size_t child_idx, const get_nodes_t< Pattern > &partial_inner_nodes)
Returns the pre-condition for the child_idx-th child (indexed from left to right starting with 0) of ...
static std::unique_ptr< Match< Actual > > instantiate(get_nodes_t< Pattern > inner_nodes, const std::vector< It > &children)
Instantiates this physical operator given the matched logical operators inner_nodes in pre-order and ...
static ConditionSet post_condition_(const Match< Actual > &M)
Returns the post-condition of this physical operator given the match M.
static ConditionSet pre_condition(std::size_t, const get_nodes_t< Pattern > &)
Overwrite this to implement custom pre-conditions.
static ConditionSet adapt_post_condition(const Match< Actual > &, const ConditionSet &post_cond_child)
Overwrite this to implement custom adaptation of a single post-condition.
Concrete PhysicalOptimizer implementation using a concrete statically-typed.
bool has_plan() const override
Returns true iff a physical operator covering is found.
void handle_match(const Operator &op, std::unique_ptr< Match< PhysOp > > &&match, const children_type &children)
Handles the found match match with children entries children for the logical plan rooted in op.
void cover(const Operator &plan) override
Finds an optimal physical operator covering for the logical plan rooted in plan.
friend struct pattern_matcher_recursive
const PhysicalPlanTable & table() const
PhysicalPlanTable::condition2entry_map_type::entry_type::cost_type cost_type
const entry_type & get_plan_entry() const
void accept(ConstPhysOptVisitor &v) const override
M_OPERATOR_LIST(DECLARE) void accept(PhysOptVisitor &v) override
entry_type & get_plan_entry()
Returns the entry for the found physical operator covering.
std::vector< typename PhysicalPlanTable::condition2entry_map_type::const_iterator > children_type
std::unique_ptr< MatchBase > extract_plan() override
Extracts the found physical operator covering by moving it out of the underlying physical plan table.
PhysicalPlanTable table_
dynamic programming table, stores the best covering for each logical operator per unique post-condit...
PhysicalPlanTable & table()
PhysicalPlanTable::condition2entry_map_type::entry_type entry_type
The physical optimizer interface.
virtual ~PhysicalOptimizer()
virtual void accept(PhysOptVisitor &v)=0
virtual void cover(const Operator &plan)=0
Finds an optimal physical operator covering for the logical plan rooted in plan.
virtual std::unique_ptr< MatchBase > extract_plan()=0
Extracts the found physical operator covering by moving it out of the underlying physical plan table.
void register_operator()
Registers a new physical operator which then may be used to find a covering.
virtual bool has_plan() const =0
Returns true iff a physical operator covering is found.
virtual void accept(ConstPhysOptVisitor &v) const =0
std::vector< std::unique_ptr< const pattern_matcher_base > > pattern_matchers_
all pattern matchers for all registered physical operators
Interface for an entire physical plan table containing a ConditionSet-entry-mapping.
void resize(size_type size)
condition2entry_map_type & back()
A Producer is an Operator that can be evaluated to a sequence of tuples.
A helper class to define CRTP class hierarchies.
decltype(std::tuple_cat(std::declval< typename get_nodes< Op >::type >(), std::declval< typename get_nodes< Children >::type >()...)) type
std::tuple< const Op * > type
Abstract base class of all matchable patterns.
virtual void matches(PhysicalOptimizer &opt, const Operator &op) const =0
virtual ~pattern_matcher_base()
void matches(PhysicalOptimizer &opt, const Operator &op) const override
friend struct pattern_matcher_recursive
void matches(PhysOpt &opt, get_nodes_t< pattern > ¤t_nodes, std::vector< typename PhysOpt::children_type > current_children_list, const Operator &op_, const OpQueue &... op_queue) const
friend struct pattern_matcher_recursive
void matches(PhysOpt &opt, get_nodes_t< pattern > ¤t_nodes, std::vector< typename PhysOpt::children_type > current_children_list, const Operator &op_, const OpQueue &... op_queue) const
static setup_t Make_Without_Parent(base_t &&callback=base_t())
std::function< void(void)> base_t
setup_t(base_t &&callback)
setup_t(setup_t &&parent_setup, base_t &&callback)
base_t::result_type operator()() const
static teardown_t Make_Without_Parent(base_t &&callback=base_t())
std::function< void(void)> base_t
base_t::result_type operator()() const
teardown_t(base_t &&callback)
teardown_t(teardown_t &&parent_teardown, base_t &&callback)