/src/duckdb/src/optimizer/rule/predicate_factoring.cpp
Line | Count | Source |
1 | | #include "duckdb/optimizer/rule/predicate_factoring.hpp" |
2 | | |
3 | | #include "duckdb/planner/expression/bound_conjunction_expression.hpp" |
4 | | #include "duckdb/planner/expression/bound_columnref_expression.hpp" |
5 | | #include "duckdb/planner/expression_iterator.hpp" |
6 | | #include "duckdb/planner/column_binding_map.hpp" |
7 | | |
8 | | namespace duckdb { |
9 | | |
10 | 72.9k | PredicateFactoringRule::PredicateFactoringRule(ExpressionRewriter &rewriter) : Rule(rewriter) { |
11 | | // Match on a ConjunctionExpression that has at least one ConjunctionExpression as a child |
12 | 72.9k | auto op = make_uniq<ConjunctionExpressionMatcher>(); |
13 | 72.9k | op->matchers.push_back(make_uniq<ConjunctionExpressionMatcher>()); |
14 | 72.9k | op->policy = SetMatcher::Policy::SOME; |
15 | 72.9k | root = std::move(op); |
16 | 72.9k | } |
17 | | |
18 | 0 | static bool ExpressionIsDisjunction(const Expression &expr) { |
19 | 0 | return expr.GetExpressionClass() == ExpressionClass::BOUND_CONJUNCTION && |
20 | 0 | expr.GetExpressionType() == ExpressionType::CONJUNCTION_OR; |
21 | 0 | } |
22 | | |
23 | 0 | static void ExtractDisjunctedPredicates(Expression &expression, vector<reference<Expression>> &disjuncted_children) { |
24 | 0 | if (ExpressionIsDisjunction(expression)) { |
25 | 0 | auto &disjunction = expression.Cast<BoundConjunctionExpression>(); |
26 | 0 | for (auto &child : disjunction.GetChildren()) { |
27 | 0 | ExtractDisjunctedPredicates(*child, disjuncted_children); |
28 | 0 | } |
29 | 0 | } else { |
30 | 0 | disjuncted_children.push_back(expression); |
31 | 0 | } |
32 | 0 | } |
33 | | |
34 | 0 | static bool ColumnBindingIsvalid(const ColumnBinding &column_binding) { |
35 | 0 | return column_binding.table_index.IsValid() && column_binding.column_index.IsValid(); |
36 | 0 | } |
37 | | |
38 | 0 | static bool GetSingleColumnBinding(const Expression &expr, ColumnBinding &column_binding) { |
39 | 0 | if (expr.IsVolatile()) { |
40 | 0 | return false; |
41 | 0 | } |
42 | | |
43 | 0 | column_binding = ColumnBinding(); |
44 | |
|
45 | 0 | bool found_multiple = false; |
46 | 0 | ExpressionIterator::VisitExpression<BoundColumnRefExpression>(expr, [&](const BoundColumnRefExpression &colref) { |
47 | 0 | if (!ColumnBindingIsvalid(column_binding)) { |
48 | 0 | column_binding = colref.Binding(); |
49 | 0 | } else if (column_binding != colref.Binding()) { |
50 | 0 | found_multiple = true; |
51 | 0 | } |
52 | 0 | }); |
53 | |
|
54 | 0 | return ColumnBindingIsvalid(column_binding) && !found_multiple; |
55 | 0 | } |
56 | | |
57 | | static void ExtractDisjunctedPredicates(Expression &expression, |
58 | | column_binding_map_t<vector<reference<Expression>>> &binding_map); |
59 | | |
60 | | static void ExtractConjunctedPredicates(BoundConjunctionExpression &conjunction, |
61 | 0 | column_binding_map_t<vector<reference<Expression>>> &binding_map) { |
62 | 0 | D_ASSERT(conjunction.GetExpressionType() == ExpressionType::CONJUNCTION_AND); |
63 | 0 | for (auto &conjunction_child : conjunction.GetChildren()) { |
64 | 0 | if (conjunction_child->GetExpressionClass() == ExpressionClass::BOUND_CONJUNCTION && |
65 | 0 | conjunction_child->GetExpressionType() == ExpressionType::CONJUNCTION_AND) { |
66 | 0 | ExtractConjunctedPredicates(conjunction_child->Cast<BoundConjunctionExpression>(), binding_map); |
67 | 0 | } else { |
68 | 0 | ExtractDisjunctedPredicates(*conjunction_child, binding_map); |
69 | 0 | } |
70 | 0 | } |
71 | 0 | } |
72 | | |
73 | | static void ExtractDisjunctedPredicates(Expression &expression, |
74 | 0 | column_binding_map_t<vector<reference<Expression>>> &binding_map) { |
75 | 0 | if (expression.GetExpressionClass() == ExpressionClass::BOUND_CONJUNCTION && |
76 | 0 | expression.GetExpressionType() == ExpressionType::CONJUNCTION_AND) { |
77 | 0 | ExtractConjunctedPredicates(expression.Cast<BoundConjunctionExpression>(), binding_map); |
78 | 0 | } else { |
79 | 0 | ColumnBinding single_binding; |
80 | 0 | if (GetSingleColumnBinding(expression, single_binding)) { |
81 | 0 | binding_map[single_binding].push_back(expression); |
82 | 0 | } |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | 0 | static column_binding_map_t<vector<reference<Expression>>> GetDisjunctedPredicateMap(Expression &expression) { |
87 | 0 | vector<reference<Expression>> disjuncted_children; |
88 | 0 | ExtractDisjunctedPredicates(expression, disjuncted_children); |
89 | 0 | D_ASSERT(disjuncted_children.size() > 1); |
90 | | |
91 | | // Extract predicates of the first child |
92 | 0 | auto &first_child = disjuncted_children[0].get(); |
93 | 0 | D_ASSERT(!ExpressionIsDisjunction(first_child)); |
94 | 0 | column_binding_map_t<vector<reference<Expression>>> remaining_binding_map; |
95 | 0 | ExtractDisjunctedPredicates(first_child, remaining_binding_map); |
96 | |
|
97 | 0 | for (idx_t child_idx = 1; child_idx < disjuncted_children.size(); child_idx++) { |
98 | 0 | auto &child = disjuncted_children[child_idx].get(); |
99 | 0 | D_ASSERT(!ExpressionIsDisjunction(child)); |
100 | 0 | column_binding_map_t<vector<reference<Expression>>> child_binding_map; |
101 | 0 | ExtractDisjunctedPredicates(child, child_binding_map); |
102 | | |
103 | | // Bindings must appear in both maps to be considered for predicate factoring |
104 | 0 | for (auto it = remaining_binding_map.begin(); it != remaining_binding_map.end();) { |
105 | 0 | auto child_it = child_binding_map.find(it->first); |
106 | 0 | if (child_it == child_binding_map.end()) { |
107 | 0 | it = remaining_binding_map.erase(it); |
108 | 0 | } else { |
109 | 0 | for (auto &new_predicate : child_it->second) { |
110 | 0 | bool found = false; |
111 | 0 | for (auto &existing_predicate : it->second) { |
112 | 0 | if (new_predicate.get().Equals(existing_predicate.get())) { |
113 | 0 | found = true; |
114 | 0 | break; |
115 | 0 | } |
116 | 0 | } |
117 | 0 | if (!found) { |
118 | 0 | it->second.push_back(new_predicate.get()); |
119 | 0 | } |
120 | 0 | } |
121 | 0 | it++; |
122 | 0 | } |
123 | 0 | } |
124 | 0 | } |
125 | |
|
126 | 0 | return remaining_binding_map; |
127 | 0 | } |
128 | | |
129 | | unique_ptr<Expression> PredicateFactoringRule::Apply(LogicalOperator &op, vector<reference<Expression>> &bindings, |
130 | 0 | bool &changes_made, bool is_root) { |
131 | | // Only applies to top-level FILTER expressions |
132 | 0 | if ((op.type != LogicalOperatorType::LOGICAL_FILTER && op.type != LogicalOperatorType::LOGICAL_ANY_JOIN) || |
133 | 0 | !is_root) { |
134 | 0 | return nullptr; |
135 | 0 | } |
136 | | |
137 | | // The expression must be an OR TODO: could also implement some common expression extraction for AND |
138 | 0 | if (!ExpressionIsDisjunction(bindings[0])) { |
139 | 0 | return nullptr; |
140 | 0 | } |
141 | | |
142 | 0 | ColumnBinding column_binding; |
143 | 0 | if (GetSingleColumnBinding(bindings[0], column_binding)) { |
144 | 0 | return nullptr; // If it only applies to one column binding it can be pushed down already |
145 | 0 | } |
146 | | |
147 | | // Extract map from single column binding to expression it is contained in |
148 | 0 | const auto binding_map = GetDisjunctedPredicateMap(bindings[0].get()); |
149 | 0 | if (binding_map.empty()) { |
150 | 0 | return nullptr; // None qualify |
151 | 0 | } |
152 | | |
153 | 0 | unique_ptr<Expression> derived_filter; |
154 | 0 | for (auto &entry : binding_map) { |
155 | 0 | D_ASSERT(!entry.second.empty()); |
156 | | |
157 | | // Create disjunction on single-column predicates |
158 | 0 | unique_ptr<Expression> column_filter; |
159 | 0 | for (auto &expr : entry.second) { |
160 | 0 | if (!column_filter) { |
161 | 0 | column_filter = expr.get().Copy(); |
162 | 0 | } else { |
163 | 0 | auto new_disjunction = make_uniq<BoundConjunctionExpression>(ExpressionType::CONJUNCTION_OR); |
164 | 0 | new_disjunction->GetChildrenMutable().push_back(std::move(column_filter)); |
165 | 0 | new_disjunction->GetChildrenMutable().push_back(expr.get().Copy()); |
166 | 0 | column_filter = std::move(new_disjunction); |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | | // Conjunct each single-column predicate together |
171 | 0 | if (!derived_filter) { |
172 | 0 | derived_filter = std::move(column_filter); |
173 | 0 | } else { |
174 | 0 | auto new_conjunction = make_uniq<BoundConjunctionExpression>(ExpressionType::CONJUNCTION_AND); |
175 | 0 | new_conjunction->GetChildrenMutable().push_back(std::move(column_filter)); |
176 | 0 | new_conjunction->GetChildrenMutable().push_back(std::move(derived_filter)); |
177 | 0 | derived_filter = std::move(new_conjunction); |
178 | 0 | } |
179 | 0 | } |
180 | | |
181 | | // Now add the derived filter as an AND to the original expression |
182 | 0 | auto result = make_uniq<BoundConjunctionExpression>(ExpressionType::CONJUNCTION_AND); |
183 | 0 | result->GetChildrenMutable().push_back(bindings[0].get().Copy()); |
184 | 0 | result->GetChildrenMutable().push_back(std::move(derived_filter)); |
185 | 0 | return std::move(result); |
186 | 0 | } |
187 | | |
188 | | } // namespace duckdb |