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