| 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 |