mutable
A Database System for Research and Fast Prototyping
Loading...
Searching...
No Matches
PhysicalOptimizer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4#include "IR/PhysicalPlanTable.hpp"
5#include <limits>
7#include <type_traits>
8#include <unordered_map>
9#include <utility>
10#include <vector>
11
12
13namespace m {
14
15// forward declarations
16template<typename T> struct Match;
17struct PhysOptVisitor;
18struct ConstPhysOptVisitor;
20
21
22/*======================================================================================================================
23 * Helper concepts
24 *====================================================================================================================*/
25
26template<typename T>
27concept logical_operator = std::is_base_of_v<Operator, T>;
28
29template<typename T>
30concept producer = std::is_base_of_v<Producer, T>;
31
32template<typename T>
33concept consumer = std::is_base_of_v<Consumer, T>;
34
35
36/*======================================================================================================================
37 * Pattern type
38 *====================================================================================================================*/
39
41
42
43template<typename>
44struct is_pattern : std::false_type {};
45
46template<typename T>
47concept is_pattern_v = is_pattern<T>::value;
48
49
50template<logical_operator Op, typename... Children>
52struct pattern_t {};
53
54
55// delayed definition
56template<typename... Ts>
57struct is_pattern<pattern_t<Ts...>> : std::true_type {};
58
59
60/*======================================================================================================================
61 * Pattern validator
62 *====================================================================================================================*/
63
64template<typename>
65struct has_producer_root : std::false_type {};
66
67template<producer Op>
68struct has_producer_root<Op> : std::true_type {};
69
70template<producer Op, typename... Children>
71struct has_producer_root<pattern_t<Op, Children...>> : std::true_type {};
72
73template<typename T>
74concept producer_root = has_producer_root<T>::value;
75
76
77template<typename>
78struct pattern_validator : std::false_type {};
79
80template<logical_operator Op>
81struct pattern_validator<Op> : std::true_type {};
82
83template<consumer Op, producer_root... Children>
84requires (sizeof...(Children) != 0) and // no singleton patterns inside pattern_t
85 (sizeof...(Children) <= 2) // at most binary operations
87{
88 static constexpr bool value = (pattern_validator<Children>::value and ...);
89};
90
91template<typename T>
92concept valid_pattern = pattern_validator<T>::value;
93
94
95/*======================================================================================================================
96 * Helper types
97 *====================================================================================================================*/
98
99template<std::size_t I, typename... Ts>
100using ith_type_of = std::tuple_element_t<I, std::tuple<Ts...>>;
101
102
103template<typename Op>
105{ using type = std::tuple<const Op*>; };
106
107template<typename Op, typename... Children>
109{
110 using type = decltype(std::tuple_cat(std::declval<typename get_nodes<Op>::type>(),
111 std::declval<typename get_nodes<Children>::type>()...));
112};
113
114template<typename T>
116
117
118template<typename>
119struct is_singleton_pattern : std::false_type {};
120
121template<logical_operator Op>
122struct is_singleton_pattern<Op> : std::true_type {};
123
124template<logical_operator Op>
125struct is_singleton_pattern<pattern_t<Op, Wildcard>> : std::true_type {};
126
127template<logical_operator Op>
128struct is_singleton_pattern<pattern_t<Op, Wildcard, Wildcard>> : std::true_type {};
129
130template<typename T>
131concept singleton_pattern = is_singleton_pattern<T>::value;
132
133
134template<typename>
136
137template<logical_operator Op>
139{ using type = Op; };
140
141template<logical_operator Op>
143{ using type = Op; };
144
145template<logical_operator Op>
147{ using type = Op; };
148
149template<singleton_pattern T>
151
152
153/*======================================================================================================================
154 * MatchBase
155 *====================================================================================================================*/
156
157using pipeline_t = std::function<void(void)>;
158
159struct setup_t : std::function<void(void)>
160{
161 using base_t = std::function<void(void)>;
162
163 private:
164 setup_t(base_t &&callback) : base_t(std::move(callback)) { }
165
166 public:
167 setup_t(setup_t &&parent_setup, base_t &&callback)
168 : base_t([parent_setup=std::move(parent_setup), callback=std::move(callback)](){
169 parent_setup();
170 callback();
171 })
172 { }
173
174 base_t::result_type operator()() const { if (*this) base_t::operator()(); }
175
176 static setup_t Make_Without_Parent(base_t &&callback = base_t()) { return setup_t(std::move(callback)); }
177};
178
179struct teardown_t : std::function<void(void)>
180{
181 using base_t = std::function<void(void)>;
182
183 private:
184 teardown_t(base_t &&callback) : base_t(std::move(callback)) { }
185
186 public:
187 teardown_t(teardown_t &&parent_teardown, base_t &&callback)
188 : base_t([parent_teardown=std::move(parent_teardown), callback=std::move(callback)](){
189 parent_teardown(); // parent teardown has to be placed before new code
190 callback();
191 })
192 { }
193
194 base_t::result_type operator()() const { if (*this) base_t::operator()(); }
195
196 static teardown_t Make_Without_Parent(base_t &&callback = base_t()) { return teardown_t(std::move(callback)); }
197};
198
200{
201 template<typename> friend struct Match; // to invoke `print()`
202 template<typename> friend struct PhysicalOptimizerImpl; // to set costs
203
204 private:
205 double cost_ = std::numeric_limits<double>::infinity();
206
207 public:
208 virtual ~MatchBase() { }
209
213 virtual void execute(setup_t setup, pipeline_t pipeline, teardown_t teardown) const = 0;
214
216 virtual const Operator & get_matched_root() const = 0;
217
218 double cost() const { return cost_; }
219 private:
220 void cost(double new_cost) { cost_ = new_cost; }
221
222 public:
223 void dump(std::ostream &out) const;
224 void dump() const;
225
226 friend std::ostream & operator<<(std::ostream &out, const MatchBase &M) {
227 M.print(out);
228 return out;
229 }
230 friend std::string to_string(const MatchBase &M) { std::ostringstream oss; oss << M; return oss.str(); }
231
232 protected:
233 static std::ostream & indent(std::ostream &out, unsigned level) {
234 if (level) out << '\n' << std::string(2 * level - 2, ' ') << "` ";
235 return out;
236 }
237 virtual void print(std::ostream &out, unsigned level = 0) const = 0;
238};
239
242{
244 virtual void matches(PhysicalOptimizer &opt, const Operator &op) const = 0;
245};
246
247
248/*======================================================================================================================
249 * PhysicalOptimizer
250 *====================================================================================================================*/
251
260{
261 protected:
263 std::vector<std::unique_ptr<const pattern_matcher_base>> pattern_matchers_;
264
265 public:
266 virtual ~PhysicalOptimizer() { }
267
269 template<typename PhysOp> void register_operator();
270
272 virtual void cover(const Operator &plan) = 0;
273
275 virtual bool has_plan() const = 0;
277 virtual std::unique_ptr<MatchBase> extract_plan() = 0;
278
279 virtual void accept(PhysOptVisitor &v) = 0;
280 virtual void accept(ConstPhysOptVisitor &v) const = 0;
281};
282
285template<typename PhysicalPlanTable>
287{
288 template<typename, std::size_t, typename...> friend struct pattern_matcher_recursive;
289
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>;
293
294 private:
297
298 public:
300 const PhysicalPlanTable & table() const { return table_; }
301
302 void cover(const Operator &plan) override {
304 table().clear();
305 table().resize(plan.id() + 1);
306 (*this)(plan);
307 }
308
309 bool has_plan() const override { return not table().back().empty(); }
310 private:
313 M_insist(has_plan(), "no physical operator covering found");
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) {
318 it_best = it;
319 min_cost = cost;
320 }
321 }
322 return it_best->entry;
323 }
324 const entry_type & get_plan_entry() const { return const_cast<PhysicalOptimizerImpl*>(this)->get_plan_entry(); }
325 public:
326 std::unique_ptr<MatchBase> extract_plan() override {
327 auto plan = get_plan_entry().extract_match();
328 return M_nothrow(plan.exclusive_shared_to_unique());
329 }
330
331 private:
333 template<typename PhysOp>
334 void handle_match(const Operator &op, std::unique_ptr<Match<PhysOp>> &&match, const children_type &children) {
335 /* Compute cost of the match and its children. */
336 auto cost = PhysOp::cost(*match);
337 for (const auto &child : children)
338 cost += child->entry.cost();
339 match->cost(cost);
340
341 /* Compute post-condition. */
342 auto post_cond = PhysOp::post_condition_(*match);
343 if (post_cond.empty()) {
344 if (children.size() <= 1) {
345 ConditionSet empty_cond;
346 post_cond = PhysOp::adapt_post_condition_(*match, children.empty() ? empty_cond
347 : children.front()->condition);
348 } else {
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));
354 }
355 }
356
357 /* Compare to the best physical operator matched so far for *that* post-condition. */
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;
360 });
361
362 /* Update table and physical operator cost. */
363 if (it == table()[op.id()].end()) {
364 table()[op.id()].insert(std::move(post_cond), entry_type(std::move(match), children, cost));
365 } else {
366 if (cost < it->entry.cost())
367 it->entry = entry_type(std::move(match), children, cost);
368 else
369 return; // better match already exists
370 }
371 }
372
373 /*----- OperatorVisitor ------------------------------------------------------------------------------------------*/
374 public:
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); \
380 }
382#undef DECLARE
383
384 void accept(PhysOptVisitor &v) override;
385 void accept(ConstPhysOptVisitor &v) const override;
386};
387
388#define M_PHYS_OPT_LIST(X) \
389 X(PhysicalOptimizerImpl<ConcretePhysicalPlanTable>)
390
393
394// explicit instantiation declarations
395#define DECLARE(CLASS) \
396 extern template struct m::CLASS;
398#undef DECLARE
399
400
401/*======================================================================================================================
402 * PhysicalOperator
403 *====================================================================================================================*/
404
407template<typename Actual, valid_pattern Pattern>
408struct PhysicalOperator : crtp<Actual, PhysicalOperator, Pattern>
409{
410 template<typename> friend struct PhysicalOptimizerImpl;
411 template<typename, std::size_t, typename...> friend struct pattern_matcher_recursive;
412
413 using crtp<Actual, PhysicalOperator, Pattern>::actual;
414 using pattern = Pattern;
415
418 static void execute(const Match<Actual> &M, setup_t setup, pipeline_t pipeline, teardown_t teardown) {
419 Actual::execute(M, std::move(setup), std::move(pipeline), std::move(teardown));
420 }
421
423 static double cost(const Match<Actual> &M) { return Actual::cost(M); }
424
425 private:
431 static ConditionSet pre_condition_(std::size_t child_idx, const get_nodes_t<Pattern> &partial_inner_nodes) {
432 return Actual::pre_condition(child_idx, partial_inner_nodes);
433 }
434 public:
436 static ConditionSet pre_condition(std::size_t, const get_nodes_t<Pattern>&) { return ConditionSet(); }
437
438 private:
440 static ConditionSet post_condition_(const Match<Actual> &M) { return Actual::post_condition(M); }
441 public:
444
445 private:
448 static ConditionSet adapt_post_condition_(const Match<Actual> &M, const ConditionSet &post_cond_child) {
449 return Actual::adapt_post_condition(M, post_cond_child);
450 }
453 static ConditionSet
455 std::vector<std::reference_wrapper<const ConditionSet>> &&post_cond_children)
456 {
457 return Actual::adapt_post_conditions(M, std::move(post_cond_children));
458 }
459 public:
461 static ConditionSet adapt_post_condition(const Match<Actual>&, const ConditionSet &post_cond_child) {
462 return ConditionSet(post_cond_child);
463 }
466 std::vector<std::reference_wrapper<const ConditionSet>>&&)
467 {
468 M_unreachable("for patterns with multiple children, either `PhysicalOperator::post_condition()` or this method "
469 "must be overwritten");
470 }
471
474 template<typename It>
475 static std::unique_ptr<Match<Actual>>
476 instantiate(get_nodes_t<Pattern> inner_nodes, const std::vector<It> &children) {
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));
483 }, inner_nodes);
484 }
485};
486
487
488/*======================================================================================================================
489 * Pattern Matcher
490 *====================================================================================================================*/
491
492template<typename PhysOp, std::size_t Idx, typename... PatternQueue>
494
495template<typename PhysOp, std::size_t Idx, typename Op, typename... PatternQueue>
496struct pattern_matcher_recursive<PhysOp, Idx, Op, PatternQueue...>
497{
498 template<typename, std::size_t, typename...> friend struct pattern_matcher_recursive;
499
500 using pattern = PhysOp::pattern;
501
502 template<typename PhysOpt, typename... OpQueue>
503 void matches(PhysOpt &opt,
504 get_nodes_t<pattern> &current_nodes,
505 std::vector<typename PhysOpt::children_type> current_children_list,
506 const Operator &op_,
507 const OpQueue&... op_queue) const
508 {
509 M_insist(sizeof...(PatternQueue) == sizeof...(op_queue));
510 M_insist(std::all_of(current_children_list.cbegin(), current_children_list.cend(), [&](auto &current_children){
511 return current_children.size() == current_children_list.front().size();
512 }));
513
514 auto op = cast<const Op>(&op_);
515 if (not op)
516 return; // current operator does not match
517
518 /* Check whether matches for all children exists and fulfill the pre-conditions for this physical operator.
519 * If so, update current nodes and current children list (using Cartesian product with newly found children list),
520 * otherwise, no match can be found. */
521 std::get<Idx>(current_nodes) = op;
522 if constexpr (std::same_as<Op, Wildcard>) {
523 if (op->id() >= opt.table().size())
524 return; // no match for this child exists
525
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))
531 {
532 new_child_list.push_back(it);
533 }
534 }
535 if (new_child_list.empty())
536 return; // no match fulfills pre-condition for this physical operator
537
538 std::vector<typename PhysOpt::children_type> _current_children_list;
539 for (auto &current_children : current_children_list) {
540 for (auto &new_child : new_child_list) {
541 auto &_current_children = _current_children_list.emplace_back(current_children); // copy for Cartesian product
542 _current_children.push_back(new_child);
543 }
544 }
545 current_children_list = std::move(_current_children_list);
546 } else if constexpr (consumer<Op>) {
547 std::vector<typename PhysOpt::children_type> new_children_list;
548 new_children_list.emplace_back(); // start with single empty children
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())
552 return; // no match for current child exists
553
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))
559 {
560 new_child_list.push_back(it);
561 }
562 }
563 if (new_child_list.empty())
564 return; // no match fulfills pre-condition for this physical operator
565
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); // copy for Cartesian product
570 _new_children.push_back(new_child);
571 }
572 }
573 new_children_list = std::move(_new_children_list);
574 }
575
576 std::vector<typename PhysOpt::children_type> _current_children_list;
577 for (auto &current_children : current_children_list) {
578 for (auto &new_children : new_children_list) {
579 auto &_current_children = _current_children_list.emplace_back(current_children); // copy for Cartesian product
580 _current_children.insert(_current_children.end(), new_children.begin(), new_children.end());
581 }
582 }
583 current_children_list = std::move(_current_children_list);
584 } else { // special case for handling leaf producer, e.g. scan operator
585 static_assert(producer<Op>);
586 ConditionSet empty;
587 if (not PhysOp::pre_condition_(current_children_list.front().size(), current_nodes).implied_by(empty))
588 return; // match does not fulfill pre-condition for this physical operator
589 }
590
591 if constexpr (sizeof...(PatternQueue) == 0) {
592 static_assert(Idx == std::tuple_size_v<get_nodes_t<pattern>> - 1);
593#ifndef NDEBUG
594 std::apply([](auto&&... ops) { (M_insist(ops != nullptr), ...); }, current_nodes);
595#endif
596 /* Perform the callback with an instance for each found found match. */
597 for (auto &current_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);
600 }
601 } else {
602 /* Proceed with queue. */
603 pattern_matcher_recursive<PhysOp, Idx + 1, PatternQueue...> m;
604 m.matches(opt, current_nodes, std::move(current_children_list), op_queue...);
605 }
606 }
607};
608
609template<typename PhysOp, std::size_t Idx, typename Op, typename... Children, typename... PatternQueue>
610struct pattern_matcher_recursive<PhysOp, Idx, pattern_t<Op, Children...>, PatternQueue...>
611{
612 template<typename, std::size_t, typename...> friend struct pattern_matcher_recursive;
613
614 using pattern = PhysOp::pattern;
615
616 template<typename PhysOpt, typename... OpQueue>
617 void matches(PhysOpt &opt,
618 get_nodes_t<pattern> &current_nodes,
619 std::vector<typename PhysOpt::children_type> current_children_list,
620 const Operator &op_,
621 const OpQueue&... op_queue) const
622 {
623 M_insist(sizeof...(PatternQueue) == sizeof...(op_queue));
624
625 auto op = cast<const Op>(&op_);
626 if (not op)
627 return; // current root does not match
628
629 if (op->children().size() != sizeof...(Children))
630 return; // children number does not match
631
632 /* Update current nodes. */
633 std::get<Idx>(current_nodes) = op;
634
635 if constexpr (sizeof...(Children) == 1) {
636 /* Recursively match single child. */
637 using Child0 = ith_type_of<0, Children...>;
638 pattern_matcher_recursive<PhysOp, Idx + 1, Child0, PatternQueue...> m;
639 m.matches(opt, current_nodes, std::move(current_children_list), *op->child(0), op_queue...);
640 } else {
641 /* Recursively match both children. Try both permutations of the logical plan. */
642 static_assert(sizeof...(Children) == 2);
643 using Child0 = ith_type_of<0, Children...>;
644 using Child1 = ith_type_of<1, Children...>;
645 {
646 pattern_matcher_recursive<PhysOp, Idx + 1, Child0, Child1, PatternQueue...> m;
647 m.matches(opt, current_nodes, current_children_list, *op->child(0), *op->child(1), op_queue...); // copy children list
648 }
649 {
650 pattern_matcher_recursive<PhysOp, Idx + 1, Child0, Child1, PatternQueue...> m;
651 m.matches(opt, current_nodes, std::move(current_children_list), *op->child(1), *op->child(0), op_queue...);
652 }
653 }
654 }
655};
656
657template<typename PhysOp, typename PhysOpt>
659{
660 using pattern = PhysOp::pattern;
661
662 void matches(PhysicalOptimizer &opt, const Operator &op) const override {
663 get_nodes_t<pattern> current_nodes;
664 std::vector<typename PhysOpt::children_type> current_children_list;
665 current_children_list.emplace_back(); // start with single empty children
667 m.matches(as<PhysOpt>(opt), current_nodes, std::move(current_children_list), op);
668 }
669};
670
671
672/*======================================================================================================================
673 * Delayed definitions
674 *====================================================================================================================*/
675
676template<typename PhysOp>
678{
680 [this]<typename PhysOpt>(PhysOpt&) {
681 pattern_matchers_.push_back(std::make_unique<pattern_matcher_impl<PhysOp, PhysOpt>>());
682 },
683 }, *this);
684}
685
686}
#define M_PHYS_OPT_LIST(X)
#define DECLARE(CLASS)
#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 ...
Definition: Visitor.hpp:181
struct @5 args
#define M_unreachable(MSG)
Definition: macro.hpp:146
#define M_nothrow(ARG)
Definition: macro.hpp:197
#define M_insist(...)
Definition: macro.hpp:129
‍mutable namespace
Definition: Backend.hpp:10
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
and
Definition: enum_ops.hpp:12
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.
Definition: Visitor.hpp:138
STL namespace.
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)
void dump() const
friend std::ostream & operator<<(std::ostream &out, const MatchBase &M)
void cost(double new_cost)
double cost() const
An Operator represents an operation in a query plan.
Definition: Operator.hpp:45
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.
Definition: Operator.hpp:81
std::size_t id() const
Returns the ID of this.
Definition: Operator.hpp:85
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 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.
Definition: Operator.hpp:120
A helper class to define CRTP class hierarchies.
Definition: crtp.hpp:50
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
void matches(PhysicalOptimizer &opt, const Operator &op) const override
void matches(PhysOpt &opt, get_nodes_t< pattern > &current_nodes, std::vector< typename PhysOpt::children_type > current_children_list, const Operator &op_, const OpQueue &... op_queue) const
void matches(PhysOpt &opt, get_nodes_t< pattern > &current_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)