| 1 | #include "duckdb/optimizer/filter_pushdown.hpp" |
|---|---|
| 2 | #include "duckdb/planner/expression/bound_operator_expression.hpp" |
| 3 | #include "duckdb/planner/operator/logical_comparison_join.hpp" |
| 4 | |
| 5 | namespace duckdb { |
| 6 | |
| 7 | using Filter = FilterPushdown::Filter; |
| 8 | |
| 9 | unique_ptr<LogicalOperator> FilterPushdown::PushdownMarkJoin(unique_ptr<LogicalOperator> op, |
| 10 | unordered_set<idx_t> &left_bindings, |
| 11 | unordered_set<idx_t> &right_bindings) { |
| 12 | auto &join = op->Cast<LogicalJoin>(); |
| 13 | auto &comp_join = op->Cast<LogicalComparisonJoin>(); |
| 14 | D_ASSERT(join.join_type == JoinType::MARK); |
| 15 | D_ASSERT(op->type == LogicalOperatorType::LOGICAL_COMPARISON_JOIN || |
| 16 | op->type == LogicalOperatorType::LOGICAL_DELIM_JOIN || op->type == LogicalOperatorType::LOGICAL_ASOF_JOIN); |
| 17 | |
| 18 | right_bindings.insert(x: comp_join.mark_index); |
| 19 | FilterPushdown left_pushdown(optimizer), right_pushdown(optimizer); |
| 20 | #ifdef DEBUG |
| 21 | bool simplified_mark_join = false; |
| 22 | #endif |
| 23 | // now check the set of filters |
| 24 | for (idx_t i = 0; i < filters.size(); i++) { |
| 25 | auto side = JoinSide::GetJoinSide(bindings: filters[i]->bindings, left_bindings, right_bindings); |
| 26 | if (side == JoinSide::LEFT) { |
| 27 | // bindings match left side: push into left |
| 28 | left_pushdown.filters.push_back(x: std::move(filters[i])); |
| 29 | // erase the filter from the list of filters |
| 30 | filters.erase(position: filters.begin() + i); |
| 31 | i--; |
| 32 | } else if (side == JoinSide::RIGHT) { |
| 33 | #ifdef DEBUG |
| 34 | D_ASSERT(!simplified_mark_join); |
| 35 | #endif |
| 36 | // this filter references the marker |
| 37 | // we can turn this into a SEMI join if the filter is on only the marker |
| 38 | if (filters[i]->filter->type == ExpressionType::BOUND_COLUMN_REF) { |
| 39 | // filter just references the marker: turn into semi join |
| 40 | #ifdef DEBUG |
| 41 | simplified_mark_join = true; |
| 42 | #endif |
| 43 | join.join_type = JoinType::SEMI; |
| 44 | filters.erase(position: filters.begin() + i); |
| 45 | i--; |
| 46 | continue; |
| 47 | } |
| 48 | // if the filter is on NOT(marker) AND the join conditions are all set to "null_values_are_equal" we can |
| 49 | // turn this into an ANTI join if all join conditions have null_values_are_equal=true, then the result of |
| 50 | // the MARK join is always TRUE or FALSE, and never NULL this happens in the case of a correlated EXISTS |
| 51 | // clause |
| 52 | if (filters[i]->filter->type == ExpressionType::OPERATOR_NOT) { |
| 53 | auto &op_expr = filters[i]->filter->Cast<BoundOperatorExpression>(); |
| 54 | if (op_expr.children[0]->type == ExpressionType::BOUND_COLUMN_REF) { |
| 55 | // the filter is NOT(marker), check the join conditions |
| 56 | bool all_null_values_are_equal = true; |
| 57 | for (auto &cond : comp_join.conditions) { |
| 58 | if (cond.comparison != ExpressionType::COMPARE_DISTINCT_FROM && |
| 59 | cond.comparison != ExpressionType::COMPARE_NOT_DISTINCT_FROM) { |
| 60 | all_null_values_are_equal = false; |
| 61 | break; |
| 62 | } |
| 63 | } |
| 64 | if (all_null_values_are_equal) { |
| 65 | #ifdef DEBUG |
| 66 | simplified_mark_join = true; |
| 67 | #endif |
| 68 | // all null values are equal, convert to ANTI join |
| 69 | join.join_type = JoinType::ANTI; |
| 70 | filters.erase(position: filters.begin() + i); |
| 71 | i--; |
| 72 | continue; |
| 73 | } |
| 74 | } |
| 75 | } |
| 76 | } |
| 77 | } |
| 78 | op->children[0] = left_pushdown.Rewrite(op: std::move(op->children[0])); |
| 79 | op->children[1] = right_pushdown.Rewrite(op: std::move(op->children[1])); |
| 80 | return PushFinalFilters(op: std::move(op)); |
| 81 | } |
| 82 | |
| 83 | } // namespace duckdb |
| 84 |