1#include "duckdb/optimizer/filter_pushdown.hpp"
2#include "duckdb/optimizer/optimizer.hpp"
3#include "duckdb/planner/expression/bound_columnref_expression.hpp"
4#include "duckdb/planner/expression/bound_parameter_expression.hpp"
5#include "duckdb/planner/operator/logical_filter.hpp"
6#include "duckdb/planner/operator/logical_get.hpp"
7#include "duckdb/storage/data_table.hpp"
8
9namespace duckdb {
10
11unique_ptr<LogicalOperator> FilterPushdown::PushdownGet(unique_ptr<LogicalOperator> op) {
12 D_ASSERT(op->type == LogicalOperatorType::LOGICAL_GET);
13 auto &get = op->Cast<LogicalGet>();
14
15 if (get.function.pushdown_complex_filter || get.function.filter_pushdown) {
16 // this scan supports some form of filter push-down
17 // check if there are any parameters
18 // if there are, invalidate them to force a re-bind on execution
19 for (auto &filter : filters) {
20 if (filter->filter->HasParameter()) {
21 // there is a parameter in the filters! invalidate it
22 BoundParameterExpression::InvalidateRecursive(expr&: *filter->filter);
23 }
24 }
25 }
26 if (get.function.pushdown_complex_filter) {
27 // for the remaining filters, check if we can push any of them into the scan as well
28 vector<unique_ptr<Expression>> expressions;
29 expressions.reserve(n: filters.size());
30 for (auto &filter : filters) {
31 expressions.push_back(x: std::move(filter->filter));
32 }
33 filters.clear();
34
35 get.function.pushdown_complex_filter(optimizer.context, get, get.bind_data.get(), expressions);
36
37 if (expressions.empty()) {
38 return op;
39 }
40 // re-generate the filters
41 for (auto &expr : expressions) {
42 auto f = make_uniq<Filter>();
43 f->filter = std::move(expr);
44 f->ExtractBindings();
45 filters.push_back(x: std::move(f));
46 }
47 }
48
49 if (!get.table_filters.filters.empty() || !get.function.filter_pushdown) {
50 // the table function does not support filter pushdown: push a LogicalFilter on top
51 return FinishPushdown(op: std::move(op));
52 }
53 PushFilters();
54
55 //! We generate the table filters that will be executed during the table scan
56 //! Right now this only executes simple AND filters
57 get.table_filters = combiner.GenerateTableScanFilters(column_ids&: get.column_ids);
58
59 // //! For more complex filters if all filters to a column are constants we generate a min max boundary used to
60 // check
61 // //! the zonemaps.
62 // auto zonemap_checks = combiner.GenerateZonemapChecks(get.column_ids, get.table_filters);
63
64 // for (auto &f : get.table_filters) {
65 // f.column_index = get.column_ids[f.column_index];
66 // }
67
68 // //! Use zonemap checks as table filters for pre-processing
69 // for (auto &zonemap_check : zonemap_checks) {
70 // if (zonemap_check.column_index != COLUMN_IDENTIFIER_ROW_ID) {
71 // get.table_filters.push_back(zonemap_check);
72 // }
73 // }
74
75 GenerateFilters();
76
77 //! Now we try to pushdown the remaining filters to perform zonemap checking
78 return FinishPushdown(op: std::move(op));
79}
80
81} // namespace duckdb
82