mutable
A Database System for Research and Fast Prototyping
Loading...
Searching...
No Matches
Operator.cpp
Go to the documentation of this file.
2
4
5
6using namespace m;
7
8
10
12std::ostream & m::operator<<(std::ostream &out, const Operator &op) {
13 std::vector<unsigned> depth({0}); // stack of indentation depths
14 struct indent {
15 std::ostream &out;
16 const Operator &op;
17 std::vector<unsigned> &depth;
18
19 indent(std::ostream &out, const Operator &op, std::vector<unsigned> &depth) : out(out), op(op), depth(depth) {
20 M_insist(not depth.empty());
21 const unsigned n = depth.back();
22 depth.pop_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), ' ') << "` ";
26 }
27
28 ~indent() {
29 out << ' ' << op.schema();
30 if (op.has_info()) {
31 auto &info = op.info();
32 out << " <" << info.estimated_cardinality << '>';
33 }
34 }
35 };
37 [&out, &depth](const CallbackOperator &op) { indent(out, op, depth).out << "CallbackOperator"; },
38 [&out, &depth](const PrintOperator &op) {
39 indent i(out, op, depth);
40 out << "PrintOperator";
41 if (&op.out == &std::cout) out << " to stdout";
42 else if (&op.out == &std::cerr) out << " to stderr";
43 },
44 [&out, &depth](const NoOpOperator &op) { indent(out, op, depth).out << "NoOpOperator"; },
45 [&out, &depth](const ScanOperator &op) {
46 indent(out, op, depth).out
47 << "ScanOperator (" << op.store().table().name() << " AS " << op.alias() << ')';
48 },
49 [&out, &depth](const FilterOperator &op) { indent(out, op, depth).out << "FilterOperator " << op.filter(); },
50 [&out, &depth](const DisjunctiveFilterOperator &op) {
51 indent(out, op, depth).out << "DisjunctiveFilterOperator " << op.filter();
52 },
53 [&out, &depth](const JoinOperator &op) {
54 indent(out, op, depth).out << "JoinOperator " << op.predicate();
55 },
56 [&out, &depth](const ProjectionOperator &op) { indent(out, op, depth).out << "ProjectionOperator"; },
57 [&out, &depth](const LimitOperator &op) {
58 indent(out, op, depth).out << "LimitOperator " << op.limit() << ", " << op.offset();
59 },
60 [&out, &depth](const GroupingOperator &op) {
61 indent i(out, op, depth);
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;
68 }
69 out << "] [";
70 for (auto begin = op.aggregates().begin(), it = begin, end = op.aggregates().end(); it != end; ++it) {
71 if (it != begin) out << ", ";
72 out << it->get();
73 }
74 out << ']';
75 },
76 [&out, &depth](const AggregationOperator &op) {
77 indent i(out, op, depth);
78 out << "AggregationOperator [";
79 for (auto begin = op.aggregates().begin(), it = begin, end = op.aggregates().end(); it != end; ++it) {
80 if (it != begin) out << ", ";
81 out << it->get();
82 }
83 out << ']';
84 },
85 [&out, &depth](const SortingOperator &op) {
86 indent i(out, op, depth);
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");
91 }
92 out << ']';
93 },
95
96 return out;
97}
99
100void Operator::dot(std::ostream &out) const
101{
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"
107 << " rankdir=BT;\n"
108 << " graph [compound=true];\n"
109 << " graph [fontname = \"DejaVu Sans\"];\n"
110 << " node [fontname = \"DejaVu Sans\"];\n"
111 << " edge [fontname = \"DejaVu Sans\"];\n";
112
113#define q(X) '"' << X << '"' // quote
114#define id(X) q(std::hex << &X << std::dec) // convert virtual address to identifier
116 [&out](const ScanOperator &op) {
117 out << " " << id(op) << " [label=<<B>" << html_escape(*op.alias()) << "</B>>];\n";
118 },
119 [&out](const FilterOperator &op) {
120 out << " " << id(op) << " [label=<<B>σ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">"
121 << html_escape(to_string(op.filter()))
122 << "</FONT></SUB>>];\n"
123 << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
124 },
125 [&out](const DisjunctiveFilterOperator &op) {
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 << " → ";
130 out << html_escape(to_string(*it));
131 }
132 out << "</FONT></SUB>>];\n"
133 << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
134 },
135 [&out](const JoinOperator &op) {
136 out << " " << id(op) << " [label=<<B>⋈</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">"
137 << html_escape(to_string(op.predicate()))
138 << "</FONT></SUB>>];\n";
139
140 for (auto c : op.children())
141 out << " " << id(*c) << EDGE << id(op) << ";\n";
142 },
143 [&out](const ProjectionOperator &op) {
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 << ", ";
148 out << it->first;
149 if (it->second.has_value())
150 out << " AS " << it->second;
151 }
152 out << "</FONT></SUB>>];\n";
153
154 if (not op.children().empty())
155 out << id(*op.child(0)) << EDGE << id(op) << ";\n";
156 },
157 [&out](const LimitOperator &op) {
158 out << " " << id(op) << " [label=<<B>λ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">"
159 << op.limit();
160 if (op.offset()) out << ", " << op.offset();
161 out << "</FONT></SUB>>];\n"
162 << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
163 },
164 [&out](const GroupingOperator &op) {
165 out << " " << id(op) << " [label=<<B>γ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
166
167 const auto &G = op.group_by();
168 const auto &A = op.aggregates();
169
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();
174 out << html_escape(oss.str());
175 if (it->second.has_value())
176 out << html_escape(*it->second);
177 }
178
179 if (G.size() and A.size()) out << ", ";
180
181 for (auto it = A.begin(); it != A.end(); ++it) {
182 if (it != A.begin()) out << ", ";
183 out << it->get();
184 }
185
186 out << "</FONT></SUB>>];\n"
187 << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
188 },
189 [&out](const AggregationOperator &op) {
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 << ", ";
194 out << it->get();
195 }
196
197 out << "</FONT></SUB>>];\n"
198 << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
199 },
200 [&out](const SortingOperator &op) {
201 out << " " << id(op) << " [label=<<B>ω</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
202
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");
207 }
208
209 out << "</FONT></SUB>>];\n"
210 << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
211 },
212 [](auto&&) { /* nothing to be done */ }
214#undef id
215#undef q
216 out << "}\n";
217}
218
220void Operator::dump(std::ostream &out) const { out << *this << std::endl; }
221void Operator::dump() const { dump(std::cerr); }
223
224ProjectionOperator::ProjectionOperator(std::vector<projection_type> projections)
225 : projections_(std::move(projections))
226{
227 /* Compute the schema of the operator. */
228 auto &S = schema();
229 for (auto &[proj, alias] : projections_) {
230 auto ty = proj.get().type();
232 if (not proj.get().can_be_null())
234 if (alias.has_value()) { // alias was given
235 Schema::Identifier id(alias.assert_not_none());
236 S.add(std::move(id), ty, constraints);
237 } else if (auto D = cast<const ast::Designator>(proj)) { // no alias, but designator -> keep name
238 Schema::Identifier id(D->table_name.text, D->attr_name.text.assert_not_none());
239 S.add(std::move(id), ty, constraints);
240 } else { // no designator, no alias -> derive name
241 if (is<const ast::Constant>(proj)) {
242 // TODO: use `Expr::is_constant()` once interpretation of constant expressions is supported
243 S.add(Schema::Identifier::GetConstant(), ty, constraints);
244 } else {
245 std::ostringstream oss;
246 oss << proj.get();
247 Schema::Identifier id(Catalog::Get().pool(oss.str().c_str()));
248 S.add(std::move(id), ty, constraints);
249 }
250 }
251 }
252}
253
254GroupingOperator::GroupingOperator(std::vector<group_type> group_by,
255 std::vector<std::reference_wrapper<const ast::FnApplicationExpr>> aggregates)
256 : group_by_(std::move(group_by))
257 , aggregates_(std::move(aggregates))
258{
259 auto &C = Catalog::Get();
260 auto &S = schema();
261 std::ostringstream oss;
262
263 {
264 for (auto &[grp, alias] : group_by_) {
265 auto pt = as<const PrimitiveType>(grp.get().type());
267 if (group_by.size() == 1)
268 constraints |= Schema::entry_type::UNIQUE;
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)) { // designator -> keep name
274 Schema::Identifier id(D->attr_name.text.assert_not_none()); // w/o table name
275 S.add(std::move(id), pt->as_scalar(), constraints);
276 } else {
277 oss.str("");
278 oss << grp.get();
279 auto alias = C.pool(oss.str().c_str());
280 S.add(std::move(alias), pt->as_scalar(), constraints);
281 }
282 }
283 }
284
285 for (auto &e : aggregates_) {
286 auto ty = e.get().type();
287 oss.str("");
288 oss << e.get();
289 auto alias = C.pool(oss.str().c_str());
291 if (not e.get().can_be_null()) // group cannot be empty, thus no default NULL will occur
293 S.add(std::move(alias), ty, constraints);
294 }
295}
296
297AggregationOperator::AggregationOperator(std::vector<std::reference_wrapper<const ast::FnApplicationExpr>> aggregates)
298 : aggregates_(std::move(aggregates))
299{
300 auto &C = Catalog::Get();
301 auto &S = schema();
302 std::ostringstream oss;
303 for (auto &e : aggregates_) {
304 auto ty = e.get().type();
305 oss.str("");
306 oss << e.get();
307 auto alias = C.pool(oss.str().c_str());
308 Schema::entry_type::constraints_t constraints{Schema::entry_type::UNIQUE}; // since a single tuple is produced
309 if (e.get().get_function().fnid == Function::FN_COUNT) // COUNT cannot be NULL (even for empty input)
311 S.add(std::move(alias), ty, constraints);
312 }
313}
314
315
316/*======================================================================================================================
317 * accept()
318 *====================================================================================================================*/
319
320#define ACCEPT(CLASS) \
321 void CLASS::accept(OperatorVisitor &V) { V(*this); } \
322 void CLASS::accept(ConstOperatorVisitor &V) const { V(*this); }
324#undef ACCEPT
325
326
327/*======================================================================================================================
328 * minimize_schema()
329 *====================================================================================================================*/
330
331struct SchemaMinimizer : OperatorVisitor
332{
333 private:
334 Schema required;
335 bool is_top_of_plan_ = true;
336
337 public:
338 using OperatorVisitor::operator();
339
340#define DECLARE(CLASS) void operator()(Const<CLASS> &op) override;
342#undef DECLARE
343
344 private:
347 void add_constraints(Schema &schema, const Schema &constraints, std::size_t n,
349 {
350 M_insist(n <= schema.num_entries(), "invalid length");
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; // merge constraints except excluded ones
356 }
357 }
358 void add_constraints(Schema &schema, const Schema &constraints,
360 {
361 add_constraints(schema, constraints, schema.num_entries(), excluded_constraints);
362 }
363};
364
365void SchemaMinimizer::operator()(ScanOperator &op)
366{
367 if (is_top_of_plan_) // scan is top of plan and leaf at the same time
368 return; // still provide all entries of the table
369
370 required = required & op.schema(); // intersect with scan operator schema to add constraints to the required schema
371 op.schema() = required; // the scan operator produces exactly those attributes required by the ancestors
372}
373
374void SchemaMinimizer::operator()(CallbackOperator &op)
375{
376 (*this)(*op.child(0)); // this operator does not affect what is required; nothing to be done
377 op.schema() = op.child(0)->schema();
378}
379
380void SchemaMinimizer::operator()(PrintOperator &op)
381{
382 (*this)(*op.child(0)); // this operator does not affect what is required; nothing to be done
383 op.schema() = op.child(0)->schema();
384}
385
386void SchemaMinimizer::operator()(NoOpOperator &op)
387{
388 (*this)(*op.child(0)); // this operator does not affect what is required; nothing to be done
389 op.schema() = op.child(0)->schema();
390}
391
392void SchemaMinimizer::operator()(FilterOperator &op)
393{
394 if (is_top_of_plan_) {
395 required = op.schema(); // require everything
396 is_top_of_plan_ = false;
397 } else {
398 auto required_by_op = op.filter().get_required(); // add what's required to evaluate the filter predicate
399 M_insist(required == (required & op.schema()), "required must be subset of operator schema");
400 op.schema() = required; // set schema to what is required above
401 required |= required_by_op; // add what's required to evaluate the filter predicate
402 }
403 (*this)(*op.child(0));
404 add_constraints(op.schema(), op.child(0)->schema()); // add constraints from child
405}
406
407void SchemaMinimizer::operator()(DisjunctiveFilterOperator &op)
408{
409 (*this)(as<FilterOperator>(op)); // delegate to FilterOperator
410}
411
412void SchemaMinimizer::operator()(JoinOperator &op)
413{
414 Schema required_from_below;
415 if (is_top_of_plan_) {
416 required_from_below = op.schema(); // require everything
417 is_top_of_plan_ = false;
418 } else {
419 required_from_below = required | op.predicate().get_required(); // what we need and all operators above us
420 M_insist(required == (required & op.schema()), "required must be subset of operator schema");
421 op.schema() = required;
422 }
423 for (auto c : const_cast<const JoinOperator&>(op).children()) {
424 required = required_from_below & c->schema(); // what we need from this child
425 (*this)(*c);
426 add_constraints(op.schema(), c->schema(), JoinOperator::REMOVED_CONSTRAINTS); // add constraints from child except removed ones
427 }
428}
429
430void SchemaMinimizer::operator()(ProjectionOperator &op)
431{
432 Schema required_by_op;
433 Schema ours;
434
435 std::size_t pos_out = 0;
436 for (std::size_t pos_in = 0; pos_in != op.projections().size(); ++pos_in) {
437 M_insist(pos_out <= pos_in);
438 auto &proj = op.projections()[pos_in];
439 auto &e = op.schema()[pos_in];
440
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]);
444 ours.add(e);
445 }
446 }
447 M_insist(pos_out <= op.projections().size());
448 const auto &dummy = op.projections()[0];
449 op.projections().resize(pos_out, dummy);
450
451 op.schema() = std::move(ours);
452 required = std::move(required_by_op);
453 is_top_of_plan_ = false;
454 if (not const_cast<const ProjectionOperator&>(op).children().empty()) {
455 (*this)(*op.child(0));
456 add_constraints(op.schema(), op.child(0)->schema()); // add constraints from child
457 }
458}
459
460void SchemaMinimizer::operator()(LimitOperator &op)
461{
462 (*this)(*op.child(0));
463 op.schema() = op.child(0)->schema();
464}
465
466void SchemaMinimizer::operator()(GroupingOperator &op)
467{
468 Catalog &C = Catalog::Get();
469
470 Schema ours;
471 Schema required_by_us;
472
473 auto it = op.schema().cbegin();
474 for (auto &[grp, _] : op.group_by()) {
475 required_by_us |= grp.get().get_required();
476 ours.add(*it++); // copy grouping keys
477 }
478
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) {
483 M_insist(pos_out <= pos_in);
484 auto &agg = op.aggregates()[pos_in];
485
486 oss.str("");
487 oss << agg.get();
488 Schema::Identifier agg_id(C.pool(oss.str()));
489
490 if (is_top_of_plan_ or required.has(agg_id)) { // if first, require everything
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]); // keep aggregate
494 ours.add(op.schema()[agg_id].second);
495 }
496 }
497 M_insist(pos_out <= op.aggregates().size());
498 const ast::FnApplicationExpr &dummy = op.aggregates()[0].get();
499 op.aggregates().resize(pos_out, dummy); // discard unrequired aggregates
500 }
501
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()); // add constraints to grouping keys from child
507}
508
509void SchemaMinimizer::operator()(AggregationOperator &op)
510{
511 M_insist(not op.aggregates().empty());
512 Catalog &C = Catalog::Get();
513 std::ostringstream oss;
514
515 Schema required_by_op; // the AggregationOperator doesn't care what later operators require
516 Schema ours;
517
518 std::size_t pos_out = 0;
519 for (std::size_t pos_in = 0; pos_in != op.aggregates().size(); ++pos_in) {
520 M_insist(pos_out <= pos_in);
521 auto &agg = op.aggregates()[pos_in];
522
523 oss.str("");
524 oss << agg.get();
525 Schema::Identifier agg_id(C.pool(oss.str()));
526
527 if (is_top_of_plan_ or required.has(agg_id)) { // if first, require everything
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]); // keep aggregate
531 ours.add(op.schema()[agg_id].second);
532 }
533 }
534 M_insist(pos_out <= op.aggregates().size());
535 const ast::FnApplicationExpr &dummy = op.aggregates()[0].get();
536 op.aggregates().resize(pos_out, dummy); // discard unrequired aggregates
537
538 op.schema() = std::move(ours);
539 required = std::move(required_by_op);
540 is_top_of_plan_ = false;
541 (*this)(*op.child(0));
542 /* do not add constraints from child since aggregates are newly computed */
543}
544
545void SchemaMinimizer::operator()(SortingOperator &op)
546{
547 if (is_top_of_plan_) {
548 required = op.schema(); // require everything
549 is_top_of_plan_ = false;
550 } else {
551 Schema required_by_op;
552 for (auto &ord : op.order_by())
553 required_by_op |= ord.first.get().get_required(); // we require *all* expressions to order by
554 M_insist(required == (required & op.schema()), "required must be subset of operator schema");
555 op.schema() = required; // set schema to what is required above
556 required |= required_by_op; // add what we require to compute the order
557 }
558 (*this)(*op.child(0));
559 add_constraints(op.schema(), op.child(0)->schema()); // add constraints from child
560}
561
563{
564 SchemaMinimizer M;
565 M(*this);
566}
567
568__attribute__((constructor(202)))
569void register_post_optimization()
570{
571 Catalog &C = Catalog::Get();
572
573 C.register_logical_post_optimization(C.pool("minimize schema"), [](std::unique_ptr<Producer> plan) {
574 plan->minimize_schema();
575 return plan;
576 }, "minimizes the schema of an operator tree");
577
578 C.register_physical_post_optimization("minimize schema", [](std::unique_ptr<MatchBase> plan) {
579 const_cast<Operator*>(&plan->get_matched_root())->minimize_schema();
580 return plan;
581 }, "minimizes the schema of an operator tree");
582}
#define ACCEPT(CLASS)
__attribute__((constructor(202))) static void register_interpreter()
#define M_OPERATOR_LIST(X)
Definition: Operator.hpp:560
#define DECLARE(CLASS)
#define M_insist(...)
Definition: macro.hpp:129
std::ostream & indent(std::ostream &out, unsigned indentation)
Start a new line with proper indentation.
Definition: DataLayout.cpp:100
auto op
Definition: WasmDSL.hpp:2384
‍mutable namespace
Definition: Backend.hpp:10
std::string M_EXPORT html_escape(std::string str)
Escapes special characters in a string to be printable in HTML documents.
Definition: fn.cpp:60
std::string to_string(const TokenType tt)
Definition: TokenType.hpp:58
and
Definition: enum_ops.hpp:12
M_LCOV_EXCL_START std::ostream & operator<<(std::ostream &out, const PlanTableBase< Actual > &PT)
Definition: PlanTable.hpp:401
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.
AggregationOperator(std::vector< std::reference_wrapper< const ast::FnApplicationExpr > > aggregates)
Definition: Operator.cpp:297
std::vector< std::reference_wrapper< const ast::FnApplicationExpr > > aggregates_
the aggregates to compute
Definition: Operator.hpp:506
The catalog contains all Databases and keeps track of all meta information of the database system.
Definition: Catalog.hpp:215
void register_logical_post_optimization(ThreadSafePooledString name, LogicalPostOptimizationCallback optimization, const char *description=nullptr)
Registers a new logical post-optimization with the given name.
Definition: Catalog.hpp:632
ThreadSafePooledString pool(const char *str) const
Creates an internalized copy of the string str by adding it to the internal StringPool.
Definition: Catalog.hpp:274
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.
Definition: Catalog.hpp:656
GroupingOperator(std::vector< group_type > group_by, std::vector< std::reference_wrapper< const ast::FnApplicationExpr > > aggregates)
Definition: Operator.cpp:254
std::vector< group_type > group_by_
the compound grouping key
Definition: Operator.hpp:447
std::vector< std::reference_wrapper< const ast::FnApplicationExpr > > aggregates_
the aggregates to compute
Definition: Operator.hpp:448
const auto & group_by() const
Definition: Operator.hpp:495
static constexpr Schema::entry_type::constraints_t REMOVED_CONSTRAINTS
Definition: Operator.hpp:320
Drops the produced results and outputs only the number of result tuples produced.
Definition: Operator.hpp:238
virtual ~OperatorData()=0
Definition: Operator.cpp:9
An Operator represents an operation in a query plan.
Definition: Operator.hpp:45
void minimize_schema()
Minimizes the Schema of this Operator.
Definition: Operator.cpp:562
void dump() const
Definition: Operator.cpp:221
Schema & schema()
Returns the Schema of this Operator.
Definition: Operator.hpp:67
void dot(std::ostream &out) const
Prints a representation of this Operator and its descendants in the dot language.
Definition: Operator.cpp:100
std::size_t id() const
Returns the ID of this.
Definition: Operator.hpp:85
Prints the produced Tuples to a std::ostream instance.
Definition: Operator.hpp:223
std::vector< projection_type > projections_
Definition: Operator.hpp:363
ProjectionOperator(std::vector< projection_type > projections)
Definition: Operator.cpp:224
An Identifier is composed of a name and an optional prefix.
Definition: Schema.hpp:42
static Identifier GetConstant()
Definition: Schema.cpp:28
@ NOT_NULLABLE
entry must not be NULL
Definition: Schema.hpp:80
@ UNIQUE
entry has unique values
Definition: Schema.hpp:81
A Schema represents a sequence of identifiers, optionally with a prefix, and their associated types.
Definition: Schema.hpp:39
std::size_t num_entries() const
Returns the number of entries in this Schema.
Definition: Schema.hpp:124
iterator end()
Definition: Schema.hpp:117
iterator find(const Identifier &id)
Returns an iterator to the entry with the given Identifier id, or end() if no such entry exists.
Definition: Schema.hpp:129
void add(entry_type e)
Adds the entry e to this Schema.
Definition: Schema.hpp:181
A function application.
Definition: AST.hpp:246
Definition: tag.hpp:8