1 /* This program is free software; you can redistribute it and/or modify
2    it under the terms of the GNU General Public License as published by
3    the Free Software Foundation; version 2 of the License.
4 
5    This program is distributed in the hope that it will be useful,
6    but WITHOUT ANY WARRANTY; without even the implied warranty of
7    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
8    GNU General Public License for more details.
9 
10    You should have received a copy of the GNU General Public License
11    along with this program; if not, write to the Free Software
12    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
13 
14 #include "mariadb.h"
15 #include "sql_array.h"
16 #include "sql_string.h"
17 #include "sql_class.h"
18 #include "sql_show.h"
19 #include "field.h"
20 #include "sql_i_s.h"
21 #include "opt_trace.h"
22 #include "sql_parse.h"
23 #include "set_var.h"
24 #include "my_json_writer.h"
25 #include "sp_head.h"
26 
27 #include "rowid_filter.h"
28 
29 const char I_S_table_name[]= "OPTIMIZER_TRACE";
30 
31 /**
32    Whether a list of tables contains information_schema.OPTIMIZER_TRACE.
33    @param  tbl  list of tables
34 
35    Can we do better than this here??
36    @note this does not catch that a stored routine or view accesses
37    the OPTIMIZER_TRACE table. So using a stored routine or view to read
38    OPTIMIZER_TRACE will overwrite OPTIMIZER_TRACE as it runs and provide
39    uninteresting info.
40 */
list_has_optimizer_trace_table(const TABLE_LIST * tbl)41 bool list_has_optimizer_trace_table(const TABLE_LIST *tbl)
42 {
43   for (; tbl; tbl= tbl->next_global)
44   {
45     if (tbl->schema_table &&
46         0 == strcmp(tbl->schema_table->table_name, I_S_table_name))
47       return true;
48   }
49   return false;
50 }
51 
52 /*
53   Returns if a query has a set command with optimizer_trace being switched on/off.
54   True: Don't trace the query(uninteresting)
55 */
56 
sets_var_optimizer_trace(enum enum_sql_command sql_command,List<set_var_base> * set_vars)57 bool sets_var_optimizer_trace(enum enum_sql_command sql_command,
58                               List<set_var_base> *set_vars)
59 {
60   if (sql_command == SQLCOM_SET_OPTION)
61   {
62     List_iterator_fast<set_var_base> it(*set_vars);
63     const set_var_base *var;
64     while ((var= it++))
65       if (var->is_var_optimizer_trace()) return true;
66   }
67   return false;
68 }
69 
70 
71 namespace Show {
72 
73 
74 ST_FIELD_INFO optimizer_trace_info[]=
75 {
76   Column("QUERY",                             Longtext(65535), NOT_NULL),
77   Column("TRACE",                             Longtext(65535), NOT_NULL),
78   Column("MISSING_BYTES_BEYOND_MAX_MEM_SIZE", SLong(20),       NOT_NULL),
79   Column("INSUFFICIENT_PRIVILEGES",           STiny(1),        NOT_NULL),
80   CEnd()
81 };
82 
83 } // namespace Show
84 
85 
86 /*
87   TODO: one-line needs to be implemented seperately
88 */
89 const char *Opt_trace_context::flag_names[]= {"enabled", "default",
90                                                NullS};
91 
92 /*
93   Returns if a particular command will be traced or not
94 */
95 
sql_command_can_be_traced(enum enum_sql_command sql_command)96 inline bool sql_command_can_be_traced(enum enum_sql_command sql_command)
97 {
98   /*
99     For first iteration we are only allowing select queries.
100     TODO: change to allow other queries.
101   */
102   return sql_command == SQLCOM_SELECT ||
103          sql_command == SQLCOM_UPDATE ||
104          sql_command == SQLCOM_DELETE ||
105          sql_command == SQLCOM_DELETE_MULTI ||
106          sql_command == SQLCOM_UPDATE_MULTI;
107 }
108 
opt_trace_print_expanded_query(THD * thd,SELECT_LEX * select_lex,Json_writer_object * writer)109 void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex,
110                                     Json_writer_object *writer)
111 
112 {
113   DBUG_ASSERT(thd->trace_started());
114 
115   StringBuffer<1024> str(system_charset_info);
116   ulonglong save_option_bits= thd->variables.option_bits;
117   thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
118   select_lex->print(thd, &str,
119                     enum_query_type(QT_TO_SYSTEM_CHARSET |
120                                     QT_SHOW_SELECT_NUMBER |
121                                     QT_ITEM_IDENT_SKIP_DB_NAMES |
122                                     QT_VIEW_INTERNAL));
123   thd->variables.option_bits= save_option_bits;
124   /*
125     The output is not very pretty lots of back-ticks, the output
126     is as the one in explain extended , lets try to improved it here.
127   */
128   writer->add("expanded_query", str.c_ptr_safe(), str.length());
129 }
130 
opt_trace_disable_if_no_security_context_access(THD * thd)131 void opt_trace_disable_if_no_security_context_access(THD *thd)
132 {
133   if (likely(!(thd->variables.optimizer_trace &
134                Opt_trace_context::FLAG_ENABLED)) ||  // (1)
135       thd->system_thread)                            // (2)
136   {
137     /*
138       (1) We know that the routine's execution starts with "enabled=off".
139       If it stays so until the routine ends, we needn't do security checks on
140       the routine.
141       If it does not stay so, it means the definer sets it to "on" somewhere
142       in the routine's body. Then it is his conscious decision to generate
143       traces, thus it is still correct to skip the security check.
144 
145       (2) Threads of the Events Scheduler have an unusual security context
146       (thd->m_main_security_ctx.priv_user==NULL, see comment in
147       Security_context::change_security_context()).
148     */
149     return;
150   }
151   Opt_trace_context *const trace= &thd->opt_trace;
152   if (!thd->trace_started())
153   {
154     /*
155       @@optimizer_trace has "enabled=on" but trace is not started.
156       Either Opt_trace_start ctor was not called for our statement (3), or it
157       was called but at that time, the variable had "enabled=off" (4).
158 
159       There are no known cases of (3).
160 
161       (4) suggests that the user managed to change the variable during
162       execution of the statement, and this statement is using
163       view/routine (note that we have not been able to provoke this, maybe
164       this is impossible). If it happens it is suspicious.
165 
166       We disable I_S output. And we cannot do otherwise: we have no place to
167       store a possible "missing privilege" information (no Opt_trace_stmt, as
168       is_started() is false), so cannot do security checks, so cannot safely
169       do tracing, so have to disable I_S output. And even then, we don't know
170       when to re-enable I_S output, as we have no place to store the
171       information "re-enable tracing at the end of this statement", and we
172       don't even have a notion of statement here (statements in the optimizer
173       trace world mean an Opt_trace_stmt object, and there is none here). So
174       we must disable for the session's life.
175 
176       COM_FIELD_LIST opens views, thus used to be a case of (3). To avoid
177       disabling I_S output for the session's life when this command is issued
178       (like in: "SET OPTIMIZER_TRACE='ENABLED=ON';USE somedb;" in the 'mysql'
179       command-line client), we have decided to create a Opt_trace_start for
180       this command. The command itself is not traced though
181       (SQLCOM_SHOW_FIELDS does not have CF_OPTIMIZER_TRACE).
182     */
183     return;
184   }
185   /*
186     Note that thd->main_security_ctx.master_access is probably invariant
187     accross the life of THD: GRANT/REVOKE don't affect global privileges of an
188     existing connection, per the manual.
189   */
190   if (!(thd->main_security_ctx.check_access(GLOBAL_ACLS & ~GRANT_ACL)) &&
191       (0 != strcmp(thd->main_security_ctx.priv_user,
192                    thd->security_context()->priv_user) ||
193        0 != my_strcasecmp(system_charset_info,
194                           thd->main_security_ctx.priv_host,
195                           thd->security_context()->priv_host)))
196     trace->missing_privilege();
197 }
198 
opt_trace_disable_if_no_stored_proc_func_access(THD * thd,sp_head * sp)199 void opt_trace_disable_if_no_stored_proc_func_access(THD *thd, sp_head *sp)
200 {
201   if (likely(!(thd->variables.optimizer_trace &
202                Opt_trace_context::FLAG_ENABLED)) ||
203       thd->system_thread ||
204       !thd->trace_started())
205     return;
206 
207   Opt_trace_context *const trace= &thd->opt_trace;
208   bool full_access;
209   Security_context *const backup_thd_sctx= thd->security_context();
210   thd->set_security_context(&thd->main_security_ctx);
211   const bool rc= check_show_routine_access(thd, sp, &full_access) || !full_access;
212   thd->set_security_context(backup_thd_sctx);
213   if (rc)
214     trace->missing_privilege();
215 }
216 
217 /**
218    If tracing is on, checks additional privileges on a list of tables/views,
219    to make sure that the user has the right to do SHOW CREATE TABLE/VIEW and
220    "SELECT *". For that:
221    - this functions checks table-level SELECT
222    - which is sufficient for SHOW CREATE TABLE and "SELECT *", if a base table
223    - if a view, if the view has not been identified as such then
224    opt_trace_disable_if_no_view_access() will be later called and check SHOW
225    VIEW; other we check SHOW VIEW here; SHOW VIEW + SELECT is sufficient for
226    SHOW CREATE VIEW.
227    If a privilege is missing, notifies the trace system.
228 
229    @param thd
230    @param tbl list of tables to check
231 */
232 
opt_trace_disable_if_no_tables_access(THD * thd,TABLE_LIST * tbl)233 void opt_trace_disable_if_no_tables_access(THD *thd, TABLE_LIST *tbl)
234 {
235   if (likely(!(thd->variables.optimizer_trace &
236               Opt_trace_context::FLAG_ENABLED)) ||
237       thd->system_thread ||
238       !thd->trace_started())
239     return;
240 
241   Opt_trace_context *const trace= &thd->opt_trace;
242   Security_context *const backup_thd_sctx= thd->security_context();
243   thd->set_security_context(&thd->main_security_ctx);
244   const TABLE_LIST *const first_not_own_table= thd->lex->first_not_own_table();
245   for (TABLE_LIST *t= tbl; t != NULL && t != first_not_own_table;
246        t= t->next_global)
247   {
248     /*
249       Anonymous derived tables (as in
250       "SELECT ... FROM (SELECT ...)") don't have their grant.privilege set.
251     */
252     if (!t->is_anonymous_derived_table())
253     {
254       const GRANT_INFO backup_grant_info= t->grant;
255       Security_context *const backup_table_sctx= t->security_ctx;
256       t->security_ctx= NULL;
257       /*
258         (1) check_table_access() fills t->grant.privilege.
259         (2) Because SELECT privileges can be column-based,
260         check_table_access() will return 'false' as long as there is SELECT
261         privilege on one column. But we want a table-level privilege.
262       */
263 
264       bool rc =
265           check_table_access(thd, SELECT_ACL, t, false, 1, true) ||  // (1)
266           ((t->grant.privilege & SELECT_ACL) == NO_ACL);             // (2)
267       if (t->is_view())
268       {
269         /*
270           It's a view which has already been opened: we are executing a
271           prepared statement. The view has been unfolded in the global list of
272           tables. So underlying tables will be automatically checked in the
273           present function, but we need an explicit check of SHOW VIEW:
274         */
275         rc |= check_table_access(thd, SHOW_VIEW_ACL, t, false, 1, true);
276       }
277       t->security_ctx= backup_table_sctx;
278       t->grant= backup_grant_info;
279       if (rc)
280       {
281         trace->missing_privilege();
282         break;
283       }
284     }
285   }
286   thd->set_security_context(backup_thd_sctx);
287   return;
288 }
289 
opt_trace_disable_if_no_view_access(THD * thd,TABLE_LIST * view,TABLE_LIST * underlying_tables)290 void opt_trace_disable_if_no_view_access(THD *thd, TABLE_LIST *view,
291                                          TABLE_LIST *underlying_tables)
292 {
293 
294   if (likely(!(thd->variables.optimizer_trace &
295                Opt_trace_context::FLAG_ENABLED)) ||
296       thd->system_thread ||
297       !thd->trace_started())
298     return;
299 
300   Opt_trace_context *const trace= &thd->opt_trace;
301   Security_context *const backup_table_sctx= view->security_ctx;
302   Security_context *const backup_thd_sctx= thd->security_context();
303   const GRANT_INFO backup_grant_info= view->grant;
304 
305   view->security_ctx= NULL;  // no SUID context for view
306   // no SUID context for THD
307   thd->set_security_context(&thd->main_security_ctx);
308   const int rc= check_table_access(thd, SHOW_VIEW_ACL, view, false, 1, true);
309 
310   view->security_ctx= backup_table_sctx;
311   thd->set_security_context(backup_thd_sctx);
312   view->grant= backup_grant_info;
313 
314   if (rc)
315   {
316     trace->missing_privilege();
317     return;
318   }
319   /*
320     We needn't check SELECT privilege on this view. Some
321     opt_trace_disable_if_no_tables_access() call has or will check it.
322 
323     Now we check underlying tables/views of our view:
324   */
325   opt_trace_disable_if_no_tables_access(thd, underlying_tables);
326   return;
327 }
328 
329 
330 /**
331   @class Opt_trace_stmt
332 
333   The trace of one statement.
334 */
335 
Opt_trace_stmt(Opt_trace_context * ctx_arg)336 Opt_trace_stmt::Opt_trace_stmt(Opt_trace_context *ctx_arg)
337 {
338   ctx= ctx_arg;
339   current_json= new Json_writer();
340   missing_priv= false;
341   I_S_disabled= 0;
342 }
343 
~Opt_trace_stmt()344 Opt_trace_stmt::~Opt_trace_stmt()
345 {
346   delete current_json;
347 }
348 
get_length()349 size_t Opt_trace_stmt::get_length()
350 {
351   return current_json->output.length();
352 }
353 
get_truncated_bytes()354 size_t Opt_trace_stmt::get_truncated_bytes()
355 {
356   return current_json->get_truncated_bytes();
357 }
358 
set_query(const char * query_ptr,size_t length,const CHARSET_INFO * charset)359 void Opt_trace_stmt::set_query(const char *query_ptr, size_t length,
360                                const CHARSET_INFO *charset)
361 {
362   query.append(query_ptr, length, charset);
363 }
364 
missing_privilege()365 void Opt_trace_context::missing_privilege()
366 {
367   if (current_trace)
368     current_trace->missing_privilege();
369 }
370 
set_allowed_mem_size(size_t mem_size)371 void Opt_trace_context::set_allowed_mem_size(size_t mem_size)
372 {
373   current_trace->set_allowed_mem_size(mem_size);
374 }
375 
376 /*
377   TODO: In future when we would be saving multiple trace,
378   this function would return
379   max_mem_size - memory_occupied_by_the_saved_traces
380 */
381 
remaining_mem_size()382 size_t Opt_trace_context::remaining_mem_size()
383 {
384   return max_mem_size;
385 }
386 
387 /*
388   Disable tracing for children if the current trace is already present.
389   Currently only one trace is stored and there is no mechanism
390   to restore traces, so disabling tracing for children is the best option.
391 */
392 
disable_tracing_if_required()393 bool Opt_trace_context::disable_tracing_if_required()
394 {
395   if (current_trace)
396   {
397     current_trace->disable_tracing_for_children();
398     return true;
399   }
400   return false;
401 }
402 
enable_tracing_if_required()403 bool Opt_trace_context::enable_tracing_if_required()
404 {
405   if (current_trace)
406   {
407     current_trace->enable_tracing_for_children();
408     return true;
409   }
410   return false;
411 }
412 
is_enabled()413 bool Opt_trace_context::is_enabled()
414 {
415   if (current_trace)
416     return current_trace->is_enabled();
417   return false;
418 }
419 
Opt_trace_context()420 Opt_trace_context::Opt_trace_context() : traces(PSI_INSTRUMENT_MEM)
421 {
422   current_trace= NULL;
423   max_mem_size= 0;
424 }
~Opt_trace_context()425 Opt_trace_context::~Opt_trace_context()
426 {
427   delete_traces();
428 }
429 
set_query(const char * query,size_t length,const CHARSET_INFO * charset)430 void Opt_trace_context::set_query(const char *query, size_t length, const CHARSET_INFO *charset)
431 {
432   current_trace->set_query(query, length, charset);
433 }
434 
start(THD * thd,TABLE_LIST * tbl,enum enum_sql_command sql_command,const char * query,size_t query_length,const CHARSET_INFO * query_charset,ulong max_mem_size_arg)435 void Opt_trace_context::start(THD *thd, TABLE_LIST *tbl,
436                   enum enum_sql_command sql_command,
437                   const char *query,
438                   size_t query_length,
439                   const CHARSET_INFO *query_charset,
440                   ulong max_mem_size_arg)
441 {
442   /*
443     This is done currently because we don't want to have multiple
444     traces open at the same time, so as soon as a new trace is created
445     we forcefully end the previous one, if it has not ended by itself.
446     This would mostly happen with stored functions or procedures.
447 
448     TODO: handle multiple traces
449   */
450   DBUG_ASSERT(!current_trace);
451   current_trace= new Opt_trace_stmt(this);
452   max_mem_size= max_mem_size_arg;
453   set_allowed_mem_size(remaining_mem_size());
454 }
455 
end()456 void Opt_trace_context::end()
457 {
458   if (current_trace)
459     traces.push(current_trace);
460 
461   if (!traces.elements())
462     return;
463   if (traces.elements() > 1)
464   {
465     Opt_trace_stmt *prev= traces.at(0);
466     delete prev;
467     traces.del(0);
468   }
469   current_trace= NULL;
470 }
471 
Opt_trace_start(THD * thd,TABLE_LIST * tbl,enum enum_sql_command sql_command,List<set_var_base> * set_vars,const char * query,size_t query_length,const CHARSET_INFO * query_charset)472 Opt_trace_start::Opt_trace_start(THD *thd, TABLE_LIST *tbl,
473                   enum enum_sql_command sql_command,
474                   List<set_var_base> *set_vars,
475                   const char *query,
476                   size_t query_length,
477                   const CHARSET_INFO *query_charset):ctx(&thd->opt_trace)
478 {
479   /*
480     if optimizer trace is enabled and the statment we have is traceable,
481     then we start the context.
482   */
483   const ulonglong var= thd->variables.optimizer_trace;
484   traceable= FALSE;
485   if (unlikely(var & Opt_trace_context::FLAG_ENABLED) &&
486       sql_command_can_be_traced(sql_command) &&
487       !list_has_optimizer_trace_table(tbl) &&
488       !sets_var_optimizer_trace(sql_command, set_vars) &&
489       !thd->system_thread &&
490       !ctx->disable_tracing_if_required())
491   {
492     ctx->start(thd, tbl, sql_command, query, query_length, query_charset,
493                thd->variables.optimizer_trace_max_mem_size);
494     ctx->set_query(query, query_length, query_charset);
495     traceable= TRUE;
496     opt_trace_disable_if_no_tables_access(thd, tbl);
497   }
498 }
499 
~Opt_trace_start()500 Opt_trace_start::~Opt_trace_start()
501 {
502   if (traceable)
503   {
504     ctx->end();
505     traceable= FALSE;
506   }
507   else
508   {
509     ctx->enable_tracing_if_required();
510   }
511 }
512 
fill_info(Opt_trace_info * info)513 void Opt_trace_stmt::fill_info(Opt_trace_info* info)
514 {
515   if (unlikely(info->missing_priv= get_missing_priv()))
516   {
517     info->trace_ptr= info->query_ptr= "";
518     info->trace_length= info->query_length= 0;
519     info->query_charset= &my_charset_bin;
520     info->missing_bytes= 0;
521   }
522   else
523   {
524     info->trace_ptr= current_json->output.get_string()->ptr();
525     info->trace_length= get_length();
526     info->query_ptr= query.ptr();
527     info->query_length= query.length();
528     info->query_charset= query.charset();
529     info->missing_bytes= get_truncated_bytes();
530     info->missing_priv= get_missing_priv();
531   }
532 }
533 
missing_privilege()534 void Opt_trace_stmt::missing_privilege()
535 {
536   missing_priv= true;
537 }
538 
disable_tracing_for_children()539 void Opt_trace_stmt::disable_tracing_for_children()
540 {
541   ++I_S_disabled;
542 }
543 
enable_tracing_for_children()544 void Opt_trace_stmt::enable_tracing_for_children()
545 {
546   if (I_S_disabled)
547     --I_S_disabled;
548 }
549 
set_allowed_mem_size(size_t mem_size)550 void Opt_trace_stmt::set_allowed_mem_size(size_t mem_size)
551 {
552   current_json->set_size_limit(mem_size);
553 }
554 
555 /*
556   Prefer this when you are iterating over JOIN_TABs
557 */
558 
add_table_name(const JOIN_TAB * tab)559 void Json_writer::add_table_name(const JOIN_TAB *tab)
560 {
561   DBUG_ASSERT(tab->join->thd->trace_started());
562   if (tab != NULL)
563   {
564     char table_name_buffer[SAFE_NAME_LEN];
565     if (tab->table && tab->table->derived_select_number)
566     {
567       /* Derived table name generation */
568       size_t len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1,
569                              "<derived%u>",
570                              tab->table->derived_select_number);
571       add_str(table_name_buffer, len);
572     }
573     else if (tab->bush_children)
574     {
575       JOIN_TAB *ctab= tab->bush_children->start;
576       size_t len= my_snprintf(table_name_buffer,
577                            sizeof(table_name_buffer)-1,
578                            "<subquery%d>",
579                            ctab->emb_sj_nest->sj_subq_pred->get_identifier());
580       add_str(table_name_buffer, len);
581     }
582     else
583     {
584       TABLE_LIST *real_table= tab->table->pos_in_table_list;
585       add_str(real_table->alias.str, real_table->alias.length);
586     }
587   }
588   else
589     DBUG_ASSERT(0);
590 }
591 
add_table_name(const TABLE * table)592 void Json_writer::add_table_name(const TABLE *table)
593 {
594   add_str(table->pos_in_table_list->alias.str);
595 }
596 
597 
add_table_scan_values_to_trace(THD * thd,JOIN_TAB * tab)598 void add_table_scan_values_to_trace(THD *thd, JOIN_TAB *tab)
599 {
600   DBUG_ASSERT(thd->trace_started());
601   Json_writer_object table_records(thd);
602   table_records.add_table_name(tab);
603   Json_writer_object table_rec(thd, "table_scan");
604   table_rec.add("rows", tab->found_records)
605            .add("cost", tab->read_time);
606 }
607 
608 
609 /*
610   @brief
611     Add the tables inside a partial join to the optimizer trace
612 
613   @param join                join handler
614   @param idx                 length of the partial QEP in 'join->positions'
615   @table_map                 map of all non-const tables of the join
616 
617   @note
618     This function is used during best_access_path to print the tables
619     inside the partial join that were considered doing the cost based
620     analysis of the various join orders.
621 */
622 
trace_plan_prefix(JOIN * join,uint idx,table_map join_tables)623 void trace_plan_prefix(JOIN *join, uint idx, table_map join_tables)
624 {
625   THD *const thd= join->thd;
626   DBUG_ASSERT(thd->trace_started());
627 
628   Json_writer_array plan_prefix(thd, "plan_prefix");
629   for (uint i= 0; i < idx; i++)
630   {
631     TABLE_LIST *const tr= join->positions[i].table->tab_list;
632     if (!(tr->map & join_tables))
633       plan_prefix.add_table_name(join->positions[i].table);
634   }
635 }
636 
637 
638 /*
639   Print the join order of all the tables for top level select.
640 
641   For example:
642 
643   select * from ot1
644    where ot1.a IN (select it1.a from it1, it2 where it1.b=it2.a);
645 
646   So this function would print
647     ot1, <subquery2>  ----> For select #1
648 */
649 
print_final_join_order(JOIN * join)650 void print_final_join_order(JOIN *join)
651 {
652   DBUG_ASSERT(join->thd->trace_started());
653 
654   Json_writer_object join_order(join->thd);
655   Json_writer_array  best_order(join->thd, "best_join_order");
656   JOIN_TAB *j;
657   uint i;
658   for (j= join->join_tab,i=0 ; i < join->top_join_tab_count;
659        i++, j++)
660     best_order.add_table_name(j);
661 }
662 
663 
print_best_access_for_table(THD * thd,POSITION * pos,enum join_type type)664 void print_best_access_for_table(THD *thd, POSITION *pos,
665                                  enum join_type type)
666 {
667   DBUG_ASSERT(thd->trace_started());
668 
669   Json_writer_object obj(thd, "chosen_access_method");
670   obj.add("type", type == JT_ALL ? "scan" : join_type_str[type]);
671   obj.add("records", pos->records_read);
672   obj.add("cost", pos->read_time);
673   obj.add("uses_join_buffering", pos->use_join_buffer);
674   if (pos->range_rowid_filter_info)
675   {
676     uint key_no= pos->range_rowid_filter_info->key_no;
677     obj.add("rowid_filter_key",
678             pos->table->table->key_info[key_no].name);
679   }
680 }
681 
682 
683 /*
684   Introduce enum_query_type flags parameter, maybe also allow
685   EXPLAIN also use this function.
686 */
687 
add_str(Item * item)688 void Json_writer::add_str(Item *item)
689 {
690   if (item)
691   {
692     THD *thd= current_thd;
693     StringBuffer<256> str(system_charset_info);
694 
695     ulonglong save_option_bits= thd->variables.option_bits;
696     thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
697     item->print(&str,
698                 enum_query_type(QT_TO_SYSTEM_CHARSET | QT_SHOW_SELECT_NUMBER
699                                | QT_ITEM_IDENT_SKIP_DB_NAMES));
700     thd->variables.option_bits= save_option_bits;
701     add_str(str.c_ptr_safe());
702   }
703   else
704     add_null();
705 }
706 
delete_traces()707 void Opt_trace_context::delete_traces()
708 {
709   if (traces.elements())
710   {
711     while (traces.elements())
712     {
713       Opt_trace_stmt *prev= traces.at(0);
714       delete prev;
715       traces.del(0);
716     }
717   }
718 }
719 
720 
fill_optimizer_trace_info(THD * thd,TABLE_LIST * tables,Item *)721 int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *)
722 {
723   TABLE *table= tables->table;
724   Opt_trace_info info;
725 
726   /*  get_values of trace, query , missing bytes and missing_priv
727 
728       @todo: Need an iterator here to walk over all the traces
729   */
730   Opt_trace_context* ctx= &thd->opt_trace;
731 
732   if (!thd->opt_trace.empty())
733   {
734     Opt_trace_stmt *stmt= ctx->get_top_trace();
735     stmt->fill_info(&info);
736 
737     table->field[0]->store(info.query_ptr, static_cast<uint>(info.query_length),
738                            info.query_charset);
739     table->field[1]->store(info.trace_ptr, static_cast<uint>(info.trace_length),
740                            system_charset_info);
741     table->field[2]->store(info.missing_bytes, true);
742     table->field[3]->store(info.missing_priv, true);
743     //  Store in IS
744     if (schema_table_store_record(thd, table))
745       return 1;
746   }
747   return 0;
748 }
749