MySQL 9.5.0
Source Code Documentation
walk_access_paths.h
Go to the documentation of this file.
1/* Copyright (c) 2020, 2025, 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>
257 requires std::is_invocable_r_v<bool, Func, TABLE *>
258void WalkTablesUnderAccessPath(const AccessPath *root_path, Func &&func,
259 bool include_pruned_tables) {
261 root_path, /*join=*/nullptr,
263 [&](const AccessPath *path, const JOIN *) {
264 switch (path->type) {
265 case AccessPath::TABLE_SCAN:
266 return func(path->table_scan().table);
267 case AccessPath::SAMPLE_SCAN:
268 // SampleScan can be executed only in the secondary engine.
269 return false; /* LCOV_EXCL_LINE */
270 case AccessPath::INDEX_SCAN:
271 return func(path->index_scan().table);
272 case AccessPath::INDEX_DISTANCE_SCAN:
273 return func(path->index_distance_scan().table);
274 case AccessPath::REF:
275 return func(path->ref().table);
276 case AccessPath::REF_OR_NULL:
277 return func(path->ref_or_null().table);
278 case AccessPath::EQ_REF:
279 return func(path->eq_ref().table);
280 case AccessPath::PUSHED_JOIN_REF:
281 return func(path->pushed_join_ref().table);
282 case AccessPath::FULL_TEXT_SEARCH:
283 return func(path->full_text_search().table);
284 case AccessPath::CONST_TABLE:
285 return func(path->const_table().table);
286 case AccessPath::MRR:
287 return func(path->mrr().table);
288 case AccessPath::FOLLOW_TAIL:
289 return func(path->follow_tail().table);
290 case AccessPath::INDEX_RANGE_SCAN:
291 return func(path->index_range_scan().used_key_part[0].field->table);
292 case AccessPath::INDEX_SKIP_SCAN:
293 return func(path->index_skip_scan().table);
294 case AccessPath::GROUP_INDEX_SKIP_SCAN:
295 return func(path->group_index_skip_scan().table);
296 case AccessPath::DYNAMIC_INDEX_RANGE_SCAN:
297 return func(path->dynamic_index_range_scan().table);
298 case AccessPath::STREAM:
299 return func(path->stream().table);
300 case AccessPath::MATERIALIZED_TABLE_FUNCTION:
301 return func(path->materialized_table_function().table);
302 case AccessPath::ALTERNATIVE:
303 return func(
304 path->alternative().table_scan_path->table_scan().table);
305 case AccessPath::UNQUALIFIED_COUNT:
306 // Should never be below anything that needs
307 // WalkTablesUnderAccessPath().
308 assert(false);
309 return true;
310 case AccessPath::ZERO_ROWS:
311 if (include_pruned_tables && path->zero_rows().child != nullptr) {
312 WalkTablesUnderAccessPath(path->zero_rows().child, func,
313 include_pruned_tables);
314 }
315 return false;
317 return func(path->window().temp_table);
332 case AccessPath::SORT:
342 return false;
343 }
344 assert(false);
345 return true;
346 });
347}
348
349#endif // SQL_JOIN_OPTIMIZER_WALK_ACCESS_PATHS_H
Definition: sql_optimizer.h:133
static char * path
Definition: mysqldump.cc:150
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:238
@ FOLLOW_TAIL
Definition: access_path.h:254
@ FILTER
Definition: access_path.h:278
@ PUSHED_JOIN_REF
Definition: access_path.h:250
@ ZERO_ROWS_AGGREGATED
Definition: access_path.h:267
@ UPDATE_ROWS
Definition: access_path.h:296
@ AGGREGATE
Definition: access_path.h:280
@ BKA_JOIN
Definition: access_path.h:274
@ ZERO_ROWS
Definition: access_path.h:266
@ CONST_TABLE
Definition: access_path.h:252
@ GROUP_INDEX_SKIP_SCAN
Definition: access_path.h:260
@ SAMPLE_SCAN
Definition: access_path.h:244
@ INDEX_RANGE_SCAN
Definition: access_path.h:255
@ UNQUALIFIED_COUNT
Definition: access_path.h:269
@ EQ_REF
Definition: access_path.h:249
@ FAKE_SINGLE_ROW
Definition: access_path.h:265
@ MATERIALIZE_INFORMATION_SCHEMA_TABLE
Definition: access_path.h:285
@ WINDOW
Definition: access_path.h:287
@ REF_OR_NULL
Definition: access_path.h:248
@ MATERIALIZE
Definition: access_path.h:284
@ NESTED_LOOP_SEMIJOIN_WITH_DUPLICATE_REMOVAL
Definition: access_path.h:273
@ ROWID_UNION
Definition: access_path.h:258
@ INDEX_SKIP_SCAN
Definition: access_path.h:259
@ MRR
Definition: access_path.h:253
@ CACHE_INVALIDATOR
Definition: access_path.h:292
@ INDEX_SCAN
Definition: access_path.h:245
@ TABLE_VALUE_CONSTRUCTOR
Definition: access_path.h:264
@ WEEDOUT
Definition: access_path.h:288
@ MATERIALIZED_TABLE_FUNCTION
Definition: access_path.h:268
@ REMOVE_DUPLICATES_ON_INDEX
Definition: access_path.h:290
@ TABLE_SCAN
Definition: access_path.h:243
@ REF
Definition: access_path.h:247
@ TEMPTABLE_AGGREGATE
Definition: access_path.h:281
@ LIMIT_OFFSET
Definition: access_path.h:282
@ APPEND
Definition: access_path.h:286
@ NESTED_LOOP_JOIN
Definition: access_path.h:272
@ INDEX_MERGE
Definition: access_path.h:256
@ FULL_TEXT_SEARCH
Definition: access_path.h:251
@ ALTERNATIVE
Definition: access_path.h:291
@ STREAM
Definition: access_path.h:283
@ REMOVE_DUPLICATES
Definition: access_path.h:289
@ ROWID_INTERSECTION
Definition: access_path.h:257
@ DYNAMIC_INDEX_RANGE_SCAN
Definition: access_path.h:261
@ DELETE_ROWS
Definition: access_path.h:295
@ SORT
Definition: access_path.h:279
@ INDEX_DISTANCE_SCAN
Definition: access_path.h:246
@ HASH_JOIN
Definition: access_path.h:275
Definition: access_path.h:190
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 WalkTablesUnderAccessPath(const 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:258
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