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-1301 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
32const 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") },
44 { STRING_WITH_LEN("MESSAGE_TEXT") },
45 { STRING_WITH_LEN("MYSQL_ERRNO") },
46
47 { STRING_WITH_LEN("CONDITION_IDENTIFIER") },
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
67Set_signal_information::Set_signal_information(
68 const Set_signal_information& set)
69{
70 memcpy(m_item, set.m_item, sizeof(m_item));
71}
72
73void Set_signal_information::clear()
74{
75 memset(m_item, 0, sizeof(m_item));
76}
77
78
79static 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
150static 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
183int 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 set= m_set_signal_information.m_item[i];
222 if (set)
223 {
224 if (! set->fixed)
225 {
226 if (set->fix_fields(thd, & set))
227 goto end;
228 m_set_signal_information.m_item[i]= set;
229 }
230 }
231 }
232
233 /*
234 Generically assign all the UTF8 String 64 condition items
235 described in the map.
236 */
237 for (j= 0; j < array_elements(map); j++)
238 {
239 item_enum= map[j].m_item;
240 set= m_set_signal_information.m_item[item_enum];
241 if (set != NULL)
242 {
243 member= & (cond->* map[j].m_member);
244 name= & Diag_condition_item_names[item_enum];
245 if (assign_condition_item(cond->m_mem_root, name->str, thd, set, member))
246 goto end;
247 }
248 }
249
250 /*
251 Assign the remaining attributes.
252 */
253
254 set= m_set_signal_information.m_item[DIAG_MESSAGE_TEXT];
255 if (set != NULL)
256 {
257 if (set->is_null())
258 {
259 thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
260 "MESSAGE_TEXT", "NULL");
261 goto end;
262 }
263 /*
264 Enforce that SET MESSAGE_TEXT = <value> evaluates the value
265 as VARCHAR(MYSQL_ERRMSG_SIZE) CHARACTER SET UTF8.
266 */
267 bool truncated;
268 String utf8_text;
269 str= set->val_str(& str_value);
270 truncated= assign_fixed_string(thd->mem_root, & my_charset_utf8_bin,
271 MYSQL_ERRMSG_SIZE,
272 & utf8_text, str);
273 if (truncated)
274 {
275 if (thd->is_strict_mode())
276 {
277 thd->raise_error_printf(ER_COND_ITEM_TOO_LONG,
278 "MESSAGE_TEXT");
279 goto end;
280 }
281
282 thd->raise_warning_printf(WARN_COND_ITEM_TRUNCATED,
283 "MESSAGE_TEXT");
284 }
285
286 /*
287 See the comments
288 "Design notes about Sql_condition::m_message_text."
289 in file sql_error.cc
290 */
291 String converted_text;
292 converted_text.set_charset(error_message_charset_info);
293 converted_text.append(utf8_text.ptr(), utf8_text.length(),
294 utf8_text.charset());
295 cond->set_builtin_message_text(converted_text.c_ptr_safe());
296 }
297
298 set= m_set_signal_information.m_item[DIAG_MYSQL_ERRNO];
299 if (set != NULL)
300 {
301 if (set->is_null())
302 {
303 thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
304 "MYSQL_ERRNO", "NULL");
305 goto end;
306 }
307 longlong code= set->val_int();
308 if ((code <= 0) || (code > MAX_MYSQL_ERRNO))
309 {
310 str= set->val_str(& str_value);
311 thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
312 "MYSQL_ERRNO", str->c_ptr_safe());
313 goto end;
314 }
315 cond->m_sql_errno= (int) code;
316 }
317
318 /*
319 The various item->val_xxx() methods don't return an error code,
320 but flag thd in case of failure.
321 */
322 if (likely(!thd->is_error()))
323 result= 0;
324
325end:
326 for (i= FIRST_DIAG_SET_PROPERTY;
327 i <= LAST_DIAG_SET_PROPERTY;
328 i++)
329 {
330 set= m_set_signal_information.m_item[i];
331 if (set)
332 {
333 if (set->fixed)
334 set->cleanup();
335 }
336 }
337
338 DBUG_RETURN(result);
339}
340
341bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond)
342{
343 bool result= TRUE;
344
345 DBUG_ENTER("Sql_cmd_common_signal::raise_condition");
346
347 DBUG_ASSERT(thd->lex->query_tables == NULL);
348
349 cond->assign_defaults(thd, m_cond);
350 if (eval_signal_informations(thd, cond))
351 DBUG_RETURN(result);
352
353 /* SIGNAL should not signal WARN_LEVEL_NOTE */
354 DBUG_ASSERT((cond->m_level == Sql_condition::WARN_LEVEL_WARN) ||
355 (cond->m_level == Sql_condition::WARN_LEVEL_ERROR));
356
357 (void) thd->raise_condition(cond);
358
359 if (cond->m_level == Sql_condition::WARN_LEVEL_WARN)
360 {
361 my_ok(thd);
362 result= FALSE;
363 }
364
365 DBUG_RETURN(result);
366}
367
368bool Sql_cmd_signal::execute(THD *thd)
369{
370 bool result= TRUE;
371 DBUG_ASSERT(m_cond);
372 Sql_condition cond(thd->mem_root, m_cond->get_user_condition_identity());
373
374 DBUG_ENTER("Sql_cmd_signal::execute");
375
376 /*
377 WL#2110 SIGNAL specification says:
378
379 When SIGNAL is executed, it has five effects, in the following order:
380
381 (1) First, the diagnostics area is completely cleared. So if the
382 SIGNAL is in a DECLARE HANDLER then any pending errors or warnings
383 are gone. So is 'row count'.
384
385 This has roots in the SQL standard specification for SIGNAL.
386 */
387
388 thd->get_stmt_da()->reset_diagnostics_area();
389 thd->set_row_count_func(0);
390 thd->get_stmt_da()->clear_warning_info(thd->query_id);
391
392 result= raise_condition(thd, &cond);
393
394 DBUG_RETURN(result);
395}
396
397
398/**
399 Execute RESIGNAL SQL-statement.
400
401 @param thd Thread context.
402
403 @return Error status
404 @retval true in case of error
405 @retval false on success
406*/
407
408bool Sql_cmd_resignal::execute(THD *thd)
409{
410 Diagnostics_area *da= thd->get_stmt_da();
411 const sp_rcontext::Sql_condition_info *signaled;
412 int result= TRUE;
413
414 DBUG_ENTER("Resignal_statement::execute");
415
416 // This is a way to force sql_conditions from the current Warning_info to be
417 // passed to the caller's Warning_info.
418 da->set_warning_info_id(thd->query_id);
419
420 if (! thd->spcont || ! (signaled= thd->spcont->raised_condition()))
421 {
422 thd->raise_error(ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER);
423 DBUG_RETURN(result);
424 }
425
426 Sql_condition signaled_err(thd->mem_root, *signaled, signaled->message);
427
428 if (m_cond)
429 {
430 query_cache_abort(thd, &thd->query_cache_tls);
431
432 /* Keep handled conditions. */
433 da->unmark_sql_conditions_from_removal();
434
435 /* Check if the old condition still exists. */
436 if (da->has_sql_condition(signaled->message, strlen(signaled->message)))
437 {
438 /*
439 Make room for the new RESIGNAL condition and one for the stack trace
440 note.
441 */
442 da->reserve_space(thd, 2);
443 }
444 else
445 {
446 /*
447 Make room for old condition + the new RESIGNAL condition + the stack
448 trace note.
449 */
450 da->reserve_space(thd, 3);
451
452 da->push_warning(thd, &signaled_err);
453 }
454 }
455
456 /* RESIGNAL with signal_value */
457 result= raise_condition(thd, &signaled_err);
458
459 DBUG_RETURN(result);
460
461}
462
463