MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
opt_trace2server.cc
Go to the documentation of this file.
1 /* Copyright (c) 2011, 2012, 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 as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
26 #include "opt_trace.h"
27 #include "sql_show.h" // schema_table_stored_record()
28 #include "sql_parse.h" // sql_command_flags
29 #include "sp_head.h" // for sp_head
30 
31 #ifdef OPTIMIZER_TRACE
32 
33 namespace {
34 
35 const char I_S_table_name[]= "OPTIMIZER_TRACE";
36 
37 /* Standalone functions */
38 
47 bool list_has_optimizer_trace_table(const TABLE_LIST *tbl)
48 {
49  for( ; tbl ; tbl= tbl->next_global)
50  {
51  if (tbl->schema_table &&
52  0 == strcmp(tbl->schema_table->table_name, I_S_table_name))
53  return true;
54  }
55  return false;
56 }
57 
58 
63 inline bool sql_command_can_be_traced(enum enum_sql_command sql_command)
64 {
65  /*
66  Tracing is limited to a few SQL commands only.
67 
68  Reasons to not trace other commands:
69  - it reduces the range of potential unknown bugs and misuse
70  - they probably don't have anything interesting optimizer-related
71  - select_lex for them might be uninitialized and unprintable.
72  - SHOW WARNINGS would create an uninteresting trace and thus overwrite the
73  previous interesting one.
74 
75  About prepared statements: note that we don't turn on tracing for
76  SQLCOM_PREPARE (respectively SQLCOM_EXECUTE), because we don't know yet
77  what command is being prepared (resp. executed). We turn tracing on later,
78  if the prepared (resp. executed) command is in the allowed set above, in
79  check_prepared_statement() (resp. mysql_execute_command() called by
80  Prepared_statement::execute()).
81  PREPARE SELECT is worth tracing as it does permanent query
82  transformations.
83 
84  Note that SQLCOM_SELECT includes EXPLAIN.
85  */
86  return (sql_command_flags[sql_command] & CF_OPTIMIZER_TRACE);
87 }
88 
89 
91 bool sets_var_optimizer_trace(enum enum_sql_command sql_command,
92  List<set_var_base> *set_vars)
93 {
94  if (sql_command == SQLCOM_SET_OPTION)
95  {
97  const set_var_base *var;
98  while ((var= it++))
99  if (var->is_var_optimizer_trace())
100  return true;
101  }
102  return false;
103 }
104 
105 void opt_trace_disable_if_no_tables_access(THD *thd, TABLE_LIST *tbl);
106 
107 } // namespace
108 
109 
110 Opt_trace_start::Opt_trace_start(THD *thd, TABLE_LIST *tbl,
111  enum enum_sql_command sql_command,
112  List<set_var_base> *set_vars,
113  const char *query, size_t query_length,
114  sp_printable *instr,
115  const CHARSET_INFO *query_charset)
116  : ctx(&thd->opt_trace)
117 {
118  DBUG_ENTER("opt_trace_start");
119 
120  /*
121  By default, we need an optimizer trace:
122  - if the user asked for it or
123  - if we are using --debug (because the trace serves as a relay for it, for
124  optimizer debug printouts).
125  */
126  const ulonglong var= thd->variables.optimizer_trace;
127  bool support_I_S= false, support_dbug_or_missing_priv= false;
128 
129  /* This will be triggered if --debug or --debug=d:opt_trace is used */
130  DBUG_EXECUTE("opt", support_dbug_or_missing_priv= true;);
131 
132  // First step, decide on what type of I_S support we want
133  if (unlikely(var & Opt_trace_context::FLAG_ENABLED))
134  {
135  if (sql_command_can_be_traced(sql_command) && // (1)
136  !sets_var_optimizer_trace(sql_command, set_vars) && // (2)
137  !list_has_optimizer_trace_table(tbl) && // (3)
138  !thd->system_thread) // (4)
139  {
140  /*
141  (1) This command is interesting Optimizer-wise.
142 
143  (2) This command is not "SET ... @@optimizer_trace=...". Otherwise,
144  this simple usage:
145  a) enable opt trace with SET
146  b) run SELECT query of interest
147  c) disable opt trace with SET
148  d) read OPTIMIZER_TRACE table
149  would not work: (c) would be traced which would delete the trace of
150  (b).
151 
152  (3) If a SELECT of I_S.OPTIMIZER_TRACE were traced, it would overwrite
153  the interesting trace of the previous statement. Note that
154  list_has_optimizer_trace_table() is an expensive function (scanning
155  the list of all used tables, doing checks on their names) but we call
156  it only if @@optimizer_trace has enabled=on.
157 
158  (4) Usage of the trace in a system thread would be
159  impractical. Additionally:
160  - threads of the Events Scheduler have an unusual security context
161  (thd->main_security_ctx.priv_user==NULL, see comment in
162  Security_context::change_security_context()), so we can do no security
163  checks on them, so cannot safely enable tracing.
164  - statement-based replication of
165  "INSERT INTO real_table SELECT * FROM I_S.OPTIMIZER_TRACE" is
166  anyway impossible as @@optimizer_trace* are not replicated, and trace
167  would be different between master and slave unless data and engines
168  and version of the optimizer are strictly identical.
169  - row-based replication of the INSERT SELECT above is still allowed,
170  it does not require enabling optimizer trace on the slave.
171  */
172  support_I_S= true;
173  }
174  else
175  {
176  /*
177  - statement will not be traced in I_S,
178  - if it uses a subquery, this subquery will not be traced,
179  - if it uses a stored routine, this routine's substatements may be
180  traced.
181  */
182  }
183  /*
184  We will do security checks. This is true even in the exceptions
185  (1)...(3) above. Otherwise, in:
186  SET OPTIMIZER_TRACE="ENABLED=ON";
187  SELECT stored_func() FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
188  (exception 2), we would not check for privilege to do SHOW CREATE on
189  stored_func, then we would enter a substatement, which would be traced,
190  and would expose the function's body.
191  So we will do security checks. So need to inform the trace system that
192  it should be ready for a possible call to missing_privilege() later:
193  */
194  support_dbug_or_missing_priv= true;
195  }
196 
197  error= ctx->start(support_I_S, support_dbug_or_missing_priv,
198  thd->variables.end_markers_in_json,
199  (var & Opt_trace_context::FLAG_ONE_LINE),
200  thd->variables.optimizer_trace_offset,
201  thd->variables.optimizer_trace_limit,
202  thd->variables.optimizer_trace_max_mem_size,
203  thd->variables.optimizer_trace_features);
204 
205  if (likely(!error))
206  {
207  if (unlikely(support_I_S) && ctx->is_started())
208  {
209  if (instr != NULL)
210  {
211  String buffer;
212  buffer.set_charset(system_charset_info);
213  instr->print(&buffer);
214  ctx->set_query(buffer.ptr(), buffer.length(), query_charset);
215  }
216  else
217  ctx->set_query(query, query_length, query_charset);
218  }
219  }
220  opt_trace_disable_if_no_tables_access(thd, tbl);
221  DBUG_VOID_RETURN;
222 }
223 
224 
225 Opt_trace_start::~Opt_trace_start()
226 {
227  DBUG_ENTER("~opt_trace_start");
228  if (likely(!error))
229  ctx->end();
230  DBUG_VOID_RETURN;
231 }
232 
233 
234 void opt_trace_print_expanded_query(THD *thd, st_select_lex *select_lex,
235  Opt_trace_object *trace_object)
236 
237 {
238  Opt_trace_context * const trace= &thd->opt_trace;
249  if (likely(!trace->support_I_S()))
250  return;
251  char buff[1024];
252  String str(buff,(uint32) sizeof(buff), system_charset_info);
253  str.length(0);
254  /*
255  If this statement is not SELECT, what is shown here can be inexact.
256  INSERT SELECT is shown as SELECT. DELETE WHERE is shown as SELECT WHERE.
257  This is acceptable given the audience (developers) and the goal (the
258  inexact parts are irrelevant for the optimizer).
259  */
260  select_lex->print(thd, &str, enum_query_type(QT_TO_SYSTEM_CHARSET |
261  QT_SHOW_SELECT_NUMBER |
262  QT_NO_DEFAULT_DB));
263  trace_object->add_utf8("expanded_query", str.ptr(), str.length());
264 }
265 
266 
267 void opt_trace_disable_if_no_security_context_access(THD *thd)
268 {
269 #ifndef NO_EMBEDDED_ACCESS_CHECKS
270  DBUG_ENTER("opt_trace_check_disable_if_no_security_context_access");
271  if (likely(!(thd->variables.optimizer_trace &
272  Opt_trace_context::FLAG_ENABLED)) || // (1)
273  thd->system_thread) // (2)
274  {
275  /*
276  (1) We know that the routine's execution starts with "enabled=off".
277  If it stays so until the routine ends, we needn't do security checks on
278  the routine.
279  If it does not stay so, it means the definer sets it to "on" somewhere
280  in the routine's body. Then it is his conscious decision to generate
281  traces, thus it is still correct to skip the security check.
282 
283  (2) Threads of the Events Scheduler have an unusual security context
284  (thd->main_security_ctx.priv_user==NULL, see comment in
285  Security_context::change_security_context()).
286  */
287  DBUG_VOID_RETURN;
288  }
289  Opt_trace_context * const trace= &thd->opt_trace;
290  if (!trace->is_started())
291  {
292  /*
293  @@optimizer_trace has "enabled=on" but trace is not started.
294  Either Opt_trace_start ctor was not called for our statement (3), or it
295  was called but at that time, the variable had "enabled=off" (4).
296 
297  There are no known cases of (3).
298 
299  (4) suggests that the user managed to change the variable during
300  execution of the statement, and this statement is using
301  view/routine (note that we have not been able to provoke this, maybe
302  this is impossible). If it happens it is suspicious.
303 
304  We disable I_S output. And we cannot do otherwise: we have no place to
305  store a possible "missing privilege" information (no Opt_trace_stmt, as
306  is_started() is false), so cannot do security checks, so cannot safely
307  do tracing, so have to disable I_S output. And even then, we don't know
308  when to re-enable I_S output, as we have no place to store the
309  information "re-enable tracing at the end of this statement", and we
310  don't even have a notion of statement here (statements in the optimizer
311  trace world mean an Opt_trace_stmt object, and there is none here). So
312  we must disable for the session's life.
313 
314  COM_FIELD_LIST opens views, thus used to be a case of (3). To avoid
315  disabling I_S output for the session's life when this command is issued
316  (like in: "SET OPTIMIZER_TRACE='ENABLED=ON';USE somedb;" in the 'mysql'
317  command-line client), we have decided to create a Opt_trace_start for
318  this command. The command itself is not traced though
319  (SQLCOM_SHOW_FIELDS does not have CF_OPTIMIZER_TRACE).
320  */
321  DBUG_ASSERT(false);
322  trace->disable_I_S_for_this_and_children();
323  DBUG_VOID_RETURN;
324  }
325  /*
326  Note that thd->main_security_ctx.master_access is probably invariant
327  accross the life of THD: GRANT/REVOKE don't affect global privileges of an
328  existing connection, per the manual.
329  */
330  if (!(test_all_bits(thd->main_security_ctx.master_access,
331  (GLOBAL_ACLS & ~GRANT_ACL))) &&
332  (0 != strcmp(thd->main_security_ctx.priv_user,
333  thd->security_ctx->priv_user) ||
334  0 != my_strcasecmp(system_charset_info,
335  thd->main_security_ctx.priv_host,
336  thd->security_ctx->priv_host)))
337  trace->missing_privilege();
338  DBUG_VOID_RETURN;
339 #endif
340 }
341 
342 
343 void opt_trace_disable_if_no_stored_proc_func_access(THD *thd, sp_head *sp)
344 {
345 #ifndef NO_EMBEDDED_ACCESS_CHECKS
346  DBUG_ENTER("opt_trace_disable_if_no_stored_proc_func_access");
347  if (likely(!(thd->variables.optimizer_trace &
348  Opt_trace_context::FLAG_ENABLED)) || thd->system_thread)
349  DBUG_VOID_RETURN;
350  Opt_trace_context * const trace= &thd->opt_trace;
351  if (!trace->is_started())
352  {
353  DBUG_ASSERT(false);
354  trace->disable_I_S_for_this_and_children();
355  DBUG_VOID_RETURN;
356  }
357  bool full_access;
358  Security_context * const backup_thd_sctx= thd->security_ctx;
359  DBUG_PRINT("opt", ("routine: '%s'", sp->m_name.str));
360  thd->security_ctx= &thd->main_security_ctx;
361  const bool rc= sp->check_show_access(thd, &full_access) ||
362  !full_access;
363  thd->security_ctx= backup_thd_sctx;
364  if (rc)
365  trace->missing_privilege();
366  DBUG_VOID_RETURN;
367 #endif
368 }
369 
370 
371 void opt_trace_disable_if_no_view_access(THD *thd, TABLE_LIST *view,
372  TABLE_LIST *underlying_tables)
373 {
374 #ifndef NO_EMBEDDED_ACCESS_CHECKS
375  DBUG_ENTER("opt_trace_disable_if_no_view_access");
376  if (likely(!(thd->variables.optimizer_trace &
377  Opt_trace_context::FLAG_ENABLED)) || thd->system_thread)
378  DBUG_VOID_RETURN;
379  Opt_trace_context * const trace= &thd->opt_trace;
380  if (!trace->is_started())
381  {
382  DBUG_ASSERT(false);
383  trace->disable_I_S_for_this_and_children();
384  DBUG_VOID_RETURN;
385  }
386  DBUG_PRINT("opt", ("view: '%s'", view->table_name));
387  Security_context * const backup_table_sctx= view->security_ctx;
388  Security_context * const backup_thd_sctx= thd->security_ctx;
389  const GRANT_INFO backup_grant_info= view->grant;
390 
391  view->security_ctx= NULL; // no SUID context for view
392  thd->security_ctx= &thd->main_security_ctx; // no SUID context for THD
393  const int rc= check_table_access(thd, SHOW_VIEW_ACL, view, false, 1, true);
394 
395  view->security_ctx= backup_table_sctx;
396  thd->security_ctx= backup_thd_sctx;
397  view->grant= backup_grant_info;
398 
399  if (rc)
400  {
401  trace->missing_privilege();
402  DBUG_VOID_RETURN;
403  }
404  /*
405  We needn't check SELECT privilege on this view. Some
406  opt_trace_disable_if_no_tables_access() call has or will check it.
407 
408  Now we check underlying tables/views of our view:
409  */
410  opt_trace_disable_if_no_tables_access(thd, underlying_tables);
411  DBUG_VOID_RETURN;
412 #endif
413 }
414 
415 
416 namespace {
417 
433 void opt_trace_disable_if_no_tables_access(THD *thd, TABLE_LIST *tbl)
434 {
435 #ifndef NO_EMBEDDED_ACCESS_CHECKS
436  DBUG_ENTER("opt_trace_disable_if_no_tables_access");
437  if (likely(!(thd->variables.optimizer_trace &
438  Opt_trace_context::FLAG_ENABLED)) || thd->system_thread)
439  DBUG_VOID_RETURN;
440  Opt_trace_context * const trace= &thd->opt_trace;
441  if (!trace->is_started())
442  {
443  DBUG_ASSERT(false);
444  trace->disable_I_S_for_this_and_children();
445  DBUG_VOID_RETURN;
446  }
447  Security_context * const backup_thd_sctx= thd->security_ctx;
448  thd->security_ctx= &thd->main_security_ctx;
449  const TABLE_LIST * const first_not_own_table=
450  thd->lex->first_not_own_table();
451  for (TABLE_LIST *t= tbl;
452  t != NULL && t != first_not_own_table;
453  t= t->next_global)
454  {
455  DBUG_PRINT("opt", ("table: '%s'", t->table_name));
456  /*
457  Anonymous derived tables (as in
458  "SELECT ... FROM (SELECT ...)") don't have their grant.privilege set.
459  */
460  if (!t->is_anonymous_derived_table())
461  {
462  const GRANT_INFO backup_grant_info= t->grant;
463  Security_context * const backup_table_sctx= t->security_ctx;
464  t->security_ctx= NULL;
465  /*
466  (1) check_table_access() fills t->grant.privilege.
467  (2) Because SELECT privileges can be column-based,
468  check_table_access() will return 'false' as long as there is SELECT
469  privilege on one column. But we want a table-level privilege.
470  */
471 
472  bool rc=
473  check_table_access(thd, SELECT_ACL, t, false, 1, true) || // (1)
474  ((t->grant.privilege & SELECT_ACL) == 0); // (2)
475  if (t->view)
476  {
477  /*
478  It's a view which has already been opened: we are executing a
479  prepared statement. The view has been unfolded in the global list of
480  tables. So underlying tables will be automatically checked in the
481  present function, but we need an explicit check of SHOW VIEW:
482  */
483  rc|= check_table_access(thd, SHOW_VIEW_ACL, t, false, 1, true);
484  }
485  t->security_ctx= backup_table_sctx;
486  t->grant= backup_grant_info;
487  if (rc)
488  {
489  trace->missing_privilege();
490  break;
491  }
492  }
493  }
494  thd->security_ctx= backup_thd_sctx;
495  DBUG_VOID_RETURN;
496 #endif
497 }
498 
499 } // namespace
500 
501 
502 int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *cond)
503 {
504  TABLE *table= tables->table;
505  Opt_trace_info info;
506 
507  /*
508  When executing a routine which is SQL SECURITY DEFINER, opt-trace specific
509  checks are done with the connected user's privileges; this isn't
510  respecting the meaning of SQL SECURITY DEFINER. If a highly privileged
511  user doesn't know that, he may confidently execute a routine, while this
512  routine nastily uses the connected user's privileges to be allowed to do
513  tracing and gain knowledge about secret objects.
514  This possibility is prevented, by making I_S.OPTIMIZER_TRACE look empty
515  when read from a security context which isn't the connected user's
516  context; with an exception if the SUID security context has all
517  global privileges (in which case the nasty definer has anyway all rights
518  to trace everything).
519 
520  Objects which are SQL SECURITY INVOKER are not considered here: with or
521  without optimizer trace, a highly privileged user must always inspect the
522  body of such object before invoking it.
523  */
524  if (!test_all_bits(thd->security_ctx->master_access,
525  (GLOBAL_ACLS & ~GRANT_ACL)) &&
526  (0 != strcmp(thd->main_security_ctx.priv_user,
527  thd->security_ctx->priv_user) ||
528  0 != my_strcasecmp(system_charset_info,
529  thd->main_security_ctx.priv_host,
530  thd->security_ctx->priv_host)))
531  return 0;
532  /*
533  The list must not change during the iterator's life time. This is ok as
534  the life time is only the present block which cannot change the list.
535  */
536  for (Opt_trace_iterator it(&thd->opt_trace) ; !it.at_end() ; it.next())
537  {
538  it.get_value(&info);
539  restore_record(table, s->default_values);
540  /*
541  We will put the query, which is in character_set_client, into a column
542  using character_set_client; this is better than UTF8 (see BUG#57306).
543  When literals with introducers are used, see "LiteralsWithIntroducers"
544  in this file.
545  */
546  table->field[0]->store(info.query_ptr,
547  static_cast<uint>(info.query_length),
548  info.query_charset);
549  table->field[1]->store(info.trace_ptr,
550  static_cast<uint>(info.trace_length),
551  system_charset_info);
552  table->field[2]->store(info.missing_bytes, true);
553  table->field[3]->store(info.missing_priv, true);
554  if (schema_table_store_record(thd, table))
555  return 1;
556  }
557 
558  return 0;
559 }
560 
561 #endif // OPTIMIZER_TRACE
562 
564 {
565  /* name, length, type, value, maybe_null, old_name, open_method */
566  {"QUERY", 65535, MYSQL_TYPE_STRING, 0, false, NULL, SKIP_OPEN_TABLE},
567  {"TRACE", 65535, MYSQL_TYPE_STRING, 0, false, NULL, SKIP_OPEN_TABLE},
568  {"MISSING_BYTES_BEYOND_MAX_MEM_SIZE", 20, MYSQL_TYPE_LONG,
569  0, false, NULL, SKIP_OPEN_TABLE},
570  {"INSUFFICIENT_PRIVILEGES", 1, MYSQL_TYPE_TINY,
571  0, false, NULL, SKIP_OPEN_TABLE},
572  {NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL, 0}
573 };
574 
575 
576 /*
577  LiteralsWithIntroducers :
578 
579  They may be significantly altered; but this isn't specific to the optimizer
580  trace, it also happens with SHOW PROCESSLIST, and is deemed a not too
581  important problem.
582 
583  Consider
584  mysql> set names latin1;
585  mysql> SELECT 'í', _cp850'í';
586  | í | Ý |
587  This sends the binary string:
588  SELECT <0xED>, _cp850<0xED>
589  to the server (í is 0xED in latin1).
590  Now we put this into OPTIMIZER_TRACE.QUERY, using latin1
591  (character_set_client), and the client has switched to utf8: we convert the
592  query from latin1 to utf8 when sending to client, which receives:
593  SELECT <0xC3><0xAD>, _cp850<0xC3><0xAD>
594  (í is <0xC3><0xAD> in utf8).
595  But <0xC3><0xAD> in _cp850 means a completely different character:
596  mysql> set names utf8;
597  mysql> SELECT 'í', _cp850'í';
598  | í | ├¡ |
599 
600  If the client had originally issued
601  SELECT 'í', _cp850 0xED;
602  there would be no problem ('0', 'x', 'E', and 'D' are identical in latin1
603  and utf8: they would be preserved during conversion).
604 */