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#ifdef USE_PRAGMA_IMPLEMENTATION
18#pragma implementation // gcc: Class implementation
19#endif
20
21#include "mariadb.h"
22#include "sql_priv.h"
23#include "sql_select.h"
24#include "my_json_writer.h"
25#include "opt_range.h"
26#include "sql_expression_cache.h"
27
28const char * STR_DELETING_ALL_ROWS= "Deleting all rows";
29const char * STR_IMPOSSIBLE_WHERE= "Impossible WHERE";
30const char * STR_NO_ROWS_AFTER_PRUNING= "No matching rows after partition pruning";
31
32const char *unit_operation_text[4]=
33{
34 "UNIT RESULT","UNION RESULT","INTERSECT RESULT","EXCEPT RESULT"
35};
36
37static void write_item(Json_writer *writer, Item *item);
38static void append_item_to_str(String *out, Item *item);
39
40Explain_query::Explain_query(THD *thd_arg, MEM_ROOT *root) :
41 mem_root(root), upd_del_plan(NULL), insert_plan(NULL),
42 unions(root), selects(root), thd(thd_arg), apc_enabled(false),
43 operations(0)
44{
45}
46
47static void print_json_array(Json_writer *writer,
48 const char *title, String_list &list)
49{
50 List_iterator_fast<char> it(list);
51 const char *name;
52 writer->add_member(title).start_array();
53 while ((name= it++))
54 writer->add_str(name);
55 writer->end_array();
56}
57
58
59
60Explain_query::~Explain_query()
61{
62 if (apc_enabled)
63 thd->apc_target.disable();
64
65 delete upd_del_plan;
66 delete insert_plan;
67 uint i;
68 for (i= 0 ; i < unions.elements(); i++)
69 delete unions.at(i);
70 for (i= 0 ; i < selects.elements(); i++)
71 delete selects.at(i);
72}
73
74
75Explain_node *Explain_query::get_node(uint select_id)
76{
77 Explain_union *u;
78 if ((u= get_union(select_id)))
79 return u;
80 else
81 return get_select(select_id);
82}
83
84Explain_union *Explain_query::get_union(uint select_id)
85{
86 return (unions.elements() > select_id) ? unions.at(select_id) : NULL;
87}
88
89Explain_select *Explain_query::get_select(uint select_id)
90{
91 return (selects.elements() > select_id) ? selects.at(select_id) : NULL;
92}
93
94
95void Explain_query::add_node(Explain_node *node)
96{
97 uint select_id;
98 operations++;
99 if (node->get_type() == Explain_node::EXPLAIN_UNION)
100 {
101 Explain_union *u= (Explain_union*)node;
102 select_id= u->get_select_id();
103 if (unions.elements() <= select_id)
104 unions.resize(MY_MAX(select_id+1, unions.elements()*2), NULL);
105
106 Explain_union *old_node;
107 if ((old_node= get_union(select_id)))
108 delete old_node;
109
110 unions.at(select_id)= u;
111 }
112 else
113 {
114 Explain_select *sel= (Explain_select*)node;
115 if (sel->select_id == FAKE_SELECT_LEX_ID)
116 {
117 DBUG_ASSERT(0); // this is a "fake select" from a UNION.
118 }
119 else
120 {
121 select_id= sel->select_id;
122 Explain_select *old_node;
123
124 if (selects.elements() <= select_id)
125 selects.resize(MY_MAX(select_id+1, selects.elements()*2), NULL);
126
127 if ((old_node= get_select(select_id)))
128 delete old_node;
129
130 selects.at(select_id)= sel;
131 }
132 }
133}
134
135
136void Explain_query::add_insert_plan(Explain_insert *insert_plan_arg)
137{
138 insert_plan= insert_plan_arg;
139 query_plan_ready();
140}
141
142
143void Explain_query::add_upd_del_plan(Explain_update *upd_del_plan_arg)
144{
145 upd_del_plan= upd_del_plan_arg;
146 query_plan_ready();
147}
148
149
150void Explain_query::query_plan_ready()
151{
152 if (!apc_enabled)
153 thd->apc_target.enable();
154 apc_enabled= true;
155}
156
157/*
158 Send EXPLAIN output to the client.
159*/
160
161int Explain_query::send_explain(THD *thd)
162{
163 select_result *result;
164 LEX *lex= thd->lex;
165
166 if (!(result= new (thd->mem_root) select_send(thd)) ||
167 thd->send_explain_fields(result, lex->describe, lex->analyze_stmt))
168 return 1;
169
170 int res= 0;
171 if (thd->lex->explain_json)
172 print_explain_json(result, thd->lex->analyze_stmt);
173 else
174 res= print_explain(result, lex->describe, thd->lex->analyze_stmt);
175
176 if (res)
177 result->abort_result_set();
178 else
179 result->send_eof();
180
181 return res;
182}
183
184
185/*
186 The main entry point to print EXPLAIN of the entire query
187*/
188
189int Explain_query::print_explain(select_result_sink *output,
190 uint8 explain_flags, bool is_analyze)
191{
192 if (upd_del_plan)
193 {
194 upd_del_plan->print_explain(this, output, explain_flags, is_analyze);
195 return 0;
196 }
197 else if (insert_plan)
198 {
199 insert_plan->print_explain(this, output, explain_flags, is_analyze);
200 return 0;
201 }
202 else
203 {
204 /* Start printing from node with id=1 */
205 Explain_node *node= get_node(1);
206 if (!node)
207 return 1; /* No query plan */
208 return node->print_explain(this, output, explain_flags, is_analyze);
209 }
210}
211
212
213void Explain_query::print_explain_json(select_result_sink *output,
214 bool is_analyze)
215{
216 Json_writer writer;
217 writer.start_object();
218
219 if (upd_del_plan)
220 upd_del_plan->print_explain_json(this, &writer, is_analyze);
221 else if (insert_plan)
222 insert_plan->print_explain_json(this, &writer, is_analyze);
223 else
224 {
225 /* Start printing from node with id=1 */
226 Explain_node *node= get_node(1);
227 if (!node)
228 return; /* No query plan */
229 node->print_explain_json(this, &writer, is_analyze);
230 }
231
232 writer.end_object();
233
234 CHARSET_INFO *cs= system_charset_info;
235 List<Item> item_list;
236 String *buf= &writer.output;
237 item_list.push_back(new (thd->mem_root)
238 Item_string(thd, buf->ptr(), buf->length(), cs),
239 thd->mem_root);
240 output->send_data(item_list);
241}
242
243
244bool print_explain_for_slow_log(LEX *lex, THD *thd, String *str)
245{
246 return lex->explain->print_explain_str(thd, str, /*is_analyze*/ true);
247}
248
249
250/*
251 Return tabular EXPLAIN output as a text string
252*/
253
254bool Explain_query::print_explain_str(THD *thd, String *out_str,
255 bool is_analyze)
256{
257 List<Item> fields;
258 thd->make_explain_field_list(fields, thd->lex->describe, is_analyze);
259
260 select_result_text_buffer output_buf(thd);
261 output_buf.send_result_set_metadata(fields, thd->lex->describe);
262 if (print_explain(&output_buf, thd->lex->describe, is_analyze))
263 return true;
264 output_buf.save_to(out_str);
265 return false;
266}
267
268
269static void push_str(THD *thd, List<Item> *item_list, const char *str)
270{
271 item_list->push_back(new (thd->mem_root) Item_string_sys(thd, str),
272 thd->mem_root);
273}
274
275
276static void push_string(THD *thd, List<Item> *item_list, String *str)
277{
278 item_list->push_back(new (thd->mem_root)
279 Item_string_sys(thd, str->ptr(), str->length()),
280 thd->mem_root);
281}
282
283static void push_string_list(THD *thd, List<Item> *item_list,
284 String_list &lines, String *buf)
285{
286 List_iterator_fast<char> it(lines);
287 char *line;
288 bool first= true;
289 while ((line= it++))
290 {
291 if (first)
292 first= false;
293 else
294 buf->append(',');
295
296 buf->append(line);
297 }
298 push_string(thd, item_list, buf);
299}
300
301
302/*
303 Print an EXPLAIN output row, based on information provided in the parameters
304
305 @note
306 Parameters that may have NULL value in EXPLAIN output, should be passed
307 (char*)NULL.
308
309 @return
310 0 - OK
311 1 - OOM Error
312*/
313
314static
315int print_explain_row(select_result_sink *result,
316 uint8 options, bool is_analyze,
317 uint select_number,
318 const char *select_type,
319 const char *table_name,
320 const char *partitions,
321 enum join_type jtype,
322 String_list *possible_keys,
323 const char *index,
324 const char *key_len,
325 const char *ref,
326 ha_rows *rows,
327 double *r_rows,
328 double r_filtered,
329 const char *extra)
330{
331 THD *thd= result->thd;
332 MEM_ROOT *mem_root= thd->mem_root;
333 Item *item_null= new (mem_root) Item_null(thd);
334 List<Item> item_list;
335 Item *item;
336
337 item_list.push_back(new (mem_root) Item_int(thd, (int32) select_number),
338 mem_root);
339 item_list.push_back(new (mem_root) Item_string_sys(thd, select_type),
340 mem_root);
341 item_list.push_back(new (mem_root) Item_string_sys(thd, table_name),
342 mem_root);
343 if (options & DESCRIBE_PARTITIONS)
344 {
345 if (partitions)
346 {
347 item_list.push_back(new (mem_root) Item_string_sys(thd, partitions),
348 mem_root);
349 }
350 else
351 item_list.push_back(item_null, mem_root);
352 }
353
354 const char *jtype_str= join_type_str[jtype];
355 item_list.push_back(new (mem_root) Item_string_sys(thd, jtype_str),
356 mem_root);
357
358 /* 'possible_keys'
359 The buffer must not be deallocated before we call send_data, otherwise
360 we may end up reading freed memory.
361 */
362 StringBuffer<64> possible_keys_buf;
363 if (possible_keys && !possible_keys->is_empty())
364 {
365 push_string_list(thd, &item_list, *possible_keys, &possible_keys_buf);
366 }
367 else
368 item_list.push_back(item_null, mem_root);
369
370 /* 'index */
371 item= index ? new (mem_root) Item_string_sys(thd, index) : item_null;
372 item_list.push_back(item, mem_root);
373
374 /* 'key_len */
375 item= key_len ? new (mem_root) Item_string_sys(thd, key_len) : item_null;
376 item_list.push_back(item, mem_root);
377
378 /* 'ref' */
379 item= ref ? new (mem_root) Item_string_sys(thd, ref) : item_null;
380 item_list.push_back(item, mem_root);
381
382 /* 'rows' */
383 if (rows)
384 {
385 item_list.push_back(new (mem_root)
386 Item_int(thd, *rows, MY_INT64_NUM_DECIMAL_DIGITS),
387 mem_root);
388 }
389 else
390 item_list.push_back(item_null, mem_root);
391
392 /* 'r_rows' */
393 if (is_analyze)
394 {
395 if (r_rows)
396 item_list.push_back(new (mem_root) Item_float(thd, *r_rows, 2),
397 mem_root);
398 else
399 item_list.push_back(item_null, mem_root);
400 }
401
402 /* 'filtered' */
403 const double filtered=100.0;
404 if (options & DESCRIBE_EXTENDED || is_analyze)
405 item_list.push_back(new (mem_root) Item_float(thd, filtered, 2), mem_root);
406
407 /* 'r_filtered' */
408 if (is_analyze)
409 item_list.push_back(new (mem_root) Item_float(thd, r_filtered, 2),
410 mem_root);
411
412 /* 'Extra' */
413 if (extra)
414 item_list.push_back(new (mem_root) Item_string_sys(thd, extra), mem_root);
415 else
416 item_list.push_back(item_null, mem_root);
417
418 if (result->send_data(item_list))
419 return 1;
420 return 0;
421}
422
423
424
425
426uint Explain_union::make_union_table_name(char *buf)
427{
428 uint childno= 0;
429 uint len, lastop= 0;
430 LEX_CSTRING type;
431 switch (operation)
432 {
433 case OP_MIX:
434 lex_string_set3(&type, STRING_WITH_LEN("<unit"));
435 break;
436 case OP_UNION:
437 lex_string_set3(&type, STRING_WITH_LEN("<union"));
438 break;
439 case OP_INTERSECT:
440 lex_string_set3(&type, STRING_WITH_LEN("<intersect"));
441 break;
442 case OP_EXCEPT:
443 lex_string_set3(&type, STRING_WITH_LEN("<except"));
444 break;
445 default:
446 DBUG_ASSERT(0);
447 }
448 memcpy(buf, type.str, (len= (uint)type.length));
449
450 for (; childno < union_members.elements() && len + lastop + 5 < NAME_LEN;
451 childno++)
452 {
453 len+= lastop;
454 lastop= (uint)my_snprintf(buf + len, NAME_LEN - len,
455 "%u,", union_members.at(childno));
456 }
457
458 if (childno < union_members.elements() || len + lastop >= NAME_LEN)
459 {
460 memcpy(buf + len, STRING_WITH_LEN("...>") + 1);
461 len+= 4;
462 }
463 else
464 {
465 len+= lastop;
466 buf[len - 1]= '>'; // change ',' to '>'
467 }
468 return len;
469}
470
471
472int Explain_union::print_explain(Explain_query *query,
473 select_result_sink *output,
474 uint8 explain_flags,
475 bool is_analyze)
476{
477 THD *thd= output->thd;
478 MEM_ROOT *mem_root= thd->mem_root;
479 char table_name_buffer[SAFE_NAME_LEN];
480
481 /* print all UNION children, in order */
482 for (int i= 0; i < (int) union_members.elements(); i++)
483 {
484 Explain_select *sel= query->get_select(union_members.at(i));
485 sel->print_explain(query, output, explain_flags, is_analyze);
486 }
487
488 if (!using_tmp)
489 return 0;
490
491 /* Print a line with "UNIT RESULT" */
492 List<Item> item_list;
493 Item *item_null= new (mem_root) Item_null(thd);
494
495 /* `id` column */
496 item_list.push_back(item_null, mem_root);
497
498 /* `select_type` column */
499 push_str(thd, &item_list, fake_select_type);
500
501 /* `table` column: something like "<union1,2>" */
502 uint len= make_union_table_name(table_name_buffer);
503 item_list.push_back(new (mem_root)
504 Item_string_sys(thd, table_name_buffer, len),
505 mem_root);
506
507 /* `partitions` column */
508 if (explain_flags & DESCRIBE_PARTITIONS)
509 item_list.push_back(item_null, mem_root);
510
511 /* `type` column */
512 push_str(thd, &item_list, join_type_str[JT_ALL]);
513
514 /* `possible_keys` column */
515 item_list.push_back(item_null, mem_root);
516
517 /* `key` */
518 item_list.push_back(item_null, mem_root);
519
520 /* `key_len` */
521 item_list.push_back(item_null, mem_root);
522
523 /* `ref` */
524 item_list.push_back(item_null, mem_root);
525
526 /* `rows` */
527 item_list.push_back(item_null, mem_root);
528
529 /* `r_rows` */
530 if (is_analyze)
531 {
532 double avg_rows= fake_select_lex_tracker.get_avg_rows();
533 item_list.push_back(new (mem_root) Item_float(thd, avg_rows, 2), mem_root);
534 }
535
536 /* `filtered` */
537 if (explain_flags & DESCRIBE_EXTENDED || is_analyze)
538 item_list.push_back(item_null, mem_root);
539
540 /* `r_filtered` */
541 if (is_analyze)
542 item_list.push_back(item_null, mem_root);
543
544 /* `Extra` */
545 StringBuffer<256> extra_buf;
546 if (using_filesort)
547 {
548 extra_buf.append(STRING_WITH_LEN("Using filesort"));
549 }
550 item_list.push_back(new (mem_root)
551 Item_string_sys(thd, extra_buf.ptr(),
552 extra_buf.length()),
553 mem_root);
554
555 //output->unit.offset_limit_cnt= 0;
556 if (output->send_data(item_list))
557 return 1;
558
559 /*
560 Print all subquery children (UNION children have already been printed at
561 the start of this function)
562 */
563 return print_explain_for_children(query, output, explain_flags, is_analyze);
564}
565
566
567void Explain_union::print_explain_json(Explain_query *query,
568 Json_writer *writer, bool is_analyze)
569{
570 Json_writer_nesting_guard guard(writer);
571 char table_name_buffer[SAFE_NAME_LEN];
572
573 bool started_object= print_explain_json_cache(writer, is_analyze);
574
575 writer->add_member("query_block").start_object();
576
577 if (is_recursive_cte)
578 writer->add_member("recursive_union").start_object();
579 else
580 writer->add_member("union_result").start_object();
581
582 // using_temporary_table
583 make_union_table_name(table_name_buffer);
584 writer->add_member("table_name").add_str(table_name_buffer);
585 writer->add_member("access_type").add_str("ALL"); // not very useful
586
587 /* r_loops (not present in tabular output) */
588 if (is_analyze)
589 {
590 writer->add_member("r_loops").add_ll(fake_select_lex_tracker.get_loops());
591 }
592
593 /* `r_rows` */
594 if (is_analyze)
595 {
596 writer->add_member("r_rows");
597 if (fake_select_lex_tracker.has_scans())
598 writer->add_double(fake_select_lex_tracker.get_avg_rows());
599 else
600 writer->add_null();
601 }
602
603 writer->add_member("query_specifications").start_array();
604
605 for (int i= 0; i < (int) union_members.elements(); i++)
606 {
607 writer->start_object();
608 //writer->add_member("dependent").add_str("TODO");
609 //writer->add_member("cacheable").add_str("TODO");
610 Explain_select *sel= query->get_select(union_members.at(i));
611 sel->print_explain_json(query, writer, is_analyze);
612 writer->end_object();
613 }
614 writer->end_array();
615
616 print_explain_json_for_children(query, writer, is_analyze);
617
618 writer->end_object(); // union_result
619 writer->end_object(); // query_block
620
621 if (started_object)
622 writer->end_object();
623}
624
625
626/*
627 Print EXPLAINs for all children nodes (i.e. for subqueries)
628*/
629
630int Explain_node::print_explain_for_children(Explain_query *query,
631 select_result_sink *output,
632 uint8 explain_flags,
633 bool is_analyze)
634{
635 for (int i= 0; i < (int) children.elements(); i++)
636 {
637 Explain_node *node= query->get_node(children.at(i));
638 if (node->print_explain(query, output, explain_flags, is_analyze))
639 return 1;
640 }
641 return 0;
642}
643
644bool Explain_basic_join::add_table(Explain_table_access *tab, Explain_query *query)
645{
646 if (!join_tabs)
647 {
648 n_join_tabs= 0;
649 if (!(join_tabs= ((Explain_table_access**)
650 alloc_root(query->mem_root,
651 sizeof(Explain_table_access*) *
652 MAX_TABLES))))
653 return true;
654 }
655 join_tabs[n_join_tabs++]= tab;
656 return false;
657}
658
659/*
660 This tells whether a child subquery should be printed in JSON output.
661
662 Derived tables and Non-merged semi-joins should not be printed, because they
663 are printed inline in Explain_table_access.
664*/
665bool is_connection_printable_in_json(enum Explain_node::explain_connection_type type)
666{
667 return (type != Explain_node::EXPLAIN_NODE_DERIVED &&
668 type != Explain_node::EXPLAIN_NODE_NON_MERGED_SJ);
669}
670
671
672void Explain_node::print_explain_json_for_children(Explain_query *query,
673 Json_writer *writer,
674 bool is_analyze)
675{
676 Json_writer_nesting_guard guard(writer);
677
678 bool started= false;
679 for (int i= 0; i < (int) children.elements(); i++)
680 {
681 Explain_node *node= query->get_node(children.at(i));
682 /* Derived tables are printed inside Explain_table_access objects */
683
684 if (!is_connection_printable_in_json(node->connection_type))
685 continue;
686
687 if (!started)
688 {
689 writer->add_member("subqueries").start_array();
690 started= true;
691 }
692
693 writer->start_object();
694 node->print_explain_json(query, writer, is_analyze);
695 writer->end_object();
696 }
697
698 if (started)
699 writer->end_array();
700}
701
702
703bool Explain_node::print_explain_json_cache(Json_writer *writer,
704 bool is_analyze)
705{
706 if (cache_tracker)
707 {
708 cache_tracker->fetch_current_stats();
709 writer->add_member("expression_cache").start_object();
710 if (cache_tracker->state != Expression_cache_tracker::OK)
711 {
712 writer->add_member("state").
713 add_str(Expression_cache_tracker::state_str[cache_tracker->state]);
714 }
715
716 if (is_analyze)
717 {
718 longlong cache_reads= cache_tracker->hit + cache_tracker->miss;
719 writer->add_member("r_loops").add_ll(cache_reads);
720 if (cache_reads != 0)
721 {
722 double hit_ratio= double(cache_tracker->hit) / cache_reads * 100.0;
723 writer->add_member("r_hit_ratio").add_double(hit_ratio);
724 }
725 }
726 return true;
727 }
728 return false;
729}
730
731
732Explain_basic_join::~Explain_basic_join()
733{
734 if (join_tabs)
735 {
736 for (uint i= 0; i< n_join_tabs; i++)
737 delete join_tabs[i];
738 }
739}
740
741
742int Explain_select::print_explain(Explain_query *query,
743 select_result_sink *output,
744 uint8 explain_flags, bool is_analyze)
745{
746 THD *thd= output->thd;
747 MEM_ROOT *mem_root= thd->mem_root;
748
749 if (message)
750 {
751 List<Item> item_list;
752 Item *item_null= new (mem_root) Item_null(thd);
753
754 item_list.push_back(new (mem_root) Item_int(thd, (int32) select_id),
755 mem_root);
756 item_list.push_back(new (mem_root) Item_string_sys(thd, select_type),
757 mem_root);
758 for (uint i=0 ; i < 7; i++)
759 item_list.push_back(item_null, mem_root);
760 if (explain_flags & DESCRIBE_PARTITIONS)
761 item_list.push_back(item_null, mem_root);
762
763 /* filtered */
764 if (is_analyze || explain_flags & DESCRIBE_EXTENDED)
765 item_list.push_back(item_null, mem_root);
766
767 if (is_analyze)
768 {
769 /* r_rows, r_filtered */
770 item_list.push_back(item_null, mem_root);
771 item_list.push_back(item_null, mem_root);
772 }
773
774 item_list.push_back(new (mem_root) Item_string_sys(thd, message),
775 mem_root);
776
777 if (output->send_data(item_list))
778 return 1;
779 }
780 else
781 {
782 bool using_tmp= false;
783 bool using_fs= false;
784
785 for (Explain_aggr_node *node= aggr_tree; node; node= node->child)
786 {
787 switch (node->get_type())
788 {
789 case AGGR_OP_TEMP_TABLE:
790 using_tmp= true;
791 break;
792 case AGGR_OP_FILESORT:
793 using_fs= true;
794 break;
795 default:
796 break;
797 }
798 }
799
800 for (uint i=0; i< n_join_tabs; i++)
801 {
802 join_tabs[i]->print_explain(output, explain_flags, is_analyze, select_id,
803 select_type, using_tmp, using_fs);
804 if (i == 0)
805 {
806 /*
807 "Using temporary; Using filesort" should only be shown near the 1st
808 table
809 */
810 using_tmp= false;
811 using_fs= false;
812 }
813 }
814 for (uint i=0; i< n_join_tabs; i++)
815 {
816 Explain_basic_join* nest;
817 if ((nest= join_tabs[i]->sjm_nest))
818 nest->print_explain(query, output, explain_flags, is_analyze);
819 }
820 }
821
822 return print_explain_for_children(query, output, explain_flags, is_analyze);
823}
824
825
826int Explain_basic_join::print_explain(Explain_query *query,
827 select_result_sink *output,
828 uint8 explain_flags, bool is_analyze)
829{
830 for (uint i=0; i< n_join_tabs; i++)
831 {
832 if (join_tabs[i]->print_explain(output, explain_flags, is_analyze,
833 select_id,
834 "MATERIALIZED" /*select_type*/,
835 FALSE /*using temporary*/,
836 FALSE /*using filesort*/))
837 return 1;
838 }
839 return 0;
840}
841
842
843void Explain_select::add_linkage(Json_writer *writer)
844{
845 const char *operation= NULL;
846 switch (linkage)
847 {
848 case UNION_TYPE:
849 operation= "UNION";
850 break;
851 case INTERSECT_TYPE:
852 operation= "INTERSECT";
853 break;
854 case EXCEPT_TYPE:
855 operation= "EXCEPT";
856 break;
857 default:
858 // It is the first or the only SELECT => no operation
859 break;
860 }
861 if (operation)
862 writer->add_member("operation").add_str(operation);
863}
864
865void Explain_select::print_explain_json(Explain_query *query,
866 Json_writer *writer, bool is_analyze)
867{
868 Json_writer_nesting_guard guard(writer);
869
870 bool started_cache= print_explain_json_cache(writer, is_analyze);
871
872 if (message)
873 {
874 writer->add_member("query_block").start_object();
875 writer->add_member("select_id").add_ll(select_id);
876 add_linkage(writer);
877
878 writer->add_member("table").start_object();
879 writer->add_member("message").add_str(message);
880 writer->end_object();
881
882 print_explain_json_for_children(query, writer, is_analyze);
883 writer->end_object();
884 }
885 else
886 {
887 writer->add_member("query_block").start_object();
888 writer->add_member("select_id").add_ll(select_id);
889 add_linkage(writer);
890
891 if (is_analyze && time_tracker.get_loops())
892 {
893 writer->add_member("r_loops").add_ll(time_tracker.get_loops());
894 writer->add_member("r_total_time_ms").add_double(time_tracker.get_time_ms());
895 }
896
897 if (exec_const_cond)
898 {
899 writer->add_member("const_condition");
900 write_item(writer, exec_const_cond);
901 }
902 if (outer_ref_cond)
903 {
904 writer->add_member("outer_ref_condition");
905 write_item(writer, outer_ref_cond);
906 }
907
908 /* we do not print HAVING which always evaluates to TRUE */
909 if (having || (having_value == Item::COND_FALSE))
910 {
911 writer->add_member("having_condition");
912 if (likely(having))
913 write_item(writer, having);
914 else
915 {
916 /* Normally we should not go this branch, left just for safety */
917 DBUG_ASSERT(having_value == Item::COND_FALSE);
918 writer->add_str("0");
919 }
920 }
921
922 int started_objects= 0;
923
924 Explain_aggr_node *node= aggr_tree;
925
926 for (; node; node= node->child)
927 {
928 switch (node->get_type())
929 {
930 case AGGR_OP_TEMP_TABLE:
931 writer->add_member("temporary_table").start_object();
932 break;
933 case AGGR_OP_FILESORT:
934 {
935 writer->add_member("filesort").start_object();
936 ((Explain_aggr_filesort*)node)->print_json_members(writer, is_analyze);
937 break;
938 }
939 case AGGR_OP_REMOVE_DUPLICATES:
940 writer->add_member("duplicate_removal").start_object();
941 break;
942 case AGGR_OP_WINDOW_FUNCS:
943 {
944 //TODO: make print_json_members virtual?
945 writer->add_member("window_functions_computation").start_object();
946 ((Explain_aggr_window_funcs*)node)->print_json_members(writer, is_analyze);
947 break;
948 }
949 default:
950 DBUG_ASSERT(0);
951 }
952 started_objects++;
953 }
954
955 Explain_basic_join::print_explain_json_interns(query, writer, is_analyze);
956
957 for (;started_objects; started_objects--)
958 writer->end_object();
959
960 writer->end_object();
961 }
962
963 if (started_cache)
964 writer->end_object();
965}
966
967
968Explain_aggr_filesort::Explain_aggr_filesort(MEM_ROOT *mem_root,
969 bool is_analyze,
970 Filesort *filesort)
971 : tracker(is_analyze)
972{
973 child= NULL;
974 for (ORDER *ord= filesort->order; ord; ord= ord->next)
975 {
976 sort_items.push_back(ord->item[0], mem_root);
977 sort_directions.push_back(&ord->direction, mem_root);
978 }
979 filesort->tracker= &tracker;
980}
981
982
983void Explain_aggr_filesort::print_json_members(Json_writer *writer,
984 bool is_analyze)
985{
986 char item_buf[256];
987 String str(item_buf, sizeof(item_buf), &my_charset_bin);
988 str.length(0);
989
990 List_iterator_fast<Item> it(sort_items);
991 List_iterator_fast<ORDER::enum_order> it_dir(sort_directions);
992 Item* item;
993 ORDER::enum_order *direction;
994 bool first= true;
995 while ((item= it++))
996 {
997 direction= it_dir++;
998 if (first)
999 first= false;
1000 else
1001 {
1002 str.append(", ");
1003 }
1004 append_item_to_str(&str, item);
1005 if (*direction == ORDER::ORDER_DESC)
1006 str.append(" desc");
1007 }
1008
1009 writer->add_member("sort_key").add_str(str.c_ptr_safe());
1010
1011 if (is_analyze)
1012 tracker.print_json_members(writer);
1013}
1014
1015
1016void Explain_aggr_window_funcs::print_json_members(Json_writer *writer,
1017 bool is_analyze)
1018{
1019 Explain_aggr_filesort *srt;
1020 List_iterator<Explain_aggr_filesort> it(sorts);
1021 writer->add_member("sorts").start_object();
1022 while ((srt= it++))
1023 {
1024 writer->add_member("filesort").start_object();
1025 srt->print_json_members(writer, is_analyze);
1026 writer->end_object(); // filesort
1027 }
1028 writer->end_object(); // sorts
1029}
1030
1031
1032void Explain_basic_join::print_explain_json(Explain_query *query,
1033 Json_writer *writer,
1034 bool is_analyze)
1035{
1036 writer->add_member("query_block").start_object();
1037 writer->add_member("select_id").add_ll(select_id);
1038
1039 print_explain_json_interns(query, writer, is_analyze);
1040
1041 writer->end_object();
1042}
1043
1044
1045void Explain_basic_join::
1046print_explain_json_interns(Explain_query *query,
1047 Json_writer *writer,
1048 bool is_analyze)
1049{
1050 Json_writer_nesting_guard guard(writer);
1051 for (uint i=0; i< n_join_tabs; i++)
1052 {
1053 if (join_tabs[i]->start_dups_weedout)
1054 writer->add_member("duplicates_removal").start_object();
1055
1056 join_tabs[i]->print_explain_json(query, writer, is_analyze);
1057
1058 if (join_tabs[i]->end_dups_weedout)
1059 writer->end_object();
1060 }
1061 print_explain_json_for_children(query, writer, is_analyze);
1062}
1063
1064
1065void Explain_table_access::push_extra(enum explain_extra_tag extra_tag)
1066{
1067 extra_tags.append(extra_tag);
1068}
1069
1070
1071/*
1072 Put the contents of 'key' field of EXPLAIN otuput into key_str.
1073
1074 It is surprisingly complex:
1075 - hash join shows #hash#used_key
1076 - quick selects that use single index will print index name
1077*/
1078
1079void Explain_table_access::fill_key_str(String *key_str, bool is_json) const
1080{
1081 CHARSET_INFO *cs= system_charset_info;
1082 bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT ||
1083 type == JT_HASH_RANGE || type == JT_HASH_INDEX_MERGE);
1084 const char *hash_key_prefix= "#hash#";
1085
1086 if (key.get_key_name())
1087 {
1088 if (is_hj)
1089 key_str->append(hash_key_prefix, strlen(hash_key_prefix), cs);
1090
1091 key_str->append(key.get_key_name());
1092
1093 if (is_hj && type != JT_HASH)
1094 key_str->append(':');
1095 }
1096
1097 if (quick_info)
1098 {
1099 StringBuffer<64> buf2;
1100 if (is_json)
1101 quick_info->print_extra_recursive(&buf2);
1102 else
1103 quick_info->print_key(&buf2);
1104 key_str->append(buf2);
1105 }
1106 if (type == JT_HASH_NEXT)
1107 key_str->append(hash_next_key.get_key_name());
1108}
1109
1110
1111/*
1112 Fill "key_length".
1113 - this is just used key length for ref/range
1114 - for index_merge, it is a comma-separated list of lengths.
1115 - for hash join, it is key_len:pseudo_key_len
1116
1117 The column looks identical in tabular and json forms. In JSON, we consider
1118 the column legacy, it is superceded by used_key_parts.
1119*/
1120
1121void Explain_table_access::fill_key_len_str(String *key_len_str) const
1122{
1123 bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT ||
1124 type == JT_HASH_RANGE || type == JT_HASH_INDEX_MERGE);
1125 if (key.get_key_len() != (uint)-1)
1126 {
1127 char buf[64];
1128 size_t length;
1129 length= longlong10_to_str(key.get_key_len(), buf, 10) - buf;
1130 key_len_str->append(buf, length);
1131 if (is_hj && type != JT_HASH)
1132 key_len_str->append(':');
1133 }
1134
1135 if (quick_info)
1136 {
1137 StringBuffer<64> buf2;
1138 quick_info->print_key_len(&buf2);
1139 key_len_str->append(buf2);
1140 }
1141
1142 if (type == JT_HASH_NEXT)
1143 {
1144 char buf[64];
1145 size_t length;
1146 length= longlong10_to_str(hash_next_key.get_key_len(), buf, 10) - buf;
1147 key_len_str->append(buf, length);
1148 }
1149}
1150
1151
1152bool Explain_index_use::set(MEM_ROOT *mem_root, KEY *key, uint key_len_arg)
1153{
1154 if (set_pseudo_key(mem_root, key->name.str))
1155 return 1;
1156
1157 key_len= key_len_arg;
1158 uint len= 0;
1159 for (uint i= 0; i < key->usable_key_parts; i++)
1160 {
1161 if (!key_parts_list.append_str(mem_root,
1162 key->key_part[i].field->field_name.str))
1163 return 1;
1164 len += key->key_part[i].store_length;
1165 if (len >= key_len_arg)
1166 break;
1167 }
1168 return 0;
1169}
1170
1171
1172bool Explain_index_use::set_pseudo_key(MEM_ROOT *root, const char* key_name_arg)
1173{
1174 if (key_name_arg)
1175 {
1176 if (!(key_name= strdup_root(root, key_name_arg)))
1177 return 1;
1178 }
1179 else
1180 key_name= NULL;
1181 key_len= ~(uint) 0;
1182 return 0;
1183}
1184
1185
1186/*
1187 Given r_filtered% from join buffer condition and join condition, produce a
1188 combined r_filtered% number. This is needed for tabular EXPLAIN output which
1189 has only one cell for r_filtered value.
1190*/
1191
1192double Explain_table_access::get_r_filtered()
1193{
1194 double r_filtered= tracker.get_filtered_after_where();
1195 if (bka_type.is_using_jbuf())
1196 r_filtered *= jbuf_tracker.get_filtered_after_where();
1197 return r_filtered;
1198}
1199
1200
1201int Explain_table_access::print_explain(select_result_sink *output, uint8 explain_flags,
1202 bool is_analyze,
1203 uint select_id, const char *select_type,
1204 bool using_temporary, bool using_filesort)
1205{
1206 THD *thd= output->thd;
1207 MEM_ROOT *mem_root= thd->mem_root;
1208
1209 List<Item> item_list;
1210 Item *item_null= new (mem_root) Item_null(thd);
1211
1212 /* `id` column */
1213 item_list.push_back(new (mem_root) Item_int(thd, (int32) select_id),
1214 mem_root);
1215
1216 /* `select_type` column */
1217 push_str(thd, &item_list, select_type);
1218
1219 /* `table` column */
1220 push_string(thd, &item_list, &table_name);
1221
1222 /* `partitions` column */
1223 if (explain_flags & DESCRIBE_PARTITIONS)
1224 {
1225 if (used_partitions_set)
1226 {
1227 push_string(thd, &item_list, &used_partitions);
1228 }
1229 else
1230 item_list.push_back(item_null, mem_root);
1231 }
1232
1233 /* `type` column */
1234 push_str(thd, &item_list, join_type_str[type]);
1235
1236 /* `possible_keys` column */
1237 StringBuffer<64> possible_keys_buf;
1238 if (possible_keys.is_empty())
1239 item_list.push_back(item_null, mem_root);
1240 else
1241 push_string_list(thd, &item_list, possible_keys, &possible_keys_buf);
1242
1243 /* `key` */
1244 StringBuffer<64> key_str;
1245 fill_key_str(&key_str, false);
1246
1247 if (key_str.length() > 0)
1248 push_string(thd, &item_list, &key_str);
1249 else
1250 item_list.push_back(item_null, mem_root);
1251
1252 /* `key_len` */
1253 StringBuffer<64> key_len_str;
1254 fill_key_len_str(&key_len_str);
1255
1256 if (key_len_str.length() > 0)
1257 push_string(thd, &item_list, &key_len_str);
1258 else
1259 item_list.push_back(item_null, mem_root);
1260
1261 /* `ref` */
1262 StringBuffer<64> ref_list_buf;
1263 if (ref_list.is_empty())
1264 {
1265 if (type == JT_FT)
1266 {
1267 /* Traditionally, EXPLAIN lines with type=fulltext have ref='' */
1268 push_str(thd, &item_list, "");
1269 }
1270 else
1271 item_list.push_back(item_null, mem_root);
1272 }
1273 else
1274 push_string_list(thd, &item_list, ref_list, &ref_list_buf);
1275
1276 /* `rows` */
1277 if (rows_set)
1278 {
1279 item_list.push_back(new (mem_root)
1280 Item_int(thd, (longlong) (ulonglong) rows,
1281 MY_INT64_NUM_DECIMAL_DIGITS),
1282 mem_root);
1283 }
1284 else
1285 item_list.push_back(item_null, mem_root);
1286
1287 /* `r_rows` */
1288 if (is_analyze)
1289 {
1290 if (!tracker.has_scans())
1291 {
1292 item_list.push_back(item_null, mem_root);
1293 }
1294 else
1295 {
1296 double avg_rows= tracker.get_avg_rows();
1297 item_list.push_back(new (mem_root) Item_float(thd, avg_rows, 2),
1298 mem_root);
1299 }
1300 }
1301
1302 /* `filtered` */
1303 if (explain_flags & DESCRIBE_EXTENDED || is_analyze)
1304 {
1305 if (filtered_set)
1306 {
1307 item_list.push_back(new (mem_root) Item_float(thd, filtered, 2),
1308 mem_root);
1309 }
1310 else
1311 item_list.push_back(item_null, mem_root);
1312 }
1313
1314 /* `r_filtered` */
1315 if (is_analyze)
1316 {
1317 if (!tracker.has_scans())
1318 {
1319 item_list.push_back(item_null, mem_root);
1320 }
1321 else
1322 {
1323 double r_filtered= tracker.get_filtered_after_where();
1324 if (bka_type.is_using_jbuf())
1325 r_filtered *= jbuf_tracker.get_filtered_after_where();
1326 item_list.push_back(new (mem_root)
1327 Item_float(thd, r_filtered * 100.0, 2),
1328 mem_root);
1329 }
1330 }
1331
1332 /* `Extra` */
1333 StringBuffer<256> extra_buf;
1334 bool first= true;
1335 for (int i=0; i < (int)extra_tags.elements(); i++)
1336 {
1337 if (first)
1338 first= false;
1339 else
1340 extra_buf.append(STRING_WITH_LEN("; "));
1341 append_tag_name(&extra_buf, extra_tags.at(i));
1342 }
1343
1344 if (using_temporary)
1345 {
1346 if (first)
1347 first= false;
1348 else
1349 extra_buf.append(STRING_WITH_LEN("; "));
1350 extra_buf.append(STRING_WITH_LEN("Using temporary"));
1351 }
1352
1353 if (using_filesort || this->pre_join_sort)
1354 {
1355 if (first)
1356 first= false;
1357 else
1358 extra_buf.append(STRING_WITH_LEN("; "));
1359 extra_buf.append(STRING_WITH_LEN("Using filesort"));
1360 }
1361
1362 item_list.push_back(new (mem_root)
1363 Item_string_sys(thd, extra_buf.ptr(),
1364 extra_buf.length()),
1365 mem_root);
1366
1367 if (output->send_data(item_list))
1368 return 1;
1369
1370 return 0;
1371}
1372
1373
1374/**
1375 Adds copy of the string to the list
1376
1377 @param mem_root where to allocate string
1378 @param str string to copy and add
1379
1380 @return
1381 NULL - out of memory error
1382 poiner on allocated copy of the string
1383*/
1384
1385const char *String_list::append_str(MEM_ROOT *mem_root, const char *str)
1386{
1387 size_t len= strlen(str);
1388 char *cp;
1389 if (!(cp = (char*)alloc_root(mem_root, len+1)))
1390 return NULL;
1391 memcpy(cp, str, len+1);
1392 push_back(cp, mem_root);
1393 return cp;
1394}
1395
1396
1397static void write_item(Json_writer *writer, Item *item)
1398{
1399 THD *thd= current_thd;
1400 char item_buf[256];
1401 String str(item_buf, sizeof(item_buf), &my_charset_bin);
1402 str.length(0);
1403
1404 ulonglong save_option_bits= thd->variables.option_bits;
1405 thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
1406
1407 item->print(&str, QT_EXPLAIN);
1408
1409 thd->variables.option_bits= save_option_bits;
1410 writer->add_str(str.c_ptr_safe());
1411}
1412
1413static void append_item_to_str(String *out, Item *item)
1414{
1415 THD *thd= current_thd;
1416 ulonglong save_option_bits= thd->variables.option_bits;
1417 thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
1418
1419 item->print(out, QT_EXPLAIN);
1420 thd->variables.option_bits= save_option_bits;
1421}
1422
1423void Explain_table_access::tag_to_json(Json_writer *writer, enum explain_extra_tag tag)
1424{
1425 switch (tag)
1426 {
1427 case ET_OPEN_FULL_TABLE:
1428 writer->add_member("open_full_table").add_bool(true);
1429 break;
1430 case ET_SCANNED_0_DATABASES:
1431 writer->add_member("scanned_databases").add_ll(0);
1432 break;
1433 case ET_SCANNED_1_DATABASE:
1434 writer->add_member("scanned_databases").add_ll(1);
1435 break;
1436 case ET_SCANNED_ALL_DATABASES:
1437 writer->add_member("scanned_databases").add_str("all");
1438 break;
1439 case ET_SKIP_OPEN_TABLE:
1440 writer->add_member("skip_open_table").add_bool(true);
1441 break;
1442 case ET_OPEN_FRM_ONLY:
1443 writer->add_member("open_frm_only").add_bool(true);
1444 break;
1445 case ET_USING_INDEX_CONDITION:
1446 writer->add_member("index_condition");
1447 write_item(writer, pushed_index_cond);
1448 break;
1449 case ET_USING_INDEX_CONDITION_BKA:
1450 writer->add_member("index_condition_bka");
1451 write_item(writer, pushed_index_cond);
1452 break;
1453 case ET_USING_WHERE:
1454 {
1455 /*
1456 We are printing the condition that is checked when scanning this
1457 table.
1458 - when join buffer is used, it is cache_cond.
1459 - in other cases, it is where_cond.
1460 */
1461 Item *item= bka_type.is_using_jbuf()? cache_cond: where_cond;
1462 if (item)
1463 {
1464 writer->add_member("attached_condition");
1465 write_item(writer, item);
1466 }
1467 }
1468 break;
1469 case ET_USING_INDEX:
1470 writer->add_member("using_index").add_bool(true);
1471 break;
1472 case ET_USING:
1473 // index merge: case ET_USING
1474 break;
1475 case ET_RANGE_CHECKED_FOR_EACH_RECORD:
1476 /* Handled as range_checked_fer */
1477 case ET_USING_JOIN_BUFFER:
1478 /* Do nothing. Join buffer is handled differently */
1479 case ET_START_TEMPORARY:
1480 case ET_END_TEMPORARY:
1481 /* Handled as "duplicates_removal: { ... } */
1482 case ET_FULL_SCAN_ON_NULL_KEY:
1483 /* Handled in full_scan_on_null_key */
1484 break;
1485 case ET_FIRST_MATCH:
1486 writer->add_member("first_match").add_str(firstmatch_table_name.c_ptr());
1487 break;
1488 case ET_LOOSESCAN:
1489 writer->add_member("loose_scan").add_bool(true);
1490 break;
1491 case ET_USING_MRR:
1492 writer->add_member("mrr_type").add_str(mrr_type.c_ptr());
1493 break;
1494 case ET_USING_INDEX_FOR_GROUP_BY:
1495 writer->add_member("using_index_for_group_by");
1496 if (loose_scan_is_scanning)
1497 writer->add_str("scanning");
1498 else
1499 writer->add_bool(true);
1500 break;
1501
1502 /*new:*/
1503 case ET_CONST_ROW_NOT_FOUND:
1504 writer->add_member("const_row_not_found").add_bool(true);
1505 break;
1506 case ET_UNIQUE_ROW_NOT_FOUND:
1507 /*
1508 Currently, we never get here. All SELECTs that have
1509 ET_UNIQUE_ROW_NOT_FOUND for a table are converted into degenerate
1510 SELECTs with message="Impossible WHERE ...".
1511 MySQL 5.6 has the same property.
1512 I'm leaving the handling in just for the sake of covering all enum
1513 members and safety.
1514 */
1515 writer->add_member("unique_row_not_found").add_bool(true);
1516 break;
1517 case ET_IMPOSSIBLE_ON_CONDITION:
1518 writer->add_member("impossible_on_condition").add_bool(true);
1519 break;
1520 case ET_USING_WHERE_WITH_PUSHED_CONDITION:
1521 /*
1522 It would be nice to print the pushed condition, but current Storage
1523 Engine API doesn't provide any way to do that
1524 */
1525 writer->add_member("pushed_condition").add_bool(true);
1526 break;
1527
1528 case ET_NOT_EXISTS:
1529 writer->add_member("not_exists").add_bool(true);
1530 break;
1531 case ET_DISTINCT:
1532 writer->add_member("distinct").add_bool(true);
1533 break;
1534
1535 default:
1536 DBUG_ASSERT(0);
1537 }
1538}
1539
1540
1541static
1542void add_json_keyset(Json_writer *writer, const char *elem_name,
1543 String_list *keyset)
1544{
1545 if (!keyset->is_empty())
1546 print_json_array(writer, elem_name, *keyset);
1547}
1548
1549
1550void Explain_table_access::print_explain_json(Explain_query *query,
1551 Json_writer *writer,
1552 bool is_analyze)
1553{
1554 Json_writer_nesting_guard guard(writer);
1555
1556 if (pre_join_sort)
1557 {
1558 /* filesort was invoked on this join tab before doing the join with the rest */
1559 writer->add_member("read_sorted_file").start_object();
1560 if (is_analyze)
1561 {
1562 writer->add_member("r_rows");
1563 /*
1564 r_rows when reading filesort result. This can be less than the number
1565 of rows produced by filesort due to NL-join having LIMIT.
1566 */
1567 if (tracker.has_scans())
1568 writer->add_double(tracker.get_avg_rows());
1569 else
1570 writer->add_null();
1571
1572 /*
1573 r_filtered when reading filesort result. We should have checked the
1574 WHERE while doing filesort but lets check just in case.
1575 */
1576 if (tracker.has_scans() && tracker.get_filtered_after_where() < 1.0)
1577 {
1578 writer->add_member("r_filtered");
1579 writer->add_double(tracker.get_filtered_after_where()*100.0);
1580 }
1581 }
1582 writer->add_member("filesort").start_object();
1583 pre_join_sort->print_json_members(writer, is_analyze);
1584 }
1585
1586 if (bka_type.is_using_jbuf())
1587 {
1588 writer->add_member("block-nl-join").start_object();
1589 }
1590
1591 if (range_checked_fer)
1592 {
1593 range_checked_fer->print_json(writer, is_analyze);
1594 }
1595
1596 if (full_scan_on_null_key)
1597 writer->add_member("full-scan-on-null_key").start_object();
1598
1599 writer->add_member("table").start_object();
1600
1601 writer->add_member("table_name").add_str(table_name);
1602
1603 if (used_partitions_set)
1604 print_json_array(writer, "partitions", used_partitions_list);
1605
1606 writer->add_member("access_type").add_str(join_type_str[type]);
1607
1608 add_json_keyset(writer, "possible_keys", &possible_keys);
1609
1610 /* `key` */
1611 /* For non-basic quick select, 'key' will not be present */
1612 if (!quick_info || quick_info->is_basic())
1613 {
1614 StringBuffer<64> key_str;
1615 fill_key_str(&key_str, true);
1616 if (key_str.length())
1617 writer->add_member("key").add_str(key_str);
1618 }
1619
1620 /* `key_length` */
1621 StringBuffer<64> key_len_str;
1622 fill_key_len_str(&key_len_str);
1623 if (key_len_str.length())
1624 writer->add_member("key_length").add_str(key_len_str);
1625
1626 /* `used_key_parts` */
1627 String_list *parts_list= NULL;
1628 if (quick_info && quick_info->is_basic())
1629 parts_list= &quick_info->range.key_parts_list;
1630 else
1631 parts_list= &key.key_parts_list;
1632
1633 if (parts_list && !parts_list->is_empty())
1634 print_json_array(writer, "used_key_parts", *parts_list);
1635
1636 if (quick_info && !quick_info->is_basic())
1637 {
1638 writer->add_member("index_merge").start_object();
1639 quick_info->print_json(writer);
1640 writer->end_object();
1641 }
1642
1643 /* `ref` */
1644 if (!ref_list.is_empty())
1645 print_json_array(writer, "ref", ref_list);
1646
1647 /* r_loops (not present in tabular output) */
1648 if (is_analyze)
1649 {
1650 writer->add_member("r_loops").add_ll(tracker.get_loops());
1651 }
1652
1653 /* `rows` */
1654 if (rows_set)
1655 writer->add_member("rows").add_ll(rows);
1656
1657 /* `r_rows` */
1658 if (is_analyze)
1659 {
1660 writer->add_member("r_rows");
1661 if (pre_join_sort)
1662 {
1663 /* Get r_rows value from filesort */
1664 if (pre_join_sort->tracker.get_r_loops())
1665 writer->add_double(pre_join_sort->tracker.get_avg_examined_rows());
1666 else
1667 writer->add_null();
1668 }
1669 else
1670 {
1671 if (tracker.has_scans())
1672 writer->add_double(tracker.get_avg_rows());
1673 else
1674 writer->add_null();
1675 }
1676
1677 if (op_tracker.get_loops())
1678 {
1679 writer->add_member("r_total_time_ms").
1680 add_double(op_tracker.get_time_ms());
1681 }
1682 }
1683
1684 /* `filtered` */
1685 if (filtered_set)
1686 writer->add_member("filtered").add_double(filtered);
1687
1688 /* `r_filtered` */
1689 if (is_analyze)
1690 {
1691 writer->add_member("r_filtered");
1692 if (pre_join_sort)
1693 {
1694 /* Get r_filtered value from filesort */
1695 if (pre_join_sort->tracker.get_r_loops())
1696 writer->add_double(pre_join_sort->tracker.get_r_filtered());
1697 else
1698 writer->add_null();
1699 }
1700 else
1701 {
1702 /* Get r_filtered from the NL-join runtime */
1703 if (tracker.has_scans())
1704 writer->add_double(tracker.get_filtered_after_where()*100.0);
1705 else
1706 writer->add_null();
1707 }
1708 }
1709
1710 for (int i=0; i < (int)extra_tags.elements(); i++)
1711 {
1712 tag_to_json(writer, extra_tags.at(i));
1713 }
1714
1715 if (full_scan_on_null_key)
1716 writer->end_object(); //"full-scan-on-null_key"
1717
1718 if (range_checked_fer)
1719 writer->end_object(); // "range-checked-for-each-record"
1720
1721 if (bka_type.is_using_jbuf())
1722 {
1723 writer->end_object(); // "block-nl-join"
1724 writer->add_member("buffer_type").add_str(bka_type.incremental?
1725 "incremental":"flat");
1726 writer->add_member("buffer_size").add_size(bka_type.join_buffer_size);
1727 writer->add_member("join_type").add_str(bka_type.join_alg);
1728 if (bka_type.mrr_type.length())
1729 writer->add_member("mrr_type").add_str(bka_type.mrr_type);
1730 if (where_cond)
1731 {
1732 writer->add_member("attached_condition");
1733 write_item(writer, where_cond);
1734 }
1735
1736 if (is_analyze)
1737 {
1738 //writer->add_member("r_loops").add_ll(jbuf_tracker.get_loops());
1739 writer->add_member("r_filtered");
1740 if (jbuf_tracker.has_scans())
1741 writer->add_double(jbuf_tracker.get_filtered_after_where()*100.0);
1742 else
1743 writer->add_null();
1744 }
1745 }
1746
1747 if (derived_select_number)
1748 {
1749 /* This is a derived table. Print its contents here */
1750 writer->add_member("materialized").start_object();
1751 Explain_node *node= query->get_node(derived_select_number);
1752 node->print_explain_json(query, writer, is_analyze);
1753 writer->end_object();
1754 }
1755 if (non_merged_sjm_number)
1756 {
1757 /* This is a non-merged semi-join table. Print its contents here */
1758 writer->add_member("materialized").start_object();
1759 writer->add_member("unique").add_ll(1);
1760 Explain_node *node= query->get_node(non_merged_sjm_number);
1761 node->connection_type= Explain_node::EXPLAIN_NODE_NON_MERGED_SJ;
1762 node->print_explain_json(query, writer, is_analyze);
1763 writer->end_object();
1764 }
1765 if (sjm_nest)
1766 {
1767 /* This is a non-merged semi-join table. Print its contents here */
1768 writer->add_member("materialized").start_object();
1769 writer->add_member("unique").add_ll(1);
1770 sjm_nest->print_explain_json(query, writer, is_analyze);
1771 writer->end_object();
1772 }
1773
1774 if (pre_join_sort)
1775 {
1776 writer->end_object(); // filesort
1777 writer->end_object(); // read_sorted_file
1778 }
1779
1780 writer->end_object();
1781}
1782
1783
1784/*
1785 Elements in this array match members of enum Extra_tag, defined in
1786 sql_explain.h
1787*/
1788
1789const char * extra_tag_text[]=
1790{
1791 "ET_none",
1792 "Using index condition",
1793 "Using index condition(BKA)",
1794 "Using ", // special handling
1795 "Range checked for each record (index map: 0x", // special handling
1796 "Using where with pushed condition",
1797 "Using where",
1798 "Not exists",
1799
1800 "Using index",
1801 "Full scan on NULL key",
1802 "Skip_open_table",
1803 "Open_frm_only",
1804 "Open_full_table",
1805
1806 "Scanned 0 databases",
1807 "Scanned 1 database",
1808 "Scanned all databases",
1809
1810 "Using index for group-by", // special handling
1811
1812 "USING MRR: DONT PRINT ME", // special handling
1813
1814 "Distinct",
1815 "LooseScan",
1816 "Start temporary",
1817 "End temporary",
1818 "FirstMatch", // special handling
1819
1820 "Using join buffer", // special handling
1821
1822 "Const row not found",
1823 "Unique row not found",
1824 "Impossible ON condition",
1825};
1826
1827
1828void Explain_table_access::append_tag_name(String *str, enum explain_extra_tag tag)
1829{
1830 switch (tag) {
1831 case ET_USING:
1832 {
1833 // quick select
1834 str->append(STRING_WITH_LEN("Using "));
1835 quick_info->print_extra(str);
1836 break;
1837 }
1838 case ET_RANGE_CHECKED_FOR_EACH_RECORD:
1839 {
1840 /* 4 bits per 1 hex digit + terminating '\0' */
1841 char buf[MAX_KEY / 4 + 1];
1842 str->append(STRING_WITH_LEN("Range checked for each "
1843 "record (index map: 0x"));
1844 str->append(range_checked_fer->keys_map.print(buf));
1845 str->append(')');
1846 break;
1847 }
1848 case ET_USING_MRR:
1849 {
1850 str->append(mrr_type);
1851 break;
1852 }
1853 case ET_USING_JOIN_BUFFER:
1854 {
1855 str->append(extra_tag_text[tag]);
1856
1857 str->append(STRING_WITH_LEN(" ("));
1858 const char *buffer_type= bka_type.incremental ? "incremental" : "flat";
1859 str->append(buffer_type);
1860 str->append(STRING_WITH_LEN(", "));
1861 str->append(bka_type.join_alg);
1862 str->append(STRING_WITH_LEN(" join"));
1863 str->append(STRING_WITH_LEN(")"));
1864 if (bka_type.mrr_type.length())
1865 {
1866 str->append(STRING_WITH_LEN("; "));
1867 str->append(bka_type.mrr_type);
1868 }
1869
1870 break;
1871 }
1872 case ET_FIRST_MATCH:
1873 {
1874 if (firstmatch_table_name.length())
1875 {
1876 str->append("FirstMatch(");
1877 str->append(firstmatch_table_name);
1878 str->append(")");
1879 }
1880 else
1881 str->append(extra_tag_text[tag]);
1882 break;
1883 }
1884 case ET_USING_INDEX_FOR_GROUP_BY:
1885 {
1886 str->append(extra_tag_text[tag]);
1887 if (loose_scan_is_scanning)
1888 str->append(" (scanning)");
1889 break;
1890 }
1891 default:
1892 str->append(extra_tag_text[tag]);
1893 }
1894}
1895
1896
1897/*
1898 This is called for top-level Explain_quick_select only. The point of this
1899 function is:
1900 - index_merge should print $index_merge_type (child, ...)
1901 - 'range' should not print anything.
1902*/
1903
1904void Explain_quick_select::print_extra(String *str)
1905{
1906 if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE ||
1907 quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC ||
1908 quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
1909 {
1910 /* print nothing */
1911 }
1912 else
1913 print_extra_recursive(str);
1914}
1915
1916void Explain_quick_select::print_json(Json_writer *writer)
1917{
1918 if (is_basic())
1919 {
1920 writer->add_member("range").start_object();
1921
1922 writer->add_member("key").add_str(range.get_key_name());
1923
1924 print_json_array(writer, "used_key_parts", range.key_parts_list);
1925
1926 writer->end_object();
1927 }
1928 else
1929 {
1930 writer->add_member(get_name_by_type()).start_object();
1931
1932 List_iterator_fast<Explain_quick_select> it (children);
1933 Explain_quick_select* child;
1934 while ((child = it++))
1935 child->print_json(writer);
1936
1937 writer->end_object();
1938 }
1939}
1940
1941void Explain_quick_select::print_extra_recursive(String *str)
1942{
1943 if (is_basic())
1944 {
1945 str->append(range.get_key_name());
1946 }
1947 else
1948 {
1949 str->append(get_name_by_type());
1950 str->append('(');
1951 List_iterator_fast<Explain_quick_select> it (children);
1952 Explain_quick_select* child;
1953 bool first= true;
1954 while ((child = it++))
1955 {
1956 if (first)
1957 first= false;
1958 else
1959 str->append(',');
1960
1961 child->print_extra_recursive(str);
1962 }
1963 str->append(')');
1964 }
1965}
1966
1967
1968const char * Explain_quick_select::get_name_by_type()
1969{
1970 switch (quick_type) {
1971 case QUICK_SELECT_I::QS_TYPE_INDEX_MERGE:
1972 return "sort_union";
1973 case QUICK_SELECT_I::QS_TYPE_ROR_UNION:
1974 return "union";
1975 case QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT:
1976 return "intersect";
1977 case QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT:
1978 return "sort_intersect";
1979 default:
1980 DBUG_ASSERT(0);
1981 return "unknown quick select type";
1982 }
1983}
1984
1985
1986/*
1987 This prints a comma-separated list of used indexes, ignoring nesting
1988*/
1989
1990void Explain_quick_select::print_key(String *str)
1991{
1992 if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE ||
1993 quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC ||
1994 quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
1995 {
1996 if (str->length() > 0)
1997 str->append(',');
1998 str->append(range.get_key_name());
1999 }
2000 else
2001 {
2002 List_iterator_fast<Explain_quick_select> it (children);
2003 Explain_quick_select* child;
2004 while ((child = it++))
2005 {
2006 child->print_key(str);
2007 }
2008 }
2009}
2010
2011
2012/*
2013 This prints a comma-separated list of used key_lengths, ignoring nesting
2014*/
2015
2016void Explain_quick_select::print_key_len(String *str)
2017{
2018 if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE ||
2019 quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC ||
2020 quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
2021 {
2022 char buf[64];
2023 size_t length;
2024 length= longlong10_to_str(range.get_key_len(), buf, 10) - buf;
2025 if (str->length() > 0)
2026 str->append(',');
2027 str->append(buf, length);
2028 }
2029 else
2030 {
2031 List_iterator_fast<Explain_quick_select> it (children);
2032 Explain_quick_select* child;
2033 while ((child = it++))
2034 {
2035 child->print_key_len(str);
2036 }
2037 }
2038}
2039
2040
2041int Explain_delete::print_explain(Explain_query *query,
2042 select_result_sink *output,
2043 uint8 explain_flags,
2044 bool is_analyze)
2045{
2046 if (deleting_all_rows)
2047 {
2048 const char *msg= STR_DELETING_ALL_ROWS;
2049 int res= print_explain_message_line(output, explain_flags, is_analyze,
2050 1 /*select number*/,
2051 select_type, &rows, msg);
2052 return res;
2053
2054 }
2055 else
2056 {
2057 return Explain_update::print_explain(query, output, explain_flags,
2058 is_analyze);
2059 }
2060}
2061
2062
2063void Explain_delete::print_explain_json(Explain_query *query,
2064 Json_writer *writer,
2065 bool is_analyze)
2066{
2067 Json_writer_nesting_guard guard(writer);
2068
2069 if (deleting_all_rows)
2070 {
2071 writer->add_member("query_block").start_object();
2072 writer->add_member("select_id").add_ll(1);
2073 writer->add_member("table").start_object();
2074 // just like mysql-5.6, we don't print table name. Is this ok?
2075 writer->add_member("message").add_str(STR_DELETING_ALL_ROWS);
2076 writer->end_object(); // table
2077 writer->end_object(); // query_block
2078 return;
2079 }
2080 Explain_update::print_explain_json(query, writer, is_analyze);
2081}
2082
2083
2084int Explain_update::print_explain(Explain_query *query,
2085 select_result_sink *output,
2086 uint8 explain_flags,
2087 bool is_analyze)
2088{
2089 StringBuffer<64> key_buf;
2090 StringBuffer<64> key_len_buf;
2091 StringBuffer<64> extra_str;
2092 if (impossible_where || no_partitions)
2093 {
2094 const char *msg= impossible_where ?
2095 STR_IMPOSSIBLE_WHERE :
2096 STR_NO_ROWS_AFTER_PRUNING;
2097 int res= print_explain_message_line(output, explain_flags, is_analyze,
2098 1 /*select number*/,
2099 select_type,
2100 NULL, /* rows */
2101 msg);
2102 return res;
2103 }
2104
2105 if (quick_info)
2106 {
2107 quick_info->print_key(&key_buf);
2108 quick_info->print_key_len(&key_len_buf);
2109
2110 StringBuffer<64> quick_buf;
2111 quick_info->print_extra(&quick_buf);
2112 if (quick_buf.length())
2113 {
2114 extra_str.append(STRING_WITH_LEN("Using "));
2115 extra_str.append(quick_buf);
2116 }
2117 }
2118 else if (key.get_key_name())
2119 {
2120 const char *name= key.get_key_name();
2121 key_buf.set(name, strlen(name), &my_charset_bin);
2122 char buf[64];
2123 size_t length= longlong10_to_str(key.get_key_len(), buf, 10) - buf;
2124 key_len_buf.copy(buf, length, &my_charset_bin);
2125 }
2126
2127 if (using_where)
2128 {
2129 if (extra_str.length() !=0)
2130 extra_str.append(STRING_WITH_LEN("; "));
2131 extra_str.append(STRING_WITH_LEN("Using where"));
2132 }
2133
2134 if (mrr_type.length() != 0)
2135 {
2136 if (extra_str.length() !=0)
2137 extra_str.append(STRING_WITH_LEN("; "));
2138 extra_str.append(mrr_type);
2139 }
2140
2141 if (is_using_filesort())
2142 {
2143 if (extra_str.length() !=0)
2144 extra_str.append(STRING_WITH_LEN("; "));
2145 extra_str.append(STRING_WITH_LEN("Using filesort"));
2146 }
2147
2148 if (using_io_buffer)
2149 {
2150 if (extra_str.length() !=0)
2151 extra_str.append(STRING_WITH_LEN("; "));
2152 extra_str.append(STRING_WITH_LEN("Using buffer"));
2153 }
2154
2155 /*
2156 Single-table DELETE commands do not do "Using temporary".
2157 "Using index condition" is also not possible (which is an unjustified limitation)
2158 */
2159 double r_filtered= 100 * tracker.get_filtered_after_where();
2160 double r_rows= tracker.get_avg_rows();
2161
2162 print_explain_row(output, explain_flags, is_analyze,
2163 1, /* id */
2164 select_type,
2165 table_name.c_ptr(),
2166 used_partitions_set? used_partitions.c_ptr() : NULL,
2167 jtype,
2168 &possible_keys,
2169 key_buf.length()? key_buf.c_ptr() : NULL,
2170 key_len_buf.length() ? key_len_buf.c_ptr() : NULL,
2171 NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */
2172 &rows,
2173 tracker.has_scans()? &r_rows : NULL,
2174 r_filtered,
2175 extra_str.c_ptr_safe());
2176
2177 return print_explain_for_children(query, output, explain_flags, is_analyze);
2178}
2179
2180
2181void Explain_update::print_explain_json(Explain_query *query,
2182 Json_writer *writer,
2183 bool is_analyze)
2184{
2185 Json_writer_nesting_guard guard(writer);
2186
2187 writer->add_member("query_block").start_object();
2188 writer->add_member("select_id").add_ll(1);
2189
2190 /* This is the total time it took to do the UPDATE/DELETE */
2191 if (is_analyze && command_tracker.get_loops())
2192 {
2193 writer->add_member("r_total_time_ms").
2194 add_double(command_tracker.get_time_ms());
2195 }
2196
2197 if (impossible_where || no_partitions)
2198 {
2199 const char *msg= impossible_where ? STR_IMPOSSIBLE_WHERE :
2200 STR_NO_ROWS_AFTER_PRUNING;
2201 writer->add_member("table").start_object();
2202 writer->add_member("message").add_str(msg);
2203 writer->end_object(); // table
2204 writer->end_object(); // query_block
2205 return;
2206 }
2207
2208 DBUG_ASSERT(!(is_using_filesort() && using_io_buffer));
2209
2210 bool doing_buffering= false;
2211
2212 if (is_using_filesort())
2213 {
2214 writer->add_member("filesort").start_object();
2215 if (is_analyze)
2216 filesort_tracker->print_json_members(writer);
2217 doing_buffering= true;
2218 }
2219
2220 if (using_io_buffer)
2221 {
2222 writer->add_member("buffer").start_object();
2223 doing_buffering= true;
2224 }
2225
2226 /* Produce elements that are common for buffered and un-buffered cases */
2227 writer->add_member("table").start_object();
2228
2229 if (get_type() == EXPLAIN_UPDATE)
2230 writer->add_member("update").add_ll(1);
2231 else
2232 writer->add_member("delete").add_ll(1);
2233
2234 writer->add_member("table_name").add_str(table_name);
2235
2236 if (used_partitions_set)
2237 print_json_array(writer, "partitions", used_partitions_list);
2238
2239 writer->add_member("access_type").add_str(join_type_str[jtype]);
2240
2241 if (!possible_keys.is_empty())
2242 {
2243 List_iterator_fast<char> it(possible_keys);
2244 const char *name;
2245 writer->add_member("possible_keys").start_array();
2246 while ((name= it++))
2247 writer->add_str(name);
2248 writer->end_array();
2249 }
2250
2251 /* `key`, `key_length` */
2252 if (quick_info && quick_info->is_basic())
2253 {
2254 StringBuffer<64> key_buf;
2255 StringBuffer<64> key_len_buf;
2256 quick_info->print_extra_recursive(&key_buf);
2257 quick_info->print_key_len(&key_len_buf);
2258
2259 writer->add_member("key").add_str(key_buf);
2260 writer->add_member("key_length").add_str(key_len_buf);
2261 }
2262 else if (key.get_key_name())
2263 {
2264 writer->add_member("key").add_str(key.get_key_name());
2265 writer->add_member("key_length").add_str(key.get_key_len());
2266 }
2267
2268 /* `used_key_parts` */
2269 String_list *parts_list= NULL;
2270 if (quick_info && quick_info->is_basic())
2271 parts_list= &quick_info->range.key_parts_list;
2272 else
2273 parts_list= &key.key_parts_list;
2274
2275 if (parts_list && !parts_list->is_empty())
2276 {
2277 List_iterator_fast<char> it(*parts_list);
2278 const char *name;
2279 writer->add_member("used_key_parts").start_array();
2280 while ((name= it++))
2281 writer->add_str(name);
2282 writer->end_array();
2283 }
2284
2285 if (quick_info && !quick_info->is_basic())
2286 {
2287 writer->add_member("index_merge").start_object();
2288 quick_info->print_json(writer);
2289 writer->end_object();
2290 }
2291
2292 /* `rows` */
2293 writer->add_member("rows").add_ll(rows);
2294
2295
2296 if (mrr_type.length() != 0)
2297 writer->add_member("mrr_type").add_str(mrr_type.ptr());
2298
2299 if (is_analyze)
2300 {
2301 if (doing_buffering)
2302 {
2303 ha_rows r_rows;
2304 double r_filtered;
2305
2306 if (is_using_filesort())
2307 {
2308 if (filesort_tracker->get_r_loops())
2309 r_rows= (ha_rows) filesort_tracker->get_avg_examined_rows();
2310 else
2311 r_rows= 0;
2312 r_filtered= filesort_tracker->get_r_filtered() * 100.0;
2313 }
2314 else
2315 {
2316 if (buf_tracker.has_scans())
2317 r_rows= (ha_rows) buf_tracker.get_avg_rows();
2318 else
2319 r_rows= 0;
2320 r_filtered= buf_tracker.get_filtered_after_where() * 100.0;
2321 }
2322 writer->add_member("r_rows").add_ll(r_rows);
2323 writer->add_member("r_filtered").add_double(r_filtered);
2324 }
2325 else /* Not doing buffering */
2326 {
2327 writer->add_member("r_rows");
2328 if (tracker.has_scans())
2329 writer->add_double(tracker.get_avg_rows());
2330 else
2331 writer->add_null();
2332
2333 /* There is no 'filtered' estimate in UPDATE/DELETE atm */
2334 double r_filtered= tracker.get_filtered_after_where() * 100.0;
2335 writer->add_member("r_filtered").add_double(r_filtered);
2336 }
2337
2338 if (table_tracker.get_loops())
2339 {
2340 writer->add_member("r_total_time_ms").
2341 add_double(table_tracker.get_time_ms());
2342 }
2343 }
2344
2345 if (where_cond)
2346 {
2347 writer->add_member("attached_condition");
2348 write_item(writer, where_cond);
2349 }
2350
2351 /*** The part of plan that is before the buffering/sorting ends here ***/
2352 if (is_using_filesort())
2353 writer->end_object();
2354
2355 if (using_io_buffer)
2356 writer->end_object();
2357
2358 writer->end_object(); // table
2359
2360 print_explain_json_for_children(query, writer, is_analyze);
2361 writer->end_object(); // query_block
2362}
2363
2364
2365int Explain_insert::print_explain(Explain_query *query,
2366 select_result_sink *output,
2367 uint8 explain_flags,
2368 bool is_analyze)
2369{
2370 const char *select_type="INSERT";
2371 print_explain_row(output, explain_flags, is_analyze,
2372 1, /* id */
2373 select_type,
2374 table_name.c_ptr(),
2375 NULL, // partitions
2376 JT_ALL,
2377 NULL, // possible_keys
2378 NULL, // key
2379 NULL, // key_len
2380 NULL, // ref
2381 NULL, // rows
2382 NULL, // r_rows
2383 100.0, // r_filtered
2384 NULL);
2385
2386 return print_explain_for_children(query, output, explain_flags, is_analyze);
2387}
2388
2389void Explain_insert::print_explain_json(Explain_query *query,
2390 Json_writer *writer, bool is_analyze)
2391{
2392 Json_writer_nesting_guard guard(writer);
2393
2394 writer->add_member("query_block").start_object();
2395 writer->add_member("select_id").add_ll(1);
2396 writer->add_member("table").start_object();
2397 writer->add_member("table_name").add_str(table_name.c_ptr());
2398 writer->end_object(); // table
2399 print_explain_json_for_children(query, writer, is_analyze);
2400 writer->end_object(); // query_block
2401}
2402
2403
2404void delete_explain_query(LEX *lex)
2405{
2406 DBUG_ENTER("delete_explain_query");
2407 delete lex->explain;
2408 lex->explain= NULL;
2409 DBUG_VOID_RETURN;
2410}
2411
2412
2413void create_explain_query(LEX *lex, MEM_ROOT *mem_root)
2414{
2415 DBUG_ASSERT(!lex->explain);
2416 DBUG_ENTER("create_explain_query");
2417
2418 lex->explain= new (mem_root) Explain_query(lex->thd, mem_root);
2419 DBUG_ASSERT(mem_root == current_thd->mem_root);
2420
2421 DBUG_VOID_RETURN;
2422}
2423
2424void create_explain_query_if_not_exists(LEX *lex, MEM_ROOT *mem_root)
2425{
2426 if (!lex->explain)
2427 create_explain_query(lex, mem_root);
2428}
2429
2430
2431/**
2432 Build arrays for collectiong keys statistics, sdd possible key names
2433 to the list and name array
2434
2435 @param alloc MEM_ROOT to put data in
2436 @param list list of possible key names to fill
2437 @param table table of the keys
2438 @patam possible_keys possible keys map
2439
2440 @retval 0 - OK
2441 @retval 1 - Error
2442*/
2443
2444int Explain_range_checked_fer::append_possible_keys_stat(MEM_ROOT *alloc,
2445 TABLE *table,
2446 key_map possible_keys)
2447{
2448 uint j;
2449 multi_alloc_root(alloc, &keys_stat, sizeof(ha_rows) * table->s->keys,
2450 &keys_stat_names, sizeof(char *) * table->s->keys, NULL);
2451 if ((!keys_stat) || (!keys_stat_names))
2452 {
2453 keys_stat= NULL;
2454 keys_stat_names= NULL;
2455 return 1;
2456 }
2457 keys_map= possible_keys;
2458 keys= table->s->keys;
2459 bzero(keys_stat, sizeof(ha_rows) * table->s->keys);
2460 for (j= 0; j < table->s->keys; j++)
2461 {
2462 if (possible_keys.is_set(j))
2463 {
2464 if (!(keys_stat_names[j]= key_set.append_str(alloc,
2465 table->key_info[j].name.str)))
2466 return 1;
2467 }
2468 else
2469 keys_stat_names[j]= NULL;
2470 }
2471 return 0;
2472}
2473
2474void Explain_range_checked_fer::collect_data(QUICK_SELECT_I *quick)
2475{
2476 if (quick)
2477 {
2478 if (quick->index == MAX_KEY)
2479 index_merge++;
2480 else
2481 {
2482 DBUG_ASSERT(quick->index < keys);
2483 DBUG_ASSERT(keys_stat);
2484 DBUG_ASSERT(keys_stat_names);
2485 DBUG_ASSERT(keys_stat_names[ quick->index]);
2486 keys_stat[quick->index]++;
2487 }
2488 }
2489 else
2490 full_scan++;
2491}
2492
2493
2494void Explain_range_checked_fer::print_json(Json_writer *writer,
2495 bool is_analyze)
2496{
2497 writer->add_member("range-checked-for-each-record").start_object();
2498 add_json_keyset(writer, "keys", &key_set);
2499 if (is_analyze)
2500 {
2501 writer->add_member("r_keys").start_object();
2502 writer->add_member("full_scan").add_ll(full_scan);
2503 writer->add_member("index_merge").add_ll(index_merge);
2504 if (keys_stat)
2505 {
2506 writer->add_member("range").start_object();
2507 for (uint i= 0; i < keys; i++)
2508 {
2509 if (keys_stat_names[i])
2510 {
2511 writer->add_member(keys_stat_names[i]).add_ll(keys_stat[i]);
2512 }
2513 }
2514 writer->end_object();
2515 }
2516 writer->end_object();
2517 }
2518}
2519
2520