MySQL 9.1.0
Source Code Documentation
walk_access_paths.h
Go to the documentation of this file.
1/* Copyright (c) 2020, 2024, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is designed to work with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have either included with
13 the program or referenced in the documentation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
23
24#ifndef SQL_JOIN_OPTIMIZER_WALK_ACCESS_PATHS_H
25#define SQL_JOIN_OPTIMIZER_WALK_ACCESS_PATHS_H
26
27#include <type_traits>
28
31
33 // Stop on _any_ MATERIALIZE, STREAM or TEMPTABLE_AGGREGATE paths, even if
34 // they do not cross query blocks.
35 // Also stops on APPEND paths, which always cross query blocks.
37
38 // Stop on MATERIALIZE, STREAM, TEMPTABLE_AGGREGATE or APPEND paths that
39 // cross query blocks.
41
42 // Do not stop at any kind of access path, unless func() returns true.
44};
45
46/**
47 Traverse every access path below `path` (possibly limited to the current query
48 block with the `cross_query_blocks` parameter), calling func() for each one
49 with pre- or post-order traversal. If func() returns true, the traversal does
50 not descend into the children of the current path. For post-order traversal,
51 the children have already been traversed when func() is called, so it is too
52 late to skip them, and the return value of func() is effectively ignored.
53
54 The `join` parameter signifies what query block `path` is part of, since that
55 is not implicit from the path itself. The function will track this as it
56 changes throughout the tree (in MATERIALIZE or STREAM access paths), and
57 will give the correct value to the func() callback. It is only used by
58 WalkAccessPaths() itself if the policy is ENTIRE_QUERY_BLOCK; if not, it is
59 only used for the func() callback, and you can set it to nullptr if you wish.
60 func() must have signature func(AccessPath *, const JOIN *), or it could be
61 JOIN * if a non-const JOIN is given in.
62 */
63template <class AccessPathPtr, class Func, class JoinPtr>
64 requires std::is_convertible_v<AccessPathPtr, const AccessPath *> &&
65 std::is_convertible_v<JoinPtr, const JOIN *> &&
66 std::is_invocable_r_v<bool, Func, AccessPathPtr, JoinPtr>
67void WalkAccessPaths(AccessPathPtr path, JoinPtr join,
68 WalkAccessPathPolicy cross_query_blocks, Func &&func,
69 bool post_order_traversal = false) {
70 if (cross_query_blocks == WalkAccessPathPolicy::ENTIRE_QUERY_BLOCK) {
71 assert(join != nullptr);
72 }
73
74 if (!post_order_traversal) {
75 if (func(path, join)) {
76 // Stop recursing in this branch.
77 return;
78 }
79 }
80
81 ForEachChild(path, join, cross_query_blocks,
82 [&](auto &&subpath, auto &&subjoin) {
83 WalkAccessPaths(subpath, subjoin, cross_query_blocks, func,
84 post_order_traversal);
85 });
86
87 if (post_order_traversal) {
88 if (func(path, join)) {
89 // Stop recursing in this branch. In practice a no-op, since we are
90 // already done with this branch.
91 return;
92 }
93 }
94}
95
96/**
97 Call a function on every immediate child of the given access path.
98
99 @param path The access path whose children to visit.
100 @param join The JOIN object for the current query block. Can be nullptr if
101 cross_query_blocks is not ENTIRE_QUERY_BLOCK.
102 @param cross_query_blocks Tells whether to stop traversal at materialization
103 or query block boundaries.
104 @param func The function to call. It takes two arguments: a pointer to an
105 access path (the child) and a pointer to the JOIN object for which the access
106 path was created.
107 */
108template <class AccessPathPtr, class Func, class JoinPtr>
109 requires std::is_convertible_v<AccessPathPtr, const AccessPath *> &&
110 std::is_convertible_v<JoinPtr, const JOIN *> &&
111 std::is_invocable_v<Func, AccessPathPtr, JoinPtr>
112void ForEachChild(AccessPathPtr path, JoinPtr join,
113 WalkAccessPathPolicy cross_query_blocks, Func &&func) {
114 if (cross_query_blocks == WalkAccessPathPolicy::ENTIRE_QUERY_BLOCK) {
115 assert(join != nullptr);
116 }
117
118 switch (path->type) {
123 case AccessPath::REF:
129 case AccessPath::MRR:
141 // No children.
142 break;
144 func(path->nested_loop_join().outer, join);
145 func(path->nested_loop_join().inner, join);
146 break;
148 func(path->nested_loop_semijoin_with_duplicate_removal().outer, join);
149 func(path->nested_loop_semijoin_with_duplicate_removal().inner, join);
150 break;
152 func(path->bka_join().outer, join);
153 func(path->bka_join().inner, join);
154 break;
156 func(path->hash_join().inner, join);
157 func(path->hash_join().outer, join);
158 break;
160 func(path->filter().child, join);
161 break;
162 case AccessPath::SORT:
163 func(path->sort().child, join);
164 break;
166 func(path->aggregate().child, join);
167 break;
169 if (cross_query_blocks == WalkAccessPathPolicy::ENTIRE_TREE ||
170 (cross_query_blocks == WalkAccessPathPolicy::ENTIRE_QUERY_BLOCK &&
171 path->temptable_aggregate().join == join)) {
172 func(path->temptable_aggregate().subquery_path, join);
173 }
174 func(path->temptable_aggregate().table_path, join);
175 break;
177 func(path->limit_offset().child, join);
178 break;
180 if (cross_query_blocks == WalkAccessPathPolicy::ENTIRE_TREE ||
181 (cross_query_blocks == WalkAccessPathPolicy::ENTIRE_QUERY_BLOCK &&
182 path->stream().join == join)) {
183 func(path->stream().child, path->stream().join);
184 }
185 break;
187 func(path->materialize().table_path, join);
188 for (const MaterializePathParameters::Operand &operand :
189 path->materialize().param->m_operands) {
190 if (cross_query_blocks == WalkAccessPathPolicy::ENTIRE_TREE ||
191 (cross_query_blocks == WalkAccessPathPolicy::ENTIRE_QUERY_BLOCK &&
192 operand.join == join)) {
193 func(operand.subquery_path, operand.join);
194 }
195 }
196 break;
198 func(path->materialize_information_schema_table().table_path, join);
199 break;
201 if (cross_query_blocks == WalkAccessPathPolicy::ENTIRE_TREE) {
202 for (const AppendPathParameters &child : *path->append().children) {
203 func(child.path, child.join);
204 }
205 }
206 break;
208 func(path->window().child, join);
209 break;
211 func(path->weedout().child, join);
212 break;
214 func(path->remove_duplicates().child, join);
215 break;
217 func(path->remove_duplicates_on_index().child, join);
218 break;
220 func(path->alternative().child, join);
221 break;
223 func(path->cache_invalidator().child, join);
224 break;
226 for (AccessPath *child : *path->index_merge().children) {
227 func(child, join);
228 }
229 break;
231 for (AccessPath *child : *path->rowid_intersection().children) {
232 func(child, join);
233 }
234 break;
236 for (AccessPath *child : *path->rowid_union().children) {
237 func(child, join);
238 }
239 break;
241 func(path->delete_rows().child, join);
242 break;
244 func(path->update_rows().child, join);
245 break;
246 }
247}
248
249/**
250 A wrapper around WalkAccessPaths() that collects all tables under
251 “root_path” and calls the given functor, stopping at materializations.
252 This is typically used to know which tables to sort or the like.
253
254 func() must have signature func(TABLE *), and return true upon error.
255 */
256template <class Func>
257void WalkTablesUnderAccessPath(AccessPath *root_path, Func &&func,
258 bool include_pruned_tables) {
260 root_path, /*join=*/nullptr,
262 [&](AccessPath *path, const JOIN *) {
263 switch (path->type) {
264 case AccessPath::TABLE_SCAN:
265 return func(path->table_scan().table);
266 case AccessPath::SAMPLE_SCAN:
267 // SampleScan can be executed only in the secondary engine.
268 return false; /* LCOV_EXCL_LINE */
269 case AccessPath::INDEX_SCAN:
270 return func(path->index_scan().table);
271 case AccessPath::INDEX_DISTANCE_SCAN:
272 return func(path->index_distance_scan().table);
273 case AccessPath::REF:
274 return func(path->ref().table);
275 case AccessPath::REF_OR_NULL:
276 return func(path->ref_or_null().table);
277 case AccessPath::EQ_REF:
278 return func(path->eq_ref().table);
279 case AccessPath::PUSHED_JOIN_REF:
280 return func(path->pushed_join_ref().table);
281 case AccessPath::FULL_TEXT_SEARCH:
282 return func(path->full_text_search().table);
283 case AccessPath::CONST_TABLE:
284 return func(path->const_table().table);
285 case AccessPath::MRR:
286 return func(path->mrr().table);
287 case AccessPath::FOLLOW_TAIL:
288 return func(path->follow_tail().table);
289 case AccessPath::INDEX_RANGE_SCAN:
290 return func(path->index_range_scan().used_key_part[0].field->table);
291 case AccessPath::INDEX_SKIP_SCAN:
292 return func(path->index_skip_scan().table);
293 case AccessPath::GROUP_INDEX_SKIP_SCAN:
294 return func(path->group_index_skip_scan().table);
295 case AccessPath::DYNAMIC_INDEX_RANGE_SCAN:
296 return func(path->dynamic_index_range_scan().table);
297 case AccessPath::STREAM:
298 return func(path->stream().table);
299 case AccessPath::MATERIALIZED_TABLE_FUNCTION:
300 return func(path->materialized_table_function().table);
301 case AccessPath::ALTERNATIVE:
302 return func(
303 path->alternative().table_scan_path->table_scan().table);
304 case AccessPath::UNQUALIFIED_COUNT:
305 // Should never be below anything that needs
306 // WalkTablesUnderAccessPath().
307 assert(false);
308 return true;
309 case AccessPath::ZERO_ROWS:
310 if (include_pruned_tables && path->zero_rows().child != nullptr) {
311 WalkTablesUnderAccessPath(path->zero_rows().child, func,
312 include_pruned_tables);
313 }
314 return false;
316 return func(path->window().temp_table);
331 case AccessPath::SORT:
341 return false;
342 }
343 assert(false);
344 return true;
345 });
346}
347
348#endif // SQL_JOIN_OPTIMIZER_WALK_ACCESS_PATHS_H
Definition: sql_optimizer.h:133
static char * path
Definition: mysqldump.cc:149
std::string join(const detail::range auto &rng, std::string_view delim)
join elements of a range into a string separated by a delimiter.
Definition: string.h:74
Access paths are a query planning structure that correspond 1:1 to iterators, in that an access path ...
Definition: access_path.h:227
@ FOLLOW_TAIL
Definition: access_path.h:243
@ FILTER
Definition: access_path.h:267
@ PUSHED_JOIN_REF
Definition: access_path.h:239
@ ZERO_ROWS_AGGREGATED
Definition: access_path.h:256
@ UPDATE_ROWS
Definition: access_path.h:285
@ AGGREGATE
Definition: access_path.h:269
@ BKA_JOIN
Definition: access_path.h:263
@ ZERO_ROWS
Definition: access_path.h:255
@ CONST_TABLE
Definition: access_path.h:241
@ GROUP_INDEX_SKIP_SCAN
Definition: access_path.h:249
@ SAMPLE_SCAN
Definition: access_path.h:233
@ INDEX_RANGE_SCAN
Definition: access_path.h:244
@ UNQUALIFIED_COUNT
Definition: access_path.h:258
@ EQ_REF
Definition: access_path.h:238
@ FAKE_SINGLE_ROW
Definition: access_path.h:254
@ MATERIALIZE_INFORMATION_SCHEMA_TABLE
Definition: access_path.h:274
@ WINDOW
Definition: access_path.h:276
@ REF_OR_NULL
Definition: access_path.h:237
@ MATERIALIZE
Definition: access_path.h:273
@ NESTED_LOOP_SEMIJOIN_WITH_DUPLICATE_REMOVAL
Definition: access_path.h:262
@ ROWID_UNION
Definition: access_path.h:247
@ INDEX_SKIP_SCAN
Definition: access_path.h:248
@ MRR
Definition: access_path.h:242
@ CACHE_INVALIDATOR
Definition: access_path.h:281
@ INDEX_SCAN
Definition: access_path.h:234
@ TABLE_VALUE_CONSTRUCTOR
Definition: access_path.h:253
@ WEEDOUT
Definition: access_path.h:277
@ MATERIALIZED_TABLE_FUNCTION
Definition: access_path.h:257
@ REMOVE_DUPLICATES_ON_INDEX
Definition: access_path.h:279
@ TABLE_SCAN
Definition: access_path.h:232
@ REF
Definition: access_path.h:236
@ TEMPTABLE_AGGREGATE
Definition: access_path.h:270
@ LIMIT_OFFSET
Definition: access_path.h:271
@ APPEND
Definition: access_path.h:275
@ NESTED_LOOP_JOIN
Definition: access_path.h:261
@ INDEX_MERGE
Definition: access_path.h:245
@ FULL_TEXT_SEARCH
Definition: access_path.h:240
@ ALTERNATIVE
Definition: access_path.h:280
@ STREAM
Definition: access_path.h:272
@ REMOVE_DUPLICATES
Definition: access_path.h:278
@ ROWID_INTERSECTION
Definition: access_path.h:246
@ DYNAMIC_INDEX_RANGE_SCAN
Definition: access_path.h:250
@ DELETE_ROWS
Definition: access_path.h:284
@ SORT
Definition: access_path.h:268
@ INDEX_DISTANCE_SCAN
Definition: access_path.h:235
@ HASH_JOIN
Definition: access_path.h:264
Definition: access_path.h:179
Definition: materialize_path_parameters.h:42
void ForEachChild(AccessPathPtr path, JoinPtr join, WalkAccessPathPolicy cross_query_blocks, Func &&func)
Call a function on every immediate child of the given access path.
Definition: walk_access_paths.h:112
void WalkAccessPaths(AccessPathPtr path, JoinPtr join, WalkAccessPathPolicy cross_query_blocks, Func &&func, bool post_order_traversal=false)
Traverse every access path below path (possibly limited to the current query block with the cross_que...
Definition: walk_access_paths.h:67
WalkAccessPathPolicy
Definition: walk_access_paths.h:32
void WalkTablesUnderAccessPath(AccessPath *root_path, Func &&func, bool include_pruned_tables)
A wrapper around WalkAccessPaths() that collects all tables under “root_path” and calls the given fun...
Definition: walk_access_paths.h:257