|
| 1 | +#include "duckdb/planner/filter/optional_filter.hpp" |
| 2 | + |
1 | 3 | #include "pgduckdb/scan/postgres_scan.hpp" |
2 | 4 | #include "pgduckdb/scan/postgres_table_reader.hpp" |
3 | 5 | #include "pgduckdb/pgduckdb_types.hpp" |
|
7 | 9 | #include "pgduckdb/pgduckdb_process_lock.hpp" |
8 | 10 | #include "pgduckdb/logger.hpp" |
9 | 11 |
|
| 12 | +#include <numeric> // std::accumulate |
| 13 | + |
10 | 14 | namespace pgduckdb { |
11 | 15 |
|
12 | 16 | // |
13 | 17 | // PostgresScanGlobalState |
14 | 18 | // |
15 | 19 |
|
| 20 | +static duckdb::string |
| 21 | +FilterJoin(duckdb::vector<duckdb::string> &filters, duckdb::string &&delimiter) { |
| 22 | + return std::accumulate(filters.begin() + 1, filters.end(), filters[0], |
| 23 | + [&delimiter](duckdb::string l, duckdb::string r) { return l + delimiter + r; }); |
| 24 | +} |
| 25 | + |
| 26 | +int |
| 27 | +PostgresScanGlobalState::ExtractQueryFilters(duckdb::TableFilter *filter, const char *column_name, |
| 28 | + duckdb::string &query_filters, bool is_inside_optional_filter) { |
| 29 | + switch (filter->filter_type) { |
| 30 | + case duckdb::TableFilterType::CONSTANT_COMPARISON: |
| 31 | + case duckdb::TableFilterType::IS_NULL: |
| 32 | + case duckdb::TableFilterType::IS_NOT_NULL: |
| 33 | + case duckdb::TableFilterType::IN_FILTER: { |
| 34 | + query_filters += filter->ToString(column_name).c_str(); |
| 35 | + return 1; |
| 36 | + } |
| 37 | + case duckdb::TableFilterType::CONJUNCTION_OR: |
| 38 | + case duckdb::TableFilterType::CONJUNCTION_AND: { |
| 39 | + auto conjuction_filter = reinterpret_cast<duckdb::ConjunctionFilter *>(filter); |
| 40 | + duckdb::vector<std::string> conjuction_child_filters; |
| 41 | + for (idx_t i = 0; i < conjuction_filter->child_filters.size(); i++) { |
| 42 | + std::string child_filter; |
| 43 | + if (ExtractQueryFilters(conjuction_filter->child_filters[i].get(), column_name, child_filter, |
| 44 | + is_inside_optional_filter)) { |
| 45 | + conjuction_child_filters.emplace_back(child_filter); |
| 46 | + } |
| 47 | + } |
| 48 | + duckdb::string conjuction_delimiter = |
| 49 | + filter->filter_type == duckdb::TableFilterType::CONJUNCTION_OR ? " OR " : " AND "; |
| 50 | + if (conjuction_child_filters.size()) { |
| 51 | + query_filters += "(" + FilterJoin(conjuction_child_filters, std::move(conjuction_delimiter)) + ")"; |
| 52 | + } |
| 53 | + return conjuction_child_filters.size(); |
| 54 | + } |
| 55 | + case duckdb::TableFilterType::OPTIONAL_FILTER: { |
| 56 | + auto optional_filter = reinterpret_cast<duckdb::OptionalFilter *>(filter); |
| 57 | + return ExtractQueryFilters(optional_filter->child_filter.get(), column_name, query_filters, true); |
| 58 | + } |
| 59 | + /* DYNAMIC_FILTER is push down filter from topN execution. STRUCT_EXTRACT is |
| 60 | + * only received if struct_extract function is used. Default will catch all |
| 61 | + * filter that could be added in future in DuckDB. |
| 62 | + */ |
| 63 | + case duckdb::TableFilterType::DYNAMIC_FILTER: |
| 64 | + case duckdb::TableFilterType::STRUCT_EXTRACT: |
| 65 | + default: { |
| 66 | + if (is_inside_optional_filter) { |
| 67 | + pd_log(DEBUG1, "(DuckDB/ExtractQueryFilters) Unsupported optional filter: %s", |
| 68 | + filter->ToString(column_name).c_str()); |
| 69 | + return 0; |
| 70 | + } |
| 71 | + throw duckdb::Exception(duckdb::ExceptionType::EXECUTOR, |
| 72 | + "Invalid Filter Type: " + filter->ToString(column_name)); |
| 73 | + } |
| 74 | + } |
| 75 | +} |
| 76 | + |
16 | 77 | void |
17 | 78 | PostgresScanGlobalState::ConstructTableScanQuery(const duckdb::TableFunctionInitInput &input) { |
18 | 79 | /* SELECT COUNT(*) FROM */ |
@@ -82,27 +143,23 @@ PostgresScanGlobalState::ConstructTableScanQuery(const duckdb::TableFunctionInit |
82 | 143 |
|
83 | 144 | scan_query << " FROM " << GenerateQualifiedRelationName(rel); |
84 | 145 |
|
85 | | - first = true; |
86 | | - |
| 146 | + duckdb::vector<duckdb::string> query_filters; |
87 | 147 | for (auto const &[attr_num, duckdb_scanned_index] : columns_to_scan) { |
88 | 148 | auto filter = column_filters[duckdb_scanned_index]; |
89 | | - |
90 | 149 | if (!filter) { |
91 | 150 | continue; |
92 | 151 | } |
93 | | - |
94 | | - if (first) { |
95 | | - scan_query << " WHERE "; |
96 | | - } else { |
97 | | - scan_query << " AND "; |
98 | | - } |
99 | | - |
100 | | - first = false; |
101 | | - scan_query << "("; |
| 152 | + duckdb::string column_query_filters; |
102 | 153 | auto attr = GetAttr(table_tuple_desc, attr_num - 1); |
103 | 154 | auto col = pgduckdb::QuoteIdentifier(GetAttName(attr)); |
104 | | - scan_query << filter->ToString(col).c_str(); |
105 | | - scan_query << ") "; |
| 155 | + if (ExtractQueryFilters(filter, col, column_query_filters, false)) { |
| 156 | + query_filters.emplace_back(column_query_filters); |
| 157 | + }; |
| 158 | + } |
| 159 | + |
| 160 | + if (query_filters.size()) { |
| 161 | + scan_query << " WHERE "; |
| 162 | + scan_query << FilterJoin(query_filters, " AND "); |
106 | 163 | } |
107 | 164 | } |
108 | 165 |
|
@@ -157,12 +214,12 @@ PostgresScanTableFunction::PostgresScanTableFunction() |
157 | 214 | to_string = ToString; |
158 | 215 | } |
159 | 216 |
|
160 | | -std::string |
161 | | -PostgresScanTableFunction::ToString(const duckdb::FunctionData *data) { |
162 | | - auto &bind_data = data->Cast<PostgresScanFunctionData>(); |
163 | | - std::ostringstream oss; |
164 | | - oss << "(POSTGRES_SCAN) " << GetRelationName(bind_data.rel); |
165 | | - return oss.str(); |
| 217 | +duckdb::InsertionOrderPreservingMap<duckdb::string> |
| 218 | +PostgresScanTableFunction::ToString(duckdb::TableFunctionToStringInput &input) { |
| 219 | + auto &bind_data = input.bind_data->Cast<PostgresScanFunctionData>(); |
| 220 | + duckdb::InsertionOrderPreservingMap<duckdb::string> result; |
| 221 | + result["Table"] = GetRelationName(bind_data.rel); |
| 222 | + return result; |
166 | 223 | } |
167 | 224 |
|
168 | 225 | duckdb::unique_ptr<duckdb::GlobalTableFunctionState> |
|
0 commit comments