mutable
A Database System for Research and Fast Prototyping
Loading...
Searching...
No Matches
Optimizer.cpp
Go to the documentation of this file.
2
3#include <algorithm>
6#include <mutable/Options.hpp>
9#include <numeric>
10#include <vector>
11
12
13using namespace m;
14using namespace m::ast;
15
16
17/*======================================================================================================================
18 * Helper functions
19 *====================================================================================================================*/
20
22{
23 private:
24 unsigned weight_ = 0;
25
26 public:
27 operator unsigned() const { return weight_; }
28
29 void operator()(const cnf::CNF &cnf) {
30 for (auto &clause : cnf)
31 (*this)(clause);
32 }
33
34 void operator()(const cnf::Clause &clause) {
35 for (auto pred : clause)
36 (*this)(*pred);
37 }
38
39 void operator()(const ast::Expr &e) {
41 [](const ErrorExpr&) { M_unreachable("no errors at this stage"); },
42 [this](const Designator &d) {
43 if (auto cs = cast<const CharacterSequence>(d.type()))
44 weight_ += cs->length;
45 else
46 weight_ += 1;
47 },
48 [this](const Constant &e) {
49 if (auto cs = cast<const CharacterSequence>(e.type()))
50 weight_ += cs->length;
51 // fixed-size constants are considered free, as they may be encoded as immediate constants in the instr
52 },
53 [this](const FnApplicationExpr&) {
54 weight_ += 1;
55 },
56 [this](const UnaryExpr&) { weight_ += 1; },
57 [this](const BinaryExpr&) { weight_ += 1; },
58 [this](const QueryExpr&) { weight_ += 1000; } // XXX: this should never happen because of unnesting
60 }
61};
62
63std::vector<cnf::CNF> Optimizer::optimize_filter(cnf::CNF filter)
64{
65 constexpr unsigned MAX_WEIGHT = 12; // equals to 4 comparisons of fixed-length values
66 M_insist(not filter.empty());
67
68 /* Compute clause weights. */
69 std::vector<unsigned> clause_weights;
70 clause_weights.reserve(filter.size());
71 for (auto &clause : filter) {
72 WeighExpr W;
73 W(clause);
74 clause_weights.emplace_back(W);
75 }
76
77 /* Sort clauses by weight using an index vector. */
78 std::vector<std::size_t> order(filter.size(), 0);
79 std::iota(order.begin(), order.end(), 0);
80 std::sort(order.begin(), order.end(), [&clause_weights](std::size_t first, std::size_t second) -> bool {
81 return clause_weights[first] < clause_weights[second];
82 });
83
84 /* Dissect filter into sequence of filters. */
85 std::vector<cnf::CNF> optimized_filters;
86 unsigned current_weight = 0;
87 cnf::CNF current_filter;
88 for (std::size_t i = 0, end = filter.size(); i != end; ++i) {
89 const std::size_t idx = order[i];
90 cnf::Clause clause(std::move(filter[idx]));
91 M_insist(not clause.empty());
92 const unsigned clause_weight = clause_weights[idx];
93
94 if (not current_filter.empty() and current_weight + clause_weight > MAX_WEIGHT) {
95 optimized_filters.emplace_back(std::move(current_filter)); // empties current_filter
96 current_weight = 0;
97 }
98
99 current_filter.emplace_back(std::move(clause));
100 current_weight += clause_weight;
101 }
102 if (not current_filter.empty())
103 optimized_filters.emplace_back(std::move(current_filter));
104
105 M_insist(not optimized_filters.empty());
106 return optimized_filters;
107}
108
109std::vector<Optimizer::projection_type>
110Optimizer::compute_projections_required_for_order_by(const std::vector<projection_type> &projections,
111 const std::vector<order_type> &order_by)
112{
113 std::vector<Optimizer::projection_type> required_projections;
114
115 /* Collect all required `Designator`s which are not included in the projection. */
116 auto get_required_designator = overloaded {
117 [&](const ast::Designator &d) -> void {
118 if (auto t = std::get_if<const Expr*>(&d.target())) { // refers to another expression?
119 /*----- Find `t` in projections. -----*/
120 for (auto &[expr, _] : projections) {
121 if (*t == &expr.get())
122 return; // found
123 }
124 }
125 /*----- Find `d` in projections. -----*/
126 for (auto &[expr, alias] : projections) {
127 if (not alias.has_value() and d == expr.get())
128 return; // found
129 }
130 required_projections.emplace_back(d, ThreadSafePooledOptionalString{});
131 },
132 [&](const ast::FnApplicationExpr &fn) -> void {
133 /*----- Find `fn` in projections. -----*/
134 for (auto &[expr, alias] : projections) {
135 if (not alias.has_value() and fn == expr.get())
136 throw visit_skip_subtree(); // found
137 }
138 required_projections.emplace_back(fn, ThreadSafePooledOptionalString{});
139 throw visit_skip_subtree();
140 },
141 [](auto&&) -> void { /* nothing to be done */ },
142 };
143 /* Process the ORDER BY clause. */
144 for (auto [expr, _] : order_by)
145 visit(get_required_designator, expr.get(), m::tag<m::ast::ConstPreOrderExprVisitor>());
146
147 return required_projections;
148}
149
150
151/*======================================================================================================================
152 * Optimizer
153 *====================================================================================================================*/
154
155std::pair<std::unique_ptr<Producer>, PlanTableEntry> Optimizer::optimize(QueryGraph &G) const
156{
157 switch (Options::Get().plan_table_type)
158 {
159 case Options::PT_auto: {
160 /* Select most suitable type of plan table depending on the query graph structure.
161 * Currently a simple heuristic based on the number of data sources.
162 * TODO: Consider join edges too. Eventually consider #CSGs. */
163 if (G.num_sources() <= 15) {
164 auto [plan, PT] = optimize_with_plantable<PlanTableSmallOrDense>(G);
165 return { std::move(plan), std::move(PT.get_final()) };
166 } else {
167 auto [plan, PT] = optimize_with_plantable<PlanTableLargeAndSparse>(G);
168 return { std::move(plan), std::move(PT.get_final()) };
169 }
170 }
171
173 auto [plan, PT] = optimize_with_plantable<PlanTableSmallOrDense>(G);
174 return { std::move(plan), std::move(PT.get_final()) };
175 }
176
178 auto [plan, PT] = optimize_with_plantable<PlanTableLargeAndSparse>(G);
179 return { std::move(plan), std::move(PT.get_final()) };
180 }
181 }
182}
183
184template<typename PlanTable>
185std::pair<std::unique_ptr<Producer>, PlanTable> Optimizer::optimize_with_plantable(QueryGraph &G) const
186{
187 PlanTable PT(G);
188 const auto num_sources = G.sources().size();
189 auto &C = Catalog::Get();
190 auto &CE = C.get_database_in_use().cardinality_estimator();
191
192 if (num_sources == 0) {
193 PT.get_final().cost = 0; // no sources → no cost
194 PT.get_final().model = CE.empty_model(); // XXX: should rather be 1 (single tuple) than empty
195 return { std::make_unique<ProjectionOperator>(G.projections()), std::move(PT) };
196 }
197
198 /*----- Initialize plan table and compute plans for data sources. -----*/
199 auto source_plans = optimize_source_plans(G, PT);
200
201 /*----- Compute join order and construct plan containing all joins. -----*/
202 optimize_join_order(G, PT);
203 std::unique_ptr<Producer> plan = construct_join_order(G, PT, source_plans);
204 auto &entry = PT.get_final();
205
206 /*----- Construct plan for remaining operations. -----*/
207 plan = optimize_plan(G, std::move(plan), entry);
208
209 return { std::move(plan), std::move(PT) };
210}
211
212template<typename PlanTable>
213std::unique_ptr<Producer*[]> Optimizer::optimize_source_plans(const QueryGraph &G, PlanTable &PT) const
214{
215 const auto num_sources = G.sources().size();
217
218 auto source_plans = std::make_unique<Producer*[]>(num_sources);
219 for (auto &ds : G.sources()) {
220 Subproblem s = Subproblem::Singleton(ds->id());
221 if (auto bt = cast<const BaseTable>(ds.get())) {
222 /* Produce a scan for base tables. */
223 PT[s].cost = 0;
224 PT[s].model = CE.estimate_scan(G, s);
225 auto &store = bt->table().store();
226 auto source = std::make_unique<ScanOperator>(store, bt->name().assert_not_none());
227
228 /* Set operator information. */
229 auto source_info = std::make_unique<OperatorInformation>();
230 source_info->subproblem = s;
231 source_info->estimated_cardinality = CE.predict_cardinality(*PT[s].model);
232 source->info(std::move(source_info));
233
234 source_plans[ds->id()] = source.release();
235 } else {
236 /* Recursively solve nested queries. */
237 auto &Q = as<const Query>(*ds);
238 const bool old = std::exchange(needs_projection_, Q.alias().has_value()); // aliased nested queries need projection
239 auto [sub_plan, sub] = optimize(Q.query_graph());
240 needs_projection_ = old;
241
242 /* If an alias for the nested query is given, prefix every attribute with the alias. */
243 if (Q.alias().has_value()) {
244 M_insist(is<ProjectionOperator>(sub_plan), "only projection may rename attributes");
245 Schema S;
246 for (auto &e : sub_plan->schema())
247 S.add({ Q.alias(), e.id.name }, e.type, e.constraints);
248 sub_plan->schema() = S;
249 }
250
251 /* Update the plan table with the `DataModel` and cost of the nested query and save the plan in the array of
252 * source plans. */
253 PT[s].cost = sub.cost;
254 sub.model->assign_to(s); // adapt model s.t. it describes the result of the current subproblem
255 PT[s].model = std::move(sub.model);
256 source_plans[ds->id()] = sub_plan.release();
257 }
258
259 /* Apply filter, if any. */
260 if (ds->filter().size()) {
261 /* Optimize the filter by splitting into smaller filters and ordering them. */
262 std::vector<cnf::CNF> filters = Optimizer::optimize_filter(ds->filter());
263 Producer *filtered_ds = source_plans[ds->id()];
264
265 /* Construct a plan as a sequence of filters. */
266 for (auto &&filter : filters) {
267 /* Update data model with filter. */
268 auto new_model = CE.estimate_filter(G, *PT[s].model, filter);
269 PT[s].model = std::move(new_model);
270
271 if (filter.size() == 1 and filter[0].size() > 1) { // disjunctive filter
272 auto tmp = std::make_unique<DisjunctiveFilterOperator>(std::move(filter));
273 tmp->add_child(filtered_ds);
274 filtered_ds = tmp.release();
275 } else {
276 auto tmp = std::make_unique<FilterOperator>(std::move(filter));
277 tmp->add_child(filtered_ds);
278 filtered_ds = tmp.release();
279 }
280
281 /* Set operator information. */
282 auto source_info = std::make_unique<OperatorInformation>();
283 source_info->subproblem = s;
284 source_info->estimated_cardinality = CE.predict_cardinality(*PT[s].model); // includes filters, if any
285 filtered_ds->info(std::move(source_info));
286 }
287
288 source_plans[ds->id()] = filtered_ds;
289 }
290 }
291 return source_plans;
292}
293
294template<typename PlanTable>
295void Optimizer::optimize_join_order(const QueryGraph &G, PlanTable &PT) const
296{
297 Catalog &C = Catalog::Get();
299
300#ifndef NDEBUG
301 if (Options::Get().statistics) {
302 std::size_t num_CSGs = 0, num_CCPs = 0;
303 const Subproblem All = Subproblem::All(G.num_sources());
304 auto inc_CSGs = [&num_CSGs](Subproblem) { ++num_CSGs; };
305 auto inc_CCPs = [&num_CCPs](Subproblem, Subproblem) { ++num_CCPs; };
306 G.adjacency_matrix().for_each_CSG_undirected(All, inc_CSGs);
308 std::cout << num_CSGs << " CSGs, " << num_CCPs << " CCPs" << std::endl;
309 }
310#endif
311
312 M_TIME_EXPR(plan_enumerator()(G, cost_function(), PT), "Plan enumeration", C.timer());
313
314 if (Options::Get().statistics) {
315 std::cout << "Est. total cost: " << PT.get_final().cost
316 << "\nEst. result set size: " << CE.predict_cardinality(*PT.get_final().model)
317 << "\nPlan cost: " << PT[PT.get_final().left].cost + PT[PT.get_final().right].cost
318 << std::endl;
319 }
320}
321
322template<typename PlanTable>
323std::unique_ptr<Producer> Optimizer::construct_join_order(const QueryGraph &G, const PlanTable &PT,
324 const std::unique_ptr<Producer*[]> &source_plans) const
325{
327
328 std::vector<std::reference_wrapper<Join>> joins;
329 for (auto &J : G.joins()) joins.emplace_back(*J);
330
331 /* Use nested lambdas to implement recursive lambda using CPS. */
332 const auto construct_recursive = [&](Subproblem s) -> Producer* {
333 auto construct_plan_impl = [&](Subproblem s, auto &construct_plan_rec) -> Producer* {
334 auto subproblems = PT[s].get_subproblems();
335 if (subproblems.empty()) {
336 M_insist(s.size() == 1);
337 return source_plans[*s.begin()];
338 } else {
339 /* Compute plan for each sub problem. Must happen *before* calculating the join predicate. */
340 std::vector<Producer*> sub_plans;
341 for (auto sub : subproblems)
342 sub_plans.push_back(construct_plan_rec(sub, construct_plan_rec));
343
344 /* Calculate the join predicate. */
345 cnf::CNF join_condition;
346 for (auto it = joins.begin(); it != joins.end(); ) {
347 Subproblem join_sources;
348 /* Compute subproblem of sources to join. */
349 for (auto ds : it->get().sources())
350 join_sources(ds.get().id()) = true;
351
352 if (join_sources.is_subset(s)) { // possible join
353 join_condition = join_condition and it->get().condition();
354 it = joins.erase(it);
355 } else {
356 ++it;
357 }
358 }
359
360 /* Construct the join. */
361 auto join = std::make_unique<JoinOperator>(join_condition);
362 for (auto sub_plan : sub_plans)
363 join->add_child(sub_plan);
364 auto join_info = std::make_unique<OperatorInformation>();
365 join_info->subproblem = s;
366 join_info->estimated_cardinality = CE.predict_cardinality(*PT[s].model);
367 join->info(std::move(join_info));
368 return join.release();
369 }
370 };
371 return construct_plan_impl(s, construct_plan_impl);
372 };
373
374 return std::unique_ptr<Producer>(construct_recursive(Subproblem::All(G.sources().size())));
375}
376
377std::unique_ptr<Producer> Optimizer::optimize_plan(const QueryGraph &G, std::unique_ptr<Producer> plan,
378 PlanTableEntry &entry) const
379{
381
382 /* Perform grouping. */
383 if (not G.group_by().empty()) {
384 /* Compute `DataModel` after grouping. */
385 auto new_model = CE.estimate_grouping(G, *entry.model, G.group_by()); // TODO provide aggregates
386 entry.model = std::move(new_model);
387 // TODO pick "best" algorithm
388 auto group_by = std::make_unique<GroupingOperator>(G.group_by(), G.aggregates());
389 group_by->add_child(plan.release());
390
391 /* Set operator information. */
392 auto info = std::make_unique<OperatorInformation>();
393 info->subproblem = Subproblem::All(G.sources().size());
394 info->estimated_cardinality = CE.predict_cardinality(*entry.model);
395
396 group_by->info(std::move(info));
397 plan = std::move(group_by);
398 } else if (not G.aggregates().empty()) {
399 /* Compute `DataModel` after grouping. */
400 auto new_model = CE.estimate_grouping(G, *entry.model, std::vector<GroupingOperator::group_type>());
401 entry.model = std::move(new_model);
402 auto agg = std::make_unique<AggregationOperator>(G.aggregates());
403 agg->add_child(plan.release());
404
405 /* Set operator information. */
406 auto info = std::make_unique<OperatorInformation>();
407 info->subproblem = Subproblem::All(G.sources().size());
408 info->estimated_cardinality = CE.predict_cardinality(*entry.model);
409
410 agg->info(std::move(info));
411 plan = std::move(agg);
412 }
413
414 auto additional_projections = Optimizer::compute_projections_required_for_order_by(G.projections(), G.order_by());
415 const bool requires_post_projection = not additional_projections.empty();
416
417 /* Perform projection. */
418 if (not additional_projections.empty() or not G.projections().empty()) {
419 /* Merge original projections with additional projections. */
420 additional_projections.insert(additional_projections.end(), G.projections().begin(), G.projections().end());
421 auto projection = std::make_unique<ProjectionOperator>(std::move(additional_projections));
422 projection->add_child(plan.release());
423
424 /* Set operator information. */
425 auto info = std::make_unique<OperatorInformation>();
426 info->subproblem = Subproblem::All(G.sources().size());
427 info->estimated_cardinality = projection->child(0)->info().estimated_cardinality;
428
429 projection->info(std::move(info));
430 plan = std::move(projection);
431 }
432
433 /* Perform ordering. */
434 if (not G.order_by().empty()) {
435 // TODO estimate data model
436 auto order_by = std::make_unique<SortingOperator>(G.order_by());
437 order_by->add_child(plan.release());
438
439 /* Set operator information. */
440 auto info = std::make_unique<OperatorInformation>();
441 info->subproblem = Subproblem::All(G.sources().size());
442 info->estimated_cardinality = order_by->child(0)->info().estimated_cardinality;
443
444 order_by->info(std::move(info));
445 plan = std::move(order_by);
446 }
447
448 /* Limit. */
449 if (G.limit().limit or G.limit().offset) {
450 /* Compute `DataModel` after limit. */
451 auto new_model = CE.estimate_limit(G, *entry.model, G.limit().limit, G.limit().offset);
452 entry.model = std::move(new_model);
453 // TODO estimate data model
454 auto limit = std::make_unique<LimitOperator>(G.limit().limit, G.limit().offset);
455 limit->add_child(plan.release());
456
457 /* Set operator information. */
458 auto info = std::make_unique<OperatorInformation>();
459 info->subproblem = Subproblem::All(G.sources().size());
460 info->estimated_cardinality = CE.predict_cardinality(*entry.model);
461
462 limit->info(std::move(info));
463 plan = std::move(limit);
464 }
465
466 /* Perform post-ordering projection. */
467 if (requires_post_projection or (not is<ProjectionOperator>(plan) and needs_projection_)) {
468 // TODO estimate data model
469 /* Change aliased projections in designators with the alias as name since original projection is
470 * performed beforehand. */
471 std::vector<projection_type> adapted_projections;
472 for (auto [expr, alias] : G.projections()) {
473 if (alias.has_value()) {
474 Token name(expr.get().tok.pos, alias.assert_not_none(), TK_IDENTIFIER);
475 auto d = std::make_unique<const Designator>(Token::CreateArtificial(), Token::CreateArtificial(),
476 std::move(name), expr.get().type(), &expr.get());
477 adapted_projections.emplace_back(*d, ThreadSafePooledOptionalString{});
478 created_exprs_.emplace_back(std::move(d));
479 } else {
480 adapted_projections.emplace_back(expr, ThreadSafePooledOptionalString{});
481 }
482 }
483 auto projection = std::make_unique<ProjectionOperator>(std::move(adapted_projections));
484 projection->add_child(plan.release());
485
486 /* Set operator information. */
487 auto info = std::make_unique<OperatorInformation>();
488 info->subproblem = Subproblem::All(G.sources().size());
489 info->estimated_cardinality = projection->child(0)->info().estimated_cardinality;
490
491 projection->info(std::move(info));
492 plan = std::move(projection);
493 }
494 return plan;
495}
496
497#define DEFINE(PLANTABLE) \
498template \
499std::pair<std::unique_ptr<Producer>, PLANTABLE> \
500Optimizer::optimize_with_plantable(QueryGraph&) const; \
501template \
502std::unique_ptr<Producer*[]> \
503Optimizer::optimize_source_plans(const QueryGraph&, PLANTABLE&) const; \
504template \
505void \
506Optimizer::optimize_join_order(const QueryGraph&, PLANTABLE&) const; \
507template \
508std::unique_ptr<Producer> \
509Optimizer::construct_join_order(const QueryGraph&, const PLANTABLE&, const std::unique_ptr<Producer*[]>&) const
512#undef DEFINE
#define DEFINE(TYPE)
Definition: CostModel.cpp:598
#define M_TIME_EXPR(EXPR, DESCR, TIMER)
Definition: Timer.hpp:177
#define M_unreachable(MSG)
Definition: macro.hpp:146
#define M_insist(...)
Definition: macro.hpp:129
‍mutable namespace
Definition: Backend.hpp:10
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
void operator()(const cnf::Clause &clause)
Definition: Optimizer.cpp:34
void operator()(const ast::Expr &e)
Definition: Optimizer.cpp:39
void operator()(const cnf::CNF &cnf)
Definition: Optimizer.cpp:29
unsigned weight_
Definition: Optimizer.cpp:24
void for_each_CSG_pair_undirected(SmallBitset super, std::function< void(SmallBitset, SmallBitset)> callback) const
Enumerate all pairs of connected subgraphs (CSGs) that are connected by at least one edge.
void for_each_CSG_undirected(SmallBitset super, SmallBitset source, std::function< void(SmallBitset)> callback) const
Enumerate all connected subgraphs (CSGs) of the graph induced by vertex super set super,...
The catalog contains all Databases and keeps track of all meta information of the database system.
Definition: Catalog.hpp:215
Database & get_database_in_use()
Returns a reference to the Database that is currently in use, if any.
Definition: Catalog.hpp:295
static Catalog & Get()
Return a reference to the single Catalog instance.
Timer & timer()
Returns the global Timer instance.
Definition: Catalog.hpp:264
std::unique_ptr< CardinalityEstimator > cardinality_estimator(std::unique_ptr< CardinalityEstimator > CE)
Sets the CardinalityEstimator of this Database.
Definition: Schema.hpp:946
OperatorInformation & info()
Definition: Operator.hpp:72
std::pair< std::unique_ptr< Producer >, PlanTableEntry > optimize(QueryGraph &G) const
Computes and constructs an optimal logical plan for the given query graph G.
Definition: Optimizer.cpp:155
std::pair< std::unique_ptr< Producer >, PlanTable > optimize_with_plantable(QueryGraph &G) const
Recursively computes and constructs an optimal logical plan for the given query graph G,...
Definition: Optimizer.cpp:185
void optimize_join_order(const QueryGraph &G, PlanTable &PT) const
Optimizes the join order using the plan table PT which already contains entries for all data sources ...
Definition: Optimizer.cpp:295
QueryGraph::Subproblem Subproblem
Definition: Optimizer.hpp:20
auto & plan_enumerator() const
Definition: Optimizer.hpp:33
std::unique_ptr< Producer > construct_join_order(const QueryGraph &G, const PlanTable &PT, const std::unique_ptr< Producer *[]> &source_plans) const
Constructs a join operator tree given a solved plan table PT and the plans to compute the data source...
Definition: Optimizer.cpp:323
auto & cost_function() const
Definition: Optimizer.hpp:34
std::unique_ptr< Producer > optimize_plan(const QueryGraph &G, std::unique_ptr< Producer > plan, PlanTableEntry &entry) const
Optimizes and constructs an operator tree given a join operator tree plan and the final plan table en...
Definition: Optimizer.cpp:377
bool needs_projection_
flag to determine whether current query needs a projection as root
Definition: Optimizer.hpp:28
std::vector< std::unique_ptr< const ast::Expr > > created_exprs_
additionally created expressions
Definition: Optimizer.hpp:27
std::unique_ptr< Producer *[]> optimize_source_plans(const QueryGraph &G, PlanTable &PT) const
Initializes the plan table PT with the data source entries contained in G.
Definition: Optimizer.cpp:213
static std::vector< cnf::CNF > optimize_filter(cnf::CNF filter)
Optimizes the filter filter by splitting it into smaller filters and ordering them.
Definition: Optimizer.cpp:63
static std::vector< projection_type > compute_projections_required_for_order_by(const std::vector< projection_type > &projections, const std::vector< order_type > &order_by)
Computes and returns a std::vector of additional projections required before evaluating the ORDER BY ...
Definition: Optimizer.cpp:110
static Options & Get()
Return a reference to the single Options instance.
Definition: Options.cpp:9
@ PT_LargeAndSparse
Definition: Options.hpp:18
@ PT_SmallOrDense
Definition: Options.hpp:17
std::unique_ptr< DataModel > model
the model of this subplan's result
Definition: PlanTable.hpp:42
This table represents all explored plans with their sub-plans, estimated size, cost,...
Definition: PlanTable.hpp:284
This table represents all explored plans with their sub-plans, estimated size, cost,...
Definition: PlanTable.hpp:180
A data type representing a pooled (or internalized) object.
Definition: Pool.hpp:168
A Producer is an Operator that can be evaluated to a sequence of tuples.
Definition: Operator.hpp:120
The query graph represents all data sources and joins in a graph structure.
Definition: QueryGraph.hpp:172
const auto & group_by() const
Definition: QueryGraph.hpp:259
const std::vector< projection_type > & projections() const
Definition: QueryGraph.hpp:261
const auto & joins() const
Definition: QueryGraph.hpp:258
std::size_t num_sources() const
Returns the number of DataSources in this graph.
Definition: QueryGraph.hpp:224
const auto & aggregates() const
Definition: QueryGraph.hpp:260
const auto & order_by() const
Definition: QueryGraph.hpp:262
const auto & sources() const
Definition: QueryGraph.hpp:257
uint64_t limit
Definition: QueryGraph.hpp:190
AdjacencyMatrix & adjacency_matrix()
Definition: QueryGraph.hpp:277
A Schema represents a sequence of identifiers, optionally with a prefix, and their associated types.
Definition: Schema.hpp:39
void add(entry_type e)
Adds the entry e to this Schema.
Definition: Schema.hpp:181
Implements a small and efficient set over integers in the range of 0 to 63 (including).
Definition: ADT.hpp:26
static SmallBitset All(std::size_t n)
Factory method for creating a SmallBitset with first n bits set.
Definition: ADT.hpp:117
std::size_t size() const
Returns the number of elements in this SmallBitset.
Definition: ADT.hpp:161
static SmallBitset Singleton(std::size_t n)
Factory method for creating a Singleton Smallbitset with n -th bit set.
Definition: ADT.hpp:125
auto begin() const
Definition: ADT.hpp:173
bool is_subset(SmallBitset other) const
Returns true if the set represented by this is a subset of other, i.e. this ⊆ other.
Definition: ADT.hpp:194
A binary expression.
Definition: AST.hpp:348
A constant: a string literal or a numeric constant.
Definition: AST.hpp:213
A designator.
Definition: AST.hpp:134
The error expression.
Definition: AST.hpp:116
An expression.
Definition: AST.hpp:39
const Type * type() const
Returns the Type of this Expr.
Definition: AST.hpp:58
A function application.
Definition: AST.hpp:246
A query expression for nested queries.
Definition: AST.hpp:389
static Token CreateArtificial(TokenType type=TK_EOF)
Definition: Token.hpp:29
A unary expression: "+e", "-e", "~e", "NOT e".
Definition: AST.hpp:324
A CNF represents a conjunction of cnf::Clauses.
Definition: CNF.hpp:134
A cnf::Clause represents a disjunction of Predicates.
Definition: CNF.hpp:87
Definition: tag.hpp:8
Exception class which can be thrown to skip recursion of the subtree in pre-order visitors.
Definition: Visitor.hpp:18