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