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