1 | /* |
2 | Copyright (c) 2013 Monty Program Ab |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ |
16 | |
17 | /* |
18 | |
19 | == EXPLAIN/ANALYZE architecture == |
20 | |
21 | === [SHOW] EXPLAIN data === |
22 | Query optimization produces two data structures: |
23 | 1. execution data structures themselves (eg. JOINs, JOIN_TAB, etc, etc) |
24 | 2. Explain data structures. |
25 | |
26 | #2 are self contained set of data structures that has sufficient info to |
27 | produce output of SHOW EXPLAIN, EXPLAIN [FORMAT=JSON], or |
28 | ANALYZE [FORMAT=JSON], without accessing the execution data structures. |
29 | |
30 | (the only exception is that Explain data structures keep Item* pointers, |
31 | and we require that one might call item->print(QT_EXPLAIN) when printing |
32 | FORMAT=JSON output) |
33 | |
34 | === ANALYZE data === |
35 | EXPLAIN data structures have embedded ANALYZE data structures. These are |
36 | objects that are used to track how the parts of query plan were executed: |
37 | how many times each part of query plan was invoked, how many rows were |
38 | read/returned, etc. |
39 | |
40 | Each execution data structure keeps a direct pointer to its ANALYZE data |
41 | structure. It is needed so that execution code can quickly increment the |
42 | counters. |
43 | |
44 | (note that this increases the set of data that is frequently accessed |
45 | during the execution. What is the impact of this?) |
46 | |
47 | Since ANALYZE/EXPLAIN data structures are separated from execution data |
48 | structures, it is easy to have them survive until the end of the query, |
49 | where we can return ANALYZE [FORMAT=JSON] output to the user, or print |
50 | it into the slow query log. |
51 | |
52 | */ |
53 | |
54 | #ifndef SQL_EXPLAIN_INCLUDED |
55 | #define SQL_EXPLAIN_INCLUDED |
56 | |
57 | class String_list: public List<char> |
58 | { |
59 | public: |
60 | const char *append_str(MEM_ROOT *mem_root, const char *str); |
61 | }; |
62 | |
63 | class Json_writer; |
64 | |
65 | /************************************************************************************** |
66 | |
67 | Data structures for producing EXPLAIN outputs. |
68 | |
69 | These structures |
70 | - Can be produced inexpensively from query plan. |
71 | - Store sufficient information to produce tabular EXPLAIN output (the goal is |
72 | to be able to produce JSON also) |
73 | |
74 | *************************************************************************************/ |
75 | |
76 | |
77 | const int FAKE_SELECT_LEX_ID= (int)UINT_MAX; |
78 | |
79 | class Explain_query; |
80 | |
81 | /* |
82 | A node can be either a SELECT, or a UNION. |
83 | */ |
84 | class Explain_node : public Sql_alloc |
85 | { |
86 | public: |
87 | Explain_node(MEM_ROOT *root) : |
88 | cache_tracker(NULL), |
89 | connection_type(EXPLAIN_NODE_OTHER), |
90 | children(root) |
91 | {} |
92 | /* A type specifying what kind of node this is */ |
93 | enum explain_node_type |
94 | { |
95 | EXPLAIN_UNION, |
96 | EXPLAIN_SELECT, |
97 | EXPLAIN_BASIC_JOIN, |
98 | EXPLAIN_UPDATE, |
99 | EXPLAIN_DELETE, |
100 | EXPLAIN_INSERT |
101 | }; |
102 | |
103 | /* How this node is connected */ |
104 | enum explain_connection_type { |
105 | EXPLAIN_NODE_OTHER, |
106 | EXPLAIN_NODE_DERIVED, /* Materialized derived table */ |
107 | EXPLAIN_NODE_NON_MERGED_SJ /* aka JTBM semi-join */ |
108 | }; |
109 | |
110 | virtual enum explain_node_type get_type()= 0; |
111 | virtual int get_select_id()= 0; |
112 | |
113 | /** |
114 | expression cache statistics |
115 | */ |
116 | Expression_cache_tracker* cache_tracker; |
117 | |
118 | /* |
119 | How this node is connected to its parent. |
120 | (NOTE: EXPLAIN_NODE_NON_MERGED_SJ is set very late currently) |
121 | */ |
122 | enum explain_connection_type connection_type; |
123 | |
124 | /* |
125 | A node may have children nodes. When a node's explain structure is |
126 | created, children nodes may not yet have QPFs. This is why we store ids. |
127 | */ |
128 | Dynamic_array<int> children; |
129 | void add_child(int select_no) |
130 | { |
131 | children.append(select_no); |
132 | } |
133 | |
134 | virtual int print_explain(Explain_query *query, select_result_sink *output, |
135 | uint8 explain_flags, bool is_analyze)=0; |
136 | virtual void print_explain_json(Explain_query *query, Json_writer *writer, |
137 | bool is_analyze)= 0; |
138 | |
139 | int print_explain_for_children(Explain_query *query, select_result_sink *output, |
140 | uint8 explain_flags, bool is_analyze); |
141 | void print_explain_json_for_children(Explain_query *query, |
142 | Json_writer *writer, bool is_analyze); |
143 | bool print_explain_json_cache(Json_writer *writer, bool is_analyze); |
144 | virtual ~Explain_node(){} |
145 | }; |
146 | |
147 | |
148 | class Explain_table_access; |
149 | |
150 | |
151 | /* |
152 | A basic join. This is only used for SJ-Materialization nests. |
153 | |
154 | Basic join doesn't have ORDER/GROUP/DISTINCT operations. It also cannot be |
155 | degenerate. |
156 | |
157 | It has its own select_id. |
158 | */ |
159 | class Explain_basic_join : public Explain_node |
160 | { |
161 | public: |
162 | enum explain_node_type get_type() { return EXPLAIN_BASIC_JOIN; } |
163 | |
164 | Explain_basic_join(MEM_ROOT *root) : Explain_node(root), join_tabs(NULL) {} |
165 | ~Explain_basic_join(); |
166 | |
167 | bool add_table(Explain_table_access *tab, Explain_query *query); |
168 | |
169 | int get_select_id() { return select_id; } |
170 | |
171 | int select_id; |
172 | |
173 | int print_explain(Explain_query *query, select_result_sink *output, |
174 | uint8 explain_flags, bool is_analyze); |
175 | void print_explain_json(Explain_query *query, Json_writer *writer, |
176 | bool is_analyze); |
177 | |
178 | void print_explain_json_interns(Explain_query *query, Json_writer *writer, |
179 | bool is_analyze); |
180 | |
181 | /* A flat array of Explain structs for tables. */ |
182 | Explain_table_access** join_tabs; |
183 | uint n_join_tabs; |
184 | }; |
185 | |
186 | |
187 | class Explain_aggr_node; |
188 | /* |
189 | EXPLAIN structure for a SELECT. |
190 | |
191 | A select can be: |
192 | 1. A degenerate case. In this case, message!=NULL, and it contains a |
193 | description of what kind of degenerate case it is (e.g. "Impossible |
194 | WHERE"). |
195 | 2. a non-degenrate join. In this case, join_tabs describes the join. |
196 | |
197 | In the non-degenerate case, a SELECT may have a GROUP BY/ORDER BY operation. |
198 | |
199 | In both cases, the select may have children nodes. class Explain_node |
200 | provides a way get node's children. |
201 | */ |
202 | |
203 | class Explain_select : public Explain_basic_join |
204 | { |
205 | public: |
206 | enum explain_node_type get_type() { return EXPLAIN_SELECT; } |
207 | |
208 | Explain_select(MEM_ROOT *root, bool is_analyze) : |
209 | Explain_basic_join(root), |
210 | #ifndef DBUG_OFF |
211 | select_lex(NULL), |
212 | #endif |
213 | linkage(UNSPECIFIED_TYPE), |
214 | message(NULL), |
215 | having(NULL), having_value(Item::COND_UNDEF), |
216 | using_temporary(false), using_filesort(false), |
217 | time_tracker(is_analyze), |
218 | aggr_tree(NULL) |
219 | {} |
220 | |
221 | void add_linkage(Json_writer *writer); |
222 | |
223 | public: |
224 | #ifndef DBUG_OFF |
225 | SELECT_LEX *select_lex; |
226 | #endif |
227 | const char *select_type; |
228 | enum sub_select_type linkage; |
229 | |
230 | /* |
231 | If message != NULL, this is a degenerate join plan, and all subsequent |
232 | members have no info |
233 | */ |
234 | const char *message; |
235 | |
236 | /* Expensive constant condition */ |
237 | Item *exec_const_cond; |
238 | Item *outer_ref_cond; |
239 | |
240 | /* HAVING condition */ |
241 | Item *having; |
242 | Item::cond_result having_value; |
243 | |
244 | /* Global join attributes. In tabular form, they are printed on the first row */ |
245 | bool using_temporary; |
246 | bool using_filesort; |
247 | |
248 | /* ANALYZE members */ |
249 | Time_and_counter_tracker time_tracker; |
250 | |
251 | /* |
252 | Part of query plan describing sorting, temp.table usage, and duplicate |
253 | removal |
254 | */ |
255 | Explain_aggr_node* aggr_tree; |
256 | |
257 | int print_explain(Explain_query *query, select_result_sink *output, |
258 | uint8 explain_flags, bool is_analyze); |
259 | void print_explain_json(Explain_query *query, Json_writer *writer, |
260 | bool is_analyze); |
261 | |
262 | Table_access_tracker *get_using_temporary_read_tracker() |
263 | { |
264 | return &using_temporary_read_tracker; |
265 | } |
266 | private: |
267 | Table_access_tracker using_temporary_read_tracker; |
268 | }; |
269 | |
270 | ///////////////////////////////////////////////////////////////////////////// |
271 | // EXPLAIN structures for ORDER/GROUP operations. |
272 | ///////////////////////////////////////////////////////////////////////////// |
273 | typedef enum |
274 | { |
275 | AGGR_OP_TEMP_TABLE, |
276 | AGGR_OP_FILESORT, |
277 | //AGGR_OP_READ_SORTED_FILE, // need this? |
278 | AGGR_OP_REMOVE_DUPLICATES, |
279 | AGGR_OP_WINDOW_FUNCS |
280 | //AGGR_OP_JOIN // Need this? |
281 | } enum_explain_aggr_node_type; |
282 | |
283 | |
284 | class Explain_aggr_node : public Sql_alloc |
285 | { |
286 | public: |
287 | virtual enum_explain_aggr_node_type get_type()= 0; |
288 | virtual ~Explain_aggr_node() {} |
289 | Explain_aggr_node *child; |
290 | }; |
291 | |
292 | class Explain_aggr_filesort : public Explain_aggr_node |
293 | { |
294 | List<Item> sort_items; |
295 | List<ORDER::enum_order> sort_directions; |
296 | public: |
297 | enum_explain_aggr_node_type get_type() { return AGGR_OP_FILESORT; } |
298 | Filesort_tracker tracker; |
299 | |
300 | Explain_aggr_filesort(MEM_ROOT *mem_root, bool is_analyze, |
301 | Filesort *filesort); |
302 | |
303 | void print_json_members(Json_writer *writer, bool is_analyze); |
304 | }; |
305 | |
306 | class Explain_aggr_tmp_table : public Explain_aggr_node |
307 | { |
308 | public: |
309 | enum_explain_aggr_node_type get_type() { return AGGR_OP_TEMP_TABLE; } |
310 | }; |
311 | |
312 | class Explain_aggr_remove_dups : public Explain_aggr_node |
313 | { |
314 | public: |
315 | enum_explain_aggr_node_type get_type() { return AGGR_OP_REMOVE_DUPLICATES; } |
316 | }; |
317 | |
318 | class Explain_aggr_window_funcs : public Explain_aggr_node |
319 | { |
320 | List<Explain_aggr_filesort> sorts; |
321 | public: |
322 | enum_explain_aggr_node_type get_type() { return AGGR_OP_WINDOW_FUNCS; } |
323 | |
324 | void print_json_members(Json_writer *writer, bool is_analyze); |
325 | friend class Window_funcs_computation; |
326 | }; |
327 | |
328 | ///////////////////////////////////////////////////////////////////////////// |
329 | |
330 | extern const char *unit_operation_text[4]; |
331 | |
332 | /* |
333 | Explain structure for a UNION. |
334 | |
335 | A UNION may or may not have "Using filesort". |
336 | */ |
337 | |
338 | class Explain_union : public Explain_node |
339 | { |
340 | public: |
341 | Explain_union(MEM_ROOT *root, bool is_analyze) : |
342 | Explain_node(root), |
343 | is_recursive_cte(false), |
344 | fake_select_lex_explain(root, is_analyze) |
345 | {} |
346 | |
347 | enum explain_node_type get_type() { return EXPLAIN_UNION; } |
348 | unit_common_op operation; |
349 | |
350 | int get_select_id() |
351 | { |
352 | DBUG_ASSERT(union_members.elements() > 0); |
353 | return union_members.at(0); |
354 | } |
355 | /* |
356 | Members of the UNION. Note: these are different from UNION's "children". |
357 | Example: |
358 | |
359 | (select * from t1) union |
360 | (select * from t2) order by (select col1 from t3 ...) |
361 | |
362 | here |
363 | - select-from-t1 and select-from-t2 are "union members", |
364 | - select-from-t3 is the only "child". |
365 | */ |
366 | Dynamic_array<int> union_members; |
367 | |
368 | void add_select(int select_no) |
369 | { |
370 | union_members.append(select_no); |
371 | } |
372 | int print_explain(Explain_query *query, select_result_sink *output, |
373 | uint8 explain_flags, bool is_analyze); |
374 | void print_explain_json(Explain_query *query, Json_writer *writer, |
375 | bool is_analyze); |
376 | |
377 | const char *fake_select_type; |
378 | bool using_filesort; |
379 | bool using_tmp; |
380 | bool is_recursive_cte; |
381 | |
382 | /* |
383 | Explain data structure for "fake_select_lex" (i.e. for the degenerate |
384 | SELECT that reads UNION result). |
385 | It doesn't have a query plan, but we still need execution tracker, etc. |
386 | */ |
387 | Explain_select fake_select_lex_explain; |
388 | |
389 | Table_access_tracker *get_fake_select_lex_tracker() |
390 | { |
391 | return &fake_select_lex_tracker; |
392 | } |
393 | Table_access_tracker *get_tmptable_read_tracker() |
394 | { |
395 | return &tmptable_read_tracker; |
396 | } |
397 | private: |
398 | uint make_union_table_name(char *buf); |
399 | |
400 | Table_access_tracker fake_select_lex_tracker; |
401 | /* This one is for reading after ORDER BY */ |
402 | Table_access_tracker tmptable_read_tracker; |
403 | }; |
404 | |
405 | |
406 | class Explain_update; |
407 | class Explain_delete; |
408 | class Explain_insert; |
409 | |
410 | |
411 | /* |
412 | Explain structure for a query (i.e. a statement). |
413 | |
414 | This should be able to survive when the query plan was deleted. Currently, |
415 | we do not intend for it survive until after query's MEM_ROOT is freed. It |
416 | does surivive freeing of query's items. |
417 | |
418 | For reference, the process of post-query cleanup is as follows: |
419 | |
420 | >dispatch_command |
421 | | >mysql_parse |
422 | | | ... |
423 | | | lex_end() |
424 | | | ... |
425 | | | >THD::cleanup_after_query |
426 | | | | ... |
427 | | | | free_items() |
428 | | | | ... |
429 | | | <THD::cleanup_after_query |
430 | | | |
431 | | <mysql_parse |
432 | | |
433 | | log_slow_statement() |
434 | | |
435 | | free_root() |
436 | | |
437 | >dispatch_command |
438 | |
439 | That is, the order of actions is: |
440 | - free query's Items |
441 | - write to slow query log |
442 | - free query's MEM_ROOT |
443 | |
444 | */ |
445 | |
446 | class Explain_query : public Sql_alloc |
447 | { |
448 | public: |
449 | Explain_query(THD *thd, MEM_ROOT *root); |
450 | ~Explain_query(); |
451 | |
452 | /* Add a new node */ |
453 | void add_node(Explain_node *node); |
454 | void add_insert_plan(Explain_insert *insert_plan_arg); |
455 | void add_upd_del_plan(Explain_update *upd_del_plan_arg); |
456 | |
457 | /* This will return a select, or a union */ |
458 | Explain_node *get_node(uint select_id); |
459 | |
460 | /* This will return a select (even if there is a union with this id) */ |
461 | Explain_select *get_select(uint select_id); |
462 | |
463 | Explain_union *get_union(uint select_id); |
464 | |
465 | /* Produce a tabular EXPLAIN output */ |
466 | int print_explain(select_result_sink *output, uint8 explain_flags, |
467 | bool is_analyze); |
468 | |
469 | /* Send tabular EXPLAIN to the client */ |
470 | int send_explain(THD *thd); |
471 | |
472 | /* Return tabular EXPLAIN output as a text string */ |
473 | bool print_explain_str(THD *thd, String *out_str, bool is_analyze); |
474 | |
475 | void print_explain_json(select_result_sink *output, bool is_analyze); |
476 | |
477 | /* If true, at least part of EXPLAIN can be printed */ |
478 | bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; } |
479 | |
480 | void query_plan_ready(); |
481 | |
482 | MEM_ROOT *mem_root; |
483 | |
484 | Explain_update *get_upd_del_plan() { return upd_del_plan; } |
485 | private: |
486 | /* Explain_delete inherits from Explain_update */ |
487 | Explain_update *upd_del_plan; |
488 | |
489 | /* Query "plan" for INSERTs */ |
490 | Explain_insert *insert_plan; |
491 | |
492 | Dynamic_array<Explain_union*> unions; |
493 | Dynamic_array<Explain_select*> selects; |
494 | |
495 | THD *thd; // for APC start/stop |
496 | bool apc_enabled; |
497 | /* |
498 | Debugging aid: count how many times add_node() was called. Ideally, it |
499 | should be one, we currently allow O(1) query plan saves for each |
500 | select or union. The goal is not to have O(#rows_in_some_table), which |
501 | is unacceptable. |
502 | */ |
503 | longlong operations; |
504 | }; |
505 | |
506 | |
507 | /* |
508 | Some of the tags have matching text. See extra_tag_text for text names, and |
509 | Explain_table_access::append_tag_name() for code to convert from tag form to text |
510 | form. |
511 | */ |
512 | enum |
513 | { |
514 | ET_none= 0, /* not-a-tag */ |
515 | ET_USING_INDEX_CONDITION, |
516 | ET_USING_INDEX_CONDITION_BKA, |
517 | ET_USING, /* For quick selects of various kinds */ |
518 | ET_RANGE_CHECKED_FOR_EACH_RECORD, |
519 | ET_USING_WHERE_WITH_PUSHED_CONDITION, |
520 | ET_USING_WHERE, |
521 | ET_NOT_EXISTS, |
522 | |
523 | ET_USING_INDEX, |
524 | ET_FULL_SCAN_ON_NULL_KEY, |
525 | ET_SKIP_OPEN_TABLE, |
526 | ET_OPEN_FRM_ONLY, |
527 | ET_OPEN_FULL_TABLE, |
528 | |
529 | ET_SCANNED_0_DATABASES, |
530 | ET_SCANNED_1_DATABASE, |
531 | ET_SCANNED_ALL_DATABASES, |
532 | |
533 | ET_USING_INDEX_FOR_GROUP_BY, |
534 | |
535 | ET_USING_MRR, // does not print "Using mrr". |
536 | |
537 | ET_DISTINCT, |
538 | ET_LOOSESCAN, |
539 | ET_START_TEMPORARY, |
540 | ET_END_TEMPORARY, |
541 | ET_FIRST_MATCH, |
542 | |
543 | ET_USING_JOIN_BUFFER, |
544 | |
545 | ET_CONST_ROW_NOT_FOUND, |
546 | ET_UNIQUE_ROW_NOT_FOUND, |
547 | ET_IMPOSSIBLE_ON_CONDITION, |
548 | |
549 | ET_total |
550 | }; |
551 | |
552 | |
553 | /* |
554 | Explain data structure describing join buffering use. |
555 | */ |
556 | |
557 | class EXPLAIN_BKA_TYPE |
558 | { |
559 | public: |
560 | EXPLAIN_BKA_TYPE() : join_alg(NULL) {} |
561 | |
562 | size_t join_buffer_size; |
563 | |
564 | bool incremental; |
565 | |
566 | /* |
567 | NULL if no join buferring used. |
568 | Other values: BNL, BNLH, BKA, BKAH. |
569 | */ |
570 | const char *join_alg; |
571 | |
572 | /* Information about MRR usage. */ |
573 | StringBuffer<64> mrr_type; |
574 | |
575 | bool is_using_jbuf() { return (join_alg != NULL); } |
576 | }; |
577 | |
578 | |
579 | /* |
580 | Data about how an index is used by some access method |
581 | */ |
582 | class Explain_index_use : public Sql_alloc |
583 | { |
584 | char *key_name; |
585 | uint key_len; |
586 | public: |
587 | String_list key_parts_list; |
588 | |
589 | Explain_index_use() |
590 | { |
591 | clear(); |
592 | } |
593 | |
594 | void clear() |
595 | { |
596 | key_name= NULL; |
597 | key_len= (uint)-1; |
598 | } |
599 | bool set(MEM_ROOT *root, KEY *key_name, uint key_len_arg); |
600 | bool set_pseudo_key(MEM_ROOT *root, const char *key_name); |
601 | |
602 | inline const char *get_key_name() const { return key_name; } |
603 | inline uint get_key_len() const { return key_len; } |
604 | }; |
605 | |
606 | |
607 | /* |
608 | QPF for quick range selects, as well as index_merge select |
609 | */ |
610 | class Explain_quick_select : public Sql_alloc |
611 | { |
612 | public: |
613 | Explain_quick_select(int quick_type_arg) : quick_type(quick_type_arg) |
614 | {} |
615 | |
616 | const int quick_type; |
617 | |
618 | bool is_basic() |
619 | { |
620 | return (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE || |
621 | quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC || |
622 | quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX); |
623 | } |
624 | |
625 | /* This is used when quick_type == QUICK_SELECT_I::QS_TYPE_RANGE */ |
626 | Explain_index_use range; |
627 | |
628 | /* Used in all other cases */ |
629 | List<Explain_quick_select> children; |
630 | |
631 | void (String *str); |
632 | void print_key(String *str); |
633 | void print_key_len(String *str); |
634 | |
635 | void print_json(Json_writer *writer); |
636 | |
637 | void (String *str); |
638 | private: |
639 | const char *get_name_by_type(); |
640 | }; |
641 | |
642 | |
643 | /* |
644 | Data structure for "range checked for each record". |
645 | It's a set of keys, tabular explain prints hex bitmap, json prints key names. |
646 | */ |
647 | |
648 | typedef const char* NAME; |
649 | |
650 | class Explain_range_checked_fer : public Sql_alloc |
651 | { |
652 | public: |
653 | String_list key_set; |
654 | key_map keys_map; |
655 | private: |
656 | ha_rows full_scan, index_merge; |
657 | ha_rows *keys_stat; |
658 | NAME *keys_stat_names; |
659 | uint keys; |
660 | |
661 | public: |
662 | Explain_range_checked_fer() |
663 | :Sql_alloc(), full_scan(0), index_merge(0), |
664 | keys_stat(0), keys_stat_names(0), keys(0) |
665 | {} |
666 | |
667 | int append_possible_keys_stat(MEM_ROOT *alloc, |
668 | TABLE *table, key_map possible_keys); |
669 | void collect_data(QUICK_SELECT_I *quick); |
670 | void print_json(Json_writer *writer, bool is_analyze); |
671 | }; |
672 | |
673 | /* |
674 | EXPLAIN data structure for a single JOIN_TAB. |
675 | */ |
676 | |
677 | class Explain_table_access : public Sql_alloc |
678 | { |
679 | public: |
680 | Explain_table_access(MEM_ROOT *root) : |
681 | derived_select_number(0), |
682 | non_merged_sjm_number(0), |
683 | extra_tags(root), |
684 | range_checked_fer(NULL), |
685 | full_scan_on_null_key(false), |
686 | start_dups_weedout(false), |
687 | end_dups_weedout(false), |
688 | where_cond(NULL), |
689 | cache_cond(NULL), |
690 | pushed_index_cond(NULL), |
691 | sjm_nest(NULL), |
692 | pre_join_sort(NULL) |
693 | {} |
694 | ~Explain_table_access() { delete sjm_nest; } |
695 | |
696 | void (enum explain_extra_tag ); |
697 | |
698 | /* Internals */ |
699 | |
700 | /* id and 'select_type' are cared-of by the parent Explain_select */ |
701 | StringBuffer<32> table_name; |
702 | StringBuffer<32> used_partitions; |
703 | String_list used_partitions_list; |
704 | // valid with ET_USING_MRR |
705 | StringBuffer<32> mrr_type; |
706 | StringBuffer<32> firstmatch_table_name; |
707 | |
708 | /* |
709 | Non-zero number means this is a derived table. The number can be used to |
710 | find the query plan for the derived table |
711 | */ |
712 | int derived_select_number; |
713 | /* TODO: join with the previous member. */ |
714 | int non_merged_sjm_number; |
715 | |
716 | enum join_type type; |
717 | |
718 | bool used_partitions_set; |
719 | |
720 | /* Empty means "NULL" will be printed */ |
721 | String_list possible_keys; |
722 | |
723 | bool rows_set; /* not set means 'NULL' should be printed */ |
724 | bool filtered_set; /* not set means 'NULL' should be printed */ |
725 | // Valid if ET_USING_INDEX_FOR_GROUP_BY is present |
726 | bool loose_scan_is_scanning; |
727 | |
728 | /* |
729 | Index use: key name and length. |
730 | Note: that when one is accessing I_S tables, those may show use of |
731 | non-existant indexes. |
732 | |
733 | key.key_name == NULL means 'NULL' will be shown in tabular output. |
734 | key.key_len == (uint)-1 means 'NULL' will be shown in tabular output. |
735 | */ |
736 | Explain_index_use key; |
737 | |
738 | /* |
739 | when type==JT_HASH_NEXT, 'key' stores the hash join pseudo-key. |
740 | hash_next_key stores the table's key. |
741 | */ |
742 | Explain_index_use hash_next_key; |
743 | |
744 | String_list ref_list; |
745 | |
746 | ha_rows rows; |
747 | double filtered; |
748 | |
749 | /* |
750 | Contents of the 'Extra' column. Some are converted into strings, some have |
751 | parameters, values for which are stored below. |
752 | */ |
753 | Dynamic_array<enum explain_extra_tag> ; |
754 | |
755 | // Valid if ET_USING tag is present |
756 | Explain_quick_select *quick_info; |
757 | |
758 | /* Non-NULL value means this tab uses "range checked for each record" */ |
759 | Explain_range_checked_fer *range_checked_fer; |
760 | |
761 | bool full_scan_on_null_key; |
762 | |
763 | // valid with ET_USING_JOIN_BUFFER |
764 | EXPLAIN_BKA_TYPE bka_type; |
765 | |
766 | bool start_dups_weedout; |
767 | bool end_dups_weedout; |
768 | |
769 | /* |
770 | Note: lifespan of WHERE condition is less than lifespan of this object. |
771 | The below two are valid if tags include "ET_USING_WHERE". |
772 | (TODO: indexsubquery may put ET_USING_WHERE without setting where_cond?) |
773 | */ |
774 | Item *where_cond; |
775 | Item *cache_cond; |
776 | |
777 | /* |
778 | This is either pushed index condition, or BKA's index condition. |
779 | (the latter refers to columns of other tables and so can only be checked by |
780 | BKA code). Examine extra_tags to tell which one it is. |
781 | */ |
782 | Item *pushed_index_cond; |
783 | |
784 | Explain_basic_join *sjm_nest; |
785 | |
786 | /* |
787 | This describes a possible filesort() call that is done before doing the |
788 | join operation. |
789 | */ |
790 | Explain_aggr_filesort *pre_join_sort; |
791 | |
792 | /* ANALYZE members */ |
793 | |
794 | /* Tracker for reading the table */ |
795 | Table_access_tracker tracker; |
796 | Exec_time_tracker op_tracker; |
797 | Table_access_tracker jbuf_tracker; |
798 | |
799 | int print_explain(select_result_sink *output, uint8 explain_flags, |
800 | bool is_analyze, |
801 | uint select_id, const char *select_type, |
802 | bool using_temporary, bool using_filesort); |
803 | void print_explain_json(Explain_query *query, Json_writer *writer, |
804 | bool is_analyze); |
805 | |
806 | private: |
807 | void (String *str, enum explain_extra_tag tag); |
808 | void fill_key_str(String *key_str, bool is_json) const; |
809 | void fill_key_len_str(String *key_len_str) const; |
810 | double get_r_filtered(); |
811 | void (Json_writer *writer, enum explain_extra_tag tag); |
812 | }; |
813 | |
814 | |
815 | /* |
816 | EXPLAIN structure for single-table UPDATE. |
817 | |
818 | This is similar to Explain_table_access, except that it is more restrictive. |
819 | Also, it can have UPDATE operation options, but currently there aren't any. |
820 | |
821 | Explain_delete inherits from this. |
822 | */ |
823 | |
824 | class Explain_update : public Explain_node |
825 | { |
826 | public: |
827 | |
828 | Explain_update(MEM_ROOT *root, bool is_analyze) : |
829 | Explain_node(root), |
830 | filesort_tracker(NULL), |
831 | command_tracker(is_analyze) |
832 | {} |
833 | |
834 | virtual enum explain_node_type get_type() { return EXPLAIN_UPDATE; } |
835 | virtual int get_select_id() { return 1; /* always root */ } |
836 | |
837 | const char *select_type; |
838 | |
839 | StringBuffer<32> used_partitions; |
840 | String_list used_partitions_list; |
841 | bool used_partitions_set; |
842 | |
843 | bool impossible_where; |
844 | bool no_partitions; |
845 | StringBuffer<64> table_name; |
846 | |
847 | enum join_type jtype; |
848 | String_list possible_keys; |
849 | |
850 | /* Used key when doing a full index scan (possibly with limit) */ |
851 | Explain_index_use key; |
852 | |
853 | /* |
854 | MRR that's used with quick select. This should probably belong to the |
855 | quick select |
856 | */ |
857 | StringBuffer<64> mrr_type; |
858 | |
859 | Explain_quick_select *quick_info; |
860 | |
861 | bool using_where; |
862 | Item *where_cond; |
863 | |
864 | ha_rows rows; |
865 | |
866 | bool using_io_buffer; |
867 | |
868 | /* Tracker for doing reads when filling the buffer */ |
869 | Table_access_tracker buf_tracker; |
870 | |
871 | bool is_using_filesort() { return filesort_tracker? true: false; } |
872 | /* |
873 | Non-null value of filesort_tracker means "using filesort" |
874 | |
875 | if we are using filesort, then table_tracker is for the io done inside |
876 | filesort. |
877 | |
878 | 'tracker' is for tracking post-filesort reads. |
879 | */ |
880 | Filesort_tracker *filesort_tracker; |
881 | |
882 | /* ANALYZE members and methods */ |
883 | Table_access_tracker tracker; |
884 | |
885 | /* This tracks execution of the whole command */ |
886 | Time_and_counter_tracker command_tracker; |
887 | |
888 | /* TODO: This tracks time to read rows from the table */ |
889 | Exec_time_tracker table_tracker; |
890 | |
891 | virtual int print_explain(Explain_query *query, select_result_sink *output, |
892 | uint8 explain_flags, bool is_analyze); |
893 | virtual void print_explain_json(Explain_query *query, Json_writer *writer, |
894 | bool is_analyze); |
895 | }; |
896 | |
897 | |
898 | /* |
899 | EXPLAIN data structure for an INSERT. |
900 | |
901 | At the moment this doesn't do much as we don't really have any query plans |
902 | for INSERT statements. |
903 | */ |
904 | |
905 | class Explain_insert : public Explain_node |
906 | { |
907 | public: |
908 | Explain_insert(MEM_ROOT *root) : |
909 | Explain_node(root) |
910 | {} |
911 | |
912 | StringBuffer<64> table_name; |
913 | |
914 | enum explain_node_type get_type() { return EXPLAIN_INSERT; } |
915 | int get_select_id() { return 1; /* always root */ } |
916 | |
917 | int print_explain(Explain_query *query, select_result_sink *output, |
918 | uint8 explain_flags, bool is_analyze); |
919 | void print_explain_json(Explain_query *query, Json_writer *writer, |
920 | bool is_analyze); |
921 | }; |
922 | |
923 | |
924 | /* |
925 | EXPLAIN data of a single-table DELETE. |
926 | */ |
927 | |
928 | class Explain_delete: public Explain_update |
929 | { |
930 | public: |
931 | Explain_delete(MEM_ROOT *root, bool is_analyze) : |
932 | Explain_update(root, is_analyze) |
933 | {} |
934 | |
935 | /* |
936 | TRUE means we're going to call handler->delete_all_rows() and not read any |
937 | rows. |
938 | */ |
939 | bool deleting_all_rows; |
940 | |
941 | virtual enum explain_node_type get_type() { return EXPLAIN_DELETE; } |
942 | virtual int get_select_id() { return 1; /* always root */ } |
943 | |
944 | virtual int print_explain(Explain_query *query, select_result_sink *output, |
945 | uint8 explain_flags, bool is_analyze); |
946 | virtual void print_explain_json(Explain_query *query, Json_writer *writer, |
947 | bool is_analyze); |
948 | }; |
949 | |
950 | |
951 | #endif //SQL_EXPLAIN_INCLUDED |
952 | |