| 1 | #include "duckdb/optimizer/filter_pullup.hpp" |
| 2 | #include "duckdb/planner/expression/bound_columnref_expression.hpp" |
| 3 | #include "duckdb/planner/expression_iterator.hpp" |
| 4 | #include "duckdb/planner/operator/logical_empty_result.hpp" |
| 5 | #include "duckdb/planner/operator/logical_projection.hpp" |
| 6 | #include "duckdb/planner/expression/bound_comparison_expression.hpp" |
| 7 | |
| 8 | namespace duckdb { |
| 9 | |
| 10 | static void RevertFilterPullup(LogicalProjection &proj, vector<unique_ptr<Expression>> &expressions) { |
| 11 | unique_ptr<LogicalFilter> filter = make_uniq<LogicalFilter>(); |
| 12 | for (idx_t i = 0; i < expressions.size(); ++i) { |
| 13 | filter->expressions.push_back(x: std::move(expressions[i])); |
| 14 | } |
| 15 | expressions.clear(); |
| 16 | filter->children.push_back(x: std::move(proj.children[0])); |
| 17 | proj.children[0] = std::move(filter); |
| 18 | } |
| 19 | |
| 20 | static void ReplaceExpressionBinding(vector<unique_ptr<Expression>> &proj_expressions, Expression &expr, |
| 21 | idx_t proj_table_idx) { |
| 22 | if (expr.type == ExpressionType::BOUND_COLUMN_REF) { |
| 23 | bool found_proj_col = false; |
| 24 | BoundColumnRefExpression &colref = expr.Cast<BoundColumnRefExpression>(); |
| 25 | // find the corresponding column index in the projection expressions |
| 26 | for (idx_t proj_idx = 0; proj_idx < proj_expressions.size(); proj_idx++) { |
| 27 | auto &proj_expr = *proj_expressions[proj_idx]; |
| 28 | if (proj_expr.type == ExpressionType::BOUND_COLUMN_REF) { |
| 29 | if (colref.Equals(other: proj_expr)) { |
| 30 | colref.binding.table_index = proj_table_idx; |
| 31 | colref.binding.column_index = proj_idx; |
| 32 | found_proj_col = true; |
| 33 | break; |
| 34 | } |
| 35 | } |
| 36 | } |
| 37 | if (!found_proj_col) { |
| 38 | // Project a new column |
| 39 | auto new_colref = colref.Copy(); |
| 40 | colref.binding.table_index = proj_table_idx; |
| 41 | colref.binding.column_index = proj_expressions.size(); |
| 42 | proj_expressions.push_back(x: std::move(new_colref)); |
| 43 | } |
| 44 | } |
| 45 | ExpressionIterator::EnumerateChildren( |
| 46 | expression&: expr, callback: [&](Expression &child) { return ReplaceExpressionBinding(proj_expressions, expr&: child, proj_table_idx); }); |
| 47 | } |
| 48 | |
| 49 | void FilterPullup::ProjectSetOperation(LogicalProjection &proj) { |
| 50 | vector<unique_ptr<Expression>> copy_proj_expressions; |
| 51 | // copying the project expressions, it's useful whether we should revert the filter pullup |
| 52 | for (idx_t i = 0; i < proj.expressions.size(); ++i) { |
| 53 | copy_proj_expressions.push_back(x: proj.expressions[i]->Copy()); |
| 54 | } |
| 55 | |
| 56 | // Replace filter expression bindings, when need we add new columns into the copied projection expression |
| 57 | vector<unique_ptr<Expression>> changed_filter_expressions; |
| 58 | for (idx_t i = 0; i < filters_expr_pullup.size(); ++i) { |
| 59 | auto copy_filter_expr = filters_expr_pullup[i]->Copy(); |
| 60 | ReplaceExpressionBinding(proj_expressions&: copy_proj_expressions, expr&: (Expression &)*copy_filter_expr, proj_table_idx: proj.table_index); |
| 61 | changed_filter_expressions.push_back(x: std::move(copy_filter_expr)); |
| 62 | } |
| 63 | |
| 64 | /// Case new columns were added into the projection |
| 65 | // we must skip filter pullup because adding new columns to these operators will change the result |
| 66 | if (copy_proj_expressions.size() > proj.expressions.size()) { |
| 67 | RevertFilterPullup(proj, expressions&: filters_expr_pullup); |
| 68 | return; |
| 69 | } |
| 70 | |
| 71 | // now we must replace the filter bindings |
| 72 | D_ASSERT(filters_expr_pullup.size() == changed_filter_expressions.size()); |
| 73 | for (idx_t i = 0; i < filters_expr_pullup.size(); ++i) { |
| 74 | filters_expr_pullup[i] = std::move(changed_filter_expressions[i]); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | unique_ptr<LogicalOperator> FilterPullup::PullupProjection(unique_ptr<LogicalOperator> op) { |
| 79 | D_ASSERT(op->type == LogicalOperatorType::LOGICAL_PROJECTION); |
| 80 | op->children[0] = Rewrite(op: std::move(op->children[0])); |
| 81 | if (!filters_expr_pullup.empty()) { |
| 82 | auto &proj = op->Cast<LogicalProjection>(); |
| 83 | // INTERSECT, EXCEPT, and DISTINCT |
| 84 | if (!can_add_column) { |
| 85 | // special treatment for operators that cannot add columns, e.g., INTERSECT, EXCEPT, and DISTINCT |
| 86 | ProjectSetOperation(proj); |
| 87 | return op; |
| 88 | } |
| 89 | |
| 90 | for (idx_t i = 0; i < filters_expr_pullup.size(); ++i) { |
| 91 | auto &expr = (Expression &)*filters_expr_pullup[i]; |
| 92 | ReplaceExpressionBinding(proj_expressions&: proj.expressions, expr, proj_table_idx: proj.table_index); |
| 93 | } |
| 94 | } |
| 95 | return op; |
| 96 | } |
| 97 | |
| 98 | } // namespace duckdb |
| 99 | |