1010#include " duckdb/planner/filter/table_filter_functions.hpp"
1111#include " duckdb/common/enum_util.hpp"
1212
13+ #include " dbconnector/table_scan/filter_pushdown.hpp"
14+ #include " dbconnector/table_scan/filter_util.hpp"
15+
1316#include " postgres_utils.hpp"
1417
1518namespace duckdb {
1619
17- string PostgresFilterPushdown::CreateExpression (const string &column_name,
18- const vector<unique_ptr<Expression>> &filters, string op,
19- column_t column_id) {
20- vector<string> filter_entries;
21- for (auto &filter : filters) {
22- auto filter_str = TransformExpression (column_name, *filter, column_id);
23- if (!filter_str.empty ()) {
24- filter_entries.push_back (std::move (filter_str));
25- }
26- }
27- if (filter_entries.empty ()) {
28- return string ();
29- }
30- return " (" + StringUtil::Join (filter_entries, " " + op + " " ) + " )" ;
31- }
32-
33- string PostgresFilterPushdown::TransformComparison (ExpressionType type) {
34- switch (type) {
35- case ExpressionType::COMPARE_EQUAL :
36- return " =" ;
37- case ExpressionType::COMPARE_NOTEQUAL :
38- return " <>" ;
39- case ExpressionType::COMPARE_LESSTHAN :
40- return " <" ;
41- case ExpressionType::COMPARE_GREATERTHAN :
42- return " >" ;
43- case ExpressionType::COMPARE_LESSTHANOREQUALTO :
44- return " <=" ;
45- case ExpressionType::COMPARE_GREATERTHANOREQUALTO :
46- return " >=" ;
47- default :
48- throw NotImplementedException (" Unsupported expression type" );
49- }
50- }
51-
52- string TransformBlob (const string &val) {
53- char const HEX_DIGITS [] = " 0123456789ABCDEF" ;
54-
55- string result = " '\\ x" ;
56- for (idx_t i = 0 ; i < val.size (); i++) {
57- uint8_t byte_val = static_cast <uint8_t >(val[i]);
58- result += HEX_DIGITS [(byte_val >> 4 ) & 0xf ];
59- result += HEX_DIGITS [byte_val & 0xf ];
60- }
61- result += " '::BYTEA" ;
62- return result;
63- }
64-
65- string TransformLiteral (const Value &val) {
66- switch (val.type ().id ()) {
67- case LogicalTypeId::BLOB :
68- return TransformBlob (StringValue::Get (val));
69- default :
70- return PostgresUtils::WriteLiteral (val.ToString ());
71- }
72- }
73-
74- string PostgresFilterPushdown::TransformCTIDLiteral (const Value &constant) {
75- throw InternalException (" FIXME: transform ctid literal" );
76- }
77-
78- string PostgresFilterPushdown::TransformConstantFilter (const string &column_name, ExpressionType comparison_type,
79- const Value &constant, column_t column_id) {
80- string constant_string;
81- if (IsVirtualColumn (column_id)) {
82- return " FALSE" ;
83- } else {
84- constant_string = TransformLiteral (constant);
85- }
86- auto operator_string = TransformComparison (comparison_type);
87- string comparison = StringUtil::Format (" %s %s %s" , column_name, operator_string, constant_string);
88- if (constant.type ().id () == LogicalTypeId::VARCHAR ) {
89- comparison += " COLLATE \" C\" " ;
90- }
91- return comparison;
92- }
93-
94- string PostgresFilterPushdown::TransformExpressionSubject (const string &column_name, const Expression &expr) {
95- switch (expr.GetExpressionClass ()) {
96- case ExpressionClass::BOUND_REF :
97- case ExpressionClass::BOUND_COLUMN_REF :
98- return column_name;
99- case ExpressionClass::BOUND_FUNCTION : {
100- auto &func = expr.Cast <BoundFunctionExpression>();
101- idx_t child_idx;
102- if (!TryGetStructExtractChildIndex (func, child_idx) || func.GetChildren ().empty ()) {
103- return string ();
104- }
105- auto parent_name = TransformExpressionSubject (column_name, *func.GetChildren ()[0 ]);
106- if (parent_name.empty ()) {
107- return string ();
108- }
109- auto &struct_type = func.GetChildren ()[0 ]->GetReturnType ();
110- if (struct_type.id () != LogicalTypeId::STRUCT || StructType::IsUnnamed (struct_type)) {
111- return string ();
112- }
113- auto child_name = PostgresUtils::WriteIdentifier (StructType::GetChildName (struct_type, child_idx));
114- return " (" + parent_name + " )." + child_name;
115- }
116- default :
117- return string ();
118- }
119- }
120-
121- string PostgresFilterPushdown::TransformExpression (const string &column_name, const Expression &expr,
122- column_t column_id) {
123- if (BoundComparisonExpression::IsComparison (expr)) {
124- auto &comparison = expr.Cast <BoundFunctionExpression>();
125- auto comparison_type = comparison.GetExpressionType ();
126- auto &left = BoundComparisonExpression::Left (comparison);
127- auto &right = BoundComparisonExpression::Right (comparison);
128- auto subject = TransformExpressionSubject (column_name, left);
129- const Value *constant = nullptr ;
130- if (!subject.empty () && right.GetExpressionClass () == ExpressionClass::BOUND_CONSTANT ) {
131- constant = &right.Cast <BoundConstantExpression>().GetValue ();
132- } else {
133- subject = TransformExpressionSubject (column_name, right);
134- if (!subject.empty () && left.GetExpressionClass () == ExpressionClass::BOUND_CONSTANT ) {
135- constant = &left.Cast <BoundConstantExpression>().GetValue ();
136- comparison_type = FlipComparisonExpression (comparison_type);
137- }
138- }
139- if (!constant || subject.empty ()) {
140- return string ();
141- }
142- return TransformConstantFilter (subject, comparison_type, *constant, column_id);
143- }
144-
145- switch (expr.GetExpressionClass ()) {
146- case ExpressionClass::BOUND_CONJUNCTION : {
147- auto &conjunction = expr.Cast <BoundConjunctionExpression>();
148- switch (conjunction.GetExpressionType ()) {
149- case ExpressionType::CONJUNCTION_AND :
150- return CreateExpression (column_name, conjunction.GetChildren (), " AND" , column_id);
151- case ExpressionType::CONJUNCTION_OR :
152- return CreateExpression (column_name, conjunction.GetChildren (), " OR" , column_id);
153- default :
154- return string ();
155- }
156- }
157- case ExpressionClass::BOUND_OPERATOR : {
158- auto &op = expr.Cast <BoundOperatorExpression>();
159- auto subject =
160- op.GetChildren ().empty () ? string () : TransformExpressionSubject (column_name, *op.GetChildren ()[0 ]);
161- switch (op.GetExpressionType ()) {
162- case ExpressionType::OPERATOR_IS_NULL :
163- return !subject.empty () ? subject + " IS NULL" : string ();
164- case ExpressionType::OPERATOR_IS_NOT_NULL :
165- return !subject.empty () ? subject + " IS NOT NULL" : string ();
166- case ExpressionType::COMPARE_IN : {
167- if (subject.empty ()) {
168- return string ();
169- }
170- string in_list;
171- for (idx_t i = 1 ; i < op.GetChildren ().size (); i++) {
172- if (op.GetChildren ()[i]->GetExpressionClass () != ExpressionClass::BOUND_CONSTANT ) {
173- return string ();
174- }
175- if (!in_list.empty ()) {
176- in_list += " , " ;
177- }
178- auto &constant = op.GetChildren ()[i]->Cast <BoundConstantExpression>().GetValue ();
179- in_list += IsVirtualColumn (column_id) ? " FALSE" : TransformLiteral (constant);
180- }
181- return IsVirtualColumn (column_id) ? " FALSE" : subject + " IN (" + in_list + " )" ;
182- }
183- default :
184- return string ();
185- }
186- }
187- case ExpressionClass::BOUND_FUNCTION : {
188- auto &func = expr.Cast <BoundFunctionExpression>();
189- if (func.Function ().GetName () == OptionalFilterScalarFun::NAME && func.BindInfo ()) {
190- auto &data = func.BindInfo ()->Cast <OptionalFilterFunctionData>();
191- return data.child_filter_expr ? TransformExpression (column_name, *data.child_filter_expr , column_id)
192- : string ();
193- }
194- if (func.Function ().GetName () == SelectivityOptionalFilterScalarFun::NAME && func.BindInfo ()) {
195- auto &data = func.BindInfo ()->Cast <SelectivityOptionalFilterFunctionData>();
196- return data.child_filter_expr ? TransformExpression (column_name, *data.child_filter_expr , column_id)
197- : string ();
198- }
199- if (func.Function ().GetName () == DynamicFilterScalarFun::NAME ) {
200- return string ();
201- }
202- return string ();
203- }
204- default :
205- throw InternalException (" Unsupported table filter type" );
206- }
207- }
208-
209- string PostgresFilterPushdown::TransformFilter (const string &column_name, const TableFilter &filter,
210- column_t column_id) {
211- auto &expr_filter = ExpressionFilter::GetExpressionFilter (filter, " PostgresFilterPushdown::TransformFilter" );
212- return TransformExpression (column_name, *expr_filter.expr , column_id);
213- }
214-
21520string PostgresFilterPushdown::TransformFilters (const vector<column_t > &column_ids,
21621 optional_ptr<TableFilterSet> filters, const vector<string> &names) {
22+ using namespace dbconnector ;
21723 if (!filters || !filters->HasFilters ()) {
21824 // no filters
21925 return string ();
@@ -225,13 +31,21 @@ string PostgresFilterPushdown::TransformFilters(const vector<column_t> &column_i
22531 if (IsVirtualColumn (column_id)) {
22632 column_name = " ctid" ;
22733 } else {
228- column_name = PostgresUtils::WriteIdentifier ( names[column_id]) ;
34+ column_name = names[column_id];
22935 }
23036 auto &filter = entry.Filter ();
231- auto filter_text = TransformFilter (column_name, filter, column_id);
37+ auto config = table_scan::FilterPushdown::CreateConfig (' "' , ' \' ' , query::QuoteEscapeStyle::DOUBLE_QUOTE , " '\\ x" ,
38+ " ::BYTEA" );
39+ auto filter_text = table_scan::FilterPushdown::TransformFilter (config, column_name, filter, column_id);
23240
23341 if (filter_text.empty ()) {
234- continue ;
42+ if (table_scan::FilterUtil::IsInternalFilter (filter)) {
43+ continue ;
44+ }
45+ throw NotImplementedException (
46+ " Unsupported filter pushdown, use 'pg_experimental_filter_pushdown=FALSE' to disable pushdowns."
47+ " Problematic filter: \" %s\" " ,
48+ table_scan::FilterUtil::ToString (filter));
23549 }
23650 if (!result.empty ()) {
23751 result += " AND " ;
0 commit comments