/src/duckdb/src/optimizer/common_aggregate_optimizer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | #include "duckdb/optimizer/common_aggregate_optimizer.hpp" |
2 | | |
3 | | #include "duckdb/parser/expression_map.hpp" |
4 | | #include "duckdb/planner/expression/bound_columnref_expression.hpp" |
5 | | #include "duckdb/planner/operator/logical_aggregate.hpp" |
6 | | |
7 | | namespace duckdb { |
8 | | |
9 | 529k | void CommonAggregateOptimizer::StandardVisitOperator(LogicalOperator &op) { |
10 | 529k | VisitOperatorChildren(op); |
11 | 529k | if (!aggregate_map.empty()) { |
12 | 2.97k | VisitOperatorExpressions(op); |
13 | 2.97k | } |
14 | 529k | } |
15 | | |
16 | 529k | void CommonAggregateOptimizer::VisitOperator(LogicalOperator &op) { |
17 | 529k | switch (op.type) { |
18 | 24.9k | case LogicalOperatorType::LOGICAL_UNION: |
19 | 29.8k | case LogicalOperatorType::LOGICAL_EXCEPT: |
20 | 29.8k | case LogicalOperatorType::LOGICAL_INTERSECT: |
21 | 29.8k | case LogicalOperatorType::LOGICAL_MATERIALIZED_CTE: |
22 | 229k | case LogicalOperatorType::LOGICAL_PROJECTION: { |
23 | 229k | CommonAggregateOptimizer common_aggregate; |
24 | 229k | common_aggregate.StandardVisitOperator(op); |
25 | 229k | return; |
26 | 29.8k | } |
27 | 300k | default: |
28 | 300k | break; |
29 | 529k | } |
30 | | |
31 | 300k | StandardVisitOperator(op); |
32 | 300k | if (op.type == LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY) { |
33 | 58.5k | ExtractCommonAggregates(op.Cast<LogicalAggregate>()); |
34 | 58.5k | } |
35 | 300k | } |
36 | | |
37 | | unique_ptr<Expression> CommonAggregateOptimizer::VisitReplace(BoundColumnRefExpression &expr, |
38 | 55.9k | unique_ptr<Expression> *expr_ptr) { |
39 | | // check if this column ref points to an aggregate that was remapped; if it does we remap it |
40 | 55.9k | auto entry = aggregate_map.find(expr.binding); |
41 | 55.9k | if (entry != aggregate_map.end()) { |
42 | 3.23k | expr.binding = entry->second; |
43 | 3.23k | } |
44 | 55.9k | return nullptr; |
45 | 55.9k | } |
46 | | |
47 | 58.5k | void CommonAggregateOptimizer::ExtractCommonAggregates(LogicalAggregate &aggr) { |
48 | 58.5k | expression_map_t<idx_t> aggregate_remap; |
49 | 58.5k | idx_t total_erased = 0; |
50 | 222k | for (idx_t i = 0; i < aggr.expressions.size(); i++) { |
51 | 163k | idx_t original_index = i + total_erased; |
52 | 163k | auto entry = aggregate_remap.find(*aggr.expressions[i]); |
53 | 163k | if (entry == aggregate_remap.end()) { |
54 | | // aggregate does not exist yet: add it to the map |
55 | 160k | aggregate_remap[*aggr.expressions[i]] = i; |
56 | 160k | if (i != original_index) { |
57 | | // this aggregate is not erased, however an aggregate BEFORE it has been erased |
58 | | // so we need to remap this aggregate |
59 | 7 | ColumnBinding original_binding(aggr.aggregate_index, original_index); |
60 | 7 | ColumnBinding new_binding(aggr.aggregate_index, i); |
61 | 7 | aggregate_map[original_binding] = new_binding; |
62 | 7 | } |
63 | 160k | } else { |
64 | | // aggregate already exists! we can remove this entry |
65 | 3.12k | total_erased++; |
66 | 3.12k | aggr.expressions.erase_at(i); |
67 | 3.12k | i--; |
68 | | // we need to remap any references to this aggregate so they point to the other aggregate |
69 | 3.12k | ColumnBinding original_binding(aggr.aggregate_index, original_index); |
70 | 3.12k | ColumnBinding new_binding(aggr.aggregate_index, entry->second); |
71 | 3.12k | aggregate_map[original_binding] = new_binding; |
72 | 3.12k | } |
73 | 163k | } |
74 | 58.5k | } |
75 | | |
76 | | } // namespace duckdb |