Branch data Line data Source code
1 : : #include <mutable/IR/Operator.hpp>
2 : :
3 : : #include <mutable/catalog/Catalog.hpp>
4 : :
5 : :
6 : : using namespace m;
7 : :
8 : :
9 : 81 : OperatorData::~OperatorData() { }
10 : :
11 : : M_LCOV_EXCL_START
12 : : std::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 : : };
36 : : visit(overloaded {
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 : : },
94 : : }, op, tag<ConstPreOrderOperatorVisitor>());
95 : :
96 : : return out;
97 : : }
98 : : M_LCOV_EXCL_STOP
99 : :
100 : 0 : void Operator::dot(std::ostream &out) const
101 : : {
102 : 0 : constexpr const char * const GRAPH_TYPE = "digraph";
103 : 0 : constexpr const char * const EDGE = " -> ";
104 : 0 : out << GRAPH_TYPE << " plan\n{\n"
105 : 0 : << " forcelabels=true;\n"
106 : 0 : << " overlap=false;\n"
107 : 0 : << " rankdir=BT;\n"
108 : 0 : << " graph [compound=true];\n"
109 : 0 : << " graph [fontname = \"DejaVu Sans\"];\n"
110 : 0 : << " node [fontname = \"DejaVu Sans\"];\n"
111 : 0 : << " 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
115 : 0 : visit(overloaded {
116 : 0 : [&out](const ScanOperator &op) {
117 [ # # # # : 0 : out << " " << id(op) << " [label=<<B>" << html_escape(*op.alias()) << "</B>>];\n";
# # # # ]
118 : 0 : },
119 : 0 : [&out](const FilterOperator &op) {
120 : 0 : out << " " << id(op) << " [label=<<B>σ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">"
121 [ # # # # ]: 0 : << html_escape(to_string(op.filter()))
122 [ # # ]: 0 : << "</FONT></SUB>>];\n"
123 [ # # # # : 0 : << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
124 : 0 : },
125 : 0 : [&out](const DisjunctiveFilterOperator &op) {
126 : 0 : out << " " << id(op) << " [label=<<B>σ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
127 : 0 : const auto &clause = op.filter()[0];
128 [ # # ]: 0 : for (auto it = clause.cbegin(); it != clause.cend(); ++it) {
129 [ # # ]: 0 : if (it != clause.cbegin()) out << " → ";
130 [ # # # # ]: 0 : out << html_escape(to_string(*it));
131 : 0 : }
132 : 0 : out << "</FONT></SUB>>];\n"
133 : 0 : << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
134 : 0 : },
135 : 0 : [&out](const JoinOperator &op) {
136 : 0 : out << " " << id(op) << " [label=<<B>⋈</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">"
137 [ # # # # ]: 0 : << html_escape(to_string(op.predicate()))
138 [ # # ]: 0 : << "</FONT></SUB>>];\n";
139 : :
140 [ # # ]: 0 : for (auto c : op.children())
141 : 0 : out << " " << id(*c) << EDGE << id(op) << ";\n";
142 : 0 : },
143 : 0 : [&out](const ProjectionOperator &op) {
144 : 0 : out << id(op) << " [label=<<B>π</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
145 : 0 : const auto &P = op.projections();
146 [ # # ]: 0 : for (auto it = P.begin(); it != P.end(); ++it) {
147 [ # # ]: 0 : if (it != P.begin()) out << ", ";
148 : 0 : out << it->first;
149 [ # # ]: 0 : if (it->second.has_value())
150 : 0 : out << " AS " << it->second;
151 : 0 : }
152 : 0 : out << "</FONT></SUB>>];\n";
153 : :
154 [ # # ]: 0 : if (not op.children().empty())
155 : 0 : out << id(*op.child(0)) << EDGE << id(op) << ";\n";
156 : 0 : },
157 : 0 : [&out](const LimitOperator &op) {
158 : 0 : out << " " << id(op) << " [label=<<B>λ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">"
159 : 0 : << op.limit();
160 [ # # ]: 0 : if (op.offset()) out << ", " << op.offset();
161 : 0 : out << "</FONT></SUB>>];\n"
162 : 0 : << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
163 : 0 : },
164 : 0 : [&out](const GroupingOperator &op) {
165 : 0 : out << " " << id(op) << " [label=<<B>γ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
166 : :
167 : 0 : const auto &G = op.group_by();
168 : 0 : const auto &A = op.aggregates();
169 : :
170 [ # # ]: 0 : for (auto it = G.begin(); it != G.end(); ++it) {
171 [ # # ]: 0 : if (it != G.begin()) out << ", ";
172 : 0 : std::ostringstream oss;
173 [ # # ]: 0 : oss << it->first.get();
174 [ # # # # : 0 : out << html_escape(oss.str());
# # ]
175 [ # # # # ]: 0 : if (it->second.has_value())
176 [ # # # # : 0 : out << html_escape(*it->second);
# # # # ]
177 : 0 : }
178 : :
179 [ # # # # ]: 0 : if (G.size() and A.size()) out << ", ";
180 : :
181 [ # # ]: 0 : for (auto it = A.begin(); it != A.end(); ++it) {
182 [ # # ]: 0 : if (it != A.begin()) out << ", ";
183 : 0 : out << it->get();
184 : 0 : }
185 : :
186 : 0 : out << "</FONT></SUB>>];\n"
187 : 0 : << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
188 : 0 : },
189 : 0 : [&out](const AggregationOperator &op) {
190 : 0 : out << " " << id(op) << " [label=<<B>Γ</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
191 : 0 : const auto &A = op.aggregates();
192 [ # # ]: 0 : for (auto it = A.begin(); it != A.end(); ++it) {
193 [ # # ]: 0 : if (it != A.begin()) out << ", ";
194 : 0 : out << it->get();
195 : 0 : }
196 : :
197 : 0 : out << "</FONT></SUB>>];\n"
198 : 0 : << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
199 : 0 : },
200 : 0 : [&out](const SortingOperator &op) {
201 : 0 : out << " " << id(op) << " [label=<<B>ω</B><SUB><FONT COLOR=\"0.0 0.0 0.25\" POINT-SIZE=\"10\">";
202 : :
203 : 0 : const auto &O = op.order_by();
204 [ # # ]: 0 : for (auto it = O.begin(); it != O.end(); ++it) {
205 [ # # ]: 0 : if (it != O.begin()) out << ", ";
206 : 0 : out << it->first.get() << ' ' << (it->second ? "ASC" : "DESC");
207 : 0 : }
208 : :
209 : 0 : out << "</FONT></SUB>>];\n"
210 : 0 : << " " << id(*op.child(0)) << EDGE << id(op) << ";\n";
211 : 0 : },
212 : 0 : [](auto&&) { /* nothing to be done */ }
213 : : }, *this, tag<ConstPostOrderOperatorVisitor>());
214 : : #undef id
215 : : #undef q
216 : 0 : out << "}\n";
217 : 0 : }
218 : :
219 : : M_LCOV_EXCL_START
220 : : void Operator::dump(std::ostream &out) const { out << *this << std::endl; }
221 : : void Operator::dump() const { dump(std::cerr); }
222 : : M_LCOV_EXCL_STOP
223 : :
224 : 243 : ProjectionOperator::ProjectionOperator(std::vector<projection_type> projections)
225 : 81 : : projections_(std::move(projections))
226 : 243 : {
227 : : /* Compute the schema of the operator. */
228 [ # # + - ]: 81 : auto &S = schema();
229 [ # # + + ]: 1167 : for (auto &[proj, alias] : projections_) {
230 [ # # + - ]: 362 : auto ty = proj.get().type();
231 : 362 : Schema::entry_type::constraints_t constraints{0};
232 [ # # # # : 362 : if (not proj.get().can_be_null())
+ - - + ]
233 [ # # # # ]: 0 : constraints |= Schema::entry_type::NOT_NULLABLE;
234 [ # # # # : 362 : if (alias.has_value()) { // alias was given
+ - - + ]
235 [ # # # # : 0 : Schema::Identifier id(alias.assert_not_none());
# # # # ]
236 [ # # # # : 0 : S.add(std::move(id), ty, constraints);
# # # # ]
237 [ # # # # : 362 : } else if (auto D = cast<const ast::Designator>(proj)) { // no alias, but designator -> keep name
+ - + - ]
238 [ # # # # : 362 : Schema::Identifier id(D->table_name.text, D->attr_name.text.assert_not_none());
# # + - +
- + - ]
239 [ # # # # : 362 : S.add(std::move(id), ty, constraints);
+ - + - ]
240 : 362 : } else { // no designator, no alias -> derive name
241 [ # # # # : 0 : if (is<const ast::Constant>(proj)) {
# # # # ]
242 : : // TODO: use `Expr::is_constant()` once interpretation of constant expressions is supported
243 [ # # # # : 0 : S.add(Schema::Identifier::GetConstant(), ty, constraints);
# # # # ]
244 : 0 : } else {
245 [ # # # # ]: 0 : std::ostringstream oss;
246 [ # # # # ]: 0 : oss << proj.get();
247 [ # # # # : 0 : Schema::Identifier id(Catalog::Get().pool(oss.str().c_str()));
# # # # #
# # # # #
# # ]
248 [ # # # # : 0 : S.add(std::move(id), ty, constraints);
# # # # ]
249 : 0 : }
250 : : }
251 : : }
252 : 81 : }
253 : :
254 : 0 : GroupingOperator::GroupingOperator(std::vector<group_type> group_by,
255 : : std::vector<std::reference_wrapper<const ast::FnApplicationExpr>> aggregates)
256 : 0 : : group_by_(std::move(group_by))
257 : 0 : , aggregates_(std::move(aggregates))
258 : 0 : {
259 [ # # # # ]: 0 : auto &C = Catalog::Get();
260 [ # # # # ]: 0 : auto &S = schema();
261 [ # # # # ]: 0 : std::ostringstream oss;
262 : :
263 : : {
264 [ # # # # ]: 0 : for (auto &[grp, alias] : group_by_) {
265 [ # # # # : 0 : auto pt = as<const PrimitiveType>(grp.get().type());
# # # # ]
266 : 0 : Schema::entry_type::constraints_t constraints{0};
267 [ # # # # ]: 0 : if (group_by.size() == 1)
268 [ # # # # ]: 0 : constraints |= Schema::entry_type::UNIQUE;
269 [ # # # # : 0 : if (not grp.get().can_be_null())
# # # # ]
270 [ # # # # ]: 0 : constraints |= Schema::entry_type::NOT_NULLABLE;
271 [ # # # # : 0 : if (alias.has_value()) {
# # # # ]
272 [ # # # # : 0 : S.add(alias.assert_not_none(), pt->as_scalar(), constraints);
# # # # #
# # # # #
# # # # #
# ]
273 [ # # # # : 0 : } else if (auto D = cast<const ast::Designator>(grp)) { // designator -> keep name
# # # # ]
274 [ # # # # : 0 : Schema::Identifier id(D->attr_name.text.assert_not_none()); // w/o table name
# # # # ]
275 [ # # # # : 0 : S.add(std::move(id), pt->as_scalar(), constraints);
# # # # #
# # # ]
276 : 0 : } else {
277 [ # # # # : 0 : oss.str("");
# # # # ]
278 [ # # # # ]: 0 : oss << grp.get();
279 [ # # # # : 0 : auto alias = C.pool(oss.str().c_str());
# # # # ]
280 [ # # # # : 0 : S.add(std::move(alias), pt->as_scalar(), constraints);
# # # # #
# # # # #
# # ]
281 : 0 : }
282 : : }
283 : : }
284 : :
285 [ # # # # ]: 0 : for (auto &e : aggregates_) {
286 [ # # # # ]: 0 : auto ty = e.get().type();
287 [ # # # # : 0 : oss.str("");
# # # # ]
288 [ # # # # ]: 0 : oss << e.get();
289 [ # # # # : 0 : auto alias = C.pool(oss.str().c_str());
# # # # ]
290 : 0 : Schema::entry_type::constraints_t constraints{0};
291 [ # # # # : 0 : if (not e.get().can_be_null()) // group cannot be empty, thus no default NULL will occur
# # # # ]
292 [ # # # # ]: 0 : constraints |= Schema::entry_type::NOT_NULLABLE;
293 [ # # # # : 0 : S.add(std::move(alias), ty, constraints);
# # # # #
# # # ]
294 : 0 : }
295 : 0 : }
296 : :
297 : 0 : AggregationOperator::AggregationOperator(std::vector<std::reference_wrapper<const ast::FnApplicationExpr>> aggregates)
298 : 0 : : aggregates_(std::move(aggregates))
299 : 0 : {
300 [ # # # # ]: 0 : auto &C = Catalog::Get();
301 [ # # # # ]: 0 : auto &S = schema();
302 [ # # # # ]: 0 : std::ostringstream oss;
303 [ # # # # ]: 0 : for (auto &e : aggregates_) {
304 [ # # # # ]: 0 : auto ty = e.get().type();
305 [ # # # # : 0 : oss.str("");
# # # # ]
306 [ # # # # ]: 0 : oss << e.get();
307 [ # # # # : 0 : auto alias = C.pool(oss.str().c_str());
# # # # ]
308 : 0 : Schema::entry_type::constraints_t constraints{Schema::entry_type::UNIQUE}; // since a single tuple is produced
309 [ # # # # : 0 : if (e.get().get_function().fnid == Function::FN_COUNT) // COUNT cannot be NULL (even for empty input)
# # # # ]
310 [ # # # # ]: 0 : constraints |= Schema::entry_type::NOT_NULLABLE;
311 [ # # # # : 0 : S.add(std::move(alias), ty, constraints);
# # # # #
# # # ]
312 : 0 : }
313 : 0 : }
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); }
323 : 787 : M_OPERATOR_LIST(ACCEPT)
324 : : #undef ACCEPT
325 : :
326 : :
327 : : /*======================================================================================================================
328 : : * minimize_schema()
329 : : *====================================================================================================================*/
330 : :
331 : 0 : struct SchemaMinimizer : OperatorVisitor
332 : : {
333 : : private:
334 : : Schema required;
335 : 0 : bool is_top_of_plan_ = true;
336 : :
337 : : public:
338 : : using OperatorVisitor::operator();
339 : :
340 : : #define DECLARE(CLASS) void operator()(Const<CLASS> &op) override;
341 : : M_OPERATOR_LIST(DECLARE)
342 : : #undef DECLARE
343 : :
344 : : private:
345 : : /** Add the constraints of entries from \p constraints to matching entries in \p schema except the
346 : : * \p excluded_constraints. Adapts only the first \p n entries of \p schema. */
347 : 0 : void add_constraints(Schema &schema, const Schema &constraints, std::size_t n,
348 : : Schema::entry_type::constraints_t excluded_constraints = Schema::entry_type::constraints_t{0})
349 : : {
350 : 0 : M_insist(n <= schema.num_entries(), "invalid length");
351 [ # # ]: 0 : for (std::size_t idx = 0; idx < n; ++idx) {
352 : 0 : auto &e = schema[idx];
353 : 0 : auto it = constraints.find(e.id);
354 [ # # ]: 0 : if (it != constraints.end())
355 : 0 : e.constraints |= it->constraints & ~excluded_constraints; // merge constraints except excluded ones
356 : 0 : }
357 : 0 : }
358 : 0 : void add_constraints(Schema &schema, const Schema &constraints,
359 : : Schema::entry_type::constraints_t excluded_constraints = Schema::entry_type::constraints_t{0})
360 : : {
361 : 0 : add_constraints(schema, constraints, schema.num_entries(), excluded_constraints);
362 : 0 : }
363 : : };
364 : :
365 : 0 : void SchemaMinimizer::operator()(ScanOperator &op)
366 : : {
367 [ # # ]: 0 : if (is_top_of_plan_) // scan is top of plan and leaf at the same time
368 : 0 : return; // still provide all entries of the table
369 : :
370 : 0 : required = required & op.schema(); // intersect with scan operator schema to add constraints to the required schema
371 : 0 : op.schema() = required; // the scan operator produces exactly those attributes required by the ancestors
372 : 0 : }
373 : :
374 : 0 : void SchemaMinimizer::operator()(CallbackOperator &op)
375 : : {
376 : 0 : (*this)(*op.child(0)); // this operator does not affect what is required; nothing to be done
377 : 0 : op.schema() = op.child(0)->schema();
378 : 0 : }
379 : :
380 : 0 : void SchemaMinimizer::operator()(PrintOperator &op)
381 : : {
382 : 0 : (*this)(*op.child(0)); // this operator does not affect what is required; nothing to be done
383 : 0 : op.schema() = op.child(0)->schema();
384 : 0 : }
385 : :
386 : 0 : void SchemaMinimizer::operator()(NoOpOperator &op)
387 : : {
388 : 0 : (*this)(*op.child(0)); // this operator does not affect what is required; nothing to be done
389 : 0 : op.schema() = op.child(0)->schema();
390 : 0 : }
391 : :
392 : 0 : void SchemaMinimizer::operator()(FilterOperator &op)
393 : : {
394 [ # # ]: 0 : if (is_top_of_plan_) {
395 : 0 : required = op.schema(); // require everything
396 : 0 : is_top_of_plan_ = false;
397 : 0 : } else {
398 : 0 : auto required_by_op = op.filter().get_required(); // add what's required to evaluate the filter predicate
399 [ # # # # : 0 : M_insist(required == (required & op.schema()), "required must be subset of operator schema");
# # # # ]
400 [ # # # # ]: 0 : op.schema() = required; // set schema to what is required above
401 [ # # ]: 0 : required |= required_by_op; // add what's required to evaluate the filter predicate
402 : 0 : }
403 : 0 : (*this)(*op.child(0));
404 : 0 : add_constraints(op.schema(), op.child(0)->schema()); // add constraints from child
405 : 0 : }
406 : :
407 : 0 : void SchemaMinimizer::operator()(DisjunctiveFilterOperator &op)
408 : : {
409 : 0 : (*this)(as<FilterOperator>(op)); // delegate to FilterOperator
410 : 0 : }
411 : :
412 : 0 : void SchemaMinimizer::operator()(JoinOperator &op)
413 : : {
414 : 0 : Schema required_from_below;
415 [ # # ]: 0 : if (is_top_of_plan_) {
416 [ # # # # ]: 0 : required_from_below = op.schema(); // require everything
417 : 0 : is_top_of_plan_ = false;
418 : 0 : } else {
419 [ # # # # : 0 : required_from_below = required | op.predicate().get_required(); // what we need and all operators above us
# # ]
420 [ # # # # : 0 : M_insist(required == (required & op.schema()), "required must be subset of operator schema");
# # # # ]
421 [ # # # # ]: 0 : op.schema() = required;
422 : : }
423 [ # # # # ]: 0 : for (auto c : const_cast<const JoinOperator&>(op).children()) {
424 [ # # # # ]: 0 : required = required_from_below & c->schema(); // what we need from this child
425 [ # # ]: 0 : (*this)(*c);
426 [ # # # # : 0 : add_constraints(op.schema(), c->schema(), JoinOperator::REMOVED_CONSTRAINTS); // add constraints from child except removed ones
# # ]
427 : : }
428 : 0 : }
429 : :
430 : 0 : void SchemaMinimizer::operator()(ProjectionOperator &op)
431 : : {
432 : 0 : Schema required_by_op;
433 : 0 : Schema ours;
434 : :
435 : 0 : std::size_t pos_out = 0;
436 [ # # # # ]: 0 : for (std::size_t pos_in = 0; pos_in != op.projections().size(); ++pos_in) {
437 [ # # ]: 0 : M_insist(pos_out <= pos_in);
438 [ # # ]: 0 : auto &proj = op.projections()[pos_in];
439 [ # # # # ]: 0 : auto &e = op.schema()[pos_in];
440 : :
441 [ # # # # : 0 : if (is_top_of_plan_ or required.has(e.id)) {
# # ]
442 [ # # # # ]: 0 : required_by_op |= proj.first.get().get_required();
443 [ # # # # : 0 : op.projections()[pos_out++] = std::move(op.projections()[pos_in]);
# # ]
444 [ # # # # ]: 0 : ours.add(e);
445 : 0 : }
446 : 0 : }
447 [ # # # # ]: 0 : M_insist(pos_out <= op.projections().size());
448 [ # # ]: 0 : const auto &dummy = op.projections()[0];
449 [ # # # # ]: 0 : op.projections().resize(pos_out, dummy);
450 : :
451 [ # # ]: 0 : op.schema() = std::move(ours);
452 : 0 : required = std::move(required_by_op);
453 : 0 : is_top_of_plan_ = false;
454 [ # # # # ]: 0 : if (not const_cast<const ProjectionOperator&>(op).children().empty()) {
455 [ # # # # ]: 0 : (*this)(*op.child(0));
456 [ # # # # : 0 : add_constraints(op.schema(), op.child(0)->schema()); // add constraints from child
# # # # ]
457 : 0 : }
458 : 0 : }
459 : :
460 : 0 : void SchemaMinimizer::operator()(LimitOperator &op)
461 : : {
462 : 0 : (*this)(*op.child(0));
463 : 0 : op.schema() = op.child(0)->schema();
464 : 0 : }
465 : :
466 : 0 : void SchemaMinimizer::operator()(GroupingOperator &op)
467 : : {
468 : 0 : Catalog &C = Catalog::Get();
469 : :
470 : 0 : Schema ours;
471 : 0 : Schema required_by_us;
472 : :
473 [ # # # # ]: 0 : auto it = op.schema().cbegin();
474 [ # # # # ]: 0 : for (auto &[grp, _] : op.group_by()) {
475 [ # # # # ]: 0 : required_by_us |= grp.get().get_required();
476 [ # # # # ]: 0 : ours.add(*it++); // copy grouping keys
477 : : }
478 : :
479 [ # # # # ]: 0 : if (not op.aggregates().empty()) {
480 [ # # ]: 0 : std::ostringstream oss;
481 : 0 : std::size_t pos_out = 0;
482 [ # # # # ]: 0 : for (std::size_t pos_in = 0; pos_in != op.aggregates().size(); ++pos_in) {
483 [ # # ]: 0 : M_insist(pos_out <= pos_in);
484 [ # # ]: 0 : auto &agg = op.aggregates()[pos_in];
485 : :
486 [ # # # # ]: 0 : oss.str("");
487 [ # # ]: 0 : oss << agg.get();
488 [ # # # # : 0 : Schema::Identifier agg_id(C.pool(oss.str()));
# # ]
489 : :
490 [ # # # # : 0 : if (is_top_of_plan_ or required.has(agg_id)) { // if first, require everything
# # ]
491 [ # # ]: 0 : for (auto &arg : agg.get().args)
492 [ # # # # ]: 0 : required_by_us |= arg->get_required();
493 [ # # # # ]: 0 : op.aggregates()[pos_out++] = std::move(op.aggregates()[pos_in]); // keep aggregate
494 [ # # # # : 0 : ours.add(op.schema()[agg_id].second);
# # # # ]
495 : 0 : }
496 : 0 : }
497 [ # # # # ]: 0 : M_insist(pos_out <= op.aggregates().size());
498 [ # # ]: 0 : const ast::FnApplicationExpr &dummy = op.aggregates()[0].get();
499 [ # # # # ]: 0 : op.aggregates().resize(pos_out, dummy); // discard unrequired aggregates
500 : 0 : }
501 : :
502 [ # # ]: 0 : op.schema() = std::move(ours);
503 : 0 : required = std::move(required_by_us);
504 : 0 : is_top_of_plan_ = false;
505 [ # # # # ]: 0 : (*this)(*op.child(0));
506 [ # # # # : 0 : add_constraints(op.schema(), op.child(0)->schema(), op.group_by().size()); // add constraints to grouping keys from child
# # # # #
# ]
507 : 0 : }
508 : :
509 : 0 : void SchemaMinimizer::operator()(AggregationOperator &op)
510 : : {
511 : 0 : M_insist(not op.aggregates().empty());
512 : 0 : Catalog &C = Catalog::Get();
513 : 0 : std::ostringstream oss;
514 : :
515 : 0 : Schema required_by_op; // the AggregationOperator doesn't care what later operators require
516 : 0 : Schema ours;
517 : :
518 : 0 : std::size_t pos_out = 0;
519 [ # # # # ]: 0 : for (std::size_t pos_in = 0; pos_in != op.aggregates().size(); ++pos_in) {
520 [ # # ]: 0 : M_insist(pos_out <= pos_in);
521 [ # # ]: 0 : auto &agg = op.aggregates()[pos_in];
522 : :
523 [ # # # # ]: 0 : oss.str("");
524 [ # # ]: 0 : oss << agg.get();
525 [ # # # # : 0 : Schema::Identifier agg_id(C.pool(oss.str()));
# # ]
526 : :
527 [ # # # # : 0 : if (is_top_of_plan_ or required.has(agg_id)) { // if first, require everything
# # ]
528 [ # # ]: 0 : for (auto &arg : agg.get().args)
529 [ # # # # ]: 0 : required_by_op |= arg->get_required();
530 [ # # # # ]: 0 : op.aggregates()[pos_out++] = std::move(op.aggregates()[pos_in]); // keep aggregate
531 [ # # # # : 0 : ours.add(op.schema()[agg_id].second);
# # # # ]
532 : 0 : }
533 : 0 : }
534 [ # # # # ]: 0 : M_insist(pos_out <= op.aggregates().size());
535 [ # # ]: 0 : const ast::FnApplicationExpr &dummy = op.aggregates()[0].get();
536 [ # # # # ]: 0 : op.aggregates().resize(pos_out, dummy); // discard unrequired aggregates
537 : :
538 [ # # ]: 0 : op.schema() = std::move(ours);
539 : 0 : required = std::move(required_by_op);
540 : 0 : is_top_of_plan_ = false;
541 [ # # # # ]: 0 : (*this)(*op.child(0));
542 : : /* do not add constraints from child since aggregates are newly computed */
543 : 0 : }
544 : :
545 : 0 : void SchemaMinimizer::operator()(SortingOperator &op)
546 : : {
547 [ # # ]: 0 : if (is_top_of_plan_) {
548 : 0 : required = op.schema(); // require everything
549 : 0 : is_top_of_plan_ = false;
550 : 0 : } else {
551 : 0 : Schema required_by_op;
552 [ # # # # ]: 0 : for (auto &ord : op.order_by())
553 [ # # # # ]: 0 : required_by_op |= ord.first.get().get_required(); // we require *all* expressions to order by
554 [ # # # # : 0 : M_insist(required == (required & op.schema()), "required must be subset of operator schema");
# # # # ]
555 [ # # # # ]: 0 : op.schema() = required; // set schema to what is required above
556 [ # # ]: 0 : required |= required_by_op; // add what we require to compute the order
557 : 0 : }
558 : 0 : (*this)(*op.child(0));
559 : 0 : add_constraints(op.schema(), op.child(0)->schema()); // add constraints from child
560 : 0 : }
561 : :
562 : 0 : void Operator::minimize_schema()
563 : : {
564 : 0 : SchemaMinimizer M;
565 [ # # ]: 0 : M(*this);
566 : 0 : }
567 : :
568 : : __attribute__((constructor(202)))
569 : 1 : void register_post_optimization()
570 : : {
571 : 1 : Catalog &C = Catalog::Get();
572 : :
573 [ + - ]: 1 : C.register_logical_post_optimization(C.pool("minimize schema"), [](std::unique_ptr<Producer> plan) {
574 : 0 : plan->minimize_schema();
575 : 0 : return plan;
576 : : }, "minimizes the schema of an operator tree");
577 : :
578 [ - + ]: 1 : C.register_physical_post_optimization("minimize schema", [](std::unique_ptr<MatchBase> plan) {
579 : 0 : const_cast<Operator*>(&plan->get_matched_root())->minimize_schema();
580 : 0 : return plan;
581 : : }, "minimizes the schema of an operator tree");
582 : 1 : }
|