1 /* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23 /** @file "EXPLAIN <command>" implementation */
24
25 #include "opt_explain.h"
26 #include "sql_select.h"
27 #include "sql_optimizer.h" // JOIN
28 #include "sql_partition.h" // for make_used_partitions_str()
29 #include "sql_join_buffer.h" // JOIN_CACHE
30 #include "filesort.h" // Filesort
31 #include "opt_explain_format.h"
32 #include "sql_base.h" // lock_tables
33
34 typedef qep_row::extra extra;
35
36 static bool mysql_explain_unit(THD *thd, SELECT_LEX_UNIT *unit,
37 select_result *result);
38 static void propagate_explain_option(THD *thd, SELECT_LEX_UNIT *unit);
39
40 const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref",
41 "ALL","range","index","fulltext",
42 "ref_or_null","unique_subquery","index_subquery",
43 "index_merge"
44 };
45
46 static const enum_query_type cond_print_flags=
47 enum_query_type(QT_ORDINARY | QT_SHOW_SELECT_NUMBER);
48
49
50 /**
51 A base for all Explain_* classes
52
53 Explain_* classes collect and output EXPLAIN data.
54
55 This class hierarchy is a successor of the old select_describe() function of 5.5.
56 */
57
58 class Explain
59 {
60 protected:
61 THD *const thd; ///< cached THD pointer
62 const CHARSET_INFO *const cs; ///< cached pointer to system_charset_info
63 JOIN *const join; ///< top-level JOIN (if any) provided by caller
64
65 select_result *const external_result; ///< stream (if any) provided by caller
66
67 Explain_format *const fmt; ///< shortcut for thd->lex->explain_format
68 Explain_context_enum context_type; ///< associated value for struct. explain
69
70 JOIN::ORDER_with_src order_list; //< ORDER BY item tree list
71 JOIN::ORDER_with_src group_list; //< GROUP BY item tee list
72
73 protected:
74 class Lazy_condition: public Lazy
75 {
76 Item *const condition;
77 public:
Lazy_condition(Item * condition_arg)78 Lazy_condition(Item *condition_arg): condition(condition_arg) {}
eval(String * ret)79 virtual bool eval(String *ret)
80 {
81 ret->length(0);
82 if (condition)
83 condition->print(ret, cond_print_flags);
84 return false;
85 }
86 };
87
Explain(Explain_context_enum context_type_arg,THD * thd_arg,JOIN * join_arg=NULL)88 explicit Explain(Explain_context_enum context_type_arg,
89 THD *thd_arg, JOIN *join_arg= NULL)
90 : thd(thd_arg),
91 cs(system_charset_info),
92 join(join_arg),
93 external_result(join ? join->result : NULL),
94 fmt(thd->lex->explain_format),
95 context_type(context_type_arg),
96 order_list(NULL),
97 group_list(NULL)
98 {
99 if (join)
100 {
101 order_list= join->order;
102 group_list= join->group_list;
103 }
104 else
105 {
106 if (select_lex()->order_list.elements)
107 order_list= JOIN::ORDER_with_src(select_lex()->order_list.first,
108 ESC_ORDER_BY);
109 if (select_lex()->group_list.elements)
110 group_list= JOIN::ORDER_with_src(select_lex()->group_list.first,
111 ESC_GROUP_BY);
112 }
113 }
114
115 public:
~Explain()116 virtual ~Explain() {}
117
118 bool send();
119
120 protected:
121 /**
122 Explain everything but subqueries
123 */
124 virtual bool shallow_explain();
125 /**
126 Explain the rest of things after the @c shallow_explain() call
127 */
128 bool explain_subqueries(select_result *result);
129 bool mark_subqueries(Item *item, qep_row *destination,
130 Explain_context_enum type);
131 bool mark_order_subqueries(const JOIN::ORDER_with_src &order);
132 bool prepare_columns();
describe(uint8 mask) const133 bool describe(uint8 mask) const { return thd->lex->describe & mask; }
134
select_lex() const135 SELECT_LEX *select_lex() const
136 {
137 return join ? join->select_lex : &thd->lex->select_lex;
138 }
139
140
141 /**
142 Prepare the self-allocated result object
143
144 For queries with top-level JOIN the caller provides pre-allocated
145 select_send object. Then that JOIN object prepares the select_send
146 object calling result->prepare() in JOIN::prepare(),
147 result->initalize_tables() in JOIN::optimize() and result->prepare2()
148 in JOIN::exec().
149 However without the presence of the top-level JOIN we have to
150 prepare/initialize select_send object manually.
151 */
prepare(select_result * result)152 bool prepare(select_result *result)
153 {
154 DBUG_ASSERT(join == NULL);
155 List<Item> dummy;
156 return result->prepare(dummy, select_lex()->master_unit()) ||
157 result->prepare2();
158 }
159
160 /**
161 Push a part of the "extra" column into formatter
162
163 Traditional formatter outputs traditional_extra_tags[tag] as is.
164 Hierarchical formatter outputs a property with the json_extra_tags[tag] name
165 and a boolean value of true.
166
167 @param tag type of the "extra" part
168
169 @retval false Ok
170 @retval true Error (OOM)
171 */
push_extra(Extra_tag tag)172 bool push_extra(Extra_tag tag)
173 {
174 extra *e= new extra(tag);
175 return e == NULL || fmt->entry()->col_extra.push_back(e);
176 }
177
178 /**
179 Push a part of the "extra" column into formatter
180
181 @param tag type of the "extra" part
182 @param arg for traditional formatter: rest of the part text,
183 for hierarchical format: string value of the property
184
185 @retval false Ok
186 @retval true Error (OOM)
187 */
push_extra(Extra_tag tag,const String & arg)188 bool push_extra(Extra_tag tag, const String &arg)
189 {
190 if (arg.is_empty())
191 return push_extra(tag);
192 extra *e= new extra(tag, arg.dup(thd->mem_root));
193 return !e || !e->data || fmt->entry()->col_extra.push_back(e);
194 }
195
196 /**
197 Push a part of the "extra" column into formatter
198
199 @param tag type of the "extra" part
200 @param arg for traditional formatter: rest of the part text,
201 for hierarchical format: string value of the property
202
203 NOTE: arg must be a long-living string constant.
204
205 @retval false Ok
206 @retval true Error (OOM)
207 */
push_extra(Extra_tag tag,const char * arg)208 bool push_extra(Extra_tag tag, const char *arg)
209 {
210 extra *e= new extra(tag, arg);
211 return !e || fmt->entry()->col_extra.push_back(e);
212 }
213
214 /*
215 Rest of the functions are overloadable functions, those calculate and fill
216 "col_*" fields with Items for further sending as EXPLAIN columns.
217
218 "explain_*" functions return false on success and true on error (usually OOM).
219 */
220 virtual bool explain_id();
221 virtual bool explain_select_type();
explain_table_name()222 virtual bool explain_table_name() { return false; }
explain_partitions()223 virtual bool explain_partitions() { return false; }
explain_join_type()224 virtual bool explain_join_type() { return false; }
explain_possible_keys()225 virtual bool explain_possible_keys() { return false; }
226 /** fill col_key and and col_key_len fields together */
explain_key_and_len()227 virtual bool explain_key_and_len() { return false; }
explain_ref()228 virtual bool explain_ref() { return false; }
229 /** fill col_rows and col_filtered fields together */
explain_rows_and_filtered()230 virtual bool explain_rows_and_filtered() { return false; }
explain_extra()231 virtual bool explain_extra() { return false; }
explain_modify_flags()232 virtual bool explain_modify_flags() { return false; }
233 };
234
235
236 /**
237 Explain_no_table class outputs a trivial EXPLAIN row with "extra" column
238
239 This class is intended for simple cases to produce EXPLAIN output
240 with "No tables used", "No matching records" etc.
241 Optionally it can output number of estimated rows in the "row"
242 column.
243
244 @note This class also produces EXPLAIN rows for inner units (if any).
245 */
246
247 class Explain_no_table: public Explain
248 {
249 private:
250 const char *message; ///< cached "message" argument
251 const ha_rows rows; ///< HA_POS_ERROR or cached "rows" argument
252
253 public:
Explain_no_table(THD * thd_arg,JOIN * join_arg,const char * message_arg)254 Explain_no_table(THD *thd_arg, JOIN *join_arg, const char *message_arg)
255 : Explain(CTX_JOIN, thd_arg, join_arg),
256 message(message_arg), rows(HA_POS_ERROR)
257 {}
258
Explain_no_table(THD * thd_arg,const char * message_arg,ha_rows rows_arg=HA_POS_ERROR)259 Explain_no_table(THD *thd_arg, const char *message_arg,
260 ha_rows rows_arg= HA_POS_ERROR)
261 : Explain(CTX_JOIN, thd_arg),
262 message(message_arg), rows(rows_arg)
263 {}
264
265 protected:
266 virtual bool shallow_explain();
267
268 virtual bool explain_rows_and_filtered();
269 virtual bool explain_extra();
270 };
271
272
273 /**
274 Explain_union_result class outputs EXPLAIN row for UNION
275 */
276
277 class Explain_union_result : public Explain
278 {
279 public:
Explain_union_result(THD * thd_arg,JOIN * join_arg)280 Explain_union_result(THD *thd_arg, JOIN *join_arg)
281 : Explain(CTX_UNION_RESULT, thd_arg, join_arg)
282 {
283 /* it's a UNION: */
284 DBUG_ASSERT(join_arg->select_lex == join_arg->unit->fake_select_lex);
285 }
286
287 protected:
288 virtual bool explain_id();
289 virtual bool explain_table_name();
290 virtual bool explain_join_type();
291 virtual bool explain_extra();
292 };
293
294
295
296 /**
297 Common base class for Explain_join and Explain_table
298 */
299
300 class Explain_table_base : public Explain {
301 protected:
302 const TABLE *table;
303 key_map usable_keys;
304
Explain_table_base(Explain_context_enum context_type_arg,THD * const thd_arg,JOIN * const join_arg)305 Explain_table_base(Explain_context_enum context_type_arg,
306 THD *const thd_arg, JOIN *const join_arg)
307 : Explain(context_type_arg, thd_arg, join_arg), table(NULL)
308 {}
309
Explain_table_base(Explain_context_enum context_type_arg,THD * const thd_arg,TABLE * const table_arg)310 Explain_table_base(Explain_context_enum context_type_arg,
311 THD *const thd_arg, TABLE *const table_arg)
312 : Explain(context_type_arg, thd_arg), table(table_arg)
313 {}
314
315 virtual bool explain_partitions();
316 virtual bool explain_possible_keys();
317
318 bool explain_key_parts(int key, uint key_parts);
319 bool explain_key_and_len_quick(const SQL_SELECT *select);
320 bool explain_key_and_len_index(int key);
321 bool explain_key_and_len_index(int key, uint key_length, uint key_parts);
322 bool explain_extra_common(const SQL_SELECT *select,
323 const JOIN_TAB *tab,
324 int quick_type,
325 uint keyno);
326 bool explain_tmptable_and_filesort(bool need_tmp_table_arg,
327 bool need_sort_arg);
328 virtual bool explain_modify_flags();
329 };
330
331
332 /**
333 Explain_join class produces EXPLAIN output for JOINs
334 */
335
336 class Explain_join : public Explain_table_base
337 {
338 private:
339 bool need_tmp_table; ///< add "Using temporary" to "extra" if true
340 bool need_order; ///< add "Using filesort"" to "extra" if true
341 const bool distinct; ///< add "Distinct" string to "extra" column if true
342
343 uint tabnum; ///< current tab number in join->join_tab[]
344 JOIN_TAB *tab; ///< current JOIN_TAB
345 SQL_SELECT *select; ///< current SQL_SELECT
346 int quick_type; ///< current quick type, see anon. enum at QUICK_SELECT_I
347 table_map used_tables; ///< accumulate used tables bitmap
348
349 public:
Explain_join(THD * thd_arg,JOIN * join_arg,bool need_tmp_table_arg,bool need_order_arg,bool distinct_arg)350 Explain_join(THD *thd_arg, JOIN *join_arg,
351 bool need_tmp_table_arg, bool need_order_arg,
352 bool distinct_arg)
353 : Explain_table_base(CTX_JOIN, thd_arg, join_arg),
354 need_tmp_table(need_tmp_table_arg),
355 need_order(need_order_arg), distinct(distinct_arg),
356 tabnum(0), select(0), used_tables(0)
357 {
358 /* it is not UNION: */
359 DBUG_ASSERT(join_arg->select_lex != join_arg->unit->fake_select_lex);
360 }
361
362 private:
363 // Next 4 functions begin and end context for GROUP BY, ORDER BY and DISTINC
364 bool begin_sort_context(Explain_sort_clause clause, Explain_context_enum ctx);
365 bool end_sort_context(Explain_sort_clause clause, Explain_context_enum ctx);
366 bool begin_simple_sort_context(Explain_sort_clause clause,
367 Explain_context_enum ctx);
368 bool end_simple_sort_context(Explain_sort_clause clause,
369 Explain_context_enum ctx);
370 bool explain_join_tab(size_t tab_num);
371
372 protected:
373 virtual bool shallow_explain();
374
375 virtual bool explain_table_name();
376 virtual bool explain_join_type();
377 virtual bool explain_key_and_len();
378 virtual bool explain_ref();
379 virtual bool explain_rows_and_filtered();
380 virtual bool explain_extra();
381 virtual bool explain_select_type();
382 virtual bool explain_id();
383 };
384
385
386 /**
387 Explain_table class produce EXPLAIN output for queries without top-level JOIN
388
389 This class is a simplified version of the Explain_join class. It works in the
390 context of queries which implementation lacks top-level JOIN object (EXPLAIN
391 single-table UPDATE and DELETE).
392 */
393
394 class Explain_table: public Explain_table_base
395 {
396 private:
397 const SQL_SELECT *const select; ///< cached "select" argument
398 const uint key; ///< cached "key" number argument
399 const ha_rows limit; ///< HA_POS_ERROR or cached "limit" argument
400 const bool need_tmp_table; ///< cached need_tmp_table argument
401 const bool need_sort; ///< cached need_sort argument
402 const bool is_update; // is_update ? UPDATE command : DELETE command
403 const bool used_key_is_modified; ///< UPDATE command updates used key
404
405 public:
Explain_table(THD * const thd_arg,TABLE * const table_arg,const SQL_SELECT * select_arg,uint key_arg,ha_rows limit_arg,bool need_tmp_table_arg,bool need_sort_arg,bool is_update_arg,bool used_key_is_modified_arg)406 Explain_table(THD *const thd_arg, TABLE *const table_arg,
407 const SQL_SELECT *select_arg,
408 uint key_arg, ha_rows limit_arg,
409 bool need_tmp_table_arg, bool need_sort_arg,
410 bool is_update_arg, bool used_key_is_modified_arg)
411 : Explain_table_base(CTX_JOIN, thd_arg, table_arg),
412 select(select_arg), key(key_arg),
413 limit(limit_arg),
414 need_tmp_table(need_tmp_table_arg), need_sort(need_sort_arg),
415 is_update(is_update_arg), used_key_is_modified(used_key_is_modified_arg)
416 {
417 usable_keys= table->possible_quick_keys;
418 }
419
420 virtual bool explain_modify_flags();
421
422 private:
423 virtual bool explain_tmptable_and_filesort(bool need_tmp_table_arg,
424 bool need_sort_arg);
425 virtual bool shallow_explain();
426
427 virtual bool explain_ref();
428 virtual bool explain_table_name();
429 virtual bool explain_join_type();
430 virtual bool explain_key_and_len();
431 virtual bool explain_rows_and_filtered();
432 virtual bool explain_extra();
433 };
434
435
calc_join_type(int quick_type)436 static join_type calc_join_type(int quick_type)
437 {
438 if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
439 (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
440 (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
441 return JT_INDEX_MERGE;
442 else
443 return JT_RANGE;
444 }
445
446
447 /* Explain class functions ****************************************************/
448
449
shallow_explain()450 bool Explain::shallow_explain()
451 {
452 return prepare_columns() || fmt->flush_entry();
453 }
454
455
456 /**
457 Qualify subqueries with WHERE/HAVING/ORDER BY/GROUP BY clause type marker
458
459 @param item Item tree to find subqueries
460 @param destination For WHERE clauses
461 @param type Clause type
462
463 @note WHERE clause belongs to TABLE or JOIN_TAB. The @c destination parameter
464 provides a pointer to QEP data for such a table to associate a future
465 subquery EXPLAIN output with table QEP provided.
466
467 @retval false OK
468 @retval true Error
469 */
470
mark_subqueries(Item * item,qep_row * destination,Explain_context_enum type)471 bool Explain::mark_subqueries(Item *item, qep_row *destination,
472 Explain_context_enum type)
473 {
474 if (item == NULL || !fmt->is_hierarchical())
475 return false;
476
477 Explain_subquery_marker marker(destination, type);
478 Explain_subquery_marker *marker_ptr= ▮
479
480 item->compile(&Item::explain_subquery_checker,
481 reinterpret_cast<uchar **>(&marker_ptr),
482 &Item::explain_subquery_propagator,
483 NULL);
484 return false;
485 }
486
487
mark_order_subqueries(const JOIN::ORDER_with_src & order)488 bool Explain::mark_order_subqueries(const JOIN::ORDER_with_src &order)
489 {
490 if (!order)
491 return false;
492
493 Explain_context_enum sq_context;
494 switch (order.src) {
495 case ESC_ORDER_BY:
496 sq_context= CTX_ORDER_BY_SQ;
497 break;
498 case ESC_GROUP_BY:
499 sq_context= CTX_GROUP_BY_SQ;
500 break;
501 case ESC_DISTINCT:
502 // DISTINCT can't have subqueries, but we can get here when
503 // DISTINCT is converted to GROUP BY
504 return false;
505 default:
506 DBUG_ASSERT(0);
507 return true;
508 }
509 for (const ORDER *o= order; o; o= o->next)
510 {
511 if (mark_subqueries(*o->item, NULL, sq_context))
512 return true;
513 }
514 return false;
515 }
516
explain_ref_key(Explain_format * fmt,uint key_parts,store_key * key_copy[])517 static bool explain_ref_key(Explain_format *fmt,
518 uint key_parts, store_key *key_copy[])
519 {
520 if (key_parts == 0)
521 return false;
522
523 for (uint part_no= 0; part_no < key_parts; part_no++)
524 {
525 const store_key *const s_key= key_copy[part_no];
526 if (s_key == NULL)
527 continue;
528 if (fmt->entry()->col_ref.push_back(s_key->name()))
529 return true;
530 }
531 return false;
532 }
533
534
535 /**
536 Traverses SQL clauses of this query specification to identify children
537 subqueries, marks each of them with the clause they belong to.
538 Then goes though all children subqueries and produces their EXPLAIN
539 output, attached to the proper clause's context.
540
541 @param result result stream
542
543 @retval false Ok
544 @retval true Error (OOM)
545 */
explain_subqueries(select_result * result)546 bool Explain::explain_subqueries(select_result *result)
547 {
548 if (join)
549 {
550 if (mark_subqueries(join->having, NULL, CTX_HAVING))
551 return true;
552
553 if (mark_order_subqueries(group_list))
554 return true;
555
556 if (!join->fields_list.is_empty())
557 {
558 List_iterator<Item> it(join->fields_list);
559 Item *item;
560 while ((item= it++))
561 {
562 if (mark_subqueries(item, NULL, CTX_SELECT_LIST))
563 return true;
564 }
565 }
566 }
567 if (&thd->lex->select_lex == select_lex() &&
568 !thd->lex->value_list.is_empty())
569 {
570 /*
571 Collect subqueries from UPDATE ... SET foo=subquery and
572 INSERT ... SELECT ... ON DUPLICATE KEY UPDATE x=(SELECT...)
573 */
574 DBUG_ASSERT(thd->lex->sql_command == SQLCOM_UPDATE ||
575 thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
576 thd->lex->sql_command == SQLCOM_INSERT ||
577 thd->lex->sql_command == SQLCOM_INSERT_SELECT);
578 List_iterator<Item> it(thd->lex->value_list);
579 Item *item;
580 while ((item= it++))
581 {
582 if (mark_subqueries(item, NULL, CTX_UPDATE_VALUE_LIST))
583 return true;
584 }
585 }
586
587 if (mark_order_subqueries(order_list))
588 return true;
589
590 for (SELECT_LEX_UNIT *unit= select_lex()->first_inner_unit();
591 unit;
592 unit= unit->next_unit())
593 {
594 SELECT_LEX *sl= unit->first_select();
595 Explain_context_enum context;
596 if (sl->type(thd) == SELECT_LEX::SLT_DERIVED)
597 {
598 DBUG_ASSERT(unit->explain_marker == CTX_NONE);
599 context= CTX_DERIVED;
600 }
601 else if (unit->explain_marker == CTX_NONE)
602 context= CTX_OPTIMIZED_AWAY_SUBQUERY;
603 else
604 context= static_cast<Explain_context_enum>(unit->explain_marker);
605
606 if (fmt->begin_context(context, unit))
607 return true;
608
609 if (mysql_explain_unit(thd, unit, result))
610 return true;
611
612 /*
613 This must be after mysql_explain_unit() so that JOIN::optimize() has run
614 and had a chance to choose materialization.
615 */
616 if (fmt->is_hierarchical() &&
617 (context == CTX_WHERE || context == CTX_HAVING ||
618 context == CTX_SELECT_LIST ||
619 context == CTX_GROUP_BY_SQ || context == CTX_ORDER_BY_SQ) &&
620 unit->item &&
621 (unit->item->get_engine_for_explain()->engine_type() ==
622 subselect_engine::HASH_SJ_ENGINE))
623 {
624 fmt->entry()->is_materialized_from_subquery= true;
625 fmt->entry()->col_table_name.set_const("<materialized_subquery>");
626 fmt->entry()->using_temporary= true;
627 fmt->entry()->col_join_type.set_const(join_type_str[JT_EQ_REF]);
628 fmt->entry()->col_key.set_const("<auto_key>");
629
630 const subselect_hash_sj_engine * const engine=
631 static_cast<const subselect_hash_sj_engine *>
632 (unit->item->get_engine_for_explain());
633 const JOIN_TAB * const tmp_tab= engine->get_join_tab();
634
635 char buff_key_len[24];
636 fmt->entry()->col_key_len.set(buff_key_len,
637 longlong2str(tmp_tab->table->key_info[0].key_length,
638 buff_key_len, 10) - buff_key_len);
639
640 if (explain_ref_key(fmt, tmp_tab->ref.key_parts,
641 tmp_tab->ref.key_copy))
642 return true;
643
644 fmt->entry()->col_rows.set(1);
645 /*
646 The value to look up depends on the outer value, so the materialized
647 subquery is dependent and not cacheable:
648 */
649 fmt->entry()->is_dependent= true;
650 fmt->entry()->is_cacheable= false;
651 }
652
653 if (fmt->end_context(context))
654 return true;
655 }
656 return false;
657 }
658
659
660 /**
661 Pre-calculate table property values for further EXPLAIN output
662 */
prepare_columns()663 bool Explain::prepare_columns()
664 {
665 return explain_id() ||
666 explain_select_type() ||
667 explain_table_name() ||
668 explain_partitions() ||
669 explain_join_type() ||
670 explain_possible_keys() ||
671 explain_key_and_len() ||
672 explain_ref() ||
673 explain_rows_and_filtered() ||
674 explain_extra() ||
675 explain_modify_flags();
676 }
677
678
679 /**
680 Explain class main function
681
682 This function:
683 a) allocates a select_send object (if no one pre-allocated available),
684 b) calculates and sends whole EXPLAIN data.
685
686 @return false if success, true if error
687 */
688
send()689 bool Explain::send()
690 {
691 DBUG_ENTER("Explain::send");
692
693 if (fmt->begin_context(context_type, NULL))
694 DBUG_RETURN(true);
695
696 /* Don't log this into the slow query log */
697 thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED |
698 SERVER_QUERY_NO_GOOD_INDEX_USED);
699
700 select_result *result;
701 if (external_result == NULL)
702 {
703 /* Create select_result object if the caller doesn't provide one: */
704 if (!(result= new select_send))
705 DBUG_RETURN(true); /* purecov: inspected */
706 if (fmt->send_headers(result) || prepare(result))
707 DBUG_RETURN(true);
708 }
709 else
710 {
711 result= external_result;
712 external_result->reset_offset_limit_cnt();
713 }
714
715 for (SELECT_LEX_UNIT *unit= select_lex()->first_inner_unit();
716 unit;
717 unit= unit->next_unit())
718 propagate_explain_option(thd, unit);
719
720 bool ret= shallow_explain() || explain_subqueries(result);
721
722 if (!ret)
723 ret= fmt->end_context(context_type);
724
725 if (ret && join)
726 join->error= 1; /* purecov: inspected */
727
728 if (external_result == NULL)
729 {
730 if (ret)
731 result->abort_result_set();
732 else
733 result->send_eof();
734 delete result;
735 }
736
737 DBUG_RETURN(ret);
738 }
739
740
explain_id()741 bool Explain::explain_id()
742 {
743 fmt->entry()->col_id.set(select_lex()->select_number);
744 return false;
745 }
746
747
explain_select_type()748 bool Explain::explain_select_type()
749 {
750 if (&thd->lex->select_lex != select_lex()) // ignore top-level SELECT_LEXes
751 {
752 fmt->entry()->is_dependent= select_lex()->is_dependent();
753 if (select_lex()->type(thd) != SELECT_LEX::SLT_DERIVED)
754 fmt->entry()->is_cacheable= select_lex()->is_cacheable();
755 }
756 fmt->entry()->col_select_type.set(select_lex()->type(thd));
757 return false;
758 }
759
760
761 /* Explain_no_table class functions *******************************************/
762
763
shallow_explain()764 bool Explain_no_table::shallow_explain()
765 {
766 return (fmt->begin_context(CTX_MESSAGE) ||
767 Explain::shallow_explain() ||
768 mark_subqueries(select_lex()->where, fmt->entry(), CTX_WHERE) ||
769 fmt->end_context(CTX_MESSAGE));
770 }
771
772
explain_rows_and_filtered()773 bool Explain_no_table::explain_rows_and_filtered()
774 {
775 if (rows == HA_POS_ERROR)
776 return false;
777 fmt->entry()->col_rows.set(rows);
778 return false;
779 }
780
781
explain_extra()782 bool Explain_no_table::explain_extra()
783 {
784 return fmt->entry()->col_message.set(message);
785 }
786
787
788 /* Explain_union_result class functions ****************************************/
789
790
explain_id()791 bool Explain_union_result::explain_id()
792 {
793 return false;
794 }
795
796
explain_table_name()797 bool Explain_union_result::explain_table_name()
798 {
799 SELECT_LEX *last_select= join->unit->first_select()->last_select();
800 // # characters needed to print select_number of last select
801 int last_length= (int)log10((double)last_select->select_number)+1;
802
803 SELECT_LEX *sl= join->unit->first_select();
804 uint len= 6, lastop= 0;
805 char table_name_buffer[NAME_LEN];
806 memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
807 /*
808 - len + lastop: current position in table_name_buffer
809 - 6 + last_length: the number of characters needed to print
810 '...,'<last_select->select_number>'>\0'
811 */
812 for (;
813 sl && len + lastop + 6 + last_length < NAME_CHAR_LEN;
814 sl= sl->next_select())
815 {
816 len+= lastop;
817 lastop= my_snprintf(table_name_buffer + len, NAME_CHAR_LEN - len,
818 "%u,", sl->select_number);
819 }
820 if (sl || len + lastop >= NAME_CHAR_LEN)
821 {
822 memcpy(table_name_buffer + len, STRING_WITH_LEN("...,"));
823 len+= 4;
824 lastop= my_snprintf(table_name_buffer + len, NAME_CHAR_LEN - len,
825 "%u,", last_select->select_number);
826 }
827 len+= lastop;
828 table_name_buffer[len - 1]= '>'; // change ',' to '>'
829
830 return fmt->entry()->col_table_name.set(table_name_buffer, len);
831 }
832
833
explain_join_type()834 bool Explain_union_result::explain_join_type()
835 {
836 fmt->entry()->col_join_type.set_const(join_type_str[JT_ALL]);
837 return false;
838 }
839
840
explain_extra()841 bool Explain_union_result::explain_extra()
842 {
843 if (!fmt->is_hierarchical())
844 {
845 /*
846 Currently we always use temporary table for UNION result
847 */
848 if (push_extra(ET_USING_TEMPORARY))
849 return true;
850 /*
851 here we assume that the query will return at least two rows, so we
852 show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
853 and no filesort will be actually done, but executing all selects in
854 the UNION to provide precise EXPLAIN information will hardly be
855 appreciated :)
856 */
857 if (join->unit->global_parameters->order_list.first)
858 {
859 return push_extra(ET_USING_FILESORT);
860 }
861 }
862 return Explain::explain_extra();
863 }
864
865
866 /* Explain_table_base class functions *****************************************/
867
868
explain_partitions()869 bool Explain_table_base::explain_partitions()
870 {
871 #ifdef WITH_PARTITION_STORAGE_ENGINE
872 if (!table->pos_in_table_list->derived && table->part_info)
873 return make_used_partitions_str(table->part_info,
874 &fmt->entry()->col_partitions);
875 #endif
876 return false;
877 }
878
879
explain_possible_keys()880 bool Explain_table_base::explain_possible_keys()
881 {
882 if (usable_keys.is_clear_all())
883 return false;
884
885 for (uint j= 0 ; j < table->s->keys ; j++)
886 {
887 if (usable_keys.is_set(j) &&
888 fmt->entry()->col_possible_keys.push_back(table->key_info[j].name))
889 return true;
890 }
891 return false;
892 }
893
894
explain_key_parts(int key,uint key_parts)895 bool Explain_table_base::explain_key_parts(int key, uint key_parts)
896 {
897 KEY_PART_INFO *kp= table->key_info[key].key_part;
898 for (uint i= 0; i < key_parts; i++, kp++)
899 if (fmt->entry()->col_key_parts.push_back(kp->field->field_name))
900 return true;
901 return false;
902 }
903
904
explain_key_and_len_quick(const SQL_SELECT * select)905 bool Explain_table_base::explain_key_and_len_quick(const SQL_SELECT *select)
906 {
907 DBUG_ASSERT(select && select->quick);
908
909 bool ret= false;
910 StringBuffer<512> str_key(cs);
911 StringBuffer<512> str_key_len(cs);
912
913 if (select->quick->index != MAX_KEY)
914 ret= explain_key_parts(select->quick->index,
915 select->quick->used_key_parts);
916 select->quick->add_keys_and_lengths(&str_key, &str_key_len);
917 return (ret || fmt->entry()->col_key.set(str_key) ||
918 fmt->entry()->col_key_len.set(str_key_len));
919 }
920
921
explain_key_and_len_index(int key)922 bool Explain_table_base::explain_key_and_len_index(int key)
923 {
924 DBUG_ASSERT(key != MAX_KEY);
925 return explain_key_and_len_index(key, table->key_info[key].key_length,
926 table->key_info[key].user_defined_key_parts);
927 }
928
929
explain_key_and_len_index(int key,uint key_length,uint key_parts)930 bool Explain_table_base::explain_key_and_len_index(int key, uint key_length,
931 uint key_parts)
932 {
933 DBUG_ASSERT(key != MAX_KEY);
934
935 char buff_key_len[24];
936 const KEY *key_info= table->key_info + key;
937 const int length= longlong2str(key_length, buff_key_len, 10) - buff_key_len;
938 const bool ret= explain_key_parts(key, key_parts);
939 return (ret || fmt->entry()->col_key.set(key_info->name) ||
940 fmt->entry()->col_key_len.set(buff_key_len, length));
941 }
942
943
explain_extra_common(const SQL_SELECT * select,const JOIN_TAB * tab,int quick_type,uint keyno)944 bool Explain_table_base::explain_extra_common(const SQL_SELECT *select,
945 const JOIN_TAB *tab,
946 int quick_type,
947 uint keyno)
948 {
949 if (((keyno != MAX_KEY &&
950 keyno == table->file->pushed_idx_cond_keyno &&
951 table->file->pushed_idx_cond) ||
952 (tab && tab->cache_idx_cond)))
953 {
954 StringBuffer<160> buff(cs);
955 if (fmt->is_hierarchical())
956 {
957 if (table->file->pushed_idx_cond)
958 table->file->pushed_idx_cond->print(&buff, cond_print_flags);
959 else
960 tab->cache_idx_cond->print(&buff, cond_print_flags);
961 }
962 if (push_extra(ET_USING_INDEX_CONDITION, buff))
963 return true;
964 }
965
966 const TABLE* pushed_root= table->file->root_of_pushed_join();
967 if (pushed_root)
968 {
969 char buf[128];
970 int len;
971 int pushed_id= 0;
972
973 for (JOIN_TAB* prev= join->join_tab; prev <= tab; prev++)
974 {
975 const TABLE* prev_root= prev->table->file->root_of_pushed_join();
976 if (prev_root == prev->table)
977 {
978 pushed_id++;
979 if (prev_root == pushed_root)
980 break;
981 }
982 }
983 if (pushed_root == table)
984 {
985 uint pushed_count= tab->table->file->number_of_pushed_joins();
986 len= my_snprintf(buf, sizeof(buf)-1,
987 "Parent of %d pushed join@%d",
988 pushed_count, pushed_id);
989 }
990 else
991 {
992 len= my_snprintf(buf, sizeof(buf)-1,
993 "Child of '%s' in pushed join@%d",
994 tab->table->file->parent_of_pushed_join()->alias,
995 pushed_id);
996 }
997
998 {
999 StringBuffer<128> buff(cs);
1000 buff.append(buf,len);
1001 if (push_extra(ET_PUSHED_JOIN, buff))
1002 return true;
1003 }
1004 }
1005
1006 switch (quick_type) {
1007 case QUICK_SELECT_I::QS_TYPE_ROR_UNION:
1008 case QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT:
1009 case QUICK_SELECT_I::QS_TYPE_INDEX_MERGE:
1010 {
1011 StringBuffer<32> buff(cs);
1012 select->quick->add_info_string(&buff);
1013 if (fmt->is_hierarchical())
1014 {
1015 /*
1016 We are replacing existing col_key value with a quickselect info,
1017 but not the reverse:
1018 */
1019 DBUG_ASSERT(fmt->entry()->col_key.length);
1020 if (fmt->entry()->col_key.set(buff)) // keep col_key_len intact
1021 return true;
1022 }
1023 else
1024 {
1025 if (push_extra(ET_USING, buff))
1026 return true;
1027 }
1028 }
1029 break;
1030 default: ;
1031 }
1032
1033 if (select)
1034 {
1035 if (tab && tab->use_quick == QS_DYNAMIC_RANGE)
1036 {
1037 StringBuffer<64> str(STRING_WITH_LEN("index map: 0x"), cs);
1038 /* 4 bits per 1 hex digit + terminating '\0' */
1039 char buf[MAX_KEY / 4 + 1];
1040 str.append(tab->keys.print(buf));
1041 if (push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD, str))
1042 return true;
1043 }
1044 else if (select->cond)
1045 {
1046 const Item *pushed_cond= table->file->pushed_cond;
1047
1048 if (thd->optimizer_switch_flag(OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) &&
1049 pushed_cond)
1050 {
1051 StringBuffer<64> buff(cs);
1052 if (describe(DESCRIBE_EXTENDED))
1053 ((Item *)pushed_cond)->print(&buff, cond_print_flags);
1054 if (push_extra(ET_USING_WHERE_WITH_PUSHED_CONDITION, buff))
1055 return true;
1056 }
1057 else
1058 {
1059 if (fmt->is_hierarchical())
1060 {
1061 Lazy_condition *c= new Lazy_condition(tab && !tab->filesort ?
1062 tab->condition() :
1063 select->cond);
1064 if (c == NULL)
1065 return true;
1066 fmt->entry()->col_attached_condition.set(c);
1067 }
1068 else if (push_extra(ET_USING_WHERE))
1069 return true;
1070 }
1071 }
1072 else
1073 DBUG_ASSERT(!tab || !tab->condition());
1074 }
1075 if (table->reginfo.not_exists_optimize && push_extra(ET_NOT_EXISTS))
1076 return true;
1077
1078 if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE)
1079 {
1080 uint mrr_flags=
1081 ((QUICK_RANGE_SELECT*)(select->quick))->mrr_flags;
1082
1083 /*
1084 During normal execution of a query, multi_range_read_init() is
1085 called to initialize MRR. If HA_MRR_SORTED is set at this point,
1086 multi_range_read_init() for any native MRR implementation will
1087 revert to default MRR if not HA_MRR_SUPPORT_SORTED.
1088 Calling multi_range_read_init() can potentially be costly, so it
1089 is not done when executing an EXPLAIN. We therefore simulate
1090 its effect here:
1091 */
1092 if (mrr_flags & HA_MRR_SORTED && !(mrr_flags & HA_MRR_SUPPORT_SORTED))
1093 mrr_flags|= HA_MRR_USE_DEFAULT_IMPL;
1094
1095 if (!(mrr_flags & HA_MRR_USE_DEFAULT_IMPL) && push_extra(ET_USING_MRR))
1096 return true;
1097 }
1098 return false;
1099 }
1100
explain_tmptable_and_filesort(bool need_tmp_table_arg,bool need_sort_arg)1101 bool Explain_table_base::explain_tmptable_and_filesort(bool need_tmp_table_arg,
1102 bool need_sort_arg)
1103 {
1104 /*
1105 For hierarchical EXPLAIN we output "Using temporary" and
1106 "Using filesort" with related ORDER BY, GROUP BY or DISTINCT
1107 */
1108 if (fmt->is_hierarchical())
1109 return false;
1110
1111 if (need_tmp_table_arg && push_extra(ET_USING_TEMPORARY))
1112 return true;
1113 if (need_sort_arg && push_extra(ET_USING_FILESORT))
1114 return true;
1115 return false;
1116 }
1117
1118
explain_modify_flags()1119 bool Explain_table_base::explain_modify_flags()
1120 {
1121 if (!fmt->is_hierarchical())
1122 return false;
1123 switch (thd->lex->sql_command) {
1124 case SQLCOM_UPDATE_MULTI:
1125 if (!bitmap_is_clear_all(table->write_set) &&
1126 table->s->table_category != TABLE_CATEGORY_TEMPORARY)
1127 fmt->entry()->is_update= true;
1128 break;
1129 case SQLCOM_DELETE_MULTI:
1130 {
1131 TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
1132 for (TABLE_LIST *at= aux_tables; at; at= at->next_local)
1133 {
1134 if (at->table == table)
1135 {
1136 fmt->entry()->is_delete= true;
1137 break;
1138 }
1139 }
1140 break;
1141 }
1142 default: ;
1143 };
1144 return false;
1145 }
1146
1147
1148 /* Explain_join class functions ***********************************************/
1149
begin_sort_context(Explain_sort_clause clause,Explain_context_enum ctx)1150 bool Explain_join::begin_sort_context(Explain_sort_clause clause,
1151 Explain_context_enum ctx)
1152 {
1153 const Explain_format_flags *flags= &join->explain_flags;
1154 return (flags->get(clause, ESP_EXISTS) &&
1155 !flags->get(clause, ESP_IS_SIMPLE) &&
1156 fmt->begin_context(ctx, NULL, flags));
1157 }
1158
1159
end_sort_context(Explain_sort_clause clause,Explain_context_enum ctx)1160 bool Explain_join::end_sort_context(Explain_sort_clause clause,
1161 Explain_context_enum ctx)
1162 {
1163 const Explain_format_flags *flags= &join->explain_flags;
1164 return (flags->get(clause, ESP_EXISTS) &&
1165 !flags->get(clause, ESP_IS_SIMPLE) &&
1166 fmt->end_context(ctx));
1167 }
1168
1169
begin_simple_sort_context(Explain_sort_clause clause,Explain_context_enum ctx)1170 bool Explain_join::begin_simple_sort_context(Explain_sort_clause clause,
1171 Explain_context_enum ctx)
1172 {
1173 const Explain_format_flags *flags= &join->explain_flags;
1174 return (flags->get(clause, ESP_IS_SIMPLE) &&
1175 fmt->begin_context(ctx, NULL, flags));
1176 }
1177
1178
end_simple_sort_context(Explain_sort_clause clause,Explain_context_enum ctx)1179 bool Explain_join::end_simple_sort_context(Explain_sort_clause clause,
1180 Explain_context_enum ctx)
1181 {
1182 const Explain_format_flags *flags= &join->explain_flags;
1183 return (flags->get(clause, ESP_IS_SIMPLE) &&
1184 fmt->end_context(ctx));
1185 }
1186
1187
shallow_explain()1188 bool Explain_join::shallow_explain()
1189 {
1190 if (begin_sort_context(ESC_ORDER_BY, CTX_ORDER_BY))
1191 return true;
1192 if (begin_sort_context(ESC_DISTINCT, CTX_DISTINCT))
1193 return true;
1194 if (begin_sort_context(ESC_GROUP_BY, CTX_GROUP_BY))
1195 return true;
1196 if (begin_sort_context(ESC_BUFFER_RESULT, CTX_BUFFER_RESULT))
1197 return true;
1198
1199 for (size_t t= 0,
1200 cnt= fmt->is_hierarchical() ? join->primary_tables : join->tables;
1201 t < cnt; t++)
1202 {
1203 if (explain_join_tab(t))
1204 return true;
1205 }
1206
1207 if (end_sort_context(ESC_BUFFER_RESULT, CTX_BUFFER_RESULT))
1208 return true;
1209 if (end_sort_context(ESC_GROUP_BY, CTX_GROUP_BY))
1210 return true;
1211 if (end_sort_context(ESC_DISTINCT, CTX_DISTINCT))
1212 return true;
1213 if (end_sort_context(ESC_ORDER_BY, CTX_ORDER_BY))
1214 return true;
1215
1216 return false;
1217 }
1218
1219
explain_join_tab(size_t tab_num)1220 bool Explain_join::explain_join_tab(size_t tab_num)
1221 {
1222 tabnum= tab_num;
1223 tab= join->join_tab + tabnum;
1224 table= tab->table;
1225 if (!tab->position)
1226 return false;
1227 usable_keys= tab->keys;
1228 quick_type= -1;
1229 select= (tab->filesort && tab->filesort->select) ?
1230 tab->filesort->select : tab->select;
1231
1232 if (tab->type == JT_ALL && select && select->quick)
1233 {
1234 quick_type= select->quick->get_type();
1235 tab->type= calc_join_type(quick_type);
1236 }
1237
1238 if (tab->starts_weedout())
1239 fmt->begin_context(CTX_DUPLICATES_WEEDOUT);
1240
1241 const bool first_non_const= tabnum == join->const_tables;
1242
1243 if (first_non_const)
1244 {
1245 if (begin_simple_sort_context(ESC_ORDER_BY, CTX_SIMPLE_ORDER_BY))
1246 return true;
1247 if (begin_simple_sort_context(ESC_DISTINCT, CTX_SIMPLE_DISTINCT))
1248 return true;
1249 if (begin_simple_sort_context(ESC_GROUP_BY, CTX_SIMPLE_GROUP_BY))
1250 return true;
1251 }
1252
1253 Semijoin_mat_exec *sjm= tab->sj_mat_exec;
1254 Explain_context_enum c= sjm ? CTX_MATERIALIZATION : CTX_JOIN_TAB;
1255
1256 if (fmt->begin_context(c) || prepare_columns())
1257 return true;
1258
1259 fmt->entry()->query_block_id= table->pos_in_table_list->query_block_id();
1260
1261 if (sjm)
1262 {
1263 if (sjm->is_scan)
1264 {
1265 fmt->entry()->col_rows.cleanup(); // TODO: set(something reasonable)
1266 }
1267 else
1268 {
1269 fmt->entry()->col_rows.set(1);
1270 }
1271 }
1272
1273 if (fmt->flush_entry() ||
1274 mark_subqueries(tab->condition(), fmt->entry(), CTX_WHERE))
1275 return true;
1276
1277 if (sjm && fmt->is_hierarchical())
1278 {
1279 for (size_t sjt= sjm->inner_table_index, end= sjt + sjm->table_count;
1280 sjt < end; sjt++)
1281 {
1282 if (explain_join_tab(sjt))
1283 return true;
1284 }
1285 }
1286
1287 if (fmt->end_context(c))
1288 return true;
1289
1290 if (first_non_const)
1291 {
1292 if (end_simple_sort_context(ESC_GROUP_BY, CTX_SIMPLE_GROUP_BY))
1293 return true;
1294 if (end_simple_sort_context(ESC_DISTINCT, CTX_SIMPLE_DISTINCT))
1295 return true;
1296 if (end_simple_sort_context(ESC_ORDER_BY, CTX_SIMPLE_ORDER_BY))
1297 return true;
1298 }
1299
1300 if (tab->check_weed_out_table &&
1301 fmt->end_context(CTX_DUPLICATES_WEEDOUT))
1302 return true;
1303
1304 used_tables|= table->map;
1305
1306 return false;
1307 }
1308
1309
explain_table_name()1310 bool Explain_join::explain_table_name()
1311 {
1312 if (table->pos_in_table_list->derived && !fmt->is_hierarchical())
1313 {
1314 /* Derived table name generation */
1315 char table_name_buffer[NAME_LEN];
1316 const size_t len= my_snprintf(table_name_buffer,
1317 sizeof(table_name_buffer) - 1,
1318 "<derived%u>",
1319 table->pos_in_table_list->query_block_id());
1320 return fmt->entry()->col_table_name.set(table_name_buffer, len);
1321 }
1322 else
1323 return fmt->entry()->col_table_name.set(table->pos_in_table_list->alias);
1324 }
1325
1326
explain_select_type()1327 bool Explain_join::explain_select_type()
1328 {
1329 if (sj_is_materialize_strategy(tab->get_sj_strategy()))
1330 fmt->entry()->col_select_type.set(st_select_lex::SLT_MATERIALIZED);
1331 else
1332 return Explain::explain_select_type();
1333 return false;
1334 }
1335
1336
explain_id()1337 bool Explain_join::explain_id()
1338 {
1339 if (sj_is_materialize_strategy(tab->get_sj_strategy()))
1340 fmt->entry()->col_id.set(tab->sjm_query_block_id());
1341 else
1342 return Explain::explain_id();
1343 return false;
1344 }
1345
1346
explain_join_type()1347 bool Explain_join::explain_join_type()
1348 {
1349 fmt->entry()->col_join_type.set_const(join_type_str[tab->type]);
1350 return false;
1351 }
1352
1353
explain_key_and_len()1354 bool Explain_join::explain_key_and_len()
1355 {
1356 if (tab->ref.key_parts)
1357 return explain_key_and_len_index(tab->ref.key, tab->ref.key_length,
1358 tab->ref.key_parts);
1359 else if (tab->type == JT_INDEX_SCAN)
1360 return explain_key_and_len_index(tab->index);
1361 else if (select && select->quick)
1362 return explain_key_and_len_quick(select);
1363 else
1364 {
1365 const TABLE_LIST *table_list= table->pos_in_table_list;
1366 if (table_list->schema_table &&
1367 table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
1368 {
1369 StringBuffer<512> str_key(cs);
1370 const char *f_name;
1371 int f_idx;
1372 if (table_list->has_db_lookup_value)
1373 {
1374 f_idx= table_list->schema_table->idx_field1;
1375 f_name= table_list->schema_table->fields_info[f_idx].field_name;
1376 str_key.append(f_name, strlen(f_name), cs);
1377 }
1378 if (table_list->has_table_lookup_value)
1379 {
1380 if (table_list->has_db_lookup_value)
1381 str_key.append(',');
1382 f_idx= table_list->schema_table->idx_field2;
1383 f_name= table_list->schema_table->fields_info[f_idx].field_name;
1384 str_key.append(f_name, strlen(f_name), cs);
1385 }
1386 if (str_key.length())
1387 return fmt->entry()->col_key.set(str_key);
1388 }
1389 }
1390 return false;
1391 }
1392
1393
explain_ref()1394 bool Explain_join::explain_ref()
1395 {
1396 return explain_ref_key(fmt, tab->ref.key_parts, tab->ref.key_copy);
1397 }
1398
1399
explain_rows_and_filtered()1400 bool Explain_join::explain_rows_and_filtered()
1401 {
1402 if (table->pos_in_table_list->schema_table)
1403 return false;
1404
1405 double examined_rows;
1406 if (select && select->quick)
1407 examined_rows= rows2double(select->quick->records);
1408 else if (tab->type == JT_INDEX_SCAN || tab->type == JT_ALL)
1409 {
1410 if (tab->limit)
1411 examined_rows= rows2double(tab->limit);
1412 else
1413 {
1414 table->pos_in_table_list->fetch_number_of_rows();
1415 examined_rows= rows2double(table->file->stats.records);
1416 }
1417 }
1418 else
1419 examined_rows= tab->position->records_read;
1420
1421 fmt->entry()->col_rows.set(static_cast<longlong>(examined_rows));
1422
1423 /* Add "filtered" field */
1424 if (describe(DESCRIBE_EXTENDED))
1425 {
1426 float f= 0.0;
1427 if (examined_rows)
1428 f= 100.0 * tab->position->records_read / examined_rows;
1429 fmt->entry()->col_filtered.set(f);
1430 }
1431 return false;
1432 }
1433
1434
explain_extra()1435 bool Explain_join::explain_extra()
1436 {
1437 if (tab->info)
1438 {
1439 if (push_extra(tab->info))
1440 return true;
1441 }
1442 else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
1443 {
1444 if (tab->packed_info & TAB_INFO_USING_INDEX)
1445 {
1446 if (push_extra(ET_USING_INDEX))
1447 return true;
1448 }
1449 if (tab->packed_info & TAB_INFO_USING_WHERE)
1450 {
1451 if (fmt->is_hierarchical())
1452 {
1453 Lazy_condition *c= new Lazy_condition(tab->condition());
1454 if (c == NULL)
1455 return true;
1456 fmt->entry()->col_attached_condition.set(c);
1457 }
1458 else if (push_extra(ET_USING_WHERE))
1459 return true;
1460 }
1461 if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL)
1462 {
1463 if (fmt->entry()->col_extra.push_back(new
1464 extra(ET_FULL_SCAN_ON_NULL_KEY)))
1465 return true;
1466 }
1467 }
1468 else
1469 {
1470 uint keyno= MAX_KEY;
1471 if (tab->ref.key_parts)
1472 keyno= tab->ref.key;
1473 else if (select && select->quick)
1474 keyno = select->quick->index;
1475
1476 if (explain_extra_common(select, tab, quick_type, keyno))
1477 return true;
1478
1479 const TABLE_LIST *table_list= table->pos_in_table_list;
1480 if (table_list->schema_table &&
1481 table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
1482 {
1483 if (!table_list->table_open_method)
1484 {
1485 if (push_extra(ET_SKIP_OPEN_TABLE))
1486 return true;
1487 }
1488 else if (table_list->table_open_method == OPEN_FRM_ONLY)
1489 {
1490 if (push_extra(ET_OPEN_FRM_ONLY))
1491 return true;
1492 }
1493 else
1494 {
1495 if (push_extra(ET_OPEN_FULL_TABLE))
1496 return true;
1497 }
1498
1499 StringBuffer<32> buff(cs);
1500 if (table_list->has_db_lookup_value &&
1501 table_list->has_table_lookup_value)
1502 {
1503 if (push_extra(ET_SCANNED_DATABASES, "0"))
1504 return true;
1505 }
1506 else if (table_list->has_db_lookup_value ||
1507 table_list->has_table_lookup_value)
1508 {
1509 if (push_extra(ET_SCANNED_DATABASES, "1"))
1510 return true;
1511 }
1512 else
1513 {
1514 if (push_extra(ET_SCANNED_DATABASES, "all"))
1515 return true;
1516 }
1517 }
1518 if (((tab->type == JT_INDEX_SCAN || tab->type == JT_CONST) &&
1519 table->covering_keys.is_set(tab->index)) ||
1520 (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
1521 !((QUICK_ROR_INTERSECT_SELECT*) select->quick)->need_to_fetch_row) ||
1522 table->key_read)
1523 {
1524 if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
1525 {
1526 QUICK_GROUP_MIN_MAX_SELECT *qgs=
1527 (QUICK_GROUP_MIN_MAX_SELECT *) select->quick;
1528 StringBuffer<64> buff(cs);
1529 qgs->append_loose_scan_type(&buff);
1530 if (push_extra(ET_USING_INDEX_FOR_GROUP_BY, buff))
1531 return true;
1532 }
1533 else
1534 {
1535 if (push_extra(ET_USING_INDEX))
1536 return true;
1537 }
1538 }
1539
1540 if (explain_tmptable_and_filesort(need_tmp_table, need_order))
1541 return true;
1542 need_tmp_table= need_order= false;
1543
1544 if (distinct && test_all_bits(used_tables, thd->lex->used_tables) &&
1545 push_extra(ET_DISTINCT))
1546 return true;
1547
1548 if (tab->do_loosescan() && push_extra(ET_LOOSESCAN))
1549 return true;
1550
1551 if (tab->starts_weedout())
1552 {
1553 if (!fmt->is_hierarchical() && push_extra(ET_START_TEMPORARY))
1554 return true;
1555 }
1556 if (tab->finishes_weedout())
1557 {
1558 if (!fmt->is_hierarchical() && push_extra(ET_END_TEMPORARY))
1559 return true;
1560 }
1561 else if (tab->do_firstmatch())
1562 {
1563 if (tab->firstmatch_return == join->join_tab - 1)
1564 {
1565 if (push_extra(ET_FIRST_MATCH))
1566 return true;
1567 }
1568 else
1569 {
1570 StringBuffer<64> buff(cs);
1571 TABLE *prev_table= tab->firstmatch_return->table;
1572 if (prev_table->pos_in_table_list->query_block_id() &&
1573 !fmt->is_hierarchical() &&
1574 prev_table->pos_in_table_list->derived)
1575 {
1576 char namebuf[NAME_LEN];
1577 /* Derived table name generation */
1578 int len= my_snprintf(namebuf, sizeof(namebuf)-1,
1579 "<derived%u>",
1580 prev_table->pos_in_table_list->query_block_id());
1581 buff.append(namebuf, len);
1582 }
1583 else
1584 buff.append(prev_table->pos_in_table_list->alias);
1585 if (push_extra(ET_FIRST_MATCH, buff))
1586 return true;
1587 }
1588 }
1589
1590 if (tab->has_guarded_conds() && push_extra(ET_FULL_SCAN_ON_NULL_KEY))
1591 return true;
1592
1593 if (tabnum > 0 && tab->use_join_cache != JOIN_CACHE::ALG_NONE)
1594 {
1595 StringBuffer<64> buff(cs);
1596 if ((tab->use_join_cache & JOIN_CACHE::ALG_BNL))
1597 buff.append("Block Nested Loop");
1598 else if ((tab->use_join_cache & JOIN_CACHE::ALG_BKA))
1599 buff.append("Batched Key Access");
1600 else if ((tab->use_join_cache & JOIN_CACHE::ALG_BKA_UNIQUE))
1601 buff.append("Batched Key Access (unique)");
1602 else
1603 DBUG_ASSERT(0); /* purecov: inspected */
1604 if (push_extra(ET_USING_JOIN_BUFFER, buff))
1605 return true;
1606 }
1607 }
1608 return false;
1609 }
1610
1611
1612 /* Explain_table class functions **********************************************/
1613
explain_modify_flags()1614 bool Explain_table::explain_modify_flags()
1615 {
1616 if (!fmt->is_hierarchical())
1617 return false;
1618 if (is_update)
1619 fmt->entry()->is_update= true;
1620 else
1621 fmt->entry()->is_delete= true;
1622 return false;
1623 }
1624
1625
explain_tmptable_and_filesort(bool need_tmp_table_arg,bool need_sort_arg)1626 bool Explain_table::explain_tmptable_and_filesort(bool need_tmp_table_arg,
1627 bool need_sort_arg)
1628 {
1629 if (fmt->is_hierarchical())
1630 {
1631 /*
1632 For hierarchical EXPLAIN we output "using_temporary_table" and
1633 "using_filesort" with related ORDER BY, GROUP BY or DISTINCT
1634 (excluding the single-table UPDATE command that updates used key --
1635 in this case we output "using_temporary_table: for update"
1636 at the "table" node)
1637 */
1638 if (need_tmp_table_arg)
1639 {
1640 DBUG_ASSERT(used_key_is_modified || order_list);
1641 if (used_key_is_modified && push_extra(ET_USING_TEMPORARY, "for update"))
1642 return true;
1643 }
1644 }
1645 else
1646 {
1647 if (need_tmp_table_arg && push_extra(ET_USING_TEMPORARY))
1648 return true;
1649
1650 if (need_sort_arg && push_extra(ET_USING_FILESORT))
1651 return true;
1652 }
1653
1654 return false;
1655 }
1656
1657
shallow_explain()1658 bool Explain_table::shallow_explain()
1659 {
1660 Explain_format_flags flags;
1661 if (order_list)
1662 {
1663 flags.set(ESC_ORDER_BY, ESP_EXISTS);
1664 if (need_sort)
1665 flags.set(ESC_ORDER_BY, ESP_USING_FILESORT);
1666 if (!used_key_is_modified && need_tmp_table)
1667 flags.set(ESC_ORDER_BY, ESP_USING_TMPTABLE);
1668 }
1669
1670 if (order_list && fmt->begin_context(CTX_ORDER_BY, NULL, &flags))
1671 return true;
1672
1673 if (fmt->begin_context(CTX_JOIN_TAB))
1674 return true;
1675
1676 if (Explain::shallow_explain() ||
1677 mark_subqueries(select_lex()->where, fmt->entry(), CTX_WHERE))
1678 return true;
1679
1680 if (fmt->end_context(CTX_JOIN_TAB))
1681 return true;
1682
1683 if (order_list && fmt->end_context(CTX_ORDER_BY))
1684 return true;
1685
1686 return false;
1687 }
1688
1689
explain_table_name()1690 bool Explain_table::explain_table_name()
1691 {
1692 return fmt->entry()->col_table_name.set(table->alias);
1693 }
1694
1695
explain_join_type()1696 bool Explain_table::explain_join_type()
1697 {
1698 join_type jt;
1699 if (select && select->quick)
1700 jt= calc_join_type(select->quick->get_type());
1701 else if (key != MAX_KEY)
1702 jt= JT_INDEX_SCAN;
1703 else
1704 jt= JT_ALL;
1705
1706 fmt->entry()->col_join_type.set_const(join_type_str[jt]);
1707 return false;
1708 }
1709
1710
explain_ref()1711 bool Explain_table::explain_ref()
1712 {
1713 if (select && select->quick)
1714 {
1715 int key_parts= select->quick->used_key_parts;
1716 while(key_parts--)
1717 {
1718 fmt->entry()->col_ref.push_back("const");
1719 }
1720 }
1721 return false;
1722 }
1723
1724
explain_key_and_len()1725 bool Explain_table::explain_key_and_len()
1726 {
1727 if (select && select->quick)
1728 return explain_key_and_len_quick(select);
1729 else if (key != MAX_KEY)
1730 return explain_key_and_len_index(key);
1731 return false;
1732 }
1733
1734
explain_rows_and_filtered()1735 bool Explain_table::explain_rows_and_filtered()
1736 {
1737 double examined_rows;
1738 if (select && select->quick)
1739 examined_rows= rows2double(select->quick->records);
1740 else if (!select && !need_sort && limit != HA_POS_ERROR)
1741 examined_rows= rows2double(limit);
1742 else
1743 {
1744 table->pos_in_table_list->fetch_number_of_rows();
1745 examined_rows= rows2double(table->file->stats.records);
1746
1747 }
1748 fmt->entry()->col_rows.set(static_cast<long long>(examined_rows));
1749
1750 if (describe(DESCRIBE_EXTENDED))
1751 fmt->entry()->col_filtered.set(100.0);
1752
1753 return false;
1754 }
1755
1756
explain_extra()1757 bool Explain_table::explain_extra()
1758 {
1759 const uint keyno= (select && select->quick) ? select->quick->index : key;
1760 const int quick_type= (select && select->quick) ? select->quick->get_type()
1761 : -1;
1762 return (explain_extra_common(select, NULL, quick_type, keyno) ||
1763 explain_tmptable_and_filesort(need_tmp_table, need_sort));
1764 }
1765
1766
1767 /**
1768 EXPLAIN functionality for insert_select, multi_update and multi_delete
1769
1770 This class objects substitute insert_select, multi_update and multi_delete
1771 data interceptor objects to implement EXPLAIN for INSERT, REPLACE and
1772 multi-table UPDATE and DELETE queries.
1773 explain_send class object initializes tables like insert_select, multi_update
1774 or multi_delete data interceptor do, but it suppress table data modification
1775 by the underlying interceptor object.
1776 Thus, we can use explain_send object in the context of EXPLAIN INSERT/
1777 REPLACE/UPDATE/DELETE query like we use select_send in the context of
1778 EXPLAIN SELECT command:
1779 1) in presence of lex->describe flag we pass explain_send object to the
1780 mysql_select() function,
1781 2) it call prepare(), prepare2() and initialize_tables() functions to
1782 mark modified tables etc.
1783
1784 */
1785
1786 class explain_send : public select_send {
1787 protected:
1788 /*
1789 As far as we use explain_send object in a place of select_send, explain_send
1790 have to pass multiple invocation of its prepare(), prepare2() and
1791 initialize_tables() functions, since JOIN::exec() of subqueries runs
1792 these functions of select_send multiple times by design.
1793 insert_select, multi_update and multi_delete class functions are not intended
1794 for multiple invocations, so "prepared", "prepared2" and "initialized" flags
1795 guard data interceptor object from function re-invocation.
1796 */
1797 bool prepared; ///< prepare() is done
1798 bool prepared2; ///< prepare2() is done
1799 bool initialized; ///< initialize_tables() is done
1800
1801 /**
1802 Pointer to underlying insert_select, multi_update or multi_delete object
1803 */
1804 select_result_interceptor *interceptor;
1805
1806 public:
explain_send(select_result_interceptor * interceptor_arg)1807 explain_send(select_result_interceptor *interceptor_arg)
1808 : prepared(false), prepared2(false), initialized(false),
1809 interceptor(interceptor_arg)
1810 {}
1811
1812 protected:
prepare(List<Item> & list,SELECT_LEX_UNIT * u)1813 virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u)
1814 {
1815 if (prepared)
1816 return false;
1817 prepared= true;
1818 return select_send::prepare(list, u) || interceptor->prepare(list, u);
1819 }
1820
prepare2(void)1821 virtual int prepare2(void)
1822 {
1823 if (prepared2)
1824 return false;
1825 prepared2= true;
1826 return select_send::prepare2() || interceptor->prepare2();
1827 }
1828
initialize_tables(JOIN * join)1829 virtual bool initialize_tables(JOIN *join)
1830 {
1831 if (initialized)
1832 return false;
1833 initialized= true;
1834 return select_send::initialize_tables(join) ||
1835 interceptor->initialize_tables(join);
1836 }
1837
cleanup()1838 virtual void cleanup()
1839 {
1840 select_send::cleanup();
1841 interceptor->cleanup();
1842 }
1843 };
1844
1845
1846 /******************************************************************************
1847 External function implementations
1848 ******************************************************************************/
1849
1850
1851 /**
1852 Send a message as an "extra" column value
1853
1854 This function forms the 1st row of the QEP output with a simple text message.
1855 This is useful to explain such trivial cases as "No tables used" etc.
1856
1857 @note Also this function explains the rest of QEP (subqueries or joined
1858 tables if any).
1859
1860 @param thd current THD
1861 @param join JOIN
1862 @param message text message for the "extra" column.
1863
1864 @return false if success, true if error
1865 */
1866
explain_no_table(THD * thd,JOIN * join,const char * message)1867 bool explain_no_table(THD *thd, JOIN *join, const char *message)
1868 {
1869 DBUG_ENTER("explain_no_table");
1870 const bool ret= Explain_no_table(thd, join, message).send();
1871 DBUG_RETURN(ret);
1872 }
1873
1874
1875 /**
1876 Send a message as an "extra" column value
1877
1878 This function forms the 1st row of the QEP output with a simple text message.
1879 This is useful to explain such trivial cases as "No tables used" etc.
1880
1881 @note Also this function explains the rest of QEP (subqueries if any).
1882
1883 @param thd current THD
1884 @param message text message for the "extra" column.
1885 @param rows HA_POS_ERROR or a value for the "rows" column.
1886
1887 @return false if success, true if error
1888 */
1889
explain_no_table(THD * thd,const char * message,ha_rows rows)1890 bool explain_no_table(THD *thd, const char *message, ha_rows rows)
1891 {
1892 DBUG_ENTER("explain_no_table");
1893 const bool ret= Explain_no_table(thd, message, rows).send();
1894 DBUG_RETURN(ret);
1895 }
1896
1897
1898 /**
1899 EXPLAIN handling for single-table UPDATE and DELETE queries
1900
1901 Send to the client a QEP data set for single-table EXPLAIN UPDATE/DELETE
1902 queries. As far as single-table UPDATE/DELETE are implemented without
1903 the regular JOIN tree, we can't reuse explain_unit() directly,
1904 thus we deal with this single table in a special way and then call
1905 explain_unit() for subqueries (if any).
1906
1907 @param thd current THD
1908 @param table TABLE object to update/delete rows in the UPDATE/DELETE
1909 query.
1910 @param select SQL_SELECT object that represents quick access functions
1911 and WHERE clause.
1912 @param key MAX_KEY or and index number of the key that was chosen
1913 to access table data.
1914 @param limit HA_POS_ERROR or LIMIT value.
1915 @param need_tmp_table true if it requires temporary table -- "Using temporary"
1916 string in the "extra" column.
1917 @param need_sort true if it requires filesort() -- "Using filesort"
1918 string in the "extra" column.
1919 @param is_update is_update ? UPDATE command : DELETE command
1920 @param used_key_is_modified UPDATE updates used key column
1921
1922 @return false if success, true if error
1923 */
1924
explain_single_table_modification(THD * thd,TABLE * table,const SQL_SELECT * select,uint key,ha_rows limit,bool need_tmp_table,bool need_sort,bool is_update,bool used_key_is_modified)1925 bool explain_single_table_modification(THD *thd,
1926 TABLE *table,
1927 const SQL_SELECT *select,
1928 uint key,
1929 ha_rows limit,
1930 bool need_tmp_table,
1931 bool need_sort,
1932 bool is_update,
1933 bool used_key_is_modified)
1934 {
1935 DBUG_ENTER("explain_single_table_modification");
1936 const bool ret= Explain_table(thd, table, select, key, limit,
1937 need_tmp_table, need_sort, is_update,
1938 used_key_is_modified).send();
1939 DBUG_RETURN(ret);
1940 }
1941
1942
1943 /**
1944 EXPLAIN handling for EXPLAIN SELECT queries
1945
1946 Send QEP to the client.
1947
1948 @param thd current THD
1949 @param join JOIN
1950 @param need_tmp_table true if it requires a temporary table --
1951 "Using temporary" string in the "extra" column.
1952 @param need_order true if it requires filesort() -- "Using filesort"
1953 string in the "extra" column.
1954 @param distinct true if there is the DISTINCT clause (not optimized
1955 out) -- "Distinct" string in the "extra" column.
1956
1957 @return false if success, true if error
1958 */
1959
explain_query_specification(THD * thd,JOIN * join)1960 bool explain_query_specification(THD *thd, JOIN *join)
1961 {
1962 const Explain_format_flags *flags= &join->explain_flags;
1963 const bool need_tmp_table= flags->any(ESP_USING_TMPTABLE);
1964 const bool need_order= flags->any(ESP_USING_FILESORT);
1965 const bool distinct= flags->get(ESC_DISTINCT, ESP_EXISTS);
1966
1967 DBUG_ENTER("explain_query_specification");
1968 DBUG_PRINT("info", ("Select %p, type %s",
1969 join->select_lex, join->select_lex->get_type_str(thd)));
1970 bool ret;
1971 if (join->select_lex == join->unit->fake_select_lex)
1972 ret= Explain_union_result(thd, join).send();
1973 else
1974 ret= Explain_join(thd, join, need_tmp_table, need_order, distinct).send();
1975 DBUG_RETURN(ret);
1976 }
1977
1978
1979 /**
1980 EXPLAIN handling for INSERT, REPLACE and multi-table UPDATE/DELETE queries
1981
1982 Send to the client a QEP data set for data-modifying commands those have a
1983 regular JOIN tree (INSERT...SELECT, REPLACE...SELECT and multi-table
1984 UPDATE and DELETE queries) like mysql_select() does for SELECT queries in
1985 the "describe" mode.
1986
1987 @note see explain_single_table_modification() for single-table
1988 UPDATE/DELETE EXPLAIN handling.
1989
1990 @note Unlike the mysql_select function, explain_multi_table_modification
1991 calls abort_result_set() itself in the case of failure (OOM etc.)
1992 since explain_multi_table_modification() uses internally created
1993 select_result stream.
1994
1995 @param thd current THD
1996 @param result pointer to select_insert, multi_delete or multi_update object:
1997 the function uses it to call result->prepare(),
1998 result->prepare2() and result->initialize_tables() only but
1999 not to modify table data or to send a result to client.
2000 @return false if success, true if error
2001 */
2002
explain_multi_table_modification(THD * thd,select_result_interceptor * result)2003 bool explain_multi_table_modification(THD *thd,
2004 select_result_interceptor *result)
2005 {
2006 DBUG_ENTER("explain_multi_table_modification");
2007 explain_send explain(result);
2008 bool res= explain_query_expression(thd, &explain);
2009 DBUG_RETURN(res);
2010 }
2011
2012
2013 /**
2014 EXPLAIN handling for SELECT and table-modifying queries that have JOIN
2015
2016 Send to the client a QEP data set for SELECT or data-modifying commands
2017 those have a regular JOIN tree (INSERT...SELECT, REPLACE...SELECT and
2018 multi-table UPDATE and DELETE queries) like mysql_select() does for SELECT
2019 queries in the "describe" mode.
2020
2021 @note see explain_single_table_modification() for single-table
2022 UPDATE/DELETE EXPLAIN handling.
2023
2024 @note explain_query_expression() calls abort_result_set() itself in the
2025 case of failure (OOM etc.) since explain_multi_table_modification()
2026 uses internally created select_result stream.
2027
2028 @param thd current THD
2029 @param result pointer to select_result, select_insert, multi_delete or
2030 multi_update object: the function uses it to call
2031 result->prepare(), result->prepare2() and
2032 result->initialize_tables() only but not to modify table data
2033 or to send a result to client.
2034 @return false if success, true if error
2035 */
2036
explain_query_expression(THD * thd,select_result * result)2037 bool explain_query_expression(THD *thd, select_result *result)
2038 {
2039 DBUG_ENTER("explain_query_expression");
2040 const bool res= thd->lex->explain_format->send_headers(result) ||
2041 mysql_explain_unit(thd, &thd->lex->unit, result) ||
2042 thd->is_error();
2043 /*
2044 The code which prints the extended description is not robust
2045 against malformed queries, so skip it if we have an error.
2046 */
2047 if (!res && (thd->lex->describe & DESCRIBE_EXTENDED) &&
2048 thd->lex->sql_command == SQLCOM_SELECT) // TODO: implement for INSERT/etc
2049 {
2050 StringBuffer<1024> str;
2051 /*
2052 The warnings system requires input in utf8, see mysqld_show_warnings().
2053 */
2054 thd->lex->unit.print(&str, enum_query_type(QT_TO_SYSTEM_CHARSET |
2055 QT_SHOW_SELECT_NUMBER));
2056 str.append('\0');
2057 push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_YES, str.ptr());
2058 }
2059 if (res)
2060 result->abort_result_set();
2061 else
2062 result->send_eof();
2063 DBUG_RETURN(res);
2064 }
2065
2066
2067 /**
2068 Set SELECT_DESCRIBE flag for all unit's SELECT_LEXes
2069
2070 @param thd THD
2071 @param unit unit of SELECT_LEXes
2072 */
propagate_explain_option(THD * thd,SELECT_LEX_UNIT * unit)2073 static void propagate_explain_option(THD *thd, SELECT_LEX_UNIT *unit)
2074 {
2075 for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
2076 sl->options|= SELECT_DESCRIBE;
2077 }
2078
2079
2080 /**
2081 Explain UNION or subqueries of the unit
2082
2083 If the unit is a UNION, explain it as a UNION. Otherwise explain nested
2084 subselects.
2085
2086 @param thd thread object
2087 @param unit unit object
2088 @param result result stream to send QEP dataset
2089
2090 @return false if success, true if error
2091 */
mysql_explain_unit(THD * thd,SELECT_LEX_UNIT * unit,select_result * result)2092 bool mysql_explain_unit(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
2093 {
2094 DBUG_ENTER("mysql_explain_unit");
2095 bool res= 0;
2096
2097 propagate_explain_option(thd, unit);
2098
2099 if (unit->is_union())
2100 {
2101 unit->fake_select_lex->select_number= UINT_MAX; // just for initialization
2102 unit->fake_select_lex->options|= SELECT_DESCRIBE;
2103
2104 res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE);
2105
2106 if (res)
2107 DBUG_RETURN(res);
2108
2109 /*
2110 If tables are not locked at this point, it means that we have delayed
2111 this step until after prepare stage (now), in order to do better
2112 partition pruning.
2113
2114 We need to lock tables now in order to proceed with the remaning
2115 stages of query optimization.
2116 */
2117 if (! thd->lex->is_query_tables_locked() &&
2118 lock_tables(thd, thd->lex->query_tables, thd->lex->table_count, 0))
2119 DBUG_RETURN(true);
2120
2121 res= unit->optimize();
2122
2123 if (!res)
2124 res= unit->explain();
2125 }
2126 else
2127 {
2128 SELECT_LEX *first= unit->first_select();
2129 thd->lex->current_select= first;
2130 unit->set_limit(unit->global_parameters);
2131 res= mysql_select(thd,
2132 first->table_list.first,
2133 first->with_wild, first->item_list,
2134 first->where,
2135 &first->order_list,
2136 &first->group_list,
2137 first->having,
2138 first->options | thd->variables.option_bits | SELECT_DESCRIBE,
2139 result, unit, first);
2140 }
2141 DBUG_RETURN(res || thd->is_error());
2142 }
2143
2144
2145