13 std::vector<unsigned> depth({0});
17 std::vector<unsigned> &depth;
19 indent(std::ostream &out,
const Operator &op, std::vector<unsigned> &depth) : out(out), op(op), depth(depth) {
21 const unsigned n = depth.back();
23 if (
auto c = cast<const Consumer>(&op))
24 depth.insert(depth.end(), c->children().size(), n+1);
25 if (n) out <<
'\n' << std::string(2 * (n-1),
' ') <<
"` ";
29 out <<
' ' <<
op.schema();
31 auto &info =
op.info();
32 out <<
" <" << info.estimated_cardinality <<
'>';
40 out <<
"PrintOperator";
41 if (&
op.out == &std::cout) out <<
" to stdout";
42 else if (&
op.out == &std::cerr) out <<
" to stderr";
47 <<
"ScanOperator (" <<
op.store().table().name() <<
" AS " <<
op.alias() <<
')';
51 indent(out, op, depth).out <<
"DisjunctiveFilterOperator " <<
op.filter();
54 indent(out, op, depth).out <<
"JoinOperator " <<
op.predicate();
58 indent(out, op, depth).out <<
"LimitOperator " <<
op.limit() <<
", " <<
op.offset();
62 out <<
"GroupingOperator [";
63 for (
auto begin =
op.group_by().begin(), it = begin, end =
op.group_by().end(); it != end; ++it) {
64 if (it != begin) out <<
", ";
65 out << it->first.get();
66 if (it->second.has_value())
67 out <<
" AS " << it->second;
70 for (
auto begin =
op.aggregates().begin(), it = begin, end =
op.aggregates().end(); it != end; ++it) {
71 if (it != begin) out <<
", ";
78 out <<
"AggregationOperator [";
79 for (
auto begin =
op.aggregates().begin(), it = begin, end =
op.aggregates().end(); it != end; ++it) {
80 if (it != begin) out <<
", ";
87 out <<
"SortingOperator [";
88 for (
auto begin =
op.order_by().begin(), it = begin, end =
op.order_by().end(); it != end; ++it) {
89 if (it != begin) out <<
", ";
90 out << it->first.get() <<
' ' << (it->second ?
"ASC" :
"DESC");
102 constexpr const char *
const GRAPH_TYPE =
"digraph";
103 constexpr const char *
const EDGE =
" -> ";
104 out << GRAPH_TYPE <<
" plan\n{\n"
105 <<
" forcelabels=true;\n"
106 <<
" overlap=false;\n"
108 <<
" graph [compound=true];\n"
109 <<
" graph [fontname = \"DejaVu Sans\"];\n"
110 <<
" node [fontname = \"DejaVu Sans\"];\n"
111 <<
" edge [fontname = \"DejaVu Sans\"];\n";
113#define q(X) '"' << X << '"'
114#define id(X) q(std::hex << &X << std::dec)
117 out <<
" " <<
id(op) <<
" [label=<<B>" <<
html_escape(*op.alias()) <<
"</B>>];\n";
120 out <<
" " <<
id(op) <<
" [label=<<B>σ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">"
122 <<
"</FONT></SUB>>];\n"
123 <<
" " <<
id(*op.child(0)) << EDGE <<
id(op) <<
";\n";
126 out <<
" " <<
id(op) <<
" [label=<<B>σ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
127 const auto &clause = op.filter()[0];
128 for (
auto it = clause.cbegin(); it != clause.cend(); ++it) {
129 if (it != clause.cbegin()) out <<
" → ";
132 out <<
"</FONT></SUB>>];\n"
133 <<
" " <<
id(*op.child(0)) << EDGE <<
id(op) <<
";\n";
136 out <<
" " <<
id(op) <<
" [label=<<B>⋈</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">"
138 <<
"</FONT></SUB>>];\n";
140 for (
auto c : op.children())
141 out <<
" " <<
id(*c) << EDGE <<
id(op) <<
";\n";
144 out <<
id(op) <<
" [label=<<B>π</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
145 const auto &P = op.projections();
146 for (
auto it = P.begin(); it != P.end(); ++it) {
147 if (it != P.begin()) out <<
", ";
149 if (it->second.has_value())
150 out <<
" AS " << it->second;
152 out <<
"</FONT></SUB>>];\n";
154 if (not op.children().empty())
155 out <<
id(*op.child(0)) << EDGE <<
id(op) <<
";\n";
158 out <<
" " <<
id(op) <<
" [label=<<B>λ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">"
160 if (op.offset()) out <<
", " << op.offset();
161 out <<
"</FONT></SUB>>];\n"
162 <<
" " <<
id(*op.child(0)) << EDGE <<
id(op) <<
";\n";
165 out <<
" " <<
id(op) <<
" [label=<<B>γ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
167 const auto &G = op.group_by();
168 const auto &A = op.aggregates();
170 for (
auto it = G.begin(); it != G.end(); ++it) {
171 if (it != G.begin()) out <<
", ";
172 std::ostringstream oss;
173 oss << it->first.get();
175 if (it->second.has_value())
179 if (G.size()
and A.size()) out <<
", ";
181 for (
auto it = A.begin(); it != A.end(); ++it) {
182 if (it != A.begin()) out <<
", ";
186 out <<
"</FONT></SUB>>];\n"
187 <<
" " <<
id(*op.child(0)) << EDGE <<
id(op) <<
";\n";
190 out <<
" " <<
id(op) <<
" [label=<<B>Γ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
191 const auto &A = op.aggregates();
192 for (
auto it = A.begin(); it != A.end(); ++it) {
193 if (it != A.begin()) out <<
", ";
197 out <<
"</FONT></SUB>>];\n"
198 <<
" " <<
id(*op.child(0)) << EDGE <<
id(op) <<
";\n";
201 out <<
" " <<
id(op) <<
" [label=<<B>ω</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
203 const auto &O = op.order_by();
204 for (
auto it = O.begin(); it != O.end(); ++it) {
205 if (it != O.begin()) out <<
", ";
206 out << it->first.get() <<
' ' << (it->second ?
"ASC" :
"DESC");
209 out <<
"</FONT></SUB>>];\n"
210 <<
" " <<
id(*op.child(0)) << EDGE <<
id(op) <<
";\n";
225 : projections_(
std::move(projections))
230 auto ty = proj.get().type();
232 if (not proj.get().can_be_null())
234 if (alias.has_value()) {
236 S.add(std::move(
id), ty, constraints);
237 }
else if (
auto D = cast<const ast::Designator>(proj)) {
239 S.add(std::move(
id), ty, constraints);
241 if (is<const ast::Constant>(proj)) {
245 std::ostringstream oss;
248 S.add(std::move(
id), ty, constraints);
255 std::vector<std::reference_wrapper<const ast::FnApplicationExpr>> aggregates)
256 : group_by_(
std::move(group_by))
257 , aggregates_(
std::move(aggregates))
261 std::ostringstream oss;
265 auto pt = as<const PrimitiveType>(grp.get().type());
269 if (not grp.get().can_be_null())
271 if (alias.has_value()) {
272 S.add(alias.assert_not_none(), pt->as_scalar(), constraints);
273 }
else if (
auto D = cast<const ast::Designator>(grp)) {
275 S.add(std::move(
id), pt->as_scalar(), constraints);
279 auto alias = C.pool(oss.str().c_str());
280 S.add(std::move(alias), pt->as_scalar(), constraints);
286 auto ty = e.get().type();
289 auto alias = C.pool(oss.str().c_str());
291 if (not e.get().can_be_null())
293 S.add(std::move(alias), ty, constraints);
298 : aggregates_(
std::move(aggregates))
302 std::ostringstream oss;
304 auto ty = e.get().type();
307 auto alias = C.pool(oss.str().c_str());
309 if (e.get().get_function().fnid == Function::FN_COUNT)
311 S.add(std::move(alias), ty, constraints);
320#define ACCEPT(CLASS) \
321 void CLASS::accept(OperatorVisitor &V) { V(*this); } \
322 void CLASS::accept(ConstOperatorVisitor &V) const { V(*this); }
331struct SchemaMinimizer : OperatorVisitor
335 bool is_top_of_plan_ =
true;
338 using OperatorVisitor::operator();
340#define DECLARE(CLASS) void operator()(Const<CLASS> &op) override;
347 void add_constraints(
Schema &schema,
const Schema &constraints, std::size_t n,
351 for (std::size_t idx = 0; idx < n; ++idx) {
352 auto &e = schema[idx];
353 auto it = constraints.
find(e.id);
354 if (it != constraints.
end())
355 e.constraints |= it->constraints & ~excluded_constraints;
358 void add_constraints(
Schema &schema,
const Schema &constraints,
361 add_constraints(schema, constraints, schema.
num_entries(), excluded_constraints);
370 required = required &
op.schema();
371 op.schema() = required;
376 (*this)(*
op.child(0));
377 op.schema() =
op.child(0)->schema();
382 (*this)(*
op.child(0));
383 op.schema() =
op.child(0)->schema();
388 (*this)(*
op.child(0));
389 op.schema() =
op.child(0)->schema();
394 if (is_top_of_plan_) {
395 required =
op.schema();
396 is_top_of_plan_ =
false;
398 auto required_by_op =
op.filter().get_required();
399 M_insist(required == (required &
op.schema()),
"required must be subset of operator schema");
400 op.schema() = required;
401 required |= required_by_op;
403 (*this)(*
op.child(0));
404 add_constraints(
op.schema(),
op.child(0)->schema());
409 (*this)(as<FilterOperator>(op));
414 Schema required_from_below;
415 if (is_top_of_plan_) {
416 required_from_below =
op.schema();
417 is_top_of_plan_ =
false;
419 required_from_below = required |
op.predicate().get_required();
420 M_insist(required == (required &
op.schema()),
"required must be subset of operator schema");
421 op.schema() = required;
423 for (
auto c :
const_cast<const JoinOperator&
>(op).children()) {
424 required = required_from_below & c->schema();
435 std::size_t pos_out = 0;
436 for (std::size_t pos_in = 0; pos_in !=
op.projections().size(); ++pos_in) {
438 auto &proj =
op.projections()[pos_in];
439 auto &e =
op.schema()[pos_in];
441 if (is_top_of_plan_ or required.has(e.id)) {
442 required_by_op |= proj.first.get().get_required();
443 op.projections()[pos_out++] = std::move(
op.projections()[pos_in]);
448 const auto &dummy =
op.projections()[0];
449 op.projections().resize(pos_out, dummy);
451 op.schema() = std::move(ours);
452 required = std::move(required_by_op);
453 is_top_of_plan_ =
false;
455 (*this)(*
op.child(0));
456 add_constraints(
op.schema(),
op.child(0)->schema());
462 (*this)(*
op.child(0));
463 op.schema() =
op.child(0)->schema();
473 auto it =
op.schema().cbegin();
474 for (
auto &[grp, _] :
op.group_by()) {
475 required_by_us |= grp.get().get_required();
479 if (not
op.aggregates().empty()) {
480 std::ostringstream oss;
481 std::size_t pos_out = 0;
482 for (std::size_t pos_in = 0; pos_in !=
op.aggregates().size(); ++pos_in) {
484 auto &agg =
op.aggregates()[pos_in];
490 if (is_top_of_plan_ or required.has(agg_id)) {
491 for (
auto &arg : agg.get().args)
492 required_by_us |= arg->get_required();
493 op.aggregates()[pos_out++] = std::move(
op.aggregates()[pos_in]);
494 ours.
add(
op.schema()[agg_id].second);
499 op.aggregates().resize(pos_out, dummy);
502 op.schema() = std::move(ours);
503 required = std::move(required_by_us);
504 is_top_of_plan_ =
false;
505 (*this)(*
op.child(0));
506 add_constraints(
op.schema(),
op.child(0)->schema(),
op.group_by().size());
513 std::ostringstream oss;
518 std::size_t pos_out = 0;
519 for (std::size_t pos_in = 0; pos_in !=
op.aggregates().size(); ++pos_in) {
521 auto &agg =
op.aggregates()[pos_in];
527 if (is_top_of_plan_ or required.has(agg_id)) {
528 for (
auto &arg : agg.get().args)
529 required_by_op |= arg->get_required();
530 op.aggregates()[pos_out++] = std::move(
op.aggregates()[pos_in]);
531 ours.
add(
op.schema()[agg_id].second);
536 op.aggregates().resize(pos_out, dummy);
538 op.schema() = std::move(ours);
539 required = std::move(required_by_op);
540 is_top_of_plan_ =
false;
541 (*this)(*
op.child(0));
547 if (is_top_of_plan_) {
548 required =
op.schema();
549 is_top_of_plan_ =
false;
552 for (
auto &ord :
op.order_by())
553 required_by_op |= ord.first.get().get_required();
554 M_insist(required == (required &
op.schema()),
"required must be subset of operator schema");
555 op.schema() = required;
556 required |= required_by_op;
558 (*this)(*
op.child(0));
559 add_constraints(
op.schema(),
op.child(0)->schema());
569void register_post_optimization()
574 plan->minimize_schema();
576 },
"minimizes the schema of an operator tree");
581 },
"minimizes the schema of an operator tree");
__attribute__((constructor(202))) static void register_interpreter()
#define M_OPERATOR_LIST(X)
std::ostream & indent(std::ostream &out, unsigned indentation)
Start a new line with proper indentation.
std::string M_EXPORT html_escape(std::string str)
Escapes special characters in a string to be printable in HTML documents.
std::string to_string(const TokenType tt)
M_LCOV_EXCL_START std::ostream & operator<<(std::ostream &out, const PlanTableBase< Actual > &PT)
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.
AggregationOperator(std::vector< std::reference_wrapper< const ast::FnApplicationExpr > > aggregates)
std::vector< std::reference_wrapper< const ast::FnApplicationExpr > > aggregates_
the aggregates to compute
The catalog contains all Databases and keeps track of all meta information of the database system.
void register_logical_post_optimization(ThreadSafePooledString name, LogicalPostOptimizationCallback optimization, const char *description=nullptr)
Registers a new logical post-optimization with the given name.
ThreadSafePooledString pool(const char *str) const
Creates an internalized copy of the string str by adding it to the internal StringPool.
static Catalog & Get()
Return a reference to the single Catalog instance.
void register_physical_post_optimization(const char *name, PhysicalPostOptimizationCallback optimization, const char *description=nullptr)
Registers a new physical post-optimization with the given name.
GroupingOperator(std::vector< group_type > group_by, std::vector< std::reference_wrapper< const ast::FnApplicationExpr > > aggregates)
std::vector< group_type > group_by_
the compound grouping key
std::vector< std::reference_wrapper< const ast::FnApplicationExpr > > aggregates_
the aggregates to compute
const auto & group_by() const
static constexpr Schema::entry_type::constraints_t REMOVED_CONSTRAINTS
Drops the produced results and outputs only the number of result tuples produced.
virtual ~OperatorData()=0
An Operator represents an operation in a query plan.
void minimize_schema()
Minimizes the Schema of this Operator.
Schema & schema()
Returns the Schema of this Operator.
void dot(std::ostream &out) const
Prints a representation of this Operator and its descendants in the dot language.
std::size_t id() const
Returns the ID of this.
Prints the produced Tuples to a std::ostream instance.
std::vector< projection_type > projections_
ProjectionOperator(std::vector< projection_type > projections)
An Identifier is composed of a name and an optional prefix.
static Identifier GetConstant()
@ NOT_NULLABLE
entry must not be NULL
@ UNIQUE
entry has unique values
A Schema represents a sequence of identifiers, optionally with a prefix, and their associated types.
std::size_t num_entries() const
Returns the number of entries in this Schema.
iterator find(const Identifier &id)
Returns an iterator to the entry with the given Identifier id, or end() if no such entry exists.
void add(entry_type e)
Adds the entry e to this Schema.