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/**********************************************************************
17This 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"
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
176static 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
193void
194Sql_condition::copy_opt_attributes(const Sql_condition *cond)
195{
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);
199 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
210void
211Sql_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
224const char*
225Sql_condition::get_message_text() const
226{
227 return m_message_text.ptr();
228}
229
230int
231Sql_condition::get_message_octet_length() const
232{
233 return m_message_text.length();
234}
235
236
237void Sql_state_errno_level::assign_defaults(const Sql_state_errno *from)
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
267void 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
276Diagnostics_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
284Diagnostics_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
295/**
296 Clear this diagnostics area.
297
298 Normally called at the end of a statement.
299*/
300
301void
302Diagnostics_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
329void
330Diagnostics_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
370void
371Diagnostics_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
404void
405Diagnostics_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
431void
432Diagnostics_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
480void
481Diagnostics_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
489Warning_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
505void 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
514void Warning_info::free_memory()
515{
516 if (initialized)
517 free_root(&m_warn_root,MYF(0));
518}
519
520Warning_info::~Warning_info()
521{
522 free_memory();
523}
524
525
526bool 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
541void 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
553void 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*/
582void 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
602void 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
612void 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
630bool 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
646void 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
653Sql_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
676Sql_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
699void 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
735void 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
771const 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
779bool 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
852char *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
920size_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
996bool 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