1 /* Copyright (c) 1995, 2011, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */
15 
16 /**********************************************************************
17 This file contains the implementation of error and warnings related
18 
19   - Whenever an error or warning occurred, it pushes it to a warning list
20     that the user can retrieve with SHOW WARNINGS or SHOW ERRORS.
21 
22   - For each statement, we return the number of warnings generated from this
23     command.  Note that this can be different from @@warning_count as
24     we reset the warning list only for questions that uses a table.
25     This is done to allow on to do:
26     INSERT ...;
27     SELECT @@warning_count;
28     SHOW WARNINGS;
29     (If we would reset after each command, we could not retrieve the number
30      of warnings)
31 
32   - When client requests the information using SHOW command, then
33     server processes from this list and returns back in the form of
34     resultset.
35 
36     Supported syntaxes:
37 
38     SHOW [COUNT(*)] ERRORS [LIMIT [offset,] rows]
39     SHOW [COUNT(*)] WARNINGS [LIMIT [offset,] rows]
40     SELECT @@warning_count, @@error_count;
41 
42 ***********************************************************************/
43 
44 #include "sql_priv.h"
45 #include "unireg.h"
46 #include "sql_error.h"
47 #include "sp_rcontext.h"
48 
49 /*
50   Design notes about MYSQL_ERROR::m_message_text.
51 
52   The member MYSQL_ERROR::m_message_text contains the text associated with
53   an error, warning or note (which are all SQL 'conditions')
54 
55   Producer of MYSQL_ERROR::m_message_text:
56   ----------------------------------------
57 
58   (#1) the server implementation itself, when invoking functions like
59   my_error() or push_warning()
60 
61   (#2) user code in stored programs, when using the SIGNAL statement.
62 
63   (#3) user code in stored programs, when using the RESIGNAL statement.
64 
65   When invoking my_error(), the error number and message is typically
66   provided like this:
67   - my_error(ER_WRONG_DB_NAME, MYF(0), ...);
68   - my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
69 
70   In both cases, the message is retrieved from ER(ER_XXX), which in turn
71   is read from the resource file errmsg.sys at server startup.
72   The strings stored in the errmsg.sys file are expressed in the character set
73   that corresponds to the server --language start option
74   (see error_message_charset_info).
75 
76   When executing:
77   - a SIGNAL statement,
78   - a RESIGNAL statement,
79   the message text is provided by the user logic, and is expressed in UTF8.
80 
81   Storage of MYSQL_ERROR::m_message_text:
82   ---------------------------------------
83 
84   (#4) The class MYSQL_ERROR is used to hold the message text member.
85   This class represents a single SQL condition.
86 
87   (#5) The class Warning_info represents a SQL condition area, and contains
88   a collection of SQL conditions in the Warning_info::m_warn_list
89 
90   Consumer of MYSQL_ERROR::m_message_text:
91   ----------------------------------------
92 
93   (#6) The statements SHOW WARNINGS and SHOW ERRORS display the content of
94   the warning list.
95 
96   (#7) The GET DIAGNOSTICS statement (planned, not implemented yet) will
97   also read the content of:
98   - the top level statement condition area (when executed in a query),
99   - a sub statement (when executed in a stored program)
100   and return the data stored in a MYSQL_ERROR.
101 
102   (#8) The RESIGNAL statement reads the MYSQL_ERROR caught by an exception
103   handler, to raise a new or modified condition (in #3).
104 
105   The big picture
106   ---------------
107                                                               --------------
108                                                               |            ^
109                                                               V            |
110   my_error(#1)                 SIGNAL(#2)                 RESIGNAL(#3)     |
111       |(#A)                       |(#B)                       |(#C)        |
112       |                           |                           |            |
113       ----------------------------|----------------------------            |
114                                   |                                        |
115                                   V                                        |
116                            MYSQL_ERROR(#4)                                 |
117                                   |                                        |
118                                   |                                        |
119                                   V                                        |
120                            Warning_info(#5)                                |
121                                   |                                        |
122           -----------------------------------------------------            |
123           |                       |                           |            |
124           |                       |                           |            |
125           |                       |                           |            |
126           V                       V                           V            |
127    SHOW WARNINGS(#6)      GET DIAGNOSTICS(#7)              RESIGNAL(#8)    |
128           |  |                    |                           |            |
129           |  --------             |                           V            |
130           |         |             |                           --------------
131           V         |             |
132       Connectors    |             |
133           |         |             |
134           -------------------------
135                     |
136                     V
137              Client application
138 
139   Current implementation status
140   -----------------------------
141 
142   (#1) (my_error) produces data in the 'error_message_charset_info' CHARSET
143 
144   (#2) and (#3) (SIGNAL, RESIGNAL) produces data internally in UTF8
145 
146   (#6) (SHOW WARNINGS) produces data in the 'error_message_charset_info' CHARSET
147 
148   (#7) (GET DIAGNOSTICS) is not implemented.
149 
150   (#8) (RESIGNAL) produces data internally in UTF8 (see #3)
151 
152   As a result, the design choice for (#4) and (#5) is to store data in
153   the 'error_message_charset_info' CHARSET, to minimize impact on the code base.
154   This is implemented by using 'String MYSQL_ERROR::m_message_text'.
155 
156   The UTF8 -> error_message_charset_info conversion is implemented in
157   Signal_common::eval_signal_informations() (for path #B and #C).
158 
159   Future work
160   -----------
161 
162   - Change (#1) (my_error) to generate errors in UTF8.
163     See WL#751 (Recoding of error messages)
164 
165   - Change (#4 and #5) to store message text in UTF8 natively.
166     In practice, this means changing the type of the message text to
167     '<UTF8 String 128 class> MYSQL_ERROR::m_message_text', and is a direct
168     consequence of WL#751.
169 
170   - Implement (#9) (GET DIAGNOSTICS).
171     See WL#2111 (Stored Procedures: Implement GET DIAGNOSTICS)
172 */
173 
MYSQL_ERROR()174 MYSQL_ERROR::MYSQL_ERROR()
175  : Sql_alloc(),
176    m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin),
177    m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin),
178    m_constraint_catalog((const char*) NULL, 0, & my_charset_utf8_bin),
179    m_constraint_schema((const char*) NULL, 0, & my_charset_utf8_bin),
180    m_constraint_name((const char*) NULL, 0, & my_charset_utf8_bin),
181    m_catalog_name((const char*) NULL, 0, & my_charset_utf8_bin),
182    m_schema_name((const char*) NULL, 0, & my_charset_utf8_bin),
183    m_table_name((const char*) NULL, 0, & my_charset_utf8_bin),
184    m_column_name((const char*) NULL, 0, & my_charset_utf8_bin),
185    m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin),
186    m_message_text(),
187    m_sql_errno(0),
188    m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
189    m_mem_root(NULL)
190 {
191   memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate));
192 }
193 
init(MEM_ROOT * mem_root)194 void MYSQL_ERROR::init(MEM_ROOT *mem_root)
195 {
196   DBUG_ASSERT(mem_root != NULL);
197   DBUG_ASSERT(m_mem_root == NULL);
198   m_mem_root= mem_root;
199 }
200 
clear()201 void MYSQL_ERROR::clear()
202 {
203   m_class_origin.length(0);
204   m_subclass_origin.length(0);
205   m_constraint_catalog.length(0);
206   m_constraint_schema.length(0);
207   m_constraint_name.length(0);
208   m_catalog_name.length(0);
209   m_schema_name.length(0);
210   m_table_name.length(0);
211   m_column_name.length(0);
212   m_cursor_name.length(0);
213   m_message_text.length(0);
214   m_sql_errno= 0;
215   m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
216 }
217 
MYSQL_ERROR(MEM_ROOT * mem_root)218 MYSQL_ERROR::MYSQL_ERROR(MEM_ROOT *mem_root)
219  : Sql_alloc(),
220    m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin),
221    m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin),
222    m_constraint_catalog((const char*) NULL, 0, & my_charset_utf8_bin),
223    m_constraint_schema((const char*) NULL, 0, & my_charset_utf8_bin),
224    m_constraint_name((const char*) NULL, 0, & my_charset_utf8_bin),
225    m_catalog_name((const char*) NULL, 0, & my_charset_utf8_bin),
226    m_schema_name((const char*) NULL, 0, & my_charset_utf8_bin),
227    m_table_name((const char*) NULL, 0, & my_charset_utf8_bin),
228    m_column_name((const char*) NULL, 0, & my_charset_utf8_bin),
229    m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin),
230    m_message_text(),
231    m_sql_errno(0),
232    m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
233    m_mem_root(mem_root)
234 {
235   DBUG_ASSERT(mem_root != NULL);
236   memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate));
237 }
238 
copy_string(MEM_ROOT * mem_root,String * dst,const String * src)239 static void copy_string(MEM_ROOT *mem_root, String* dst, const String* src)
240 {
241   size_t len= src->length();
242   if (len)
243   {
244     char* copy= (char*) alloc_root(mem_root, len + 1);
245     if (copy)
246     {
247       memcpy(copy, src->ptr(), len);
248       copy[len]= '\0';
249       dst->set(copy, len, src->charset());
250     }
251   }
252   else
253     dst->length(0);
254 }
255 
256 void
copy_opt_attributes(const MYSQL_ERROR * cond)257 MYSQL_ERROR::copy_opt_attributes(const MYSQL_ERROR *cond)
258 {
259   DBUG_ASSERT(this != cond);
260   copy_string(m_mem_root, & m_class_origin, & cond->m_class_origin);
261   copy_string(m_mem_root, & m_subclass_origin, & cond->m_subclass_origin);
262   copy_string(m_mem_root, & m_constraint_catalog, & cond->m_constraint_catalog);
263   copy_string(m_mem_root, & m_constraint_schema, & cond->m_constraint_schema);
264   copy_string(m_mem_root, & m_constraint_name, & cond->m_constraint_name);
265   copy_string(m_mem_root, & m_catalog_name, & cond->m_catalog_name);
266   copy_string(m_mem_root, & m_schema_name, & cond->m_schema_name);
267   copy_string(m_mem_root, & m_table_name, & cond->m_table_name);
268   copy_string(m_mem_root, & m_column_name, & cond->m_column_name);
269   copy_string(m_mem_root, & m_cursor_name, & cond->m_cursor_name);
270 }
271 
272 void
set(uint sql_errno,const char * sqlstate,MYSQL_ERROR::enum_warning_level level,const char * msg)273 MYSQL_ERROR::set(uint sql_errno, const char* sqlstate,
274                  MYSQL_ERROR::enum_warning_level level, const char* msg)
275 {
276   DBUG_ASSERT(sql_errno != 0);
277   DBUG_ASSERT(sqlstate != NULL);
278   DBUG_ASSERT(msg != NULL);
279 
280   m_sql_errno= sql_errno;
281   memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH);
282   m_returned_sqlstate[SQLSTATE_LENGTH]= '\0';
283 
284   set_builtin_message_text(msg);
285   m_level= level;
286 }
287 
288 void
set_builtin_message_text(const char * str)289 MYSQL_ERROR::set_builtin_message_text(const char* str)
290 {
291   /*
292     See the comments
293      "Design notes about MYSQL_ERROR::m_message_text."
294   */
295   const char* copy;
296 
297   copy= strdup_root(m_mem_root, str);
298   m_message_text.set(copy, strlen(copy), error_message_charset_info);
299   DBUG_ASSERT(! m_message_text.is_alloced());
300 }
301 
302 const char*
get_message_text() const303 MYSQL_ERROR::get_message_text() const
304 {
305   return m_message_text.ptr();
306 }
307 
308 int
get_message_octet_length() const309 MYSQL_ERROR::get_message_octet_length() const
310 {
311   return m_message_text.length();
312 }
313 
314 void
set_sqlstate(const char * sqlstate)315 MYSQL_ERROR::set_sqlstate(const char* sqlstate)
316 {
317   memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH);
318   m_returned_sqlstate[SQLSTATE_LENGTH]= '\0';
319 }
320 
321 /**
322   Clear this diagnostics area.
323 
324   Normally called at the end of a statement.
325 */
326 
327 void
reset_diagnostics_area()328 Diagnostics_area::reset_diagnostics_area()
329 {
330   DBUG_ENTER("reset_diagnostics_area");
331 #ifdef DBUG_OFF
332   can_overwrite_status= FALSE;
333   /** Don't take chances in production */
334   m_message[0]= '\0';
335   m_sql_errno= 0;
336   m_affected_rows= 0;
337   m_last_insert_id= 0;
338   m_statement_warn_count= 0;
339 #endif
340   is_sent= FALSE;
341   /** Tiny reset in debug mode to see garbage right away */
342   m_status= DA_EMPTY;
343   DBUG_VOID_RETURN;
344 }
345 
346 
347 /**
348   Set OK status -- ends commands that do not return a
349   result set, e.g. INSERT/UPDATE/DELETE.
350 */
351 
352 void
set_ok_status(THD * thd,ulonglong affected_rows_arg,ulonglong last_insert_id_arg,const char * message_arg)353 Diagnostics_area::set_ok_status(THD *thd, ulonglong affected_rows_arg,
354                                 ulonglong last_insert_id_arg,
355                                 const char *message_arg)
356 {
357   DBUG_ENTER("set_ok_status");
358   DBUG_ASSERT(! is_set());
359   /*
360     In production, refuse to overwrite an error or a custom response
361     with an OK packet.
362   */
363   if (is_error() || is_disabled())
364     return;
365 
366   m_statement_warn_count= thd->warning_info->statement_warn_count();
367   m_affected_rows= affected_rows_arg;
368   m_last_insert_id= last_insert_id_arg;
369   if (message_arg)
370     strmake(m_message, message_arg, sizeof(m_message) - 1);
371   else
372     m_message[0]= '\0';
373   m_status= DA_OK;
374   DBUG_VOID_RETURN;
375 }
376 
377 
378 /**
379   Set EOF status.
380 */
381 
382 void
set_eof_status(THD * thd)383 Diagnostics_area::set_eof_status(THD *thd)
384 {
385   DBUG_ENTER("set_eof_status");
386   /* Only allowed to report eof if has not yet reported an error */
387   DBUG_ASSERT(! is_set());
388   /*
389     In production, refuse to overwrite an error or a custom response
390     with an EOF packet.
391   */
392   if (is_error() || is_disabled())
393     return;
394 
395   /*
396     If inside a stored procedure, do not return the total
397     number of warnings, since they are not available to the client
398     anyway.
399   */
400   m_statement_warn_count= (thd->spcont ?
401                            0 : thd->warning_info->statement_warn_count());
402 
403   m_status= DA_EOF;
404   DBUG_VOID_RETURN;
405 }
406 
407 /**
408   Set ERROR status.
409 */
410 
411 void
set_error_status(THD * thd,uint sql_errno_arg,const char * message_arg,const char * sqlstate)412 Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
413                                    const char *message_arg,
414                                    const char *sqlstate)
415 {
416   DBUG_ENTER("set_error_status");
417   /*
418     Only allowed to report error if has not yet reported a success
419     The only exception is when we flush the message to the client,
420     an error can happen during the flush.
421   */
422   DBUG_ASSERT(! is_set() || can_overwrite_status);
423 #ifdef DBUG_OFF
424   /*
425     In production, refuse to overwrite a custom response with an
426     ERROR packet.
427   */
428   if (is_disabled())
429     return;
430 #endif
431 
432   if (sqlstate == NULL)
433     sqlstate= mysql_errno_to_sqlstate(sql_errno_arg);
434 
435   m_sql_errno= sql_errno_arg;
436   memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH);
437   m_sqlstate[SQLSTATE_LENGTH]= '\0';
438   strmake(m_message, message_arg, sizeof(m_message)-1);
439 
440   m_status= DA_ERROR;
441   DBUG_VOID_RETURN;
442 }
443 
444 
445 /**
446   Mark the diagnostics area as 'DISABLED'.
447 
448   This is used in rare cases when the COM_ command at hand sends a response
449   in a custom format. One example is the query cache, another is
450   COM_STMT_PREPARE.
451 */
452 
453 void
disable_status()454 Diagnostics_area::disable_status()
455 {
456   DBUG_ASSERT(! is_set());
457   m_status= DA_DISABLED;
458 }
459 
Warning_info(ulonglong warn_id_arg,bool allow_unlimited_warnings)460 Warning_info::Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings)
461   :m_statement_warn_count(0),
462   m_current_row_for_warning(1),
463   m_warn_id(warn_id_arg),
464   m_allow_unlimited_warnings(allow_unlimited_warnings),
465   m_read_only(FALSE)
466 {
467   /* Initialize sub structures */
468   init_sql_alloc(&m_warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
469   m_warn_list.empty();
470   bzero((char*) m_warn_count, sizeof(m_warn_count));
471 }
472 
473 
~Warning_info()474 Warning_info::~Warning_info()
475 {
476   free_root(&m_warn_root,MYF(0));
477 }
478 
479 
480 /**
481   Reset the warning information of this connection.
482 */
483 
clear_warning_info(ulonglong warn_id_arg)484 void Warning_info::clear_warning_info(ulonglong warn_id_arg)
485 {
486   m_warn_id= warn_id_arg;
487   free_root(&m_warn_root, MYF(0));
488   bzero((char*) m_warn_count, sizeof(m_warn_count));
489   m_warn_list.empty();
490   m_statement_warn_count= 0;
491   m_current_row_for_warning= 1; /* Start counting from the first row */
492 }
493 
494 /**
495   Append warnings only if the original contents of the routine
496   warning info was replaced.
497 */
merge_with_routine_info(THD * thd,Warning_info * source)498 void Warning_info::merge_with_routine_info(THD *thd, Warning_info *source)
499 {
500   /*
501     If a routine body is empty or if a routine did not
502     generate any warnings (thus m_warn_id didn't change),
503     do not duplicate our own contents by appending the
504     contents of the called routine. We know that the called
505     routine did not change its warning info.
506 
507     On the other hand, if the routine body is not empty and
508     some statement in the routine generates a warning or
509     uses tables, m_warn_id is guaranteed to have changed.
510     In this case we know that the routine warning info
511     contains only new warnings, and thus we perform a copy.
512   */
513   if (m_warn_id != source->m_warn_id)
514   {
515     /*
516       If the invocation of the routine was a standalone statement,
517       rather than a sub-statement, in other words, if it's a CALL
518       of a procedure, rather than invocation of a function or a
519       trigger, we need to clear the current contents of the caller's
520       warning info.
521 
522       This is per MySQL rules: if a statement generates a warning,
523       warnings from the previous statement are flushed.  Normally
524       it's done in push_warning(). However, here we don't use
525       push_warning() to avoid invocation of condition handlers or
526       escalation of warnings to errors.
527     */
528     opt_clear_warning_info(thd->query_id);
529     append_warning_info(thd, source);
530   }
531 }
532 
533 /**
534   Add a warning to the list of warnings. Increment the respective
535   counters.
536 */
push_warning(THD * thd,uint sql_errno,const char * sqlstate,MYSQL_ERROR::enum_warning_level level,const char * msg)537 MYSQL_ERROR *Warning_info::push_warning(THD *thd,
538                                         uint sql_errno, const char* sqlstate,
539                                         MYSQL_ERROR::enum_warning_level level,
540                                         const char *msg)
541 {
542   MYSQL_ERROR *cond= NULL;
543 
544   if (! m_read_only)
545   {
546     if (m_allow_unlimited_warnings ||
547         m_warn_list.elements < thd->variables.max_error_count)
548     {
549       cond= new (& m_warn_root) MYSQL_ERROR(& m_warn_root);
550       if (cond)
551       {
552         cond->set(sql_errno, sqlstate, level, msg);
553         m_warn_list.push_back(cond, &m_warn_root);
554       }
555     }
556     m_warn_count[(uint) level]++;
557   }
558 
559   m_statement_warn_count++;
560   return cond;
561 }
562 
push_warning(THD * thd,const MYSQL_ERROR * sql_condition)563 MYSQL_ERROR *Warning_info::push_warning(THD *thd, const MYSQL_ERROR *sql_condition)
564 {
565   MYSQL_ERROR *new_condition= push_warning(thd,
566                                            sql_condition->get_sql_errno(),
567                                            sql_condition->get_sqlstate(),
568                                            sql_condition->get_level(),
569                                            sql_condition->get_message_text());
570 
571   if (new_condition)
572     new_condition->copy_opt_attributes(sql_condition);
573 
574   return new_condition;
575 }
576 
577 /*
578   Push the warning to error list if there is still room in the list
579 
580   SYNOPSIS
581     push_warning()
582     thd			Thread handle
583     level		Severity of warning (note, warning)
584     code		Error number
585     msg			Clear error message
586 */
587 
push_warning(THD * thd,MYSQL_ERROR::enum_warning_level level,uint code,const char * msg)588 void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
589                   uint code, const char *msg)
590 {
591   DBUG_ENTER("push_warning");
592   DBUG_PRINT("enter", ("code: %d, msg: %s", code, msg));
593 
594   /*
595     Calling push_warning/push_warning_printf with a level of
596     WARN_LEVEL_ERROR *is* a bug.  Either use my_printf_error(),
597     my_error(), or WARN_LEVEL_WARN.
598   */
599   DBUG_ASSERT(level != MYSQL_ERROR::WARN_LEVEL_ERROR);
600 
601   if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
602     level= MYSQL_ERROR::WARN_LEVEL_WARN;
603 
604   (void) thd->raise_condition(code, NULL, level, msg);
605 
606   DBUG_VOID_RETURN;
607 }
608 
609 
610 /*
611   Push the warning to error list if there is still room in the list
612 
613   SYNOPSIS
614     push_warning_printf()
615     thd			Thread handle
616     level		Severity of warning (note, warning)
617     code		Error number
618     msg			Clear error message
619 */
620 
push_warning_printf(THD * thd,MYSQL_ERROR::enum_warning_level level,uint code,const char * format,...)621 void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
622 			 uint code, const char *format, ...)
623 {
624   va_list args;
625   char    warning[MYSQL_ERRMSG_SIZE];
626   DBUG_ENTER("push_warning_printf");
627   DBUG_PRINT("enter",("warning: %u", code));
628 
629   DBUG_ASSERT(code != 0);
630   DBUG_ASSERT(format != NULL);
631 
632   va_start(args,format);
633   my_vsnprintf_ex(&my_charset_utf8_general_ci, warning,
634                   sizeof(warning), format, args);
635   va_end(args);
636   push_warning(thd, level, code, warning);
637   DBUG_VOID_RETURN;
638 }
639 
640 
641 /*
642   Send all notes, errors or warnings to the client in a result set
643 
644   SYNOPSIS
645     mysqld_show_warnings()
646     thd			Thread handler
647     levels_to_show	Bitmap for which levels to show
648 
649   DESCRIPTION
650     Takes into account the current LIMIT
651 
652   RETURN VALUES
653     FALSE ok
654     TRUE  Error sending data to client
655 */
656 
657 const LEX_STRING warning_level_names[]=
658 {
659   { C_STRING_WITH_LEN("Note") },
660   { C_STRING_WITH_LEN("Warning") },
661   { C_STRING_WITH_LEN("Error") },
662   { C_STRING_WITH_LEN("?") }
663 };
664 
mysqld_show_warnings(THD * thd,ulong levels_to_show)665 bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
666 {
667   List<Item> field_list;
668   DBUG_ENTER("mysqld_show_warnings");
669 
670   DBUG_ASSERT(thd->warning_info->is_read_only());
671 
672   field_list.push_back(new Item_empty_string("Level", 7));
673   field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG));
674   field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE));
675 
676   if (thd->protocol->send_result_set_metadata(&field_list,
677                                  Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
678     DBUG_RETURN(TRUE);
679 
680   MYSQL_ERROR *err;
681   SELECT_LEX *sel= &thd->lex->select_lex;
682   SELECT_LEX_UNIT *unit= &thd->lex->unit;
683   ulonglong idx= 0;
684   Protocol *protocol=thd->protocol;
685 
686   unit->set_limit(sel);
687 
688   List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
689   while ((err= it++))
690   {
691     /* Skip levels that the user is not interested in */
692     if (!(levels_to_show & ((ulong) 1 << err->get_level())))
693       continue;
694     if (++idx <= unit->offset_limit_cnt)
695       continue;
696     if (idx > unit->select_limit_cnt)
697       break;
698     protocol->prepare_for_resend();
699     protocol->store(warning_level_names[err->get_level()].str,
700 		    warning_level_names[err->get_level()].length,
701                     system_charset_info);
702     protocol->store((uint32) err->get_sql_errno());
703     protocol->store(err->get_message_text(),
704                     err->get_message_octet_length(),
705                     system_charset_info);
706     if (protocol->write())
707       DBUG_RETURN(TRUE);
708   }
709   my_eof(thd);
710 
711   thd->warning_info->set_read_only(FALSE);
712 
713   DBUG_RETURN(FALSE);
714 }
715 
716 
717 /**
718    Convert value for dispatch to error message(see WL#751).
719 
720    @param to          buffer for converted string
721    @param to_length   size of the buffer
722    @param from        string which should be converted
723    @param from_length string length
724    @param from_cs     charset from convert
725 
726    @retval
727    result string
728 */
729 
err_conv(char * buff,uint to_length,const char * from,uint from_length,CHARSET_INFO * from_cs)730 char *err_conv(char *buff, uint to_length, const char *from,
731                uint from_length, CHARSET_INFO *from_cs)
732 {
733   char *to= buff;
734   const char *from_start= from;
735   size_t res;
736 
737   DBUG_ASSERT(to_length > 0);
738   to_length--;
739   if (from_cs == &my_charset_bin)
740   {
741     uchar char_code;
742     res= 0;
743     while (1)
744     {
745       if ((uint)(from - from_start) >= from_length ||
746           res >= to_length)
747       {
748         *to= 0;
749         break;
750       }
751 
752       char_code= ((uchar) *from);
753       if (char_code >= 0x20 && char_code <= 0x7E)
754       {
755         *to++= char_code;
756         from++;
757         res++;
758       }
759       else
760       {
761         if (res + 4 >= to_length)
762         {
763           *to= 0;
764           break;
765         }
766         res+= my_snprintf(to, 5, "\\x%02X", (uint) char_code);
767         to+=4;
768         from++;
769       }
770     }
771   }
772   else
773   {
774     uint errors;
775     res= copy_and_convert(to, to_length, system_charset_info,
776                           from, from_length, from_cs, &errors);
777     to[res]= 0;
778   }
779   return buff;
780 }
781 
782 
783 /**
784    Convert string for dispatch to client(see WL#751).
785 
786    @param to          buffer to convert
787    @param to_length   buffer length
788    @param to_cs       chraset to convert
789    @param from        string from convert
790    @param from_length string length
791    @param from_cs     charset from convert
792    @param errors      count of errors during convertion
793 
794    @retval
795    length of converted string
796 */
797 
convert_error_message(char * to,uint32 to_length,CHARSET_INFO * to_cs,const char * from,uint32 from_length,CHARSET_INFO * from_cs,uint * errors)798 uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
799                              const char *from, uint32 from_length,
800                              CHARSET_INFO *from_cs, uint *errors)
801 {
802   int         cnvres;
803   my_wc_t     wc;
804   const uchar *from_end= (const uchar*) from+from_length;
805   char *to_start= to;
806   uchar *to_end;
807   my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
808   my_charset_conv_wc_mb wc_mb;
809   uint error_count= 0;
810   uint length;
811 
812   DBUG_ASSERT(to_length > 0);
813   /* Make room for the null terminator. */
814   to_length--;
815   to_end= (uchar*) (to + to_length);
816 
817   if (!to_cs || from_cs == to_cs || to_cs == &my_charset_bin)
818   {
819     length= min(to_length, from_length);
820     memmove(to, from, length);
821     to[length]= 0;
822     return length;
823   }
824 
825   wc_mb= to_cs->cset->wc_mb;
826   while (1)
827   {
828     if ((cnvres= (*mb_wc)(from_cs, &wc, (uchar*) from, from_end)) > 0)
829     {
830       if (!wc)
831         break;
832       from+= cnvres;
833     }
834     else if (cnvres == MY_CS_ILSEQ)
835     {
836       wc= (ulong) (uchar) *from;
837       from+=1;
838     }
839     else
840       break;
841 
842     if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0)
843       to+= cnvres;
844     else if (cnvres == MY_CS_ILUNI)
845     {
846       length= (wc <= 0xFFFF) ? 6/* '\1234' format*/ : 9 /* '\+123456' format*/;
847       if ((uchar*)(to + length) >= to_end)
848         break;
849       cnvres= my_snprintf(to, 9,
850                           (wc <= 0xFFFF) ? "\\%04X" : "\\+%06X", (uint) wc);
851       to+= cnvres;
852     }
853     else
854       break;
855   }
856 
857   *to= 0;
858   *errors= error_count;
859   return (uint32) (to - to_start);
860 }
861