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