1 /* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
2    Copyright (c) 2009, 2020, MariaDB Corporation.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
16 
17 #include "mariadb.h"
18 #include "sql_priv.h"
19 #include "sp_head.h"
20 #include "sp_pcontext.h"
21 #include "sp_rcontext.h"
22 #include "sql_signal.h"
23 
24 /*
25   The parser accepts any error code (desired)
26   The runtime internally supports any error code (desired)
27   The client server protocol is limited to 16 bits error codes (restriction),
28   and the value of 65535 is reserved for progress reporting.
29   Enforcing the 65534 limit in the runtime until the protocol can change.
30 */
31 #define MAX_MYSQL_ERRNO 65534
32 
33 const LEX_CSTRING Diag_condition_item_names[]=
34 {
35   { STRING_WITH_LEN("CLASS_ORIGIN") },
36   { STRING_WITH_LEN("SUBCLASS_ORIGIN") },
37   { STRING_WITH_LEN("CONSTRAINT_CATALOG") },
38   { STRING_WITH_LEN("CONSTRAINT_SCHEMA") },
39   { STRING_WITH_LEN("CONSTRAINT_NAME") },
40   { STRING_WITH_LEN("CATALOG_NAME") },
41   { STRING_WITH_LEN("SCHEMA_NAME") },
42   { STRING_WITH_LEN("TABLE_NAME") },
43   { STRING_WITH_LEN("COLUMN_NAME") },
44   { STRING_WITH_LEN("CURSOR_NAME") },
45   { STRING_WITH_LEN("MESSAGE_TEXT") },
46   { STRING_WITH_LEN("MYSQL_ERRNO") },
47 
48   { STRING_WITH_LEN("CONDITION_IDENTIFIER") },
49   { STRING_WITH_LEN("CONDITION_NUMBER") },
50   { STRING_WITH_LEN("CONNECTION_NAME") },
51   { STRING_WITH_LEN("MESSAGE_LENGTH") },
52   { STRING_WITH_LEN("MESSAGE_OCTET_LENGTH") },
53   { STRING_WITH_LEN("PARAMETER_MODE") },
54   { STRING_WITH_LEN("PARAMETER_NAME") },
55   { STRING_WITH_LEN("PARAMETER_ORDINAL_POSITION") },
56   { STRING_WITH_LEN("RETURNED_SQLSTATE") },
57   { STRING_WITH_LEN("ROUTINE_CATALOG") },
58   { STRING_WITH_LEN("ROUTINE_NAME") },
59   { STRING_WITH_LEN("ROUTINE_SCHEMA") },
60   { STRING_WITH_LEN("SERVER_NAME") },
61   { STRING_WITH_LEN("SPECIFIC_NAME") },
62   { STRING_WITH_LEN("TRIGGER_CATALOG") },
63   { STRING_WITH_LEN("TRIGGER_NAME") },
64   { STRING_WITH_LEN("TRIGGER_SCHEMA") }
65 };
66 
67 
Set_signal_information(const Set_signal_information & set)68 Set_signal_information::Set_signal_information(
69   const Set_signal_information& set)
70 {
71   memcpy(m_item, set.m_item, sizeof(m_item));
72 }
73 
clear()74 void Set_signal_information::clear()
75 {
76   memset(m_item, 0, sizeof(m_item));
77 }
78 
79 
assign_fixed_string(MEM_ROOT * mem_root,CHARSET_INFO * dst_cs,size_t max_char,String * dst,const String * src)80 static bool assign_fixed_string(MEM_ROOT *mem_root,
81                                 CHARSET_INFO *dst_cs,
82                                 size_t max_char,
83                                 String *dst,
84                                 const String* src)
85 {
86   bool truncated;
87   size_t numchars;
88   CHARSET_INFO *src_cs;
89   const char* src_str;
90   const char* src_end;
91   size_t src_len;
92   size_t to_copy;
93   char* dst_str;
94   size_t dst_len;
95   size_t dst_copied;
96   uint32 dummy_offset;
97 
98   src_str= src->ptr();
99   if (src_str == NULL)
100   {
101     dst->set((const char*) NULL, 0, dst_cs);
102     return false;
103   }
104 
105   src_cs= src->charset();
106   src_len= src->length();
107   src_end= src_str + src_len;
108   numchars= src_cs->numchars(src_str, src_end);
109 
110   if (numchars <= max_char)
111   {
112     to_copy= src->length();
113     truncated= false;
114   }
115   else
116   {
117     numchars= max_char;
118     to_copy= dst_cs->charpos(src_str, src_end, numchars);
119     truncated= true;
120   }
121 
122   if (String::needs_conversion(to_copy, src_cs, dst_cs, & dummy_offset))
123   {
124     dst_len= numchars * dst_cs->mbmaxlen;
125     dst_str= (char*) alloc_root(mem_root, dst_len + 1);
126     if (dst_str)
127     {
128       dst_copied= String_copier().well_formed_copy(dst_cs, dst_str, dst_len,
129                                                    src_cs, src_str, src_len,
130                                                    numchars);
131       DBUG_ASSERT(dst_copied <= dst_len);
132       dst_len= dst_copied; /* In case the copy truncated the data */
133       dst_str[dst_copied]= '\0';
134     }
135   }
136   else
137   {
138     dst_len= to_copy;
139     dst_str= (char*) alloc_root(mem_root, dst_len + 1);
140     if (dst_str)
141     {
142       memcpy(dst_str, src_str, to_copy);
143       dst_str[to_copy]= '\0';
144     }
145   }
146   dst->set(dst_str, dst_len, dst_cs);
147 
148   return truncated;
149 }
150 
assign_condition_item(MEM_ROOT * mem_root,const char * name,THD * thd,Item * set,String * ci)151 static int assign_condition_item(MEM_ROOT *mem_root, const char* name, THD *thd,
152                                  Item *set, String *ci)
153 {
154   char str_buff[(64+1)*4]; /* Room for a null terminated UTF8 String 64 */
155   String str_value(str_buff, sizeof(str_buff), & my_charset_utf8mb3_bin);
156   String *str;
157   bool truncated;
158 
159   DBUG_ENTER("assign_condition_item");
160 
161   if (set->is_null())
162   {
163     thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR, name, "NULL");
164     DBUG_RETURN(1);
165   }
166 
167   str= set->val_str(& str_value);
168   truncated= assign_fixed_string(mem_root, & my_charset_utf8mb3_bin, 64, ci, str);
169   if (truncated)
170   {
171     if (thd->is_strict_mode())
172     {
173       thd->raise_error_printf(ER_COND_ITEM_TOO_LONG, name);
174       DBUG_RETURN(1);
175     }
176 
177     thd->raise_warning_printf(WARN_COND_ITEM_TRUNCATED, name);
178   }
179 
180   DBUG_RETURN(0);
181 }
182 
183 
eval_signal_informations(THD * thd,Sql_condition * cond)184 int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *cond)
185 {
186   struct cond_item_map
187   {
188     enum enum_diag_condition_item_name m_item;
189     String Sql_condition::*m_member;
190   };
191 
192   static cond_item_map map[]=
193   {
194     { DIAG_CLASS_ORIGIN, & Sql_condition::m_class_origin },
195     { DIAG_SUBCLASS_ORIGIN, & Sql_condition::m_subclass_origin },
196     { DIAG_CONSTRAINT_CATALOG, & Sql_condition::m_constraint_catalog },
197     { DIAG_CONSTRAINT_SCHEMA, & Sql_condition::m_constraint_schema },
198     { DIAG_CONSTRAINT_NAME, & Sql_condition::m_constraint_name },
199     { DIAG_CATALOG_NAME, & Sql_condition::m_catalog_name },
200     { DIAG_SCHEMA_NAME, & Sql_condition::m_schema_name },
201     { DIAG_TABLE_NAME, & Sql_condition::m_table_name },
202     { DIAG_COLUMN_NAME, & Sql_condition::m_column_name },
203     { DIAG_CURSOR_NAME, & Sql_condition::m_cursor_name }
204   };
205 
206   Item *set;
207   String str_value;
208   String *str;
209   int i;
210   uint j;
211   int result= 1;
212   enum enum_diag_condition_item_name item_enum;
213   String *member;
214   const LEX_CSTRING *name;
215 
216   DBUG_ENTER("Sql_cmd_common_signal::eval_signal_informations");
217 
218   for (i= FIRST_DIAG_SET_PROPERTY;
219        i <= LAST_DIAG_SET_PROPERTY;
220        i++)
221   {
222     if ((set= m_set_signal_information.m_item[i]) &&
223         set->fix_fields_if_needed(thd, &m_set_signal_information.m_item[i]))
224       goto end;
225   }
226 
227   /*
228     Generically assign all the UTF8 String 64 condition items
229     described in the map.
230   */
231   for (j= 0; j < array_elements(map); j++)
232   {
233     item_enum= map[j].m_item;
234     set= m_set_signal_information.m_item[item_enum];
235     if (set != NULL)
236     {
237       member= & (cond->* map[j].m_member);
238       name= & Diag_condition_item_names[item_enum];
239       if (assign_condition_item(cond->m_mem_root, name->str, thd, set, member))
240         goto end;
241     }
242   }
243 
244   /*
245     Assign the remaining attributes.
246   */
247 
248   set= m_set_signal_information.m_item[DIAG_MESSAGE_TEXT];
249   if (set != NULL)
250   {
251     if (set->is_null())
252     {
253       thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
254                               "MESSAGE_TEXT", "NULL");
255       goto end;
256     }
257     /*
258       Enforce that SET MESSAGE_TEXT = <value> evaluates the value
259       as VARCHAR(MYSQL_ERRMSG_SIZE) CHARACTER SET UTF8.
260     */
261     bool truncated;
262     String utf8_text;
263     str= set->val_str(& str_value);
264     truncated= assign_fixed_string(thd->mem_root, & my_charset_utf8mb3_bin,
265                                    MYSQL_ERRMSG_SIZE,
266                                    & utf8_text, str);
267     if (truncated)
268     {
269       if (thd->is_strict_mode())
270       {
271         thd->raise_error_printf(ER_COND_ITEM_TOO_LONG,
272                                 "MESSAGE_TEXT");
273         goto end;
274       }
275 
276       thd->raise_warning_printf(WARN_COND_ITEM_TRUNCATED,
277                                 "MESSAGE_TEXT");
278     }
279 
280     /*
281       See the comments
282        "Design notes about Sql_condition::m_message_text."
283       in file sql_error.cc
284     */
285     String converted_text;
286     converted_text.set_charset(error_message_charset_info);
287     converted_text.append(utf8_text.ptr(), utf8_text.length(),
288                           utf8_text.charset());
289     cond->set_builtin_message_text(converted_text.c_ptr_safe());
290   }
291 
292   set= m_set_signal_information.m_item[DIAG_MYSQL_ERRNO];
293   if (set != NULL)
294   {
295     if (set->is_null())
296     {
297       thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
298                               "MYSQL_ERRNO", "NULL");
299       goto end;
300     }
301     longlong code= set->val_int();
302     if ((code <= 0) || (code > MAX_MYSQL_ERRNO))
303     {
304       str= set->val_str(& str_value);
305       thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
306                               "MYSQL_ERRNO", str->c_ptr_safe());
307       goto end;
308     }
309     cond->m_sql_errno= (int) code;
310   }
311 
312   /*
313     The various item->val_xxx() methods don't return an error code,
314     but flag thd in case of failure.
315   */
316   if (likely(!thd->is_error()))
317     result= 0;
318 
319 end:
320   for (i= FIRST_DIAG_SET_PROPERTY;
321        i <= LAST_DIAG_SET_PROPERTY;
322        i++)
323   {
324     set= m_set_signal_information.m_item[i];
325     if (set)
326     {
327       if (set->is_fixed())
328         set->cleanup();
329     }
330   }
331 
332   DBUG_RETURN(result);
333 }
334 
raise_condition(THD * thd,Sql_condition * cond)335 bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond)
336 {
337   bool result= TRUE;
338 
339   DBUG_ENTER("Sql_cmd_common_signal::raise_condition");
340 
341   DBUG_ASSERT(thd->lex->query_tables == NULL);
342 
343   cond->assign_defaults(thd, m_cond);
344   if (eval_signal_informations(thd, cond))
345     DBUG_RETURN(result);
346 
347   /* SIGNAL should not signal WARN_LEVEL_NOTE, but RESIGNAL can */
348   DBUG_ASSERT(cond->m_level == Sql_condition::WARN_LEVEL_ERROR ||
349               cond->m_level != Sql_condition::WARN_LEVEL_NOTE ||
350               sql_command_code() == SQLCOM_RESIGNAL);
351 
352   (void) thd->raise_condition(cond);
353 
354   if (cond->m_level == Sql_condition::WARN_LEVEL_WARN ||
355       cond->m_level == Sql_condition::WARN_LEVEL_NOTE)
356   {
357     my_ok(thd);
358     result= FALSE;
359   }
360 
361   DBUG_RETURN(result);
362 }
363 
execute(THD * thd)364 bool Sql_cmd_signal::execute(THD *thd)
365 {
366   bool result= TRUE;
367   DBUG_ASSERT(m_cond);
368   Sql_condition cond(thd->mem_root, m_cond->get_user_condition_identity());
369 
370   DBUG_ENTER("Sql_cmd_signal::execute");
371 
372   /*
373     WL#2110 SIGNAL specification says:
374 
375       When SIGNAL is executed, it has five effects, in the following order:
376 
377         (1) First, the diagnostics area is completely cleared. So if the
378         SIGNAL is in a DECLARE HANDLER then any pending errors or warnings
379         are gone. So is 'row count'.
380 
381     This has roots in the SQL standard specification for SIGNAL.
382   */
383 
384   thd->get_stmt_da()->reset_diagnostics_area();
385   thd->set_row_count_func(0);
386   thd->get_stmt_da()->clear_warning_info(thd->query_id);
387 
388   result= raise_condition(thd, &cond);
389 
390   DBUG_RETURN(result);
391 }
392 
393 
394 /**
395   Execute RESIGNAL SQL-statement.
396 
397   @param thd Thread context.
398 
399   @return Error status
400   @retval true  in case of error
401   @retval false on success
402 */
403 
execute(THD * thd)404 bool Sql_cmd_resignal::execute(THD *thd)
405 {
406   Diagnostics_area *da= thd->get_stmt_da();
407   const sp_rcontext::Sql_condition_info *signaled;
408   int result= TRUE;
409 
410   DBUG_ENTER("Resignal_statement::execute");
411 
412   // This is a way to force sql_conditions from the current Warning_info to be
413   // passed to the caller's Warning_info.
414   da->set_warning_info_id(thd->query_id);
415 
416   if (! thd->spcont || ! (signaled= thd->spcont->raised_condition()))
417   {
418     thd->raise_error(ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER);
419     DBUG_RETURN(result);
420   }
421 
422   Sql_condition signaled_err(thd->mem_root, *signaled, signaled->message);
423 
424   if (m_cond)
425   {
426     query_cache_abort(thd, &thd->query_cache_tls);
427 
428     /* Keep handled conditions. */
429     da->unmark_sql_conditions_from_removal();
430 
431     /* Check if the old condition still exists. */
432     if (da->has_sql_condition(signaled->message, strlen(signaled->message)))
433     {
434       /*
435         Make room for the new RESIGNAL condition and one for the stack trace
436         note.
437       */
438       da->reserve_space(thd, 2);
439     }
440     else
441     {
442       /*
443         Make room for old condition + the new RESIGNAL condition + the stack
444         trace note.
445       */
446       da->reserve_space(thd, 3);
447 
448       da->push_warning(thd, &signaled_err);
449     }
450   }
451 
452   /* RESIGNAL with signal_value */
453   result= raise_condition(thd, &signaled_err);
454 
455   DBUG_RETURN(result);
456 
457 }
458 
459