1 | #include "duckdb/optimizer/statistics_propagator.hpp" |
---|---|
2 | #include "duckdb/planner/expression/bound_constant_expression.hpp" |
3 | #include "duckdb/planner/expression/bound_operator_expression.hpp" |
4 | |
5 | namespace duckdb { |
6 | |
7 | unique_ptr<BaseStatistics> StatisticsPropagator::PropagateExpression(BoundOperatorExpression &expr, |
8 | unique_ptr<Expression> *expr_ptr) { |
9 | bool all_have_stats = true; |
10 | vector<unique_ptr<BaseStatistics>> child_stats; |
11 | child_stats.reserve(n: expr.children.size()); |
12 | for (auto &child : expr.children) { |
13 | auto stats = PropagateExpression(expr&: child); |
14 | if (!stats) { |
15 | all_have_stats = false; |
16 | } |
17 | child_stats.push_back(x: std::move(stats)); |
18 | } |
19 | if (!all_have_stats) { |
20 | return nullptr; |
21 | } |
22 | switch (expr.type) { |
23 | case ExpressionType::OPERATOR_COALESCE: |
24 | // COALESCE, merge stats of all children |
25 | for (idx_t i = 0; i < expr.children.size(); i++) { |
26 | D_ASSERT(child_stats[i]); |
27 | if (!child_stats[i]->CanHaveNoNull()) { |
28 | // this child is always NULL, we can remove it from the coalesce |
29 | // UNLESS there is only one node remaining |
30 | if (expr.children.size() > 1) { |
31 | expr.children.erase(position: expr.children.begin() + i); |
32 | child_stats.erase(position: child_stats.begin() + i); |
33 | i--; |
34 | } |
35 | } else if (!child_stats[i]->CanHaveNull()) { |
36 | // coalesce child cannot have NULL entries |
37 | // this is the last coalesce node that influences the result |
38 | // we can erase any children after this node |
39 | if (i + 1 < expr.children.size()) { |
40 | expr.children.erase(first: expr.children.begin() + i + 1, last: expr.children.end()); |
41 | child_stats.erase(first: child_stats.begin() + i + 1, last: child_stats.end()); |
42 | } |
43 | break; |
44 | } |
45 | } |
46 | D_ASSERT(!expr.children.empty()); |
47 | D_ASSERT(expr.children.size() == child_stats.size()); |
48 | if (expr.children.size() == 1) { |
49 | // coalesce of one entry: simply return that entry |
50 | *expr_ptr = std::move(expr.children[0]); |
51 | } else { |
52 | // coalesce of multiple entries |
53 | // merge the stats |
54 | for (idx_t i = 1; i < expr.children.size(); i++) { |
55 | child_stats[0]->Merge(other: *child_stats[i]); |
56 | } |
57 | } |
58 | return std::move(child_stats[0]); |
59 | case ExpressionType::OPERATOR_IS_NULL: |
60 | if (!child_stats[0]->CanHaveNull()) { |
61 | // child has no null values: x IS NULL will always be false |
62 | *expr_ptr = make_uniq<BoundConstantExpression>(args: Value::BOOLEAN(value: false)); |
63 | return PropagateExpression(expr&: *expr_ptr); |
64 | } |
65 | if (!child_stats[0]->CanHaveNoNull()) { |
66 | // child has no valid values: x IS NULL will always be true |
67 | *expr_ptr = make_uniq<BoundConstantExpression>(args: Value::BOOLEAN(value: true)); |
68 | return PropagateExpression(expr&: *expr_ptr); |
69 | } |
70 | return nullptr; |
71 | case ExpressionType::OPERATOR_IS_NOT_NULL: |
72 | if (!child_stats[0]->CanHaveNull()) { |
73 | // child has no null values: x IS NOT NULL will always be true |
74 | *expr_ptr = make_uniq<BoundConstantExpression>(args: Value::BOOLEAN(value: true)); |
75 | return PropagateExpression(expr&: *expr_ptr); |
76 | } |
77 | if (!child_stats[0]->CanHaveNoNull()) { |
78 | // child has no valid values: x IS NOT NULL will always be false |
79 | *expr_ptr = make_uniq<BoundConstantExpression>(args: Value::BOOLEAN(value: false)); |
80 | return PropagateExpression(expr&: *expr_ptr); |
81 | } |
82 | return nullptr; |
83 | default: |
84 | return nullptr; |
85 | } |
86 | } |
87 | |
88 | } // namespace duckdb |
89 |