1 /* Copyright (c) 2008, 2021, Oracle and/or its affiliates.
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 #include "sp_head.h"
24 #include "sp_pcontext.h"
25 #include "sp_rcontext.h"
26 #include "sql_signal.h"
27 #include "sql_error.h"
28
29 /*
30 The parser accepts any error code (desired)
31 The runtime internally supports any error code (desired)
32 The client server protocol is limited to 16 bits error codes (restriction)
33 Enforcing the 65535 limit in the runtime until the protocol can change.
34 */
35 #define MAX_MYSQL_ERRNO UINT_MAX16
36
37 static const LEX_STRING CONDITION_ITEM_NAMES[]=
38 {
39 { C_STRING_WITH_LEN("CLASS_ORIGIN") },
40 { C_STRING_WITH_LEN("SUBCLASS_ORIGIN") },
41 { C_STRING_WITH_LEN("CONSTRAINT_CATALOG") },
42 { C_STRING_WITH_LEN("CONSTRAINT_SCHEMA") },
43 { C_STRING_WITH_LEN("CONSTRAINT_NAME") },
44 { C_STRING_WITH_LEN("CATALOG_NAME") },
45 { C_STRING_WITH_LEN("SCHEMA_NAME") },
46 { C_STRING_WITH_LEN("TABLE_NAME") },
47 { C_STRING_WITH_LEN("COLUMN_NAME") },
48 { C_STRING_WITH_LEN("CURSOR_NAME") },
49 { C_STRING_WITH_LEN("MESSAGE_TEXT") },
50 { C_STRING_WITH_LEN("MYSQL_ERRNO") },
51
52 { C_STRING_WITH_LEN("CONDITION_IDENTIFIER") },
53 { C_STRING_WITH_LEN("CONDITION_NUMBER") },
54 { C_STRING_WITH_LEN("CONNECTION_NAME") },
55 { C_STRING_WITH_LEN("MESSAGE_LENGTH") },
56 { C_STRING_WITH_LEN("MESSAGE_OCTET_LENGTH") },
57 { C_STRING_WITH_LEN("PARAMETER_MODE") },
58 { C_STRING_WITH_LEN("PARAMETER_NAME") },
59 { C_STRING_WITH_LEN("PARAMETER_ORDINAL_POSITION") },
60 { C_STRING_WITH_LEN("RETURNED_SQLSTATE") },
61 { C_STRING_WITH_LEN("ROUTINE_CATALOG") },
62 { C_STRING_WITH_LEN("ROUTINE_NAME") },
63 { C_STRING_WITH_LEN("ROUTINE_SCHEMA") },
64 { C_STRING_WITH_LEN("SERVER_NAME") },
65 { C_STRING_WITH_LEN("SPECIFIC_NAME") },
66 { C_STRING_WITH_LEN("TRIGGER_CATALOG") },
67 { C_STRING_WITH_LEN("TRIGGER_NAME") },
68 { C_STRING_WITH_LEN("TRIGGER_SCHEMA") }
69 };
70
71
set_item(enum_condition_item_name name,Item * item)72 bool Set_signal_information::set_item(enum_condition_item_name name, Item *item)
73 {
74 if (m_item[name] != NULL)
75 {
76 my_error(ER_DUP_SIGNAL_SET, MYF(0), CONDITION_ITEM_NAMES[name].str);
77 return true;
78 }
79 m_item[name]= item;
80 return false;
81 }
82
83
assign_defaults(Sql_condition * cond,bool set_level_code,Sql_condition::enum_severity_level level,int sqlcode)84 void Sql_cmd_common_signal::assign_defaults(
85 Sql_condition *cond,
86 bool set_level_code,
87 Sql_condition::enum_severity_level level,
88 int sqlcode)
89 {
90 if (set_level_code)
91 {
92 cond->m_severity_level= level;
93 cond->m_mysql_errno= sqlcode;
94 }
95 if (! cond->message_text())
96 cond->set_message_text(ER(sqlcode));
97 }
98
eval_defaults(THD * thd,Sql_condition * cond)99 void Sql_cmd_common_signal::eval_defaults(THD *thd, Sql_condition *cond)
100 {
101 assert(cond);
102
103 const char* sqlstate;
104 bool set_defaults= (m_cond != 0);
105
106 if (set_defaults)
107 {
108 /*
109 SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions.
110 */
111 assert(m_cond->type == sp_condition_value::SQLSTATE);
112 sqlstate= m_cond->sql_state;
113 cond->set_returned_sqlstate(sqlstate);
114 }
115 else
116 sqlstate= cond->returned_sqlstate();
117
118 assert(sqlstate);
119 /* SQLSTATE class "00": illegal, rejected in the parser. */
120 assert(!is_sqlstate_completion(sqlstate));
121
122 if (is_sqlstate_warning(sqlstate))
123 {
124 /* SQLSTATE class "01": warning. */
125 assign_defaults(cond, set_defaults,
126 Sql_condition::SL_WARNING, ER_SIGNAL_WARN);
127 }
128 else if (is_sqlstate_not_found(sqlstate))
129 {
130 /* SQLSTATE class "02": not found. */
131 assign_defaults(cond, set_defaults,
132 Sql_condition::SL_ERROR, ER_SIGNAL_NOT_FOUND);
133 }
134 else
135 {
136 /* other SQLSTATE classes : error. */
137 assign_defaults(cond, set_defaults,
138 Sql_condition::SL_ERROR, ER_SIGNAL_EXCEPTION);
139 }
140 }
141
assign_fixed_string(MEM_ROOT * mem_root,CHARSET_INFO * dst_cs,size_t max_char,String * dst,const String * src)142 static bool assign_fixed_string(MEM_ROOT *mem_root,
143 CHARSET_INFO *dst_cs,
144 size_t max_char,
145 String *dst,
146 const String* src)
147 {
148 bool truncated;
149 size_t numchars;
150 const CHARSET_INFO *src_cs;
151 const char* src_str;
152 const char* src_end;
153 size_t src_len;
154 size_t to_copy;
155 char* dst_str;
156 size_t dst_len;
157 size_t dst_copied;
158 size_t dummy_offset;
159
160 src_str= src->ptr();
161 if (src_str == NULL)
162 {
163 dst->set((const char*) NULL, 0, dst_cs);
164 return false;
165 }
166
167 src_cs= src->charset();
168 src_len= src->length();
169 src_end= src_str + src_len;
170 numchars= src_cs->cset->numchars(src_cs, src_str, src_end);
171
172 if (numchars <= max_char)
173 {
174 to_copy= src->length();
175 truncated= false;
176 }
177 else
178 {
179 numchars= max_char;
180 to_copy= dst_cs->cset->charpos(dst_cs, src_str, src_end, numchars);
181 truncated= true;
182 }
183
184 if (String::needs_conversion(to_copy, src_cs, dst_cs, & dummy_offset))
185 {
186 dst_len= numchars * dst_cs->mbmaxlen;
187 dst_str= (char*) alloc_root(mem_root, dst_len + 1);
188 if (dst_str)
189 {
190 const char* well_formed_error_pos;
191 const char* cannot_convert_error_pos;
192 const char* from_end_pos;
193
194 dst_copied= well_formed_copy_nchars(dst_cs, dst_str, dst_len,
195 src_cs, src_str, src_len,
196 numchars,
197 & well_formed_error_pos,
198 & cannot_convert_error_pos,
199 & from_end_pos);
200 assert(dst_copied <= dst_len);
201 dst_len= dst_copied; /* In case the copy truncated the data */
202 dst_str[dst_copied]= '\0';
203 }
204 }
205 else
206 {
207 dst_len= to_copy;
208 dst_str= (char*) alloc_root(mem_root, dst_len + 1);
209 if (dst_str)
210 {
211 memcpy(dst_str, src_str, to_copy);
212 dst_str[to_copy]= '\0';
213 }
214 }
215 dst->set(dst_str, dst_len, dst_cs);
216
217 return truncated;
218 }
219
assign_condition_item(MEM_ROOT * mem_root,const char * name,THD * thd,Item * set,String * ci)220 static int assign_condition_item(MEM_ROOT *mem_root, const char* name, THD *thd,
221 Item *set, String *ci)
222 {
223 char str_buff[(64+1)*4]; /* Room for a null terminated UTF8 String 64 */
224 String str_value(str_buff, sizeof(str_buff), & my_charset_utf8_bin);
225 String *str;
226 bool truncated;
227
228 DBUG_ENTER("assign_condition_item");
229
230 if (set->is_null())
231 {
232 thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR, name, "NULL");
233 DBUG_RETURN(1);
234 }
235
236 str= set->val_str(& str_value);
237 truncated= assign_fixed_string(mem_root, & my_charset_utf8_bin, 64, ci, str);
238 if (truncated)
239 {
240 if (thd->is_strict_mode())
241 {
242 thd->raise_error_printf(ER_COND_ITEM_TOO_LONG, name);
243 DBUG_RETURN(1);
244 }
245
246 thd->raise_warning_printf(WARN_COND_ITEM_TRUNCATED, name);
247 }
248
249 DBUG_RETURN(0);
250 }
251
252
eval_signal_informations(THD * thd,Sql_condition * cond)253 int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *cond)
254 {
255 struct cond_item_map
256 {
257 enum_condition_item_name m_item;
258 String Sql_condition::*m_member;
259 };
260
261 static cond_item_map map[]=
262 {
263 { CIN_CLASS_ORIGIN, & Sql_condition::m_class_origin },
264 { CIN_SUBCLASS_ORIGIN, & Sql_condition::m_subclass_origin },
265 { CIN_CONSTRAINT_CATALOG, & Sql_condition::m_constraint_catalog },
266 { CIN_CONSTRAINT_SCHEMA, & Sql_condition::m_constraint_schema },
267 { CIN_CONSTRAINT_NAME, & Sql_condition::m_constraint_name },
268 { CIN_CATALOG_NAME, & Sql_condition::m_catalog_name },
269 { CIN_SCHEMA_NAME, & Sql_condition::m_schema_name },
270 { CIN_TABLE_NAME, & Sql_condition::m_table_name },
271 { CIN_COLUMN_NAME, & Sql_condition::m_column_name },
272 { CIN_CURSOR_NAME, & Sql_condition::m_cursor_name }
273 };
274
275 Item *set;
276 String str_value;
277 String *str;
278 int i;
279 uint j;
280 int result= 1;
281 enum_condition_item_name item_enum;
282 String *member;
283 const LEX_STRING *name;
284
285 DBUG_ENTER("Sql_cmd_common_signal::eval_signal_informations");
286
287 for (i= CIN_FIRST_PROPERTY; i <= CIN_LAST_PROPERTY; i++)
288 {
289 set= m_set_signal_information->m_item[i];
290 if (set)
291 {
292 if (! set->fixed)
293 {
294 if (set->fix_fields(thd, & set))
295 goto end;
296 m_set_signal_information->m_item[i]= set;
297 }
298 }
299 }
300
301 /*
302 Generically assign all the UTF8 String 64 condition items
303 described in the map.
304 */
305 for (j= 0; j < array_elements(map); j++)
306 {
307 item_enum= map[j].m_item;
308 set= m_set_signal_information->m_item[item_enum];
309 if (set != NULL)
310 {
311 member= & (cond->* map[j].m_member);
312 name= & CONDITION_ITEM_NAMES[item_enum];
313 if (assign_condition_item(cond->m_mem_root, name->str, thd, set, member))
314 goto end;
315 }
316 }
317
318 /*
319 Assign the remaining attributes.
320 */
321
322 set= m_set_signal_information->m_item[CIN_MESSAGE_TEXT];
323 if (set != NULL)
324 {
325 if (set->is_null())
326 {
327 thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
328 "MESSAGE_TEXT", "NULL");
329 goto end;
330 }
331 /*
332 Enforce that SET MESSAGE_TEXT = <value> evaluates the value
333 as VARCHAR(128) CHARACTER SET UTF8.
334 */
335 bool truncated;
336 String utf8_text;
337 str= set->val_str(& str_value);
338 truncated= assign_fixed_string(thd->mem_root, & my_charset_utf8_bin, 128,
339 & utf8_text, str);
340 if (truncated)
341 {
342 if (thd->is_strict_mode())
343 {
344 thd->raise_error_printf(ER_COND_ITEM_TOO_LONG,
345 "MESSAGE_TEXT");
346 goto end;
347 }
348
349 thd->raise_warning_printf(WARN_COND_ITEM_TRUNCATED,
350 "MESSAGE_TEXT");
351 }
352
353 /*
354 See the comments
355 "Design notes about Sql_condition::m_message_text."
356 in file sql_error.cc
357 */
358 String converted_text;
359 converted_text.set_charset(error_message_charset_info);
360 converted_text.append(utf8_text.ptr(), utf8_text.length(),
361 utf8_text.charset());
362 cond->set_message_text(converted_text.c_ptr_safe());
363 }
364
365 set= m_set_signal_information->m_item[CIN_MYSQL_ERRNO];
366 if (set != NULL)
367 {
368 if (set->is_null())
369 {
370 thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
371 "MYSQL_ERRNO", "NULL");
372 goto end;
373 }
374 longlong code= set->val_int();
375 if ((code <= 0) || (code > MAX_MYSQL_ERRNO))
376 {
377 str= set->val_str(& str_value);
378 thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
379 "MYSQL_ERRNO", str->c_ptr_safe());
380 goto end;
381 }
382 cond->m_mysql_errno= (int) code;
383 }
384
385 /*
386 The various item->val_xxx() methods don't return an error code,
387 but flag thd in case of failure.
388 */
389 if (! thd->is_error())
390 result= 0;
391
392 end:
393 for (i= CIN_FIRST_PROPERTY; i <= CIN_LAST_PROPERTY; i++)
394 {
395 set= m_set_signal_information->m_item[i];
396 if (set)
397 {
398 if (set->fixed)
399 set->cleanup();
400 }
401 }
402
403 DBUG_RETURN(result);
404 }
405
execute(THD * thd)406 bool Sql_cmd_signal::execute(THD *thd)
407 {
408 Sql_condition cond(thd->mem_root);
409
410 DBUG_ENTER("Sql_cmd_signal::execute");
411
412 /*
413 WL#2110 SIGNAL specification says:
414
415 When SIGNAL is executed, it has five effects, in the following order:
416
417 (1) First, the Diagnostics Area is completely cleared. So if the
418 SIGNAL is in a DECLARE HANDLER then any pending errors or warnings
419 are gone. So is 'row count'.
420
421 This has roots in the SQL standard specification for SIGNAL.
422 */
423
424 thd->get_stmt_da()->reset_diagnostics_area();
425 thd->set_row_count_func(0);
426 thd->get_stmt_da()->reset_condition_info(thd);
427
428 assert(thd->lex->query_tables == NULL);
429
430 eval_defaults(thd, &cond);
431 if (eval_signal_informations(thd, &cond))
432 DBUG_RETURN(true);
433
434 /* SIGNAL should not signal SL_NOTE */
435 assert((cond.severity() == Sql_condition::SL_WARNING) ||
436 (cond.severity() == Sql_condition::SL_ERROR));
437
438 Sql_condition *raised= thd->raise_condition(cond.mysql_errno(),
439 cond.returned_sqlstate(),
440 cond.severity(),
441 cond.message_text());
442 if (raised)
443 raised->copy_opt_attributes(&cond);
444
445 if (cond.severity() == Sql_condition::SL_WARNING)
446 {
447 my_ok(thd);
448 DBUG_RETURN(false);
449 }
450
451 DBUG_RETURN(true);
452 }
453
454
455 /**
456 Execute RESIGNAL SQL-statement.
457
458 @param thd Thread context.
459
460 @return Error status
461 @retval true in case of error
462 @retval false on success
463 */
464
execute(THD * thd)465 bool Sql_cmd_resignal::execute(THD *thd)
466 {
467 sp_rcontext::Handler_call_frame *frame= NULL;
468
469 DBUG_ENTER("Sql_cmd_resignal::execute");
470
471 if (! thd->sp_runtime_ctx ||
472 ! (frame= thd->sp_runtime_ctx->current_handler_frame()))
473 {
474 thd->raise_error(ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER);
475 DBUG_RETURN(true);
476 }
477
478 thd->pop_diagnostics_area();
479
480 Diagnostics_area *da= thd->get_stmt_da();
481
482 // allow set_error_status(), in raise_condition() or here.
483 da->reset_diagnostics_area();
484
485 Sql_condition *raised= NULL;
486
487 // RESIGNAL with signal_value.
488 if (m_cond)
489 {
490 // Make a temporary Sql_condition for modification
491 Sql_condition signaled_err(thd->mem_root,
492 frame->sql_condition->mysql_errno(),
493 frame->sql_condition->returned_sqlstate(),
494 frame->sql_condition->severity(),
495 frame->sql_condition->message_text());
496
497 eval_defaults(thd, &signaled_err);
498 if (!eval_signal_informations(thd, &signaled_err))
499 {
500 // Make room for the new RESIGNAL condition.
501 da->reserve_number_of_conditions(thd, 1);
502
503 raised= thd->raise_condition(signaled_err.mysql_errno(),
504 signaled_err.returned_sqlstate(),
505 signaled_err.severity(),
506 signaled_err.message_text());
507 if (raised)
508 raised->copy_opt_attributes(&signaled_err);
509 }
510 }
511 else // RESIGNAL modifying an existing condition.
512 {
513 /*
514 Get the raised condition from Handler_call_frame so it can be
515 modified directly.
516 Note: This condition might not be in the Diagnostics Area
517 if it was full when the condition was raised or if
518 Diagnostics_area::set_error_status() was used directly.
519 */
520 raised= frame->sql_condition;
521 eval_defaults(thd, raised);
522 if (!eval_signal_informations(thd, raised))
523 {
524 if (raised->severity() == Sql_condition::SL_ERROR)
525 da->set_error_status(raised->mysql_errno(),
526 raised->message_text(),
527 raised->returned_sqlstate());
528 }
529 }
530
531 // RESIGNAL should not resignal SL_NOTE
532 assert(!raised ||
533 (raised->severity() == Sql_condition::SL_WARNING) ||
534 (raised->severity() == Sql_condition::SL_ERROR));
535
536 /*
537 We now have the following possibilities:
538 1) We RESIGNAL a warning
539 2) We RESIGNAL an error for which there is another CONTINUE handler.
540 3) We RESIGNAL an error for which there is another EXIT handler.
541 4) We RESIGNAL an error for which there is no appropriate handler.
542
543 In 1) and 2) we will continue executing the handler. Therefore we
544 should add the Diagnostics Area we just popped, back. By doing
545 pop+push rather than nothing, we make sure that the condition modified
546 by RESIGNAL (if any) is modified on both the first and the second
547 Diagnostics Area (not just in the first).
548
549 For 3) we will anyway pop the handler frame, thus also popping the
550 handler DA and we should also add it back.
551
552 For 4) we could avoid pushing the DA back, but it's hard to detect
553 here if we are in fact in situation 4). Therefore it's easiest to
554 just add the DA back. It will in any case be popped by the SP
555 exit code.
556 */
557 frame->handler_da.reset_condition_info(thd);
558 frame->handler_da.reset_diagnostics_area();
559 thd->push_diagnostics_area(&frame->handler_da);
560
561 // Transfer any exception condition information.
562 if (da->is_error())
563 frame->handler_da.set_error_status(da->mysql_errno(),
564 da->message_text(),
565 da->returned_sqlstate());
566
567 /*
568 Reset the DA which was used during RESIGNAL execution.
569 This prepares it for the statement which will be executed after
570 next pop.
571 */
572 da->reset_diagnostics_area();
573
574 DBUG_RETURN(thd->is_error());
575 }
576