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
5namespace duckdb {
6
7unique_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