1 | #include "duckdb/optimizer/statistics_propagator.hpp" |
2 | #include "duckdb/planner/filter/conjunction_filter.hpp" |
3 | #include "duckdb/planner/filter/constant_filter.hpp" |
4 | #include "duckdb/planner/operator/logical_get.hpp" |
5 | #include "duckdb/planner/table_filter.hpp" |
6 | |
7 | namespace duckdb { |
8 | |
9 | FilterPropagateResult StatisticsPropagator::PropagateTableFilter(BaseStatistics &stats, TableFilter &filter) { |
10 | return filter.CheckStatistics(stats); |
11 | } |
12 | |
13 | void StatisticsPropagator::UpdateFilterStatistics(BaseStatistics &input, TableFilter &filter) { |
14 | // FIXME: update stats... |
15 | switch (filter.filter_type) { |
16 | case TableFilterType::CONJUNCTION_AND: { |
17 | auto &conjunction_and = filter.Cast<ConjunctionAndFilter>(); |
18 | for (auto &child_filter : conjunction_and.child_filters) { |
19 | UpdateFilterStatistics(input, filter&: *child_filter); |
20 | } |
21 | break; |
22 | } |
23 | case TableFilterType::CONSTANT_COMPARISON: { |
24 | auto &constant_filter = filter.Cast<ConstantFilter>(); |
25 | UpdateFilterStatistics(stats&: input, comparison_type: constant_filter.comparison_type, constant: constant_filter.constant); |
26 | break; |
27 | } |
28 | default: |
29 | break; |
30 | } |
31 | } |
32 | |
33 | unique_ptr<NodeStatistics> StatisticsPropagator::PropagateStatistics(LogicalGet &get, |
34 | unique_ptr<LogicalOperator> *node_ptr) { |
35 | if (get.function.cardinality) { |
36 | node_stats = get.function.cardinality(context, get.bind_data.get()); |
37 | } |
38 | if (!get.function.statistics) { |
39 | // no column statistics to get |
40 | return std::move(node_stats); |
41 | } |
42 | for (idx_t i = 0; i < get.column_ids.size(); i++) { |
43 | auto stats = get.function.statistics(context, get.bind_data.get(), get.column_ids[i]); |
44 | if (stats) { |
45 | ColumnBinding binding(get.table_index, i); |
46 | statistics_map.insert(x: make_pair(x&: binding, y: std::move(stats))); |
47 | } |
48 | } |
49 | // push table filters into the statistics |
50 | vector<idx_t> column_indexes; |
51 | column_indexes.reserve(n: get.table_filters.filters.size()); |
52 | for (auto &kv : get.table_filters.filters) { |
53 | column_indexes.push_back(x: kv.first); |
54 | } |
55 | |
56 | for (auto &table_filter_column : column_indexes) { |
57 | idx_t column_index; |
58 | for (column_index = 0; column_index < get.column_ids.size(); column_index++) { |
59 | if (get.column_ids[column_index] == table_filter_column) { |
60 | break; |
61 | } |
62 | } |
63 | D_ASSERT(column_index < get.column_ids.size()); |
64 | D_ASSERT(get.column_ids[column_index] == table_filter_column); |
65 | |
66 | // find the stats |
67 | ColumnBinding stats_binding(get.table_index, column_index); |
68 | auto entry = statistics_map.find(x: stats_binding); |
69 | if (entry == statistics_map.end()) { |
70 | // no stats for this entry |
71 | continue; |
72 | } |
73 | auto &stats = *entry->second; |
74 | |
75 | // fetch the table filter |
76 | D_ASSERT(get.table_filters.filters.count(table_filter_column) > 0); |
77 | auto &filter = get.table_filters.filters[table_filter_column]; |
78 | auto propagate_result = PropagateTableFilter(stats, filter&: *filter); |
79 | switch (propagate_result) { |
80 | case FilterPropagateResult::FILTER_ALWAYS_TRUE: |
81 | // filter is always true; it is useless to execute it |
82 | // erase this condition |
83 | get.table_filters.filters.erase(x: table_filter_column); |
84 | break; |
85 | case FilterPropagateResult::FILTER_FALSE_OR_NULL: |
86 | case FilterPropagateResult::FILTER_ALWAYS_FALSE: |
87 | // filter is always false; this entire filter should be replaced by an empty result block |
88 | ReplaceWithEmptyResult(node&: *node_ptr); |
89 | return make_uniq<NodeStatistics>(args: 0, args: 0); |
90 | default: |
91 | // general case: filter can be true or false, update this columns' statistics |
92 | UpdateFilterStatistics(input&: stats, filter&: *filter); |
93 | break; |
94 | } |
95 | } |
96 | return std::move(node_stats); |
97 | } |
98 | |
99 | } // namespace duckdb |
100 | |