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