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