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
5using namespace duckdb;
6using namespace std;
7
8using Filter = FilterPushdown::Filter;
9
10unique_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