1 /* Copyright (c) 2011, 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 #include "opt_trace.h"
24 #include "opt_explain_json.h"
25
26 /**
27 Property names, former parts of traditional "extra" column
28
29 This array must be in sync with Extra_tag enum.
30 */
31 static const char *json_extra_tags[ET_total]=
32 {
33 NULL, // ET_none
34 "using_temporary_table", // ET_USING_TEMPORARY
35 "using_filesort", // ET_USING_FILESORT
36 "index_condition", // ET_USING_INDEX_CONDITION
37 NULL, // ET_USING
38 "range_checked_for_each_record", // ET_RANGE_CHECKED_FOR_EACH_RECORD
39 "pushed_condition", // ET_USING_WHERE_WITH_PUSHED_CONDITION
40 NULL, // ET_USING_WHERE
41 "not_exists", // ET_NOT_EXISTS
42 "using_MRR", // ET_USING_MRR
43 "using_index", // ET_USING_INDEX
44 "full_scan_on_NULL_key", // ET_FULL_SCAN_ON_NULL_KEY
45 "skip_open_table", // ET_SKIP_OPEN_TABLE
46 "open_frm_only", // ET_OPEN_FRM_ONLY
47 "open_full_table", // ET_OPEN_FULL_TABLE
48 "scanned_databases", // ET_SCANNED_DATABASES
49 "using_index_for_group_by", // ET_USING_INDEX_FOR_GROUP_BY
50 "distinct", // ET_DISTINCT
51 "loosescan", // ET_LOOSESCAN
52 NULL, // ET_START_TEMPORARY
53 NULL, // ET_END_TEMPORARY
54 "first_match", // ET_FIRST_MATCH
55 NULL, // ET_MATERIALIZE
56 NULL, // ET_START_MATERIALIZE
57 NULL, // ET_END_MATERIALIZE
58 NULL, // ET_SCAN
59 "using_join_buffer", // ET_USING_JOIN_BUFFER
60 "const_row_not_found", // ET_CONST_ROW_NOT_FOUND
61 "unique_row_not_found", // ET_UNIQUE_ROW_NOT_FOUND
62 "impossible_on_condition", // ET_IMPOSSIBLE_ON_CONDITION
63 "pushed_join" // ET_PUSHED_JOIN
64 };
65
66
67 // JSON key names
68 static const char K_ACCESS_TYPE[]= "access_type";
69 static const char K_ATTACHED_CONDITION[]= "attached_condition";
70 static const char K_ATTACHED_SUBQUERIES[]= "attached_subqueries";
71 static const char K_BUFFER_RESULT[]= "buffer_result";
72 static const char K_CACHEABLE[]= "cacheable";
73 static const char K_DEPENDENT[]= "dependent";
74 static const char K_DUPLICATES_REMOVAL[]= "duplicates_removal";
75 static const char K_FILTERED[]= "filtered";
76 static const char K_GROUPING_OPERATION[]= "grouping_operation";
77 static const char K_GROUP_BY_SUBQUERIES[]= "group_by_subqueries";
78 static const char K_HAVING_SUBQUERIES[]= "having_subqueries";
79 static const char K_KEY[]= "key";
80 static const char K_KEY_LENGTH[]= "key_length";
81 static const char K_MATERIALIZED_FROM_SUBQUERY[]= "materialized_from_subquery";
82 static const char K_MESSAGE[]= "message";
83 static const char K_NESTED_LOOP[]= "nested_loop";
84 static const char K_OPTIMIZATION_TIME_SUBQUERIES[]= "optimized_away_subqueries";
85 static const char K_ORDERING_OPERATION[]= "ordering_operation";
86 static const char K_ORDER_BY_SUBQUERIES[]= "order_by_subqueries";
87 static const char K_PARTITIONS[]= "partitions";
88 static const char K_POSSIBLE_KEYS[]= "possible_keys";
89 static const char K_QUERY_BLOCK[]= "query_block";
90 static const char K_QUERY_SPECIFICATIONS[]= "query_specifications";
91 static const char K_REF[]= "ref";
92 static const char K_ROWS[]= "rows";
93 static const char K_SELECT_ID[]= "select_id";
94 static const char K_SELECT_LIST_SUBQUERIES[]= "select_list_subqueries";
95 static const char K_TABLE[]= "table";
96 static const char K_TABLE_NAME[]= "table_name";
97 static const char K_UNION_RESULT[]= "union_result";
98 static const char K_UPDATE_VALUE_SUBQUERIES[]= "update_value_subqueries";
99 static const char K_USED_KEY_PARTS[]= "used_key_parts";
100 static const char K_USING_FILESORT[]= "using_filesort";
101 static const char K_USING_TMP_TABLE[]= "using_temporary_table";
102
103
104 /*
105 see commentary at the beginning of opt_trace.cc
106 */
107 namespace opt_explain_json_namespace
108 {
109
110 class joinable_ctx;
111 class sort_ctx;
112 class subquery_ctx;
113 class union_result_ctx;
114
115 /**
116 @note Keep in sync with the @c list_names array.
117 */
118 enum subquery_list_enum
119 {
120 SQ_SELECT_LIST, ///< SELECT list subqueries
121 SQ_UPDATE_VALUE, ///< UPDATE ... SET field=(subquery)
122 SQ_HAVING, ///< HAVING clause subqueries
123 SQ_HOMELESS, ///< "optimized_away_subqueries"
124 //--------------
125 SQ_toplevel, ///< SQ array size for unit_ctx
126 //--------------
127 SQ_ORDER_BY, ///< ORDER BY clause subqueries
128 SQ_GROUP_BY, ///< GROUP BY clause subqueries
129 //--------------
130 SQ_total
131 };
132
133 /**
134 @note Keep in sync with @c subquery_list_enum.
135 */
136 static const char *list_names[SQ_total]=
137 {
138 K_SELECT_LIST_SUBQUERIES,
139 K_UPDATE_VALUE_SUBQUERIES,
140 K_HAVING_SUBQUERIES,
141 K_OPTIMIZATION_TIME_SUBQUERIES,
142 "",
143 K_ORDER_BY_SUBQUERIES,
144 K_GROUP_BY_SUBQUERIES,
145 };
146
147
148 /**
149 Base class for all intermediate tree nodes
150 */
151
152 class context : public Explain_context
153 {
154 protected:
155 const char *name;
156
157 public:
158 context *parent; ///< link to parent node or NULL
159
context(Explain_context_enum type_arg,const char * name_arg,context * parent_arg)160 context(Explain_context_enum type_arg, const char *name_arg,
161 context *parent_arg)
162 : Explain_context(type_arg),
163 name(name_arg),
164 parent(parent_arg)
165 {}
166
~context()167 virtual ~context() {}
168
169 /**
170 Pass the node with its child nodes to a JSON formatter
171
172 @param json Formatter
173
174 @retval false Ok
175 @retval true Error
176
177 @note The @c join_ctx class overloads this function.
178 */
format(Opt_trace_context * json)179 virtual bool format(Opt_trace_context *json)
180 {
181 Opt_trace_object obj(json, name);
182 return format_body(json, &obj);
183 }
184
is_query_block() const185 bool is_query_block() const { return name == K_QUERY_BLOCK; }
186
187 private:
188 /**
189 Format JSON object body
190
191 @param json Formatter
192 @param obj Object of this body
193
194 @retval false Ok
195 @retval true Error
196 */
197 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)= 0;
198
199 public:
200 /**
201 Analogue of the "id" column in the traditional EXPLAIN output
202
203 @param hide if true, ban the output of K_SELECT_ID JSON property
204 in the underlying table_with_where_and_derived_ctx
205 objects
206
207 @returns "Select number" that is associated with this node
208 */
209 virtual size_t id(bool hide= false)= 0;
210
cacheable()211 virtual bool cacheable() { DBUG_ASSERT(0); return true; }
dependent()212 virtual bool dependent() { DBUG_ASSERT(0); return false; }
213
entry()214 virtual class qep_row *entry() { DBUG_ASSERT(0); return NULL; }
215
216 /**
217 Associate a child node with this node
218
219 This function is to be overloaded by subquery_ctx.
220 */
set_child(context * child)221 virtual void set_child(context *child) {}
222
223 /// associate CTX_UNION_RESULT node with CTX_UNION node
set_union_result(union_result_ctx * ctx)224 virtual void set_union_result(union_result_ctx *ctx) { DBUG_ASSERT(0); }
225
226 /**
227 Append a subquery node to the specified list of the unit node
228
229 @param subquery_type Describes the Item tree where the subquery exists
230 @param ctx Subquery node
231
232 @retval false Ok
233 @retval true Error
234 */
add_subquery(subquery_list_enum subquery_type,subquery_ctx * ctx)235 virtual bool add_subquery(subquery_list_enum subquery_type,
236 subquery_ctx *ctx) { DBUG_ASSERT(0);return true; }
237 /**
238 Format nested loop join subtree (if any) to JSON formatter
239
240 @param json Formatter
241
242 @retval false Ok
243 @retval true Error
244 */
format_nested_loop(Opt_trace_context * json)245 virtual bool format_nested_loop(Opt_trace_context *json)
246 { DBUG_ASSERT(0); return true; }
247
248 /**
249 Add a CTX_JOIN_TAB node to a CTX_JOIN node
250
251 @param ctx CTX_JOIN_TAB node
252
253 @retval false Ok
254 @retval true Error
255 */
add_join_tab(joinable_ctx * ctx)256 virtual bool add_join_tab(joinable_ctx *ctx) { DBUG_ASSERT(0);return true; }
257
258 /**
259 Set nested ORDER BY/GROUP BY/DISTINCT node to @c ctx
260
261 @param json Formatter
262
263 @retval false Ok
264 @retval true Error
265 */
set_sort(sort_ctx * ctx)266 virtual void set_sort(sort_ctx *ctx) { DBUG_ASSERT(0); }
267
268 /**
269 Add a query specification node to the CTX_UNION node
270
271 @param ctx query specification node
272
273 @retval false Ok
274 @retval true Error
275 */
add_query_spec(context * ctx)276 virtual bool add_query_spec(context *ctx) { DBUG_ASSERT(0); return true; }
277
278 /**
279 Try to associate a derived subquery node with this or underlying node
280
281 @param subquery Derived subquery node
282
283 @retval true Success
284 @retval false Can't associate: this node or its child nodes are not
285 derived from the subquery
286 */
find_and_set_derived(context * subquery)287 virtual bool find_and_set_derived(context *subquery)
288 {
289 DBUG_ASSERT(0);
290 return false;
291 }
292
293 /**
294 Associate WHERE subqueries of given context and unit with this object
295
296 @param ctx Context of WHERE subquery
297 @param subquery For CTX_JOIN_TAB: match given unit with a previously
298 collected by the register_where_subquery function.
299 */
add_where_subquery(subquery_ctx * ctx,SELECT_LEX_UNIT * subquery)300 virtual bool add_where_subquery(subquery_ctx *ctx,
301 SELECT_LEX_UNIT *subquery)
302 {
303 DBUG_ASSERT(0);
304 return false;
305 }
306
307 /// Helper function to format output for derived subquery if any
format_derived(Opt_trace_context * json)308 virtual bool format_derived(Opt_trace_context *json) { return false; }
309
310 /// Helper function to format output for associated WHERE subqueries if any
format_where(Opt_trace_context * json)311 virtual bool format_where(Opt_trace_context *json) { return false; }
312
313 /// Helper function to format output for HAVING, ORDER/GROUP BY subqueries
format_unit(Opt_trace_context * json)314 virtual bool format_unit(Opt_trace_context *json) { return false; }
315 };
316
317
318 /**
319 Node class to wrap a subquery node tree
320
321 Implements CTX_WHERE, CTX_HAVING, CTX_ORDER_BY_SQ, CTX_GROUP_BY_SQ and
322 CTX_OPTIMIZED_AWAY_SUBQUERY context nodes.
323 This class hosts underlying join_ctx or uion_ctx.
324 */
325
326 class subquery_ctx : virtual public context, public qep_row
327 {
328 /*
329 TODO: After the conversion from multiple inheritace to templates
330 convert "context" to "unit_ctx" (common base of uion_ctx & join_ctx).
331 */
332 context *subquery; ///< hosted subquery tree: CTX_JOIN or CTX_UNION
333
334 public:
subquery_ctx(Explain_context_enum type_arg,const char * name_arg,context * parent_arg)335 subquery_ctx(Explain_context_enum type_arg,
336 const char *name_arg, context *parent_arg)
337 : context(type_arg, name_arg, parent_arg),
338 subquery(NULL)
339 {}
340
entry()341 virtual qep_row *entry() { return this; }
342
343 /*
344 Materialized subquery statuses of dependency on the outer query and
345 cacheability may differ from the source subquery, for example, if
346 we "push down" the outer look up value for SJ.
347 Thus, for materialized subqueries return direct is_cacheable and
348 is_dependent values instead of source subquery statuses:
349 */
cacheable()350 virtual bool cacheable()
351 {
352 return is_materialized_from_subquery ? is_cacheable : subquery->cacheable();
353 }
dependent()354 virtual bool dependent()
355 {
356 return is_materialized_from_subquery ? is_dependent : subquery->dependent();
357 }
358
format(Opt_trace_context * json)359 virtual bool format(Opt_trace_context *json)
360 {
361 if (name)
362 return context::format(json);
363 else
364 {
365 /*
366 Subquery is always a homogeneous array element,
367 create anonymous wrapper object:
368 */
369 Opt_trace_object anonymous_wrapper(json);
370 return format_body(json, &anonymous_wrapper);
371 }
372 }
373
374 private:
format_body(Opt_trace_context * json,Opt_trace_object * obj)375 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
376 {
377 if (type == CTX_DERIVED)
378 {
379 obj->add(K_USING_TMP_TABLE, true);
380 obj->add(K_DEPENDENT, dependent());
381 obj->add(K_CACHEABLE, cacheable());
382 return subquery->format(json);
383 }
384 else if (using_temporary)
385 {
386 if (!is_materialized_from_subquery)
387 {
388 obj->add(K_USING_TMP_TABLE, true);
389 obj->add(K_DEPENDENT, dependent());
390 obj->add(K_CACHEABLE, cacheable());
391 }
392
393 {
394 Opt_trace_object tmp_table(json, K_TABLE);
395
396 if (!col_table_name.is_empty())
397 obj->add_utf8(K_TABLE_NAME, col_table_name.str);
398 if (!col_join_type.is_empty())
399 tmp_table.add_alnum(K_ACCESS_TYPE, col_join_type.str);
400 if (!col_key.is_empty())
401 tmp_table.add_utf8(K_KEY, col_key.str);
402 if (!col_key_len.is_empty())
403 obj->add_alnum(K_KEY_LENGTH, col_key_len.str);
404 if (!col_rows.is_empty())
405 tmp_table.add(K_ROWS, col_rows.value);
406
407 if (is_materialized_from_subquery)
408 {
409 Opt_trace_object materialized(json, K_MATERIALIZED_FROM_SUBQUERY);
410 obj->add(K_USING_TMP_TABLE, true);
411 obj->add(K_DEPENDENT, dependent());
412 obj->add(K_CACHEABLE, cacheable());
413 return format_query_block(json);
414 }
415 }
416 return format_query_block(json);
417 }
418 else
419 {
420 obj->add(K_DEPENDENT, dependent());
421 obj->add(K_CACHEABLE, cacheable());
422 return subquery->format(json);
423 }
424 }
425
format_query_block(Opt_trace_context * json)426 bool format_query_block(Opt_trace_context *json)
427 {
428 if (subquery->is_query_block())
429 return subquery->format(json);
430
431 Opt_trace_object query_block(json, K_QUERY_BLOCK);
432 return subquery->format(json);
433 }
434
435
436 public:
set_child(context * child)437 virtual void set_child(context *child)
438 {
439 DBUG_ASSERT(subquery == NULL);
440 DBUG_ASSERT(child->type == CTX_JOIN || child->type == CTX_UNION);
441 subquery= child;
442 }
443
id(bool hide)444 virtual size_t id(bool hide) { return subquery->id(hide); }
445 };
446
447
448 /**
449 Helper function to pass a subquery list to a JSON formatter
450
451 @param json output formatter
452 @param subqueries subquery list to output
453 @param name name for the output section
454
455 @retval false Ok
456 @retval true Error
457 */
format_list(Opt_trace_context * json,List<subquery_ctx> & subqueries,const char * name)458 static bool format_list(Opt_trace_context *json,
459 List<subquery_ctx> &subqueries,
460 const char *name)
461 {
462 if (!subqueries.is_empty())
463 {
464 Opt_trace_array subs(json, name);
465
466 List_iterator<subquery_ctx> it(subqueries);
467 subquery_ctx *t;
468 while ((t= it++))
469 {
470 // Homogeneous array: additional anonymous wrapper object is not needed
471 if (t->format(json))
472 return true;
473 }
474 }
475 return false;
476 }
477
478
479 /**
480 Helper base class to host HAVING, ORDER BY and GROUP BY subquery nodes
481 */
482 class unit_ctx : virtual public context
483 {
484 List<subquery_ctx> subquery_lists[SQ_toplevel];
485
486 public:
unit_ctx(Explain_context_enum type_arg,const char * name_arg,context * parent_arg)487 unit_ctx(Explain_context_enum type_arg, const char *name_arg,
488 context *parent_arg)
489 : context(type_arg, name_arg, parent_arg)
490 {}
491
492 /**
493 Helper function to distinguish subquery-less nodes
494
495 @retval true Node hosts no subqueries
496 @retval false Node hosts some subqueries
497 */
has_no_subqueries() const498 bool has_no_subqueries() const
499 {
500 for (size_t i= 0; i < SQ_toplevel; i++)
501 {
502 if (!subquery_lists[i].is_empty())
503 return false;
504 }
505 return true;
506 }
507
format_unit(Opt_trace_context * json)508 virtual bool format_unit(Opt_trace_context *json)
509 {
510 for (size_t i= 0; i < SQ_toplevel; i++)
511 {
512 if (format_list(json, subquery_lists[i], list_names[i]))
513 return true;
514 }
515 return false;
516 }
517
add_subquery(subquery_list_enum subquery_type,subquery_ctx * ctx)518 virtual bool add_subquery(subquery_list_enum subquery_type,
519 subquery_ctx *ctx)
520 {
521 DBUG_ASSERT(subquery_type < SQ_toplevel);
522 return subquery_lists[subquery_type].push_back(ctx);
523 }
524 };
525
526
527 class table_base_ctx : virtual public context, public qep_row
528 {
529 protected:
530 bool is_hidden_id; //< if true, don't output K_SELECT_ID property
531
532 public:
table_base_ctx(Explain_context_enum type_arg,const char * name_arg,context * parent_arg)533 table_base_ctx(Explain_context_enum type_arg,
534 const char *name_arg, context *parent_arg)
535 : context(type_arg, name_arg, parent_arg), is_hidden_id(false)
536 {}
537
entry()538 virtual qep_row *entry() { return this; }
539
540 protected:
541 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj);
542
543 public:
id(bool hide)544 virtual size_t id(bool hide)
545 {
546 return col_id.is_empty() ? 0 : col_id.value;
547 }
548
cacheable()549 virtual bool cacheable() { return is_cacheable; }
dependent()550 virtual bool dependent() { return is_dependent; }
551 };
552
553
add_string_array(Opt_trace_context * json,const char * list_name,List<const char> & strings)554 static void add_string_array(Opt_trace_context *json, const char *list_name,
555 List<const char> &strings)
556 {
557 if (!strings.is_empty())
558 {
559 Opt_trace_array extra(json, list_name);
560
561 List_iterator<const char> it(strings);
562 const char *s;
563 while ((s= it++))
564 extra.add_utf8(s);
565 }
566 }
567
format_body(Opt_trace_context * json,Opt_trace_object * obj)568 bool table_base_ctx::format_body(Opt_trace_context *json, Opt_trace_object *obj)
569 {
570 StringBuffer<64> buff;
571
572 if (is_update)
573 obj->add("update", true);
574
575 if (is_delete)
576 obj->add("delete", true);
577
578 if (!col_id.is_empty() && !is_hidden_id)
579 obj->add(K_SELECT_ID, col_id.value);
580
581 if (!col_table_name.is_empty())
582 obj->add_utf8(K_TABLE_NAME, col_table_name.str);
583
584 add_string_array(json, K_PARTITIONS, col_partitions);
585
586 if (!col_join_type.is_empty())
587 obj->add_alnum(K_ACCESS_TYPE, col_join_type.str);
588
589 add_string_array(json, K_POSSIBLE_KEYS, col_possible_keys);
590
591 if (!col_key.is_empty())
592 obj->add_utf8(K_KEY, col_key.str);
593
594 if (!col_key_parts.is_empty())
595 add_string_array(json, K_USED_KEY_PARTS, col_key_parts);
596
597 if (!col_key_len.is_empty())
598 obj->add_alnum(K_KEY_LENGTH, col_key_len.str);
599
600 add_string_array(json, K_REF, col_ref);
601
602 if (!col_rows.is_empty())
603 obj->add(K_ROWS, col_rows.value);
604
605 if (!col_filtered.is_empty())
606 obj->add(K_FILTERED, col_filtered.value);
607
608 if (!col_extra.is_empty())
609 {
610 List_iterator<qep_row::extra> it(col_extra);
611 qep_row::extra *e;
612 while ((e= it++))
613 {
614 DBUG_ASSERT(json_extra_tags[e->tag] != NULL);
615 if (e->data)
616 obj->add_utf8(json_extra_tags[e->tag], e->data);
617 else
618 obj->add(json_extra_tags[e->tag], true);
619 }
620 }
621
622 if (!col_message.is_empty())
623 {
624 DBUG_ASSERT(col_extra.is_empty());
625 obj->add_alnum(K_MESSAGE, col_message.str);
626 }
627
628 { // Keep together for better output readability
629 if (!col_attached_condition.is_empty())
630 obj->add_utf8(K_ATTACHED_CONDITION, col_attached_condition.str);
631 if (format_where(json))
632 return true;
633 }
634
635 return format_derived(json) || format_unit(json);
636 }
637
638
639 /**
640 Node class for the CTX_UNION_RESULT
641 */
642 class union_result_ctx : public table_base_ctx, public unit_ctx
643 {
644 List<context> *query_specs; ///< query specification nodes (inner selects)
645 List<subquery_ctx> order_by_subqueries;
646 List<subquery_ctx> homeless_subqueries;
647
648 public:
union_result_ctx(context * parent_arg)649 explicit union_result_ctx(context *parent_arg)
650 : context(CTX_UNION_RESULT, K_UNION_RESULT, parent_arg),
651 table_base_ctx(CTX_UNION_RESULT, K_UNION_RESULT, parent_arg),
652 unit_ctx(CTX_UNION_RESULT, K_UNION_RESULT, parent_arg)
653 {}
654
655 // Remove warnings: 'inherits ... from ... via dominance'
id(bool hide)656 virtual size_t id(bool hide) { return table_base_ctx::id(hide); }
cacheable()657 virtual bool cacheable() { return table_base_ctx::cacheable(); }
dependent()658 virtual bool dependent() { return table_base_ctx::dependent(); }
entry()659 virtual qep_row *entry() { return table_base_ctx::entry(); }
format_unit(Opt_trace_context * json)660 virtual bool format_unit(Opt_trace_context *json)
661 { return table_base_ctx::format_unit(json); }
662
push_down_query_specs(List<context> * specs)663 void push_down_query_specs(List<context> *specs) { query_specs= specs; }
664
add_subquery(subquery_list_enum subquery_type,subquery_ctx * ctx)665 virtual bool add_subquery(subquery_list_enum subquery_type,
666 subquery_ctx *ctx)
667 {
668 switch (subquery_type) {
669 case SQ_ORDER_BY:
670 return order_by_subqueries.push_back(ctx);
671 case SQ_HOMELESS:
672 return homeless_subqueries.push_back(ctx);
673 default:
674 DBUG_ASSERT(!"Unknown query type!");
675 return false; // ignore in production
676 }
677 }
678
format(Opt_trace_context * json)679 virtual bool format(Opt_trace_context *json)
680 {
681 if (order_by_subqueries.is_empty() && homeless_subqueries.is_empty())
682 return table_base_ctx::format(json);
683
684 Opt_trace_object order_by(json, K_ORDERING_OPERATION);
685
686 order_by.add(K_USING_FILESORT, !order_by_subqueries.is_empty());
687
688 if (table_base_ctx::format(json))
689 return true;
690
691 if (!order_by_subqueries.is_empty() &&
692 format_list(json, order_by_subqueries, K_ORDER_BY_SUBQUERIES))
693 return true;
694
695 if (!homeless_subqueries.is_empty() &&
696 format_list(json, homeless_subqueries, K_OPTIMIZATION_TIME_SUBQUERIES))
697 return true;
698
699 return false;
700 }
701
format_body(Opt_trace_context * json,Opt_trace_object * obj)702 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
703 {
704 obj->add(K_USING_TMP_TABLE, true);
705
706 if (table_base_ctx::format_body(json, obj))
707 return true;
708
709 Opt_trace_array specs(json, K_QUERY_SPECIFICATIONS);
710
711 List_iterator<context> it(*query_specs);
712 context *ctx;
713 while ((ctx= it++))
714 {
715 if (ctx->format(json))
716 return true;
717 }
718 return false;
719 }
720 };
721
722
723 /**
724 Common part of CTX_JOIN_TAB and CTX_MESSAGE nodes
725
726 This class implements functionality for WHERE and derived subqueries that
727 are associated with the table node.
728 */
729 class table_with_where_and_derived : public table_base_ctx
730 {
731 protected:
732 List<subquery_ctx> where_subqueries; ///< associated WHERE clause subqueries
733
734 public:
table_with_where_and_derived(Explain_context_enum type_arg,const char * name_arg,context * parent_arg)735 table_with_where_and_derived(Explain_context_enum type_arg,
736 const char *name_arg, context *parent_arg)
737 : context(type_arg, name_arg, parent_arg),
738 table_base_ctx(type_arg, name_arg, parent_arg)
739 {}
740
id(bool hide)741 virtual size_t id(bool hide)
742 {
743 if (hide)
744 is_hidden_id= true;
745 return table_base_ctx::id(hide);
746 }
747
format_where(Opt_trace_context * json)748 virtual bool format_where(Opt_trace_context *json)
749 {
750 return format_list(json, where_subqueries, K_ATTACHED_SUBQUERIES);
751 }
752
format_derived(Opt_trace_context * json)753 virtual bool format_derived(Opt_trace_context *json)
754 {
755 if (derived_from.elements == 0)
756 return false;
757 else if (derived_from.elements == 1)
758 return derived_from.head()->format(json);
759 else
760 {
761 Opt_trace_array loops(json, K_NESTED_LOOP);
762
763 List_iterator<context> it(derived_from);
764 context *c;
765 while((c= it++))
766 {
767 Opt_trace_object anonymous_wrapper(json);
768 if (c->format(json))
769 return true;
770 }
771 }
772 return false;
773 }
774 };
775
776
777 /**
778 Base for CTX_JOIN_TAB, CTX_DUPLICATES_WEEDOUT and CTX_MATERIALIZATION nodes
779
780 This class implements a base to explain individual JOIN_TABs as well
781 as JOIN_TAB groups like in semi-join materialization.
782 */
783 class joinable_ctx : virtual public context
784 {
785 public:
joinable_ctx(Explain_context_enum type_arg,const char * name_arg,context * parent_arg)786 joinable_ctx(Explain_context_enum type_arg, const char *name_arg,
787 context *parent_arg)
788 : context(type_arg, name_arg, parent_arg)
789 {}
790 };
791
792
793 /**
794 Node class for CTX_MESSAGE
795
796 This class is designed to represent fake tables with some messages in the
797 "extra" column ("Impossible where" etc).
798 We do EXPLAIN of these fake tables to replace explanation of:
799 1) usual actual JOIN_TABs of the whole JOIN or
800 2) a modifying TABLE of single-table UPDATE/DELETE/etc.
801 So, message_ctx always represent a single half-empty fake table in a
802 "query_block" node with optional subqueries.
803 */
804 class message_ctx : public joinable_ctx,
805 public table_with_where_and_derived
806 {
807 public:
message_ctx(context * parent_arg)808 explicit message_ctx(context *parent_arg)
809 : context(CTX_MESSAGE, K_TABLE, parent_arg),
810 joinable_ctx(CTX_MESSAGE, K_TABLE, parent_arg),
811 table_with_where_and_derived(CTX_MESSAGE, K_TABLE, parent_arg)
812 {}
813
814 // Remove warnings: 'inherits ... from ... via dominance'
format_body(Opt_trace_context * json,Opt_trace_object * obj)815 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
816 { return table_base_ctx::format_body(json, obj); }
id(bool hide)817 virtual size_t id(bool hide)
818 { return table_with_where_and_derived::id(hide); }
cacheable()819 virtual bool cacheable() { return table_base_ctx::cacheable(); }
dependent()820 virtual bool dependent() { return table_base_ctx::dependent(); }
entry()821 virtual qep_row *entry() { return table_base_ctx::entry(); }
format_derived(Opt_trace_context * json)822 virtual bool format_derived(Opt_trace_context *json)
823 { return table_with_where_and_derived::format_derived(json); }
format_where(Opt_trace_context * json)824 virtual bool format_where(Opt_trace_context *json)
825 { return table_with_where_and_derived::format_where(json); }
826
find_and_set_derived(context * subquery)827 virtual bool find_and_set_derived(context *subquery)
828 {
829 /*
830 message_ctx is designed to represent a single fake JOIN_TAB in the JOIN,
831 so if the JOIN have a derived table, then this message_ctx represent this
832 derived table.
833 Unconditionally add subquery:
834 */
835 derived_from.push_back(subquery);
836 return true;
837 }
838
add_where_subquery(subquery_ctx * ctx,SELECT_LEX_UNIT * subquery)839 virtual bool add_where_subquery(subquery_ctx *ctx,
840 SELECT_LEX_UNIT *subquery)
841 {
842 return where_subqueries.push_back(ctx);
843 }
844 };
845
846
847 /**
848 Node class for the CTX_JOIN_TAB context
849 */
850 class join_tab_ctx : public joinable_ctx,
851 public table_with_where_and_derived
852 {
853 /**
854 Subquery units that are associated with this JOIN_TAB's condition
855
856 This list is used to match with the @c subquery parameter of
857 the @c add_where_subquery function.
858 */
859 List<SELECT_LEX_UNIT> where_subquery_units;
860
861 public:
join_tab_ctx(Explain_context_enum type_arg,context * parent_arg)862 join_tab_ctx(Explain_context_enum type_arg, context *parent_arg)
863 : context(type_arg, K_TABLE, parent_arg),
864 joinable_ctx(type_arg, K_TABLE, parent_arg),
865 table_with_where_and_derived(type_arg, K_TABLE, parent_arg)
866 {}
867
868 // Remove warnings: 'inherits ... from ... via dominance'
format_body(Opt_trace_context * json,Opt_trace_object * obj)869 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
870 { return table_base_ctx::format_body(json, obj); }
id(bool hide)871 virtual size_t id(bool hide)
872 { return table_with_where_and_derived::id(hide); }
cacheable()873 virtual bool cacheable() { return table_base_ctx::cacheable(); }
dependent()874 virtual bool dependent() { return table_base_ctx::dependent(); }
entry()875 virtual qep_row *entry() { return table_base_ctx::entry(); }
format_derived(Opt_trace_context * json)876 virtual bool format_derived(Opt_trace_context *json)
877 { return table_with_where_and_derived::format_derived(json); }
format_where(Opt_trace_context * json)878 virtual bool format_where(Opt_trace_context *json)
879 { return table_with_where_and_derived::format_where(json); }
880
register_where_subquery(SELECT_LEX_UNIT * subquery)881 virtual void register_where_subquery(SELECT_LEX_UNIT *subquery)
882 {
883 List_iterator<SELECT_LEX_UNIT> it(where_subquery_units);
884 SELECT_LEX_UNIT *u;
885 while ((u= it++))
886 {
887 /*
888 The server may transform (x = (SELECT FROM DUAL)) to
889 (x <=> (SELECT FROM DUAL) AND x = (SELECT FROM DUAL)),
890 so ignore duplicates:
891 */
892 if(u == subquery)
893 return;
894 }
895 where_subquery_units.push_back(subquery);
896 }
897
add_where_subquery(subquery_ctx * ctx,SELECT_LEX_UNIT * subquery)898 virtual bool add_where_subquery(subquery_ctx *ctx,
899 SELECT_LEX_UNIT *subquery)
900 {
901 List_iterator<SELECT_LEX_UNIT> it(where_subquery_units);
902 SELECT_LEX_UNIT *u;
903 while ((u= it++))
904 {
905 if (u == subquery)
906 return where_subqueries.push_back(ctx);
907 }
908 return false;
909 }
910
find_and_set_derived(context * subquery)911 virtual bool find_and_set_derived(context *subquery)
912 {
913 if (query_block_id == subquery->id())
914 {
915 derived_from.push_back(subquery);
916 return true;
917 }
918 return false;
919 }
920 };
921
922
923 /**
924 Base class for CTX_ORDER_BY, CTX_GROUP_BY and node class for CTX_DISTINCT
925
926 This class represents context for simple ORDER BY/GROUP BY/DISTINCT clauses
927 (the clause is effective for the single JOIN_TAB).
928 */
929
930 class simple_sort_ctx : public joinable_ctx
931 {
932 protected:
933 joinable_ctx *join_tab; //< single JOIN_TAB that we sort
934
935 private:
936 const bool using_tmptable; //< true if the clause creates intermediate table
937 const bool using_filesort; //< true if the clause uses filesort
938
939 public:
simple_sort_ctx(Explain_context_enum type_arg,const char * name_arg,context * parent_arg,const Explain_format_flags * flags,Explain_sort_clause clause)940 simple_sort_ctx(Explain_context_enum type_arg, const char *name_arg,
941 context *parent_arg,
942 const Explain_format_flags *flags,
943 Explain_sort_clause clause)
944 : context(type_arg, name_arg, parent_arg),
945 joinable_ctx(type_arg, name_arg, parent_arg),
946 join_tab(NULL),
947 using_tmptable(flags->get(clause, ESP_USING_TMPTABLE)),
948 using_filesort(flags->get(clause, ESP_USING_FILESORT))
949 {}
950
add_join_tab(joinable_ctx * ctx)951 virtual bool add_join_tab(joinable_ctx *ctx)
952 {
953 join_tab= ctx;
954 return false;
955 }
956
add_where_subquery(subquery_ctx * ctx,SELECT_LEX_UNIT * subquery)957 virtual bool add_where_subquery(subquery_ctx *ctx,
958 SELECT_LEX_UNIT *subquery)
959 {
960 return join_tab->add_where_subquery(ctx, subquery);
961 }
962
find_and_set_derived(context * subquery)963 virtual bool find_and_set_derived(context *subquery)
964 {
965 return join_tab->find_and_set_derived(subquery);
966 }
967
id(bool hide)968 virtual size_t id(bool hide) { return join_tab->id(hide); }
cacheable()969 virtual bool cacheable() { return join_tab->cacheable(); }
dependent()970 virtual bool dependent() { return join_tab->dependent(); }
971
972 protected:
format_body(Opt_trace_context * json,Opt_trace_object * obj)973 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
974 {
975 if (using_tmptable)
976 obj->add(K_USING_TMP_TABLE, true);
977 obj->add(K_USING_FILESORT, using_filesort);
978 return join_tab->format(json);
979 }
980 };
981
982
983 /**
984 Node class for "simple" CTX_ORDER_BY and CTX_GROUP_BY
985
986 This class represents context for simple ORDER BY or GROUP BY clauses
987 (the clause is effective for the single JOIN_TAB).
988 */
989
990 class simple_sort_with_subqueries_ctx : public simple_sort_ctx
991 {
992 const subquery_list_enum subquery_type; //< type of this clause subqueries
993 List<subquery_ctx> subqueries;
994
995 public:
simple_sort_with_subqueries_ctx(Explain_context_enum type_arg,const char * name_arg,context * parent_arg,subquery_list_enum subquery_type_arg,const Explain_format_flags * flags,Explain_sort_clause clause)996 simple_sort_with_subqueries_ctx(Explain_context_enum type_arg,
997 const char *name_arg,
998 context *parent_arg,
999 subquery_list_enum subquery_type_arg,
1000 const Explain_format_flags *flags,
1001 Explain_sort_clause clause)
1002 : context(type_arg, name_arg, parent_arg),
1003 simple_sort_ctx(type_arg, name_arg, parent_arg, flags, clause),
1004 subquery_type(subquery_type_arg)
1005 {}
1006
add_subquery(subquery_list_enum subquery_type_arg,subquery_ctx * ctx)1007 virtual bool add_subquery(subquery_list_enum subquery_type_arg,
1008 subquery_ctx *ctx)
1009 {
1010 if (subquery_type != subquery_type_arg)
1011 return simple_sort_ctx::add_subquery(subquery_type_arg, ctx);
1012 else
1013 return subqueries.push_back(ctx);
1014 }
1015
1016 private:
format_body(Opt_trace_context * json,Opt_trace_object * obj)1017 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
1018 {
1019 return (simple_sort_ctx::format_body(json, obj) ||
1020 (format_list(json, subqueries, list_names[subquery_type])));
1021 }
1022 };
1023
1024
1025 /**
1026 Node class for the CTX_JOIN context
1027 */
1028
1029 class join_ctx : public unit_ctx
1030 {
1031 protected:
1032 List<joinable_ctx> join_tabs; ///< hosted JOIN_TAB nodes
1033 sort_ctx *sort;
1034
1035 public:
join_ctx(Explain_context_enum type_arg,const char * name_arg,context * parent_arg)1036 join_ctx(Explain_context_enum type_arg, const char *name_arg,
1037 context *parent_arg)
1038 : context(type_arg, name_arg, parent_arg),
1039 unit_ctx(type_arg, name_arg, parent_arg),
1040 sort(0)
1041 {}
1042
add_join_tab(joinable_ctx * ctx)1043 virtual bool add_join_tab(joinable_ctx *ctx)
1044 {
1045 return join_tabs.push_back(ctx);
1046 }
1047
set_sort(sort_ctx * ctx)1048 virtual void set_sort(sort_ctx *ctx)
1049 {
1050 DBUG_ASSERT(!sort);
1051 sort= ctx;
1052 }
1053
1054 /**
1055 Associate a CTX_DERIVED node with its CTX_JOIN_TAB node
1056
1057 @param subquery derived subquery tree
1058 */
1059 virtual bool find_and_set_derived(context *subquery);
1060
1061 virtual bool add_subquery(subquery_list_enum subquery_type,
1062 subquery_ctx *ctx);
1063 protected:
1064 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj);
1065
1066 public:
1067 virtual bool format_nested_loop(Opt_trace_context *json);
1068 virtual size_t id(bool hide);
1069 virtual bool cacheable();
1070 virtual bool dependent();
1071 virtual bool add_where_subquery(subquery_ctx *ctx,
1072 SELECT_LEX_UNIT *subquery);
1073 };
1074
1075
1076 /**
1077 Node class for CTX_SIMPLE_ORDER_BY, CTX_SIMPLE_GROUP_BY and CTX_SIMPLE_DISTINCT
1078
1079 CTX_JOIN context (see join_ctx class) may contain nested loop join node *or*
1080 ORDER BY/GROUP BY/DISTINCT node that is represented by this class:
1081
1082 join: { nested_loop: [ ... ] }
1083 or
1084 join: { order_by|group_by|distinct : { ... } }
1085
1086 CTX_ORDER_BY may contain nested loop join tree *or* GROUP BY/DISTINCT node:
1087
1088 order_by: { nested_loop|group_by|distinct: ... }
1089
1090 CTX_DISTINCT context structure:
1091
1092 distinct: { nested_loop|group_by: ... }
1093
1094 CTX_GROUP_BY:
1095
1096 group_by: { nested_loop: [ ... ] }
1097
1098 I.e. the most complex CTX_JOIN may have such a structure of JSON output as:
1099
1100 join: {
1101 order_by: {
1102 distinct: {
1103 group_by: {
1104 nested_loop: [ ... ]
1105 }
1106 }
1107 }
1108 }
1109 TODO
1110 */
1111
1112 class sort_ctx : public join_ctx
1113 {
1114 const bool using_tmptable; //< the clause creates temporary table
1115 const bool using_filesort; //< the clause uses filesort
1116
1117 public:
sort_ctx(Explain_context_enum type_arg,const char * name_arg,context * parent_arg,const Explain_format_flags * flags,Explain_sort_clause clause)1118 sort_ctx(Explain_context_enum type_arg, const char *name_arg,
1119 context *parent_arg,
1120 const Explain_format_flags *flags,
1121 Explain_sort_clause clause)
1122 : context(type_arg, name_arg, parent_arg),
1123 join_ctx(type_arg, name_arg, parent_arg),
1124 using_tmptable(flags->get(clause, ESP_USING_TMPTABLE)),
1125 using_filesort(flags->get(clause, ESP_USING_FILESORT))
1126 {}
1127
1128 protected:
format_body(Opt_trace_context * json,Opt_trace_object * obj)1129 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
1130 {
1131 DBUG_ASSERT(!sort || join_tabs.is_empty());
1132
1133 if (using_tmptable)
1134 obj->add(K_USING_TMP_TABLE, true);
1135 if (type != CTX_BUFFER_RESULT)
1136 obj->add(K_USING_FILESORT, using_filesort);
1137
1138 return join_ctx::format_body(json, obj);
1139 }
1140 };
1141
1142
1143 class sort_with_subqueries_ctx : public sort_ctx
1144 {
1145 const subquery_list_enum subquery_type; //< subquery type for this clause
1146 List<subquery_ctx> subqueries;
1147
1148 public:
sort_with_subqueries_ctx(Explain_context_enum type_arg,const char * name_arg,context * parent_arg,subquery_list_enum subquery_type_arg,const Explain_format_flags * flags,Explain_sort_clause clause)1149 sort_with_subqueries_ctx(Explain_context_enum type_arg, const char *name_arg,
1150 context *parent_arg,
1151 subquery_list_enum subquery_type_arg,
1152 const Explain_format_flags *flags,
1153 Explain_sort_clause clause)
1154 : context(type_arg, name_arg, parent_arg),
1155 sort_ctx(type_arg, name_arg, parent_arg, flags, clause),
1156 subquery_type(subquery_type_arg)
1157 {}
1158
add_subquery(subquery_list_enum subquery_type_arg,subquery_ctx * ctx)1159 virtual bool add_subquery(subquery_list_enum subquery_type_arg,
1160 subquery_ctx *ctx)
1161 {
1162 if (subquery_type_arg != subquery_type)
1163 return sort_ctx::add_subquery(subquery_type_arg, ctx);
1164 else
1165 return subqueries.push_back(ctx);
1166 return false;
1167 }
1168
1169 private:
format_body(Opt_trace_context * json,Opt_trace_object * obj)1170 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
1171 {
1172 return (sort_ctx::format_body(json, obj) ||
1173 format_list(json, subqueries, list_names[subquery_type]));
1174 }
1175 };
1176
1177
find_and_set_derived(context * subquery)1178 bool join_ctx::find_and_set_derived(context *subquery)
1179 {
1180 DBUG_ASSERT(subquery->id() != 0);
1181
1182 if (sort)
1183 return sort->find_and_set_derived(subquery);
1184
1185 List_iterator<joinable_ctx> it(join_tabs);
1186 joinable_ctx *t;
1187 while ((t= it++))
1188 {
1189 if (t->find_and_set_derived(subquery))
1190 return true;
1191 }
1192 return false;
1193 }
1194
1195
add_subquery(subquery_list_enum subquery_type,subquery_ctx * ctx)1196 bool join_ctx::add_subquery(subquery_list_enum subquery_type,
1197 subquery_ctx *ctx)
1198 {
1199 if (sort)
1200 return sort->add_subquery(subquery_type, ctx);
1201 if (subquery_type > SQ_toplevel)
1202 {
1203 List_iterator<joinable_ctx> it(join_tabs);
1204 joinable_ctx *j;
1205 while ((j= it++))
1206 {
1207 switch (j->type) {
1208 case CTX_ORDER_BY:
1209 case CTX_DISTINCT:
1210 case CTX_GROUP_BY:
1211 case CTX_SIMPLE_ORDER_BY:
1212 case CTX_SIMPLE_DISTINCT:
1213 case CTX_SIMPLE_GROUP_BY:
1214 return j->add_subquery(subquery_type, ctx);
1215 case CTX_MESSAGE:
1216 DBUG_ASSERT(subquery_type == SQ_ORDER_BY || subquery_type == SQ_GROUP_BY);
1217 /* As far as CTX_MESSAGE is actually an "optimized out" subquery,
1218 so ORDER/GROUP BY subqueries of such a subquery are "optimized out" as well,
1219 so we can replace ORDER/GROUP BY subquery type to SQ_HOMELESS:
1220 */
1221 return unit_ctx::add_subquery(SQ_HOMELESS, ctx);
1222 default: ;
1223 }
1224 }
1225 DBUG_ASSERT(0);
1226 }
1227 else
1228 return unit_ctx::add_subquery(subquery_type, ctx);
1229 return true;
1230 }
1231
1232
format_body(Opt_trace_context * json,Opt_trace_object * obj)1233 bool join_ctx::format_body(Opt_trace_context *json, Opt_trace_object *obj)
1234 {
1235 DBUG_ASSERT(!sort || join_tabs.is_empty());
1236 if (type == CTX_JOIN)
1237 obj->add(K_SELECT_ID, id(true));
1238 if (sort ? sort->format(json) : format_nested_loop(json))
1239 return true;
1240 return format_unit(json);
1241 }
1242
1243
format_nested_loop(Opt_trace_context * json)1244 bool join_ctx::format_nested_loop(Opt_trace_context *json)
1245 {
1246 DBUG_ASSERT(join_tabs.elements > 0);
1247
1248 /*
1249 For single table skip "nested_loop" object creation and
1250 format its contents only (the 1st join_tab).
1251 */
1252 if (join_tabs.elements == 1)
1253 return join_tabs.head()->format(json);
1254
1255 Opt_trace_array loops(json, K_NESTED_LOOP);
1256
1257 List_iterator<joinable_ctx> it(join_tabs);
1258 joinable_ctx *t;
1259 while ((t= it++))
1260 {
1261 Opt_trace_object anonymous_wrapper(json);
1262 if (t->format(json))
1263 return true;
1264 }
1265 return false;
1266 }
1267
1268
1269 /**
1270 Auxiliary function to walk through the list and propagate "hide" value
1271
1272 @param list list of context (*_ctx) objects
1273 @param hide if true, ban the output of K_SELECT_ID JSON property
1274 in the underlying table_with_where_and_derived_ctx
1275 objects
1276
1277 @return id of underlying objects
1278 */
1279 template<typename T>
get_id(List<T> & list,bool hide)1280 static size_t get_id(List<T> &list, bool hide)
1281 {
1282 if (!hide)
1283 return list.head()->id();
1284
1285 List_iterator<T> it(list);
1286 T *j;
1287 size_t ret= 0;
1288 while ((j= it++))
1289 ret= j->id(hide);
1290 return ret;
1291 }
1292
1293
id(bool hide)1294 size_t join_ctx::id(bool hide)
1295 {
1296 return sort ? sort->id(hide) : get_id(join_tabs, hide);
1297 }
1298
1299
cacheable()1300 bool join_ctx::cacheable()
1301 {
1302 return sort ? sort->cacheable() : join_tabs.head()->cacheable();
1303 }
1304
1305
dependent()1306 bool join_ctx::dependent()
1307 {
1308 return sort ? sort->dependent() : join_tabs.head()->dependent();
1309 }
1310
1311
add_where_subquery(subquery_ctx * ctx,SELECT_LEX_UNIT * subquery)1312 bool join_ctx::add_where_subquery(subquery_ctx *ctx,
1313 SELECT_LEX_UNIT *subquery)
1314 {
1315 if (sort)
1316 return sort->join_ctx::add_where_subquery(ctx, subquery);
1317
1318 List_iterator<joinable_ctx> it(join_tabs);
1319 joinable_ctx *j;
1320 while ((j= it++))
1321 {
1322 if (j->add_where_subquery(ctx, subquery))
1323 return true;
1324 }
1325 return false;
1326 }
1327
1328
1329 /**
1330 Context class to group materialized JOIN_TABs to "matirealized" array
1331 */
1332
1333 class materialize_ctx : public joinable_ctx, public join_ctx,
1334 public table_base_ctx
1335 {
1336 public:
materialize_ctx(context * parent_arg)1337 explicit materialize_ctx(context *parent_arg)
1338 : context(CTX_MATERIALIZATION, K_TABLE, parent_arg),
1339 joinable_ctx(CTX_MATERIALIZATION, K_TABLE, parent_arg),
1340 join_ctx(CTX_MATERIALIZATION, K_TABLE, parent_arg),
1341 table_base_ctx(CTX_MATERIALIZATION, K_TABLE, parent_arg)
1342 {}
1343
id(bool hide)1344 virtual size_t id(bool hide) { return join_ctx::id(hide); }
cacheable()1345 virtual bool cacheable() { return join_ctx::cacheable(); }
dependent()1346 virtual bool dependent() { return join_ctx::dependent(); }
1347
1348 // Remove warnings: 'inherits ... from ... via dominance'
entry()1349 virtual qep_row *entry() { return table_base_ctx::entry(); }
add_subquery(subquery_list_enum subquery_type,subquery_ctx * ctx)1350 virtual bool add_subquery(subquery_list_enum subquery_type, subquery_ctx *ctx)
1351 { return join_ctx::add_subquery(subquery_type, ctx); }
add_join_tab(joinable_ctx * ctx)1352 virtual bool add_join_tab(joinable_ctx *ctx)
1353 { return join_ctx::add_join_tab(ctx); }
add_where_subquery(subquery_ctx * ctx,SELECT_LEX_UNIT * subquery)1354 virtual bool add_where_subquery(subquery_ctx *ctx, SELECT_LEX_UNIT *subquery)
1355 { return join_ctx::add_where_subquery(ctx, subquery); }
find_and_set_derived(context * subquery)1356 virtual bool find_and_set_derived(context *subquery)
1357 { return join_ctx::find_and_set_derived(subquery); }
format_unit(Opt_trace_context * json)1358 virtual bool format_unit(Opt_trace_context *json)
1359 { return unit_ctx::format_unit(json); }
format_nested_loop(Opt_trace_context * json)1360 virtual bool format_nested_loop(Opt_trace_context *json)
1361 { return join_ctx::format_nested_loop(json); }
set_sort(sort_ctx * ctx)1362 virtual void set_sort(sort_ctx *ctx)
1363 { return join_ctx::set_sort(ctx); }
1364
1365 private:
format_body(Opt_trace_context * json,Opt_trace_object * obj)1366 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
1367 {
1368 DBUG_ASSERT(!col_join_type.is_empty());
1369
1370 if (!col_table_name.is_empty())
1371 obj->add_utf8(K_TABLE_NAME, col_table_name.str);
1372
1373 obj->add_alnum(K_ACCESS_TYPE, col_join_type.str);
1374
1375 if (!col_key.is_empty())
1376 obj->add_utf8(K_KEY, col_key.str);
1377
1378 if (!col_key_len.is_empty())
1379 obj->add_alnum(K_KEY_LENGTH, col_key_len.str);
1380
1381 add_string_array(json, K_REF, col_ref);
1382
1383 if (!col_rows.is_empty())
1384 obj->add(K_ROWS, col_rows.value);
1385
1386 /*
1387 Currently K-REF/col_ref is not shown; it would always be "func", since
1388 {subquery,semijoin} materialization use store_key_item; using
1389 get_store_key() instead would allow "const" and outer column's name,
1390 if applicable.
1391 The looked up expression can anyway be inferred from the condition:
1392 */
1393 if (!col_attached_condition.is_empty())
1394 obj->add_utf8(K_ATTACHED_CONDITION, col_attached_condition.str);
1395 if (format_where(json))
1396 return true;
1397
1398 Opt_trace_object m(json, K_MATERIALIZED_FROM_SUBQUERY);
1399 obj->add(K_USING_TMP_TABLE, true);
1400 Opt_trace_object q(json, K_QUERY_BLOCK);
1401 return format_nested_loop(json);
1402 }
1403 };
1404
1405
1406 /**
1407 Context class to represent JOIN_TABs in duplication weedout sequence
1408 */
1409
1410 class duplication_weedout_ctx : public joinable_ctx, public join_ctx
1411 {
1412 public:
duplication_weedout_ctx(context * parent_arg)1413 explicit duplication_weedout_ctx(context *parent_arg)
1414 : context(CTX_DUPLICATES_WEEDOUT, K_DUPLICATES_REMOVAL, parent_arg),
1415 joinable_ctx(CTX_DUPLICATES_WEEDOUT, K_DUPLICATES_REMOVAL, parent_arg),
1416 join_ctx(CTX_DUPLICATES_WEEDOUT, K_DUPLICATES_REMOVAL, parent_arg)
1417 {}
1418
id(bool hide)1419 virtual size_t id(bool hide) { return join_ctx::id(hide); }
cacheable()1420 virtual bool cacheable() { return join_ctx::cacheable(); }
dependent()1421 virtual bool dependent() { return join_ctx::dependent(); }
1422
1423 // Remove warnings: 'inherits ... from ... via dominance'
add_join_tab(joinable_ctx * ctx)1424 virtual bool add_join_tab(joinable_ctx *ctx)
1425 { return join_ctx::add_join_tab(ctx); }
add_subquery(subquery_list_enum subquery_type,subquery_ctx * ctx)1426 virtual bool add_subquery(subquery_list_enum subquery_type, subquery_ctx *ctx)
1427 { return join_ctx::add_subquery(subquery_type, ctx); }
add_where_subquery(subquery_ctx * ctx,SELECT_LEX_UNIT * subquery)1428 virtual bool add_where_subquery(subquery_ctx *ctx, SELECT_LEX_UNIT *subquery)
1429 { return join_ctx::add_where_subquery(ctx, subquery); }
find_and_set_derived(context * subquery)1430 virtual bool find_and_set_derived(context *subquery)
1431 { return join_ctx::find_and_set_derived(subquery); }
format_nested_loop(Opt_trace_context * json)1432 virtual bool format_nested_loop(Opt_trace_context *json)
1433 { return join_ctx::format_nested_loop(json); }
format_unit(Opt_trace_context * json)1434 virtual bool format_unit(Opt_trace_context *json)
1435 { return unit_ctx::format_unit(json); }
set_sort(sort_ctx * ctx)1436 virtual void set_sort(sort_ctx *ctx)
1437 { return join_ctx::set_sort(ctx); }
1438
1439 private:
format_body(Opt_trace_context * json,Opt_trace_object * obj)1440 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
1441 {
1442 obj->add(K_USING_TMP_TABLE, true);
1443 return format_nested_loop(json);
1444 }
1445 };
1446
1447
1448 /**
1449 Node class for UNION (query expression)
1450 */
1451
1452 class union_ctx : public unit_ctx
1453 {
1454 union_result_ctx *union_result; ///< associated CTX_UNION_RESULT node
1455 List<context> query_specs; ///< query specification nodes (inner selects)
1456
1457 public:
union_ctx(context * parent_arg)1458 explicit union_ctx(context * parent_arg)
1459 : context(CTX_UNION, K_QUERY_BLOCK, parent_arg),
1460 unit_ctx(CTX_UNION, K_QUERY_BLOCK, parent_arg),
1461 union_result(NULL)
1462 {}
1463
1464 private:
format_body(Opt_trace_context * json,Opt_trace_object * obj)1465 virtual bool format_body(Opt_trace_context *json, Opt_trace_object *obj)
1466 {
1467 return union_result->format(json) || format_unit(json);
1468 }
1469
1470 public:
id(bool hide)1471 virtual size_t id(bool hide) { return get_id(query_specs, hide); }
cacheable()1472 virtual bool cacheable() { return query_specs.head()->cacheable(); }
dependent()1473 virtual bool dependent() { return query_specs.head()->dependent(); }
1474
set_union_result(union_result_ctx * ctx)1475 virtual void set_union_result(union_result_ctx *ctx)
1476 {
1477 DBUG_ASSERT(union_result == NULL);
1478 union_result= ctx;
1479 union_result->push_down_query_specs(&query_specs);
1480 }
add_query_spec(context * ctx)1481 virtual bool add_query_spec(context *ctx)
1482 { return query_specs.push_back(ctx); }
1483 };
1484
1485 } // namespace
1486
1487
entry()1488 qep_row *Explain_format_JSON::entry()
1489 {
1490 return current_context->entry();
1491 }
1492
1493
begin_context(Explain_context_enum ctx,SELECT_LEX_UNIT * subquery,const Explain_format_flags * flags)1494 bool Explain_format_JSON::begin_context(Explain_context_enum ctx,
1495 SELECT_LEX_UNIT *subquery,
1496 const Explain_format_flags *flags)
1497 {
1498 using namespace opt_explain_json_namespace;
1499
1500 context *prev_context= current_context;
1501 switch(ctx) {
1502 case CTX_JOIN:
1503 DBUG_ASSERT(current_context == NULL ||
1504 // subqueries:
1505 current_context->type == CTX_SELECT_LIST ||
1506 current_context->type == CTX_UPDATE_VALUE_LIST ||
1507 current_context->type == CTX_DERIVED ||
1508 current_context->type == CTX_OPTIMIZED_AWAY_SUBQUERY ||
1509 current_context->type == CTX_WHERE ||
1510 current_context->type == CTX_HAVING ||
1511 current_context->type == CTX_ORDER_BY_SQ ||
1512 current_context->type == CTX_GROUP_BY_SQ ||
1513 current_context->type == CTX_QUERY_SPEC);
1514 if ((current_context=
1515 new join_ctx(CTX_JOIN, K_QUERY_BLOCK, current_context)) == NULL)
1516 return true;
1517 break;
1518 case CTX_ORDER_BY:
1519 {
1520 DBUG_ASSERT(current_context->type == CTX_JOIN);
1521 sort_ctx *ctx= new sort_with_subqueries_ctx(CTX_ORDER_BY,
1522 K_ORDERING_OPERATION,
1523 current_context,
1524 SQ_ORDER_BY, flags,
1525 ESC_ORDER_BY);
1526 if (ctx == NULL)
1527 return true;
1528 current_context->set_sort(ctx);
1529 current_context= ctx;
1530 break;
1531 }
1532 case CTX_GROUP_BY:
1533 {
1534 DBUG_ASSERT(current_context->type == CTX_JOIN ||
1535 current_context->type == CTX_ORDER_BY ||
1536 current_context->type == CTX_DISTINCT);
1537 sort_ctx *ctx= new sort_with_subqueries_ctx(CTX_GROUP_BY,
1538 K_GROUPING_OPERATION,
1539 current_context,
1540 SQ_GROUP_BY, flags,
1541 ESC_GROUP_BY);
1542 if (ctx == NULL)
1543 return true;
1544 current_context->set_sort(ctx);
1545 current_context= ctx;
1546 break;
1547 }
1548 case CTX_DISTINCT:
1549 {
1550 DBUG_ASSERT(current_context->type == CTX_JOIN ||
1551 current_context->type == CTX_ORDER_BY);
1552 sort_ctx *ctx= new sort_ctx(CTX_DISTINCT, K_DUPLICATES_REMOVAL,
1553 current_context, flags, ESC_DISTINCT);
1554 if (ctx == NULL)
1555 return true;
1556 current_context->set_sort(ctx);
1557 current_context= ctx;
1558 break;
1559 }
1560 case CTX_BUFFER_RESULT:
1561 {
1562 DBUG_ASSERT(current_context->type == CTX_JOIN ||
1563 current_context->type == CTX_ORDER_BY ||
1564 current_context->type == CTX_DISTINCT ||
1565 current_context->type == CTX_GROUP_BY);
1566 sort_ctx *ctx= new sort_ctx(CTX_BUFFER_RESULT, K_BUFFER_RESULT,
1567 current_context, flags, ESC_BUFFER_RESULT);
1568 if (ctx == NULL)
1569 return true;
1570 current_context->set_sort(ctx);
1571 current_context= ctx;
1572 break;
1573 }
1574 case CTX_JOIN_TAB:
1575 {
1576 DBUG_ASSERT(current_context->type == CTX_JOIN ||
1577 current_context->type == CTX_MATERIALIZATION ||
1578 current_context->type == CTX_DUPLICATES_WEEDOUT ||
1579 current_context->type == CTX_GROUP_BY ||
1580 current_context->type == CTX_ORDER_BY ||
1581 current_context->type == CTX_DISTINCT ||
1582 current_context->type == CTX_BUFFER_RESULT ||
1583 current_context->type == CTX_SIMPLE_GROUP_BY ||
1584 current_context->type == CTX_SIMPLE_ORDER_BY ||
1585 current_context->type == CTX_SIMPLE_DISTINCT);
1586 join_tab_ctx *ctx= new join_tab_ctx(CTX_JOIN_TAB, current_context);
1587 if (ctx == NULL || current_context->add_join_tab(ctx))
1588 return true;
1589 current_context= ctx;
1590 break;
1591 }
1592 case CTX_SIMPLE_ORDER_BY:
1593 {
1594 DBUG_ASSERT(current_context->type == CTX_JOIN ||
1595 current_context->type == CTX_MATERIALIZATION ||
1596 current_context->type == CTX_DUPLICATES_WEEDOUT ||
1597 current_context->type == CTX_GROUP_BY ||
1598 current_context->type == CTX_ORDER_BY ||
1599 current_context->type == CTX_BUFFER_RESULT ||
1600 current_context->type == CTX_DISTINCT);
1601 simple_sort_ctx *ctx=
1602 new simple_sort_with_subqueries_ctx(CTX_SIMPLE_ORDER_BY,
1603 K_ORDERING_OPERATION,
1604 current_context, SQ_ORDER_BY, flags,
1605 ESC_ORDER_BY);
1606
1607 if (ctx == NULL || current_context->add_join_tab(ctx))
1608 return true;
1609 current_context= ctx;
1610 break;
1611 }
1612 case CTX_SIMPLE_GROUP_BY:
1613 {
1614 DBUG_ASSERT(current_context->type == CTX_JOIN ||
1615 current_context->type == CTX_MATERIALIZATION ||
1616 current_context->type == CTX_DUPLICATES_WEEDOUT ||
1617 current_context->type == CTX_GROUP_BY ||
1618 current_context->type == CTX_ORDER_BY ||
1619 current_context->type == CTX_DISTINCT ||
1620 current_context->type == CTX_BUFFER_RESULT ||
1621 current_context->type == CTX_SIMPLE_ORDER_BY ||
1622 current_context->type == CTX_SIMPLE_DISTINCT);
1623 simple_sort_ctx *ctx=
1624 new simple_sort_with_subqueries_ctx(CTX_SIMPLE_GROUP_BY,
1625 K_GROUPING_OPERATION,
1626 current_context, SQ_GROUP_BY, flags,
1627 ESC_GROUP_BY);
1628 if (ctx == NULL || current_context->add_join_tab(ctx))
1629 return true;
1630 current_context= ctx;
1631 break;
1632 }
1633 case CTX_SIMPLE_DISTINCT:
1634 {
1635 DBUG_ASSERT(current_context->type == CTX_JOIN ||
1636 current_context->type == CTX_MATERIALIZATION ||
1637 current_context->type == CTX_DUPLICATES_WEEDOUT ||
1638 current_context->type == CTX_GROUP_BY ||
1639 current_context->type == CTX_ORDER_BY ||
1640 current_context->type == CTX_DISTINCT ||
1641 current_context->type == CTX_BUFFER_RESULT ||
1642 current_context->type == CTX_SIMPLE_ORDER_BY);
1643 simple_sort_ctx *ctx=
1644 new simple_sort_ctx(CTX_SIMPLE_DISTINCT, K_DUPLICATES_REMOVAL,
1645 current_context, flags, ESC_DISTINCT);
1646 if (ctx == NULL || current_context->add_join_tab(ctx))
1647 return true;
1648 current_context= ctx;
1649 break;
1650 }
1651 case CTX_MATERIALIZATION:
1652 {
1653 DBUG_ASSERT(current_context->type == CTX_JOIN ||
1654 current_context->type == CTX_GROUP_BY ||
1655 current_context->type == CTX_ORDER_BY ||
1656 current_context->type == CTX_DISTINCT ||
1657 current_context->type == CTX_BUFFER_RESULT ||
1658 current_context->type == CTX_DUPLICATES_WEEDOUT);
1659 materialize_ctx *ctx= new materialize_ctx(current_context);
1660 if (ctx == NULL || current_context->add_join_tab(ctx))
1661 return true;
1662 current_context= ctx;
1663 break;
1664 }
1665 case CTX_DUPLICATES_WEEDOUT:
1666 {
1667 DBUG_ASSERT(current_context->type == CTX_JOIN ||
1668 current_context->type == CTX_GROUP_BY ||
1669 current_context->type == CTX_ORDER_BY ||
1670 current_context->type == CTX_DISTINCT ||
1671 current_context->type == CTX_BUFFER_RESULT ||
1672 current_context->type == CTX_MATERIALIZATION);
1673 duplication_weedout_ctx *ctx=
1674 new duplication_weedout_ctx(current_context);
1675 if (ctx == NULL || current_context->add_join_tab(ctx))
1676 return true;
1677 current_context= ctx;
1678 break;
1679 }
1680 case CTX_SELECT_LIST:
1681 {
1682 subquery_ctx *ctx= new subquery_ctx(CTX_SELECT_LIST, NULL,
1683 current_context);
1684 if (ctx == NULL ||
1685 current_context->add_subquery(SQ_SELECT_LIST, ctx))
1686 return true;
1687 current_context= ctx;
1688 break;
1689 }
1690 case CTX_UPDATE_VALUE_LIST:
1691 {
1692 subquery_ctx *ctx= new subquery_ctx(CTX_UPDATE_VALUE_LIST, NULL,
1693 current_context);
1694 if (ctx == NULL ||
1695 current_context->add_subquery(SQ_UPDATE_VALUE, ctx))
1696 return true;
1697 current_context= ctx;
1698 break;
1699 }
1700 case CTX_DERIVED:
1701 {
1702 current_context= new subquery_ctx(CTX_DERIVED,
1703 K_MATERIALIZED_FROM_SUBQUERY,
1704 current_context);
1705 if (current_context == NULL)
1706 return true;
1707 break;
1708 }
1709 case CTX_OPTIMIZED_AWAY_SUBQUERY:
1710 {
1711 subquery_ctx *ctx= new subquery_ctx(CTX_OPTIMIZED_AWAY_SUBQUERY, NULL,
1712 current_context);
1713 if (ctx == NULL || current_context->add_subquery(SQ_HOMELESS, ctx))
1714 return true;
1715 current_context= ctx;
1716 break;
1717 }
1718 case CTX_WHERE:
1719 {
1720 DBUG_ASSERT(subquery != NULL);
1721 subquery_ctx *ctx= new subquery_ctx(CTX_WHERE, NULL, current_context);
1722 if (ctx == NULL ||
1723 current_context->add_where_subquery(ctx, subquery))
1724 return true;
1725 current_context= ctx;
1726 break;
1727 }
1728 case CTX_HAVING:
1729 {
1730 subquery_ctx *ctx= new subquery_ctx(CTX_HAVING, NULL, current_context);
1731 if (ctx == NULL || current_context->add_subquery(SQ_HAVING, ctx))
1732 return true;
1733 current_context= ctx;
1734 break;
1735 }
1736 case CTX_ORDER_BY_SQ:
1737 {
1738 subquery_ctx *ctx= new subquery_ctx(CTX_ORDER_BY_SQ, NULL,
1739 current_context);
1740 if (ctx == NULL || current_context->add_subquery(SQ_ORDER_BY, ctx))
1741 return true;
1742 current_context= ctx;
1743 break;
1744 }
1745 case CTX_GROUP_BY_SQ:
1746 {
1747 subquery_ctx *ctx= new subquery_ctx(CTX_GROUP_BY_SQ, NULL,
1748 current_context);
1749 if (ctx == NULL || current_context->add_subquery(SQ_GROUP_BY, ctx))
1750 return true;
1751 current_context= ctx;
1752 break;
1753 }
1754 case CTX_UNION:
1755 DBUG_ASSERT(current_context == NULL ||
1756 // subqueries:
1757 current_context->type == CTX_SELECT_LIST ||
1758 current_context->type == CTX_UPDATE_VALUE_LIST ||
1759 current_context->type == CTX_DERIVED ||
1760 current_context->type == CTX_OPTIMIZED_AWAY_SUBQUERY ||
1761 current_context->type == CTX_WHERE ||
1762 current_context->type == CTX_HAVING ||
1763 current_context->type == CTX_ORDER_BY_SQ ||
1764 current_context->type == CTX_GROUP_BY_SQ ||
1765 current_context->type == CTX_QUERY_SPEC);
1766 current_context= new union_ctx(current_context);
1767 if (current_context == NULL)
1768 return true;
1769 break;
1770 case CTX_UNION_RESULT:
1771 {
1772 DBUG_ASSERT(current_context->type == CTX_UNION);
1773 union_result_ctx *ctx= new union_result_ctx(current_context);
1774 if (ctx == NULL)
1775 return true;
1776 current_context->set_union_result(ctx);
1777 current_context= ctx;
1778 break;
1779 }
1780 case CTX_QUERY_SPEC:
1781 {
1782 DBUG_ASSERT(current_context->type == CTX_UNION);
1783 subquery_ctx *ctx= new subquery_ctx(CTX_QUERY_SPEC, NULL,
1784 current_context);
1785 if (ctx == NULL || current_context->add_query_spec(ctx))
1786 return true;
1787 current_context= ctx;
1788 break;
1789 }
1790 case CTX_MESSAGE:
1791 {
1792 /*
1793 Like CTX_JOIN_TAB:
1794 */
1795 DBUG_ASSERT(current_context->type == CTX_JOIN ||
1796 current_context->type == CTX_MATERIALIZATION ||
1797 current_context->type == CTX_DUPLICATES_WEEDOUT ||
1798 current_context->type == CTX_GROUP_BY ||
1799 current_context->type == CTX_ORDER_BY ||
1800 current_context->type == CTX_DISTINCT ||
1801 current_context->type == CTX_BUFFER_RESULT ||
1802 current_context->type == CTX_SIMPLE_GROUP_BY ||
1803 current_context->type == CTX_SIMPLE_ORDER_BY ||
1804 current_context->type == CTX_SIMPLE_DISTINCT);
1805 joinable_ctx *ctx= new message_ctx(current_context);
1806 if (ctx == NULL || current_context->add_join_tab(ctx))
1807 return true;
1808 current_context= ctx;
1809 break;
1810 }
1811 default:
1812 DBUG_ASSERT(!"Unknown EXPLAIN context!");
1813 return true;
1814 }
1815
1816 if (prev_context)
1817 prev_context->set_child(current_context);
1818
1819 return false;
1820 }
1821
1822
end_context(Explain_context_enum ctx)1823 bool Explain_format_JSON::end_context(Explain_context_enum ctx)
1824 {
1825 DBUG_ASSERT(current_context->type == ctx);
1826
1827 bool ret= false;
1828 if (current_context->parent == NULL)
1829 {
1830 Item* item;
1831 #ifdef OPTIMIZER_TRACE
1832 Opt_trace_context json;
1833 const size_t max_size= ULONG_MAX;
1834 if (json.start(true, // support_I_S (enable JSON generation)
1835 false, // support_dbug_or_missing_priv
1836 current_thd->variables.end_markers_in_json, // end_marker
1837 false, // one_line
1838 0, // offset
1839 1, // limit
1840 max_size, // max_mem_size
1841 Opt_trace_context::MISC))
1842 return true;
1843
1844 {
1845 Opt_trace_object braces(&json);
1846
1847 if (current_context->format(&json))
1848 return true;
1849 }
1850 json.end();
1851
1852 Opt_trace_iterator it(&json);
1853 if (!it.at_end())
1854 {
1855 Opt_trace_info info;
1856 it.get_value(&info);
1857 item= new Item_string(info.trace_ptr,
1858 static_cast<uint>(info.trace_length),
1859 system_charset_info);
1860 }
1861 else
1862 #endif
1863 item= new Item_null();
1864
1865 List<Item> field_list;
1866 ret= (item == NULL ||
1867 field_list.push_back(item) ||
1868 output->send_data(field_list));
1869 }
1870 else if (ctx == CTX_DERIVED)
1871 {
1872 if (!current_context->parent->find_and_set_derived(current_context))
1873 {
1874 DBUG_ASSERT(!"No derived table found!");
1875 return true;
1876 }
1877 }
1878
1879 current_context= current_context->parent;
1880 return ret;
1881 }
1882
1883
send_headers(select_result * result)1884 bool Explain_format_JSON::send_headers(select_result *result)
1885 {
1886 output= result;
1887 if (Explain_format::send_headers(result))
1888 return true;
1889
1890 List<Item> field_list;
1891 Item *item= new Item_empty_string("EXPLAIN", 78, system_charset_info);
1892 if (item == NULL || field_list.push_back(item))
1893 return true;
1894 return result->send_result_set_metadata(field_list,
1895 Protocol::SEND_NUM_ROWS |
1896 Protocol::SEND_EOF);
1897 }
1898
1899