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-1335  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 "mariadb.h"
45 #include "sql_priv.h"
46 #include "unireg.h"
47 #include "sql_error.h"
executor()48 #include "sp_rcontext.h"
49 
50 /*
51   Design notes about Sql_condition::m_message_text.
52 
53   The member Sql_condition::m_message_text contains the text associated with
54   an error, warning or note (which are all SQL 'conditions')
55 
56   Producer of Sql_condition::m_message_text:
57   ----------------------------------------
58 
59   (#1) the server implementation itself, when invoking functions like
60   my_error() or push_warning()
61 
62   (#2) user code in stored programs, when using the SIGNAL statement.
63 
64   (#3) user code in stored programs, when using the RESIGNAL statement.
65 
66   When invoking my_error(), the error number and message is typically
67   provided like this:
68   - my_error(ER_WRONG_DB_NAME, MYF(0), ...);
69   - my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
70 
71   In both cases, the message is retrieved from ER(ER_XXX), which in turn
72   is read from the resource file errmsg.sys at server startup.
73   The strings stored in the errmsg.sys file are expressed in the character set
74   that corresponds to the server --language start option
75   (see error_message_charset_info).
76 
77   When executing:
78   - a SIGNAL statement,
79   - a RESIGNAL statement,
80   the message text is provided by the user logic, and is expressed in UTF8.
81 
82   Storage of Sql_condition::m_message_text:
83   ---------------------------------------
84 
85   (#4) The class Sql_condition is used to hold the message text member.
86   This class represents a single SQL condition.
87 
88   (#5) The class Warning_info represents a SQL condition area, and contains
89   a collection of SQL conditions in the Warning_info::m_warn_list
90 
91   Consumer of Sql_condition::m_message_text:
92   ----------------------------------------
93 
94   (#6) The statements SHOW WARNINGS and SHOW ERRORS display the content of
95   the warning list.
96 
97   (#7) The GET DIAGNOSTICS statement (planned, not implemented yet) will
98   also read the content of:
99   - the top level statement condition area (when executed in a query),
100   - a sub statement (when executed in a stored program)
101   and return the data stored in a Sql_condition.
102 
103   (#8) The RESIGNAL statement reads the Sql_condition caught by an exception
104   handler, to raise a new or modified condition (in #3).
105 
106   The big picture
107   ---------------
108                                                               --------------
109                                                               |            ^
110                                                               V            |
111   my_error(#1)                 SIGNAL(#2)                 RESIGNAL(#3)     |
112       |(#A)                       |(#B)                       |(#C)        |
113       |                           |                           |            |
114       ----------------------------|----------------------------            |
115                                   |                                        |
116                                   V                                        |
117                            Sql_condition(#4)                                 |
118                                   |                                        |
119                                   |                                        |
120                                   V                                        |
121                            Warning_info(#5)                                |
122                                   |                                        |
123           -----------------------------------------------------            |
124           |                       |                           |            |
125           |                       |                           |            |
126           |                       |                           |            |
127           V                       V                           V            |
128    SHOW WARNINGS(#6)      GET DIAGNOSTICS(#7)              RESIGNAL(#8)    |
129           |  |                    |                           |            |
130           |  --------             |                           V            |
131           |         |             |                           --------------
132           V         |             |
133       Connectors    |             |
134           |         |             |
135           -------------------------
136                     |
137                     V
138              Client application
139 
140   Current implementation status
141   -----------------------------
142 
143   (#1) (my_error) produces data in the 'error_message_charset_info' CHARSET
144 
145   (#2) and (#3) (SIGNAL, RESIGNAL) produces data internally in UTF8
146 
147   (#6) (SHOW WARNINGS) produces data in the 'error_message_charset_info' CHARSET
148 
149   (#7) (GET DIAGNOSTICS) is not implemented.
150 
151   (#8) (RESIGNAL) produces data internally in UTF8 (see #3)
152 
153   As a result, the design choice for (#4) and (#5) is to store data in
154   the 'error_message_charset_info' CHARSET, to minimize impact on the code base.
155   This is implemented by using 'String Sql_condition::m_message_text'.
156 
157   The UTF8 -> error_message_charset_info conversion is implemented in
158   Sql_cmd_common_signal::eval_signal_informations() (for path #B and #C).
159 
160   Future work
161   -----------
162 
163   - Change (#1) (my_error) to generate errors in UTF8.
164     See WL#751 (Recoding of error messages)
165 
166   - Change (#4 and #5) to store message text in UTF8 natively.
167     In practice, this means changing the type of the message text to
168     '<UTF8 String 128 class> Sql_condition::m_message_text', and is a direct
169     consequence of WL#751.
170 
171   - Implement (#9) (GET DIAGNOSTICS).
172     See WL#2111 (Stored Procedures: Implement GET DIAGNOSTICS)
173 */
174 
175 
176 static void copy_string(MEM_ROOT *mem_root, String* dst, const String* src)
177 {
178   size_t len= src->length();
179   if (len)
180   {
181     char* copy= (char*) alloc_root(mem_root, len + 1);
182     if (copy)
183     {
184       memcpy(copy, src->ptr(), len);
185       copy[len]= '\0';
186       dst->set(copy, len, src->charset());
187     }
188   }
189   else
190     dst->length(0);
191 }
192 
193 void
194 Sql_condition::copy_opt_attributes(const Sql_condition *cond)
195 {
unspecified_bool_true(unspecified_bool_type_t)196   DBUG_ASSERT(this != cond);
197   copy_string(m_mem_root, & m_class_origin, & cond->m_class_origin);
198   copy_string(m_mem_root, & m_subclass_origin, & cond->m_subclass_origin);
operator unspecified_bool_type() const199   copy_string(m_mem_root, & m_constraint_catalog, & cond->m_constraint_catalog);
200   copy_string(m_mem_root, & m_constraint_schema, & cond->m_constraint_schema);
201   copy_string(m_mem_root, & m_constraint_name, & cond->m_constraint_name);
202   copy_string(m_mem_root, & m_catalog_name, & cond->m_catalog_name);
203   copy_string(m_mem_root, & m_schema_name, & cond->m_schema_name);
204   copy_string(m_mem_root, & m_table_name, & cond->m_table_name);
205   copy_string(m_mem_root, & m_column_name, & cond->m_column_name);
206   copy_string(m_mem_root, & m_cursor_name, & cond->m_cursor_name);
207 }
208 
209 
target_type() const210 void
211 Sql_condition::set_builtin_message_text(const char* str)
212 {
213   /*
214     See the comments
215      "Design notes about Sql_condition::m_message_text."
216   */
217   const char* copy;
218 
219   copy= strdup_root(m_mem_root, str);
220   m_message_text.set(copy, strlen(copy), error_message_charset_info);
221   DBUG_ASSERT(! m_message_text.is_alloced());
222 }
223 
224 const char*
225 Sql_condition::get_message_text() const
226 {
227   return m_message_text.ptr();
228 }
229 
230 int
231 Sql_condition::get_message_octet_length() const
232 {
233   return m_message_text.length();
234 }
235 
236 
237 void Sql_state_errno_level::assign_defaults(const Sql_state_errno *from)
operator ==(const executor & a,const executor & b)238 {
239   DBUG_ASSERT(from);
240   int sqlerrno= from->get_sql_errno();
241   /*
242     SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions.
243   */
244   DBUG_ASSERT(from->has_sql_state());
245   set_sqlstate(from);
246   /* SQLSTATE class "00": illegal, rejected in the parser. */
247   DBUG_ASSERT(m_sqlstate[0] != '0' || get_sqlstate()[1] != '0');
248 
249   if (Sql_state::is_warning())        /* SQLSTATE class "01": warning. */
250   {
251     m_level= Sql_condition::WARN_LEVEL_WARN;
252     m_sql_errno= sqlerrno ? sqlerrno : ER_SIGNAL_WARN;
253   }
254   else if (Sql_state::is_not_found()) /* SQLSTATE class "02": not found. */
255   {
256     m_level= Sql_condition::WARN_LEVEL_ERROR;
257     m_sql_errno= sqlerrno ? sqlerrno : ER_SIGNAL_NOT_FOUND;
258   }
259   else                               /* other SQLSTATE classes : error. */
260   {
261     m_level= Sql_condition::WARN_LEVEL_ERROR;
262     m_sql_errno= sqlerrno ? sqlerrno : ER_SIGNAL_EXCEPTION;
263   }
264 }
265 
266 
type_id()267 void Sql_condition::assign_defaults(THD *thd, const Sql_state_errno *from)
268 {
269   if (from)
270     Sql_state_errno_level::assign_defaults(from);
271   if (!get_message_text())
272     set_builtin_message_text(ER(get_sql_errno()));
273 }
274 
275 
276 Diagnostics_area::Diagnostics_area(bool initialize)
277   : is_bulk_execution(0), m_main_wi(0, false, initialize)
278 {
279   push_warning_info(&m_main_wi);
280 
281   reset_diagnostics_area();
282 }
283 
284 Diagnostics_area::Diagnostics_area(ulonglong warning_info_id,
285                                    bool allow_unlimited_warnings,
286                                    bool initialize)
287   : is_bulk_execution(0),
288   m_main_wi(warning_info_id, allow_unlimited_warnings, initialize)
289 {
290   push_warning_info(&m_main_wi);
291 
292   reset_diagnostics_area();
293 }
294 
impl_base(bool fast_dispatch)295 /**
296   Clear this diagnostics area.
297 
298   Normally called at the end of a statement.
299 */
300 
301 void
302 Diagnostics_area::reset_diagnostics_area()
303 {
304   DBUG_ENTER("reset_diagnostics_area");
305   m_skip_flush= FALSE;
306 #ifdef DBUG_OFF
307   m_can_overwrite_status= FALSE;
308   /** Don't take chances in production */
309   m_message[0]= '\0';
310   Sql_state_errno::clear();
311   Sql_user_condition_identity::clear();
312   m_affected_rows= 0;
313   m_last_insert_id= 0;
314   m_statement_warn_count= 0;
315 #endif
316   get_warning_info()->clear_error_condition();
317   set_is_sent(false);
318   /** Tiny reset in debug mode to see garbage right away */
319   m_status= DA_EMPTY;
320   DBUG_VOID_RETURN;
321 }
322 
323 
324 /**
325   Set OK status -- ends commands that do not return a
326   result set, e.g. INSERT/UPDATE/DELETE.
327 */
328 
329 void
330 Diagnostics_area::set_ok_status(ulonglong affected_rows,
331                                 ulonglong last_insert_id,
332                                 const char *message)
333 {
334   DBUG_ENTER("set_ok_status");
335   DBUG_ASSERT(!is_set() || (m_status == DA_OK_BULK && is_bulk_op()));
336   /*
337     In production, refuse to overwrite an error or a custom response
338     with an OK packet.
339   */
340   if (unlikely(is_error() || is_disabled()))
341     return;
342   /*
343     When running a bulk operation, m_status will be DA_OK for the first
344     operation and set to DA_OK_BULK for all following operations.
345   */
346   if (m_status == DA_OK_BULK)
347   {
348     m_statement_warn_count+= current_statement_warn_count();
349     m_affected_rows+= affected_rows;
350   }
351   else
352   {
353     m_statement_warn_count= current_statement_warn_count();
354     m_affected_rows= affected_rows;
355     m_status= (is_bulk_op() ? DA_OK_BULK : DA_OK);
356   }
357   m_last_insert_id= last_insert_id;
358   if (message)
359     strmake_buf(m_message, message);
360   else
361     m_message[0]= '\0';
362   DBUG_VOID_RETURN;
363 }
364 
365 
366 /**
367   Set EOF status.
368 */
369 
370 void
371 Diagnostics_area::set_eof_status(THD *thd)
372 {
373   DBUG_ENTER("set_eof_status");
374   /* Only allowed to report eof if has not yet reported an error */
375   DBUG_ASSERT(! is_set());
376   /*
377     In production, refuse to overwrite an error or a custom response
378     with an EOF packet.
379   */
380   if (unlikely(is_error() || is_disabled()))
381     return;
382 
383   /*
384     If inside a stored procedure, do not return the total
385     number of warnings, since they are not available to the client
386     anyway.
387   */
388   m_statement_warn_count= (thd->spcont ?
389                            0 :
390                            current_statement_warn_count());
391 
392   m_status= DA_EOF;
393   DBUG_VOID_RETURN;
394 }
395 
396 /**
397   Set ERROR status in the Diagnostics Area. This function should be used to
398   report fatal errors (such as out-of-memory errors) when no further
399   processing is possible.
400 
401   @param sql_errno        SQL-condition error number
402 */
403 
404 void
405 Diagnostics_area::set_error_status(uint sql_errno)
406 {
407   set_error_status(sql_errno,
408                    ER(sql_errno),
409                    mysql_errno_to_sqlstate(sql_errno),
410                    Sql_user_condition_identity(),
411                    NULL);
412 }
413 
414 
415 /**
416   Set ERROR status in the Diagnostics Area.
417 
418   @note error_condition may be NULL. It happens if a) OOM error is being
419   reported; or b) when Warning_info is full.
420 
421   @param sql_errno        SQL-condition error number
422   @param message          SQL-condition message
423   @param sqlstate         SQL-condition state
424   @param ucid             User defined condition identity
425   @param error_condition  SQL-condition object representing the error state
426 
427   @note Note, that error_condition may be NULL. It happens if a) OOM error is
428   being reported; or b) when Warning_info is full.
429 */
430 
431 void
432 Diagnostics_area::set_error_status(uint sql_errno,
433                                    const char *message,
434                                    const char *sqlstate,
435                                    const Sql_user_condition_identity &ucid,
436                                    const Sql_condition *error_condition)
437 {
438   DBUG_ENTER("set_error_status");
439   DBUG_PRINT("enter", ("error: %d", sql_errno));
440   /*
441     Only allowed to report error if has not yet reported a success
442     The only exception is when we flush the message to the client,
443     an error can happen during the flush.
444   */
445   DBUG_ASSERT(! is_set() || m_can_overwrite_status);
446 
447   // message must be set properly by the caller.
448   DBUG_ASSERT(message);
449 
450   // sqlstate must be set properly by the caller.
451   DBUG_ASSERT(sqlstate);
452 
453 #ifdef DBUG_OFF
454   /*
455     In production, refuse to overwrite a custom response with an
456     ERROR packet.
457   */
458   if (is_disabled())
459     return;
460 #endif
461 
462   Sql_state_errno::set(sql_errno, sqlstate);
463   Sql_user_condition_identity::set(ucid);
464   strmake_buf(m_message, message);
465 
466   get_warning_info()->set_error_condition(error_condition);
467 
468   m_status= DA_ERROR;
469   DBUG_VOID_RETURN;
470 }
471 
472 /**
473   Mark the diagnostics area as 'DISABLED'.
474 
475   This is used in rare cases when the COM_ command at hand sends a response
476   in a custom format. One example is the query cache, another is
477   COM_STMT_PREPARE.
478 */
479 
480 void
481 Diagnostics_area::disable_status()
482 {
483   DBUG_ENTER("disable_status");
484   DBUG_ASSERT(! is_set());
485   m_status= DA_DISABLED;
486   DBUG_VOID_RETURN;
487 }
488 
489 Warning_info::Warning_info(ulonglong warn_id_arg,
490                            bool allow_unlimited_warnings, bool initialize)
491   :m_current_statement_warn_count(0),
492   m_current_row_for_warning(1),
493   m_warn_id(warn_id_arg),
494   m_error_condition(NULL),
495   m_allow_unlimited_warnings(allow_unlimited_warnings),
496   initialized(0),
497   m_read_only(FALSE)
498 {
499   m_warn_list.empty();
500   memset(m_warn_count, 0, sizeof(m_warn_count));
501   if (initialize)
502     init();
503 }
504 
505 void Warning_info::init()
506 {
507   /* Initialize sub structures */
508   DBUG_ASSERT(initialized == 0);
509   init_sql_alloc(&m_warn_root, "Warning_info", WARN_ALLOC_BLOCK_SIZE,
510                  WARN_ALLOC_PREALLOC_SIZE, MYF(MY_THREAD_SPECIFIC));
511   initialized= 1;
512 }
513 
514 void Warning_info::free_memory()
515 {
516   if (initialized)
517     free_root(&m_warn_root,MYF(0));
518 }
519 
520 Warning_info::~Warning_info()
521 {
522   free_memory();
523 }
524 
525 
526 bool Warning_info::has_sql_condition(const char *message_str, size_t message_length) const
527 {
528   Diagnostics_area::Sql_condition_iterator it(m_warn_list);
529   const Sql_condition *err;
530 
531   while ((err= it++))
532   {
533     if (strncmp(message_str, err->get_message_text(), message_length) == 0)
534       return true;
535   }
536 
537   return false;
538 }
539 
540 
541 void Warning_info::clear(ulonglong new_id)
542 {
543   id(new_id);
544   m_warn_list.empty();
545   m_marked_sql_conditions.empty();
546   free_memory();
547   memset(m_warn_count, 0, sizeof(m_warn_count));
548   m_current_statement_warn_count= 0;
549   m_current_row_for_warning= 1; /* Start counting from the first row */
550   clear_error_condition();
551 }
552 
553 void Warning_info::append_warning_info(THD *thd, const Warning_info *source)
554 {
555   const Sql_condition *err;
556   Diagnostics_area::Sql_condition_iterator it(source->m_warn_list);
557   const Sql_condition *src_error_condition = source->get_error_condition();
558 
559   while ((err= it++))
560   {
561     // Do not use ::push_warning() to avoid invocation of THD-internal-handlers.
562     Sql_condition *new_error= Warning_info::push_warning(thd, err);
563 
564     if (src_error_condition && src_error_condition == err)
565       set_error_condition(new_error);
566 
567     if (source->is_marked_for_removal(err))
568       mark_condition_for_removal(new_error);
569   }
570 }
571 
572 
573 /**
574   Copy Sql_conditions that are not WARN_LEVEL_ERROR from the source
575   Warning_info to the current Warning_info.
576 
577   @param thd    Thread context.
578   @param sp_wi  Stored-program Warning_info
579   @param thd     Thread context.
580   @param src_wi  Warning_info to copy from.
581 */
582 void Diagnostics_area::copy_non_errors_from_wi(THD *thd,
583                                                const Warning_info *src_wi)
584 {
585   Sql_condition_iterator it(src_wi->m_warn_list);
586   const Sql_condition *cond;
587   Warning_info *wi= get_warning_info();
588 
589   while ((cond= it++))
590   {
591     if (cond->get_level() == Sql_condition::WARN_LEVEL_ERROR)
592       continue;
593 
594     Sql_condition *new_condition= wi->push_warning(thd, cond);
595 
596     if (src_wi->is_marked_for_removal(cond))
597       wi->mark_condition_for_removal(new_condition);
598   }
599 }
600 
601 
602 void Warning_info::mark_sql_conditions_for_removal()
603 {
604   Sql_condition_list::Iterator it(m_warn_list);
605   Sql_condition *cond;
606 
607   while ((cond= it++))
608     mark_condition_for_removal(cond);
609 }
610 
611 
612 void Warning_info::remove_marked_sql_conditions()
613 {
614   List_iterator_fast<Sql_condition> it(m_marked_sql_conditions);
615   Sql_condition *cond;
616 
617   while ((cond= it++))
618   {
619     m_warn_list.remove(cond);
620     m_warn_count[cond->get_level()]--;
621     m_current_statement_warn_count--;
622     if (cond == m_error_condition)
623       m_error_condition= NULL;
624   }
625 
626   m_marked_sql_conditions.empty();
627 }
628 
629 
630 bool Warning_info::is_marked_for_removal(const Sql_condition *cond) const
631 {
632   List_iterator_fast<Sql_condition> it(
633     const_cast<List<Sql_condition>&> (m_marked_sql_conditions));
634   Sql_condition *c;
635 
636   while ((c= it++))
637   {
638     if (c == cond)
639       return true;
640   }
641 
642   return false;
643 }
644 
645 
646 void Warning_info::reserve_space(THD *thd, uint count)
647 {
648   while (m_warn_list.elements() &&
649          (m_warn_list.elements() + count) > thd->variables.max_error_count)
650     m_warn_list.remove(m_warn_list.front());
651 }
652 
653 Sql_condition *Warning_info::push_warning(THD *thd,
654                                           const Sql_condition_identity *value,
655                                           const char *msg)
656 {
657   Sql_condition *cond= NULL;
658 
659   if (! m_read_only)
660   {
661     if (m_allow_unlimited_warnings ||
662         m_warn_list.elements() < thd->variables.max_error_count)
663     {
664       cond= new (& m_warn_root) Sql_condition(& m_warn_root, *value, msg);
665       if (cond)
666         m_warn_list.push_back(cond);
667     }
668     m_warn_count[(uint) value->get_level()]++;
669   }
670 
671   m_current_statement_warn_count++;
672   return cond;
673 }
674 
675 
676 Sql_condition *Warning_info::push_warning(THD *thd,
677                                           const Sql_condition *sql_condition)
678 {
679   Sql_condition *new_condition= push_warning(thd, sql_condition,
680                                              sql_condition->get_message_text());
681 
682   if (new_condition)
683     new_condition->copy_opt_attributes(sql_condition);
684 
685   return new_condition;
686 }
687 
688 /*
689   Push the warning to error list if there is still room in the list
690 
691   SYNOPSIS
692     push_warning()
693     thd			Thread handle
694     level		Severity of warning (note, warning)
695     code		Error number
696     msg			Clear error message
697 */
698 
699 void push_warning(THD *thd, Sql_condition::enum_warning_level level,
700                   uint code, const char *msg)
701 {
702   DBUG_ENTER("push_warning");
703   DBUG_PRINT("enter", ("code: %d, msg: %s", code, msg));
704 
705   /*
706     Calling push_warning/push_warning_printf with a level of
707     WARN_LEVEL_ERROR *is* a bug.  Either use my_printf_error(),
708     my_error(), or WARN_LEVEL_WARN.
709   */
710   DBUG_ASSERT(level != Sql_condition::WARN_LEVEL_ERROR);
711 
712   if (level == Sql_condition::WARN_LEVEL_ERROR)
713     level= Sql_condition::WARN_LEVEL_WARN;
714 
715   (void) thd->raise_condition(code, NULL, level, msg);
716 
717   /* Make sure we also count warnings pushed after calling set_ok_status(). */
718   thd->get_stmt_da()->increment_warning();
719 
720   DBUG_VOID_RETURN;
721 }
722 
723 
724 /*
725   Push the warning to error list if there is still room in the list
726 
727   SYNOPSIS
728     push_warning_printf()
729     thd			Thread handle
730     level		Severity of warning (note, warning)
731     code		Error number
732     msg			Clear error message
733 */
734 
735 void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level,
736 			 uint code, const char *format, ...)
737 {
738   va_list args;
739   char    warning[MYSQL_ERRMSG_SIZE];
740   DBUG_ENTER("push_warning_printf");
741   DBUG_PRINT("enter",("warning: %u", code));
742 
743   DBUG_ASSERT(code != 0);
744   DBUG_ASSERT(format != NULL);
745 
746   va_start(args,format);
747   my_vsnprintf_ex(&my_charset_utf8_general_ci, warning,
748                   sizeof(warning), format, args);
749   va_end(args);
750   push_warning(thd, level, code, warning);
751   DBUG_VOID_RETURN;
752 }
753 
754 
755 /*
756   Send all notes, errors or warnings to the client in a result set
757 
758   SYNOPSIS
759     mysqld_show_warnings()
760     thd			Thread handler
761     levels_to_show	Bitmap for which levels to show
762 
763   DESCRIPTION
764     Takes into account the current LIMIT
765 
766   RETURN VALUES
767     FALSE ok
768     TRUE  Error sending data to client
769 */
770 
771 const LEX_CSTRING warning_level_names[]=
772 {
773   { STRING_WITH_LEN("Note") },
774   { STRING_WITH_LEN("Warning") },
775   { STRING_WITH_LEN("Error") },
776   { STRING_WITH_LEN("?") }
777 };
778 
779 bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
780 {
781   List<Item> field_list;
782   MEM_ROOT *mem_root= thd->mem_root;
783   const Sql_condition *err;
784   SELECT_LEX *sel= &thd->lex->select_lex;
785   SELECT_LEX_UNIT *unit= &thd->lex->unit;
786   ulonglong idx= 0;
787   Protocol *protocol=thd->protocol;
788   DBUG_ENTER("mysqld_show_warnings");
789 
790   DBUG_ASSERT(thd->get_stmt_da()->is_warning_info_read_only());
791 
792   field_list.push_back(new (mem_root)
793                        Item_empty_string(thd, "Level", 7),
794                        mem_root);
795   field_list.push_back(new (mem_root)
796                        Item_return_int(thd, "Code", 4, MYSQL_TYPE_LONG),
797                        mem_root);
798   field_list.push_back(new (mem_root)
799                        Item_empty_string(thd, "Message", MYSQL_ERRMSG_SIZE),
800                        mem_root);
801 
802   if (protocol->send_result_set_metadata(&field_list,
803                                          Protocol::SEND_NUM_ROWS |
804                                          Protocol::SEND_EOF))
805     DBUG_RETURN(TRUE);
806 
807   unit->set_limit(sel);
808 
809   Diagnostics_area::Sql_condition_iterator it=
810     thd->get_stmt_da()->sql_conditions();
811   while ((err= it++))
812   {
813     /* Skip levels that the user is not interested in */
814     if (!(levels_to_show & ((ulong) 1 << err->get_level())))
815       continue;
816     if (++idx <= unit->offset_limit_cnt)
817       continue;
818     if (idx > unit->select_limit_cnt)
819       break;
820     protocol->prepare_for_resend();
821     protocol->store(warning_level_names[err->get_level()].str,
822 		    warning_level_names[err->get_level()].length,
823                     system_charset_info);
824     protocol->store((uint32) err->get_sql_errno());
825     protocol->store(err->get_message_text(),
826                     err->get_message_octet_length(),
827                     system_charset_info);
828     if (protocol->write())
829       DBUG_RETURN(TRUE);
830   }
831   my_eof(thd);
832 
833   thd->get_stmt_da()->set_warning_info_read_only(FALSE);
834 
835   DBUG_RETURN(FALSE);
836 }
837 
838 
839 /**
840    Convert value for dispatch to error message(see WL#751).
841 
842    @param to          buffer for converted string
843    @param to_length   size of the buffer
844    @param from        string which should be converted
845    @param from_length string length
846    @param from_cs     charset from convert
847 
848    @retval
849    result string
850 */
851 
852 char *err_conv(char *buff, uint to_length, const char *from,
853                uint from_length, CHARSET_INFO *from_cs)
854 {
855   char *to= buff;
856   const char *from_start= from;
857   size_t res;
858 
859   DBUG_ASSERT(to_length > 0);
860   to_length--;
861   if (from_cs == &my_charset_bin)
862   {
863     uchar char_code;
864     res= 0;
865     while (1)
866     {
867       if ((uint)(from - from_start) >= from_length ||
868           res >= to_length)
869       {
870         *to= 0;
871         break;
872       }
873 
874       char_code= ((uchar) *from);
875       if (char_code >= 0x20 && char_code <= 0x7E)
876       {
877         *to++= char_code;
878         from++;
879         res++;
880       }
881       else
882       {
883         if (res + 4 >= to_length)
884         {
885           *to= 0;
886           break;
887         }
888         res+= my_snprintf(to, 5, "\\x%02X", (uint) char_code);
889         to+=4;
890         from++;
891       }
892     }
893   }
894   else
895   {
896     uint errors;
897     res= copy_and_convert(to, to_length, system_charset_info,
898                           from, from_length, from_cs, &errors);
899     to[res]= 0;
900   }
901   return buff;
902 }
903 
904 
905 /**
906    Convert string for dispatch to client(see WL#751).
907 
908    @param to          buffer to convert
909    @param to_length   buffer length
910    @param to_cs       chraset to convert
911    @param from        string from convert
912    @param from_length string length
913    @param from_cs     charset from convert
914    @param errors      count of errors during convertion
915 
916    @retval
917    length of converted string
918 */
919 
920 size_t convert_error_message(char *to, size_t to_length, CHARSET_INFO *to_cs,
921                              const char *from, size_t from_length,
922                              CHARSET_INFO *from_cs, uint *errors)
923 {
924   int  cnvres;
925   my_wc_t     wc;
926   const uchar *from_end= (const uchar*) from+from_length;
927   char *to_start= to;
928   uchar *to_end;
929   my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
930   my_charset_conv_wc_mb wc_mb;
931   uint error_count= 0;
932   size_t length;
933 
934   DBUG_ASSERT(to_length > 0);
935   /* Make room for the null terminator. */
936   to_length--;
937   to_end= (uchar*) (to + to_length);
938 
939   if (!to_cs || from_cs == to_cs || to_cs == &my_charset_bin)
940   {
941     length= MY_MIN(to_length, from_length);
942     memmove(to, from, length);
943     to[length]= 0;
944     return length;
945   }
946 
947   wc_mb= to_cs->cset->wc_mb;
948   while (1)
949   {
950     if ((cnvres= (*mb_wc)(from_cs, &wc, (uchar*) from, from_end)) > 0)
951     {
952       if (!wc)
953         break;
954       from+= cnvres;
955     }
956     else if (cnvres == MY_CS_ILSEQ)
957     {
958       wc= (ulong) (uchar) *from;
959       from+=1;
960     }
961     else
962       break;
963 
964     if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0)
965       to+= cnvres;
966     else if (cnvres == MY_CS_ILUNI)
967     {
968       length= (wc <= 0xFFFF) ? 6/* '\1234' format*/ : 9 /* '\+123456' format*/;
969       if ((uchar*)(to + length) >= to_end)
970         break;
971       cnvres= (int)my_snprintf(to, 9,
972                           (wc <= 0xFFFF) ? "\\%04X" : "\\+%06X", (uint) wc);
973       to+= cnvres;
974     }
975     else
976       break;
977   }
978 
979   *to= 0;
980   *errors= error_count;
981   return (size_t) (to - to_start);
982 }
983 
984 
985 /**
986   Sanity check for SQLSTATEs. The function does not check if it's really an
987   existing SQL-state (there are just too many), it just checks string length and
988   looks for bad characters.
989 
990   @param sqlstate the condition SQLSTATE.
991 
992   @retval true if it's ok.
993   @retval false if it's bad.
994 */
995 
996 bool is_sqlstate_valid(const LEX_CSTRING *sqlstate)
997 {
998   if (sqlstate->length != 5)
999     return false;
1000 
1001   for (int i= 0 ; i < 5 ; ++i)
1002   {
1003     char c = sqlstate->str[i];
1004 
1005     if ((c < '0' || '9' < c) &&
1006 	(c < 'A' || 'Z' < c))
1007       return false;
1008   }
1009 
1010   return true;
1011 }
1012