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