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 | using namespace duckdb; |
6 | using namespace std; |
7 | |
8 | using Filter = FilterPushdown::Filter; |
9 | |
10 | unique_ptr<LogicalOperator> FilterPushdown::PushdownMarkJoin(unique_ptr<LogicalOperator> op, |
11 | unordered_set<idx_t> &left_bindings, |
12 | unordered_set<idx_t> &right_bindings) { |
13 | auto &join = (LogicalJoin &)*op; |
14 | auto &comp_join = (LogicalComparisonJoin &)*op; |
15 | assert(join.join_type == JoinType::MARK); |
16 | assert(op->type == LogicalOperatorType::COMPARISON_JOIN || op->type == LogicalOperatorType::DELIM_JOIN); |
17 | |
18 | right_bindings.insert(comp_join.mark_index); |
19 | FilterPushdown left_pushdown(optimizer), right_pushdown(optimizer); |
20 | bool found_mark_reference = false; |
21 | // now check the set of filters |
22 | for (idx_t i = 0; i < filters.size(); i++) { |
23 | auto side = JoinSide::GetJoinSide(filters[i]->bindings, left_bindings, right_bindings); |
24 | if (side == JoinSide::LEFT) { |
25 | // bindings match left side: push into left |
26 | left_pushdown.filters.push_back(move(filters[i])); |
27 | // erase the filter from the list of filters |
28 | filters.erase(filters.begin() + i); |
29 | i--; |
30 | } else if (side == JoinSide::RIGHT) { |
31 | // there can only be at most one filter referencing the marker |
32 | assert(!found_mark_reference); |
33 | found_mark_reference = true; |
34 | // this filter references the marker |
35 | // we can turn this into a SEMI join if the filter is on only the marker |
36 | if (filters[i]->filter->type == ExpressionType::BOUND_COLUMN_REF) { |
37 | // filter just references the marker: turn into semi join |
38 | join.join_type = JoinType::SEMI; |
39 | filters.erase(filters.begin() + i); |
40 | i--; |
41 | continue; |
42 | } |
43 | // if the filter is on NOT(marker) AND the join conditions are all set to "null_values_are_equal" we can |
44 | // turn this into an ANTI join if all join conditions have null_values_are_equal=true, then the result of |
45 | // the MARK join is always TRUE or FALSE, and never NULL this happens in the case of a correlated EXISTS |
46 | // clause |
47 | if (filters[i]->filter->type == ExpressionType::OPERATOR_NOT) { |
48 | auto &op_expr = (BoundOperatorExpression &)*filters[i]->filter; |
49 | if (op_expr.children[0]->type == ExpressionType::BOUND_COLUMN_REF) { |
50 | // the filter is NOT(marker), check the join conditions |
51 | bool all_null_values_are_equal = true; |
52 | for (auto &cond : comp_join.conditions) { |
53 | if (!cond.null_values_are_equal) { |
54 | all_null_values_are_equal = false; |
55 | break; |
56 | } |
57 | } |
58 | if (all_null_values_are_equal) { |
59 | // all null values are equal, convert to ANTI join |
60 | join.join_type = JoinType::ANTI; |
61 | filters.erase(filters.begin() + i); |
62 | i--; |
63 | continue; |
64 | } |
65 | } |
66 | } |
67 | } |
68 | } |
69 | op->children[0] = left_pushdown.Rewrite(move(op->children[0])); |
70 | op->children[1] = right_pushdown.Rewrite(move(op->children[1])); |
71 | return FinishPushdown(move(op)); |
72 | } |
73 |