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
7namespace duckdb {
8
9FilterPropagateResult StatisticsPropagator::PropagateTableFilter(BaseStatistics &stats, TableFilter &filter) {
10 return filter.CheckStatistics(stats);
11}
12
13void 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
33unique_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