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