/src/duckdb/src/optimizer/aggregate_rewrite_helper.cpp
Line | Count | Source |
1 | | #include "duckdb/optimizer/aggregate_rewrite_helper.hpp" |
2 | | |
3 | | #include "duckdb/common/string_util.hpp" |
4 | | #include "duckdb/optimizer/optimizer.hpp" |
5 | | #include "duckdb/planner/binder.hpp" |
6 | | #include "duckdb/planner/expression/bound_aggregate_expression.hpp" |
7 | | #include "duckdb/planner/expression/bound_columnref_expression.hpp" |
8 | | #include "duckdb/planner/expression/bound_reference_expression.hpp" |
9 | | #include "duckdb/planner/expression_iterator.hpp" |
10 | | #include "duckdb/planner/operator/logical_aggregate.hpp" |
11 | | #include "duckdb/planner/operator/logical_cteref.hpp" |
12 | | #include "duckdb/planner/operator/logical_projection.hpp" |
13 | | |
14 | | namespace duckdb { |
15 | | |
16 | 0 | optional_idx AggregateRewriteHelper::GetDirectReferenceIndex(const Expression &expression, LogicalOperator &input) { |
17 | 0 | auto bindings = input.GetColumnBindings(); |
18 | 0 | if (expression.GetExpressionClass() == ExpressionClass::BOUND_REF) { |
19 | 0 | auto index = expression.Cast<BoundReferenceExpression>().Index(); |
20 | 0 | return index < bindings.size() ? optional_idx(index) : optional_idx(); |
21 | 0 | } |
22 | 0 | if (expression.GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { |
23 | 0 | return optional_idx(); |
24 | 0 | } |
25 | 0 | auto binding = expression.Cast<BoundColumnRefExpression>().Binding(); |
26 | 0 | for (idx_t index = 0; index < bindings.size(); index++) { |
27 | 0 | if (bindings[index] == binding) { |
28 | 0 | return optional_idx(index); |
29 | 0 | } |
30 | 0 | } |
31 | 0 | return optional_idx(); |
32 | 0 | } |
33 | | |
34 | 0 | vector<Identifier> AggregateRewriteHelper::GenerateColumnNames(const string &prefix, idx_t column_count) { |
35 | 0 | vector<Identifier> result; |
36 | 0 | result.reserve(column_count); |
37 | 0 | for (idx_t col_idx = 0; col_idx < column_count; col_idx++) { |
38 | 0 | result.emplace_back(StringUtil::Format("%s_%llu", prefix, col_idx)); |
39 | 0 | } |
40 | 0 | return result; |
41 | 0 | } |
42 | | |
43 | 0 | static void RebindExpression(unique_ptr<Expression> &expr, const column_binding_map_t<ColumnBinding> &replacement_map) { |
44 | 0 | if (!expr || replacement_map.empty()) { |
45 | 0 | return; |
46 | 0 | } |
47 | 0 | ExpressionIterator::VisitExpressionMutable<BoundColumnRefExpression>( |
48 | 0 | expr, [&](BoundColumnRefExpression &colref, unique_ptr<Expression> &) { |
49 | 0 | auto entry = replacement_map.find(colref.Binding()); |
50 | 0 | if (entry != replacement_map.end()) { |
51 | 0 | colref.BindingMutable() = entry->second; |
52 | 0 | } |
53 | 0 | }); |
54 | 0 | } |
55 | | |
56 | | unique_ptr<Expression> |
57 | | AggregateRewriteHelper::CopyAndRebind(const Expression &expr, |
58 | 0 | const column_binding_map_t<ColumnBinding> &replacement_map) { |
59 | 0 | auto result = expr.Copy(); |
60 | 0 | RebindExpression(result, replacement_map); |
61 | 0 | return result; |
62 | 0 | } |
63 | | |
64 | | static void StageVolatileExpression(unique_ptr<Expression> &expr, TableIndex projection_index, |
65 | 0 | vector<unique_ptr<Expression>> &projection_expressions) { |
66 | 0 | if (!expr || !expr->IsVolatile()) { |
67 | 0 | return; |
68 | 0 | } |
69 | 0 | auto return_type = expr->GetReturnType(); |
70 | 0 | const auto column_index = projection_expressions.size(); |
71 | 0 | projection_expressions.push_back(std::move(expr)); |
72 | 0 | expr = make_uniq<BoundColumnRefExpression>(return_type, |
73 | 0 | ColumnBinding(projection_index, ProjectionIndex(column_index))); |
74 | 0 | } |
75 | | |
76 | | void AggregateRewriteHelper::StageVolatileAggregateInputs(Optimizer &optimizer, LogicalAggregate &aggr, |
77 | 0 | unique_ptr<LogicalOperator> &child) { |
78 | | // Branching rewrites must not multiply the evaluation of volatile aggregate inputs. |
79 | 0 | bool has_volatile_input = false; |
80 | 0 | for (auto &group : aggr.groups) { |
81 | 0 | has_volatile_input |= group->IsVolatile(); |
82 | 0 | } |
83 | 0 | for (auto &expr : aggr.expressions) { |
84 | 0 | has_volatile_input |= expr->IsVolatile(); |
85 | 0 | } |
86 | 0 | if (!has_volatile_input) { |
87 | 0 | return; |
88 | 0 | } |
89 | | |
90 | 0 | auto child_bindings = child->GetColumnBindings(); |
91 | 0 | auto projection_index = optimizer.binder.GenerateTableIndex(); |
92 | 0 | auto projection = LogicalProjection::CreateIdentity(projection_index, std::move(child)); |
93 | |
|
94 | 0 | column_binding_map_t<ColumnBinding> projection_replacements; |
95 | 0 | for (idx_t col_idx = 0; col_idx < child_bindings.size(); col_idx++) { |
96 | 0 | projection_replacements[child_bindings[col_idx]] = ColumnBinding(projection_index, ProjectionIndex(col_idx)); |
97 | 0 | } |
98 | |
|
99 | 0 | for (auto &group : aggr.groups) { |
100 | 0 | StageVolatileExpression(group, projection_index, projection->expressions); |
101 | 0 | } |
102 | 0 | for (auto &expr : aggr.expressions) { |
103 | 0 | auto &aggregate = expr->Cast<BoundAggregateExpression>(); |
104 | 0 | for (auto &child_expr : aggregate.GetChildrenMutable()) { |
105 | 0 | StageVolatileExpression(child_expr, projection_index, projection->expressions); |
106 | 0 | } |
107 | 0 | if (aggregate.GetOrderBys()) { |
108 | 0 | for (auto &order : aggregate.GetOrderBysMutable()->orders) { |
109 | 0 | StageVolatileExpression(order.expression, projection_index, projection->expressions); |
110 | 0 | } |
111 | 0 | } |
112 | 0 | StageVolatileExpression(aggregate.GetFilterMutable(), projection_index, projection->expressions); |
113 | 0 | } |
114 | |
|
115 | 0 | for (auto &group : aggr.groups) { |
116 | 0 | RebindExpression(group, projection_replacements); |
117 | 0 | } |
118 | 0 | for (auto &expr : aggr.expressions) { |
119 | 0 | RebindExpression(expr, projection_replacements); |
120 | 0 | } |
121 | |
|
122 | 0 | child = std::move(projection); |
123 | 0 | } |
124 | | |
125 | | unique_ptr<LogicalOperator> AggregateRewriteHelper::CreateCTERef(Optimizer &optimizer, TableIndex cte_index, |
126 | | const vector<LogicalType> &input_types, |
127 | | const vector<Identifier> &input_names, |
128 | | const vector<ColumnBinding> &input_bindings, |
129 | 0 | column_binding_map_t<ColumnBinding> &replacement_map) { |
130 | 0 | auto cte_ref_index = optimizer.binder.GenerateTableIndex(); |
131 | 0 | for (idx_t col_idx = 0; col_idx < input_bindings.size(); col_idx++) { |
132 | 0 | replacement_map[input_bindings[col_idx]] = ColumnBinding(cte_ref_index, ProjectionIndex(col_idx)); |
133 | 0 | } |
134 | 0 | return make_uniq<LogicalCTERef>(cte_ref_index, cte_index, input_types, input_names); |
135 | 0 | } |
136 | | |
137 | | } // namespace duckdb |