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