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