00001 /****************************************************** 00002 Interface between Innobase row operations and MySQL. 00003 Contains also create table and other data dictionary operations. 00004 00005 (c) 2000 Innobase Oy 00006 00007 Created 9/17/2000 Heikki Tuuri 00008 *******************************************************/ 00009 00010 #include "row0mysql.h" 00011 00012 #ifdef UNIV_NONINL 00013 #include "row0mysql.ic" 00014 #endif 00015 00016 #include "row0ins.h" 00017 #include "row0sel.h" 00018 #include "row0upd.h" 00019 #include "row0row.h" 00020 #include "que0que.h" 00021 #include "pars0pars.h" 00022 #include "dict0dict.h" 00023 #include "dict0crea.h" 00024 #include "dict0load.h" 00025 #include "dict0boot.h" 00026 #include "trx0roll.h" 00027 #include "trx0purge.h" 00028 #include "lock0lock.h" 00029 #include "rem0cmp.h" 00030 #include "log0log.h" 00031 #include "btr0sea.h" 00032 #include "fil0fil.h" 00033 #include "ibuf0ibuf.h" 00034 00035 /* A dummy variable used to fool the compiler */ 00036 ibool row_mysql_identically_false = FALSE; 00037 00038 /* List of tables we should drop in background. ALTER TABLE in MySQL requires 00039 that the table handler can drop the table in background when there are no 00040 queries to it any more. Protected by the kernel mutex. */ 00041 typedef struct row_mysql_drop_struct row_mysql_drop_t; 00042 struct row_mysql_drop_struct{ 00043 char* table_name; 00044 UT_LIST_NODE_T(row_mysql_drop_t) row_mysql_drop_list; 00045 }; 00046 00047 UT_LIST_BASE_NODE_T(row_mysql_drop_t) row_mysql_drop_list; 00048 ibool row_mysql_drop_list_inited = FALSE; 00049 00050 /* Magic table names for invoking various monitor threads */ 00051 static const char S_innodb_monitor[] = "innodb_monitor"; 00052 static const char S_innodb_lock_monitor[] = "innodb_lock_monitor"; 00053 static const char S_innodb_tablespace_monitor[] = "innodb_tablespace_monitor"; 00054 static const char S_innodb_table_monitor[] = "innodb_table_monitor"; 00055 static const char S_innodb_mem_validate[] = "innodb_mem_validate"; 00056 00057 /* Name suffix for recovered orphaned temporary tables */ 00058 static const char S_recover_innodb_tmp_table[] = "_recover_innodb_tmp_table"; 00059 /*********************************************************************** 00060 Determine if the given name ends in the suffix reserved for recovered 00061 orphaned temporary tables. */ 00062 static 00063 ibool 00064 row_mysql_is_recovered_tmp_table( 00065 /*=============================*/ 00066 /* out: TRUE if table name ends in 00067 the reserved suffix */ 00068 const char* name) 00069 { 00070 ulint namelen = strlen(name) + 1; 00071 return(namelen >= sizeof S_recover_innodb_tmp_table 00072 && !memcmp(name + namelen - 00073 sizeof S_recover_innodb_tmp_table, 00074 S_recover_innodb_tmp_table, 00075 sizeof S_recover_innodb_tmp_table)); 00076 } 00077 00078 /*********************************************************************** 00079 Determine if the given name is a name reserved for MySQL system tables. */ 00080 static 00081 ibool 00082 row_mysql_is_system_table( 00083 /*======================*/ 00084 /* out: TRUE if name is a MySQL 00085 system table name */ 00086 const char* name) 00087 { 00088 if (strncmp(name, "mysql/", 6) != 0) { 00089 00090 return(FALSE); 00091 } 00092 00093 return(0 == strcmp(name + 6, "host") 00094 || 0 == strcmp(name + 6, "user") 00095 || 0 == strcmp(name + 6, "db")); 00096 } 00097 00098 /*********************************************************************** 00099 Delays an INSERT, DELETE or UPDATE operation if the purge is lagging. */ 00100 static 00101 void 00102 row_mysql_delay_if_needed(void) 00103 /*===========================*/ 00104 { 00105 if (srv_dml_needed_delay) { 00106 os_thread_sleep(srv_dml_needed_delay); 00107 } 00108 } 00109 00110 /*********************************************************************** 00111 Frees the blob heap in prebuilt when no longer needed. */ 00112 00113 void 00114 row_mysql_prebuilt_free_blob_heap( 00115 /*==============================*/ 00116 row_prebuilt_t* prebuilt) /* in: prebuilt struct of a 00117 ha_innobase:: table handle */ 00118 { 00119 mem_heap_free(prebuilt->blob_heap); 00120 prebuilt->blob_heap = NULL; 00121 } 00122 00123 /*********************************************************************** 00124 Stores a >= 5.0.3 format true VARCHAR length to dest, in the MySQL row 00125 format. */ 00126 00127 byte* 00128 row_mysql_store_true_var_len( 00129 /*=========================*/ 00130 /* out: pointer to the data, we skip the 1 or 2 bytes 00131 at the start that are used to store the len */ 00132 byte* dest, /* in: where to store */ 00133 ulint len, /* in: length, must fit in two bytes */ 00134 ulint lenlen) /* in: storage length of len: either 1 or 2 bytes */ 00135 { 00136 if (lenlen == 2) { 00137 ut_a(len < 256 * 256); 00138 00139 mach_write_to_2_little_endian(dest, len); 00140 00141 return(dest + 2); 00142 } 00143 00144 ut_a(lenlen == 1); 00145 ut_a(len < 256); 00146 00147 mach_write_to_1(dest, len); 00148 00149 return(dest + 1); 00150 } 00151 00152 /*********************************************************************** 00153 Reads a >= 5.0.3 format true VARCHAR length, in the MySQL row format, and 00154 returns a pointer to the data. */ 00155 00156 byte* 00157 row_mysql_read_true_varchar( 00158 /*========================*/ 00159 /* out: pointer to the data, we skip the 1 or 2 bytes 00160 at the start that are used to store the len */ 00161 ulint* len, /* out: variable-length field length */ 00162 byte* field, /* in: field in the MySQL format */ 00163 ulint lenlen) /* in: storage length of len: either 1 or 2 bytes */ 00164 { 00165 if (lenlen == 2) { 00166 *len = mach_read_from_2_little_endian(field); 00167 00168 return(field + 2); 00169 } 00170 00171 ut_a(lenlen == 1); 00172 00173 *len = mach_read_from_1(field); 00174 00175 return(field + 1); 00176 } 00177 00178 /*********************************************************************** 00179 Stores a reference to a BLOB in the MySQL format. */ 00180 00181 void 00182 row_mysql_store_blob_ref( 00183 /*=====================*/ 00184 byte* dest, /* in: where to store */ 00185 ulint col_len, /* in: dest buffer size: determines into 00186 how many bytes the BLOB length is stored, 00187 the space for the length may vary from 1 00188 to 4 bytes */ 00189 byte* data, /* in: BLOB data; if the value to store 00190 is SQL NULL this should be NULL pointer */ 00191 ulint len) /* in: BLOB length; if the value to store 00192 is SQL NULL this should be 0; remember 00193 also to set the NULL bit in the MySQL record 00194 header! */ 00195 { 00196 /* MySQL might assume the field is set to zero except the length and 00197 the pointer fields */ 00198 00199 memset(dest, '\0', col_len); 00200 00201 /* In dest there are 1 - 4 bytes reserved for the BLOB length, 00202 and after that 8 bytes reserved for the pointer to the data. 00203 In 32-bit architectures we only use the first 4 bytes of the pointer 00204 slot. */ 00205 00206 ut_a(col_len - 8 > 1 || len < 256); 00207 ut_a(col_len - 8 > 2 || len < 256 * 256); 00208 ut_a(col_len - 8 > 3 || len < 256 * 256 * 256); 00209 00210 mach_write_to_n_little_endian(dest, col_len - 8, len); 00211 00212 ut_memcpy(dest + col_len - 8, &data, sizeof(byte*)); 00213 } 00214 00215 /*********************************************************************** 00216 Reads a reference to a BLOB in the MySQL format. */ 00217 00218 byte* 00219 row_mysql_read_blob_ref( 00220 /*====================*/ 00221 /* out: pointer to BLOB data */ 00222 ulint* len, /* out: BLOB length */ 00223 byte* ref, /* in: BLOB reference in the MySQL format */ 00224 ulint col_len) /* in: BLOB reference length (not BLOB 00225 length) */ 00226 { 00227 byte* data; 00228 00229 *len = mach_read_from_n_little_endian(ref, col_len - 8); 00230 00231 ut_memcpy(&data, ref + col_len - 8, sizeof(byte*)); 00232 00233 return(data); 00234 } 00235 00236 /****************************************************************** 00237 Stores a non-SQL-NULL field given in the MySQL format in the InnoDB format. 00238 The counterpart of this function is row_sel_field_store_in_mysql_format() in 00239 row0sel.c. */ 00240 00241 byte* 00242 row_mysql_store_col_in_innobase_format( 00243 /*===================================*/ 00244 /* out: up to which byte we used 00245 buf in the conversion */ 00246 dfield_t* dfield, /* in/out: dfield where dtype 00247 information must be already set when 00248 this function is called! */ 00249 byte* buf, /* in/out: buffer for a converted 00250 integer value; this must be at least 00251 col_len long then! */ 00252 ibool row_format_col, /* TRUE if the mysql_data is from 00253 a MySQL row, FALSE if from a MySQL 00254 key value; 00255 in MySQL, a true VARCHAR storage 00256 format differs in a row and in a 00257 key value: in a key value the length 00258 is always stored in 2 bytes! */ 00259 byte* mysql_data, /* in: MySQL column value, not 00260 SQL NULL; NOTE that dfield may also 00261 get a pointer to mysql_data, 00262 therefore do not discard this as long 00263 as dfield is used! */ 00264 ulint col_len, /* in: MySQL column length; NOTE that 00265 this is the storage length of the 00266 column in the MySQL format row, not 00267 necessarily the length of the actual 00268 payload data; if the column is a true 00269 VARCHAR then this is irrelevant */ 00270 ulint comp) /* in: nonzero=compact format */ 00271 { 00272 byte* ptr = mysql_data; 00273 dtype_t* dtype; 00274 ulint type; 00275 ulint lenlen; 00276 00277 dtype = dfield_get_type(dfield); 00278 00279 type = dtype->mtype; 00280 00281 if (type == DATA_INT) { 00282 /* Store integer data in Innobase in a big-endian format, 00283 sign bit negated if the data is a signed integer. In MySQL, 00284 integers are stored in a little-endian format. */ 00285 00286 ptr = buf + col_len; 00287 00288 for (;;) { 00289 ptr--; 00290 *ptr = *mysql_data; 00291 if (ptr == buf) { 00292 break; 00293 } 00294 mysql_data++; 00295 } 00296 00297 if (!(dtype->prtype & DATA_UNSIGNED)) { 00298 00299 *ptr = (byte) (*ptr ^ 128); 00300 } 00301 00302 buf += col_len; 00303 } else if ((type == DATA_VARCHAR 00304 || type == DATA_VARMYSQL 00305 || type == DATA_BINARY)) { 00306 00307 if (dtype_get_mysql_type(dtype) == DATA_MYSQL_TRUE_VARCHAR) { 00308 /* The length of the actual data is stored to 1 or 2 00309 bytes at the start of the field */ 00310 00311 if (row_format_col) { 00312 if (dtype->prtype & DATA_LONG_TRUE_VARCHAR) { 00313 lenlen = 2; 00314 } else { 00315 lenlen = 1; 00316 } 00317 } else { 00318 /* In a MySQL key value, lenlen is always 2 */ 00319 lenlen = 2; 00320 } 00321 00322 ptr = row_mysql_read_true_varchar(&col_len, mysql_data, 00323 lenlen); 00324 } else { 00325 /* Remove trailing spaces from old style VARCHAR 00326 columns. */ 00327 00328 /* Handle UCS2 strings differently. */ 00329 ulint mbminlen = dtype_get_mbminlen(dtype); 00330 00331 ptr = mysql_data; 00332 00333 if (mbminlen == 2) { 00334 /* space=0x0020 */ 00335 /* Trim "half-chars", just in case. */ 00336 col_len &= ~1; 00337 00338 while (col_len >= 2 && ptr[col_len - 2] == 0x00 00339 && ptr[col_len - 1] == 0x20) { 00340 col_len -= 2; 00341 } 00342 } else { 00343 ut_a(mbminlen == 1); 00344 /* space=0x20 */ 00345 while (col_len > 0 00346 && ptr[col_len - 1] == 0x20) { 00347 col_len--; 00348 } 00349 } 00350 } 00351 } else if (comp && type == DATA_MYSQL 00352 && dtype_get_mbminlen(dtype) == 1 00353 && dtype_get_mbmaxlen(dtype) > 1) { 00354 /* In some cases we strip trailing spaces from UTF-8 and other 00355 multibyte charsets, from FIXED-length CHAR columns, to save 00356 space. UTF-8 would otherwise normally use 3 * the string length 00357 bytes to store a latin1 string! */ 00358 00359 /* We assume that this CHAR field is encoded in a 00360 variable-length character set where spaces have 00361 1:1 correspondence to 0x20 bytes, such as UTF-8. 00362 00363 Consider a CHAR(n) field, a field of n characters. 00364 It will contain between n * mbminlen and n * mbmaxlen bytes. 00365 We will try to truncate it to n bytes by stripping 00366 space padding. If the field contains single-byte 00367 characters only, it will be truncated to n characters. 00368 Consider a CHAR(5) field containing the string ".a " 00369 where "." denotes a 3-byte character represented by 00370 the bytes "$%&". After our stripping, the string will 00371 be stored as "$%&a " (5 bytes). The string ".abc " 00372 will be stored as "$%&abc" (6 bytes). 00373 00374 The space padding will be restored in row0sel.c, function 00375 row_sel_field_store_in_mysql_format(). */ 00376 00377 ulint n_chars; 00378 00379 ut_a(!(dtype_get_len(dtype) % dtype_get_mbmaxlen(dtype))); 00380 00381 n_chars = dtype_get_len(dtype) / dtype_get_mbmaxlen(dtype); 00382 00383 /* Strip space padding. */ 00384 while (col_len > n_chars && ptr[col_len - 1] == 0x20) { 00385 col_len--; 00386 } 00387 } else if (type == DATA_BLOB && row_format_col) { 00388 00389 ptr = row_mysql_read_blob_ref(&col_len, mysql_data, col_len); 00390 } 00391 00392 dfield_set_data(dfield, ptr, col_len); 00393 00394 return(buf); 00395 } 00396 00397 /****************************************************************** 00398 Convert a row in the MySQL format to a row in the Innobase format. Note that 00399 the function to convert a MySQL format key value to an InnoDB dtuple is 00400 row_sel_convert_mysql_key_to_innobase() in row0sel.c. */ 00401 static 00402 void 00403 row_mysql_convert_row_to_innobase( 00404 /*==============================*/ 00405 dtuple_t* row, /* in/out: Innobase row where the 00406 field type information is already 00407 copied there! */ 00408 row_prebuilt_t* prebuilt, /* in: prebuilt struct where template 00409 must be of type ROW_MYSQL_WHOLE_ROW */ 00410 byte* mysql_rec) /* in: row in the MySQL format; 00411 NOTE: do not discard as long as 00412 row is used, as row may contain 00413 pointers to this record! */ 00414 { 00415 mysql_row_templ_t* templ; 00416 dfield_t* dfield; 00417 ulint i; 00418 00419 ut_ad(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); 00420 ut_ad(prebuilt->mysql_template); 00421 00422 for (i = 0; i < prebuilt->n_template; i++) { 00423 00424 templ = prebuilt->mysql_template + i; 00425 dfield = dtuple_get_nth_field(row, i); 00426 00427 if (templ->mysql_null_bit_mask != 0) { 00428 /* Column may be SQL NULL */ 00429 00430 if (mysql_rec[templ->mysql_null_byte_offset] & 00431 (byte) (templ->mysql_null_bit_mask)) { 00432 00433 /* It is SQL NULL */ 00434 00435 dfield_set_data(dfield, NULL, UNIV_SQL_NULL); 00436 00437 goto next_column; 00438 } 00439 } 00440 00441 row_mysql_store_col_in_innobase_format(dfield, 00442 prebuilt->ins_upd_rec_buff 00443 + templ->mysql_col_offset, 00444 TRUE, /* MySQL row format data */ 00445 mysql_rec + templ->mysql_col_offset, 00446 templ->mysql_col_len, 00447 dict_table_is_comp(prebuilt->table)); 00448 next_column: 00449 ; 00450 } 00451 } 00452 00453 /******************************************************************** 00454 Handles user errors and lock waits detected by the database engine. */ 00455 00456 ibool 00457 row_mysql_handle_errors( 00458 /*====================*/ 00459 /* out: TRUE if it was a lock wait and 00460 we should continue running the query thread */ 00461 ulint* new_err,/* out: possible new error encountered in 00462 lock wait, or if no new error, the value 00463 of trx->error_state at the entry of this 00464 function */ 00465 trx_t* trx, /* in: transaction */ 00466 que_thr_t* thr, /* in: query thread */ 00467 trx_savept_t* savept) /* in: savepoint or NULL */ 00468 { 00469 #ifndef UNIV_HOTBACKUP 00470 ulint err; 00471 00472 handle_new_error: 00473 err = trx->error_state; 00474 00475 ut_a(err != DB_SUCCESS); 00476 00477 trx->error_state = DB_SUCCESS; 00478 00479 if ((err == DB_DUPLICATE_KEY) 00480 || (err == DB_FOREIGN_DUPLICATE_KEY)) { 00481 if (savept) { 00482 /* Roll back the latest, possibly incomplete 00483 insertion or update */ 00484 00485 trx_general_rollback_for_mysql(trx, TRUE, savept); 00486 } 00487 } else if (err == DB_TOO_BIG_RECORD) { 00488 if (savept) { 00489 /* Roll back the latest, possibly incomplete 00490 insertion or update */ 00491 00492 trx_general_rollback_for_mysql(trx, TRUE, savept); 00493 } 00494 /* MySQL will roll back the latest SQL statement */ 00495 } else if (err == DB_ROW_IS_REFERENCED 00496 || err == DB_NO_REFERENCED_ROW 00497 || err == DB_CANNOT_ADD_CONSTRAINT) { 00498 if (savept) { 00499 /* Roll back the latest, possibly incomplete 00500 insertion or update */ 00501 00502 trx_general_rollback_for_mysql(trx, TRUE, savept); 00503 } 00504 /* MySQL will roll back the latest SQL statement */ 00505 } else if (err == DB_LOCK_WAIT) { 00506 00507 srv_suspend_mysql_thread(thr); 00508 00509 if (trx->error_state != DB_SUCCESS) { 00510 que_thr_stop_for_mysql(thr); 00511 00512 goto handle_new_error; 00513 } 00514 00515 *new_err = err; 00516 00517 return(TRUE); 00518 00519 } else if (err == DB_DEADLOCK 00520 || err == DB_LOCK_TABLE_FULL) { 00521 /* Roll back the whole transaction; this resolution was added 00522 to version 3.23.43 */ 00523 00524 trx_general_rollback_for_mysql(trx, FALSE, NULL); 00525 00526 } else if (err == DB_OUT_OF_FILE_SPACE 00527 || err == DB_LOCK_WAIT_TIMEOUT) { 00528 if (savept) { 00529 /* Roll back the latest, possibly incomplete 00530 insertion or update */ 00531 00532 trx_general_rollback_for_mysql(trx, TRUE, savept); 00533 } 00534 /* MySQL will roll back the latest SQL statement */ 00535 00536 } else if (err == DB_MUST_GET_MORE_FILE_SPACE) { 00537 00538 fputs( 00539 "InnoDB: The database cannot continue operation because of\n" 00540 "InnoDB: lack of space. You must add a new data file to\n" 00541 "InnoDB: my.cnf and restart the database.\n", stderr); 00542 00543 exit(1); 00544 } else if (err == DB_CORRUPTION) { 00545 00546 fputs( 00547 "InnoDB: We detected index corruption in an InnoDB type table.\n" 00548 "InnoDB: You have to dump + drop + reimport the table or, in\n" 00549 "InnoDB: a case of widespread corruption, dump all InnoDB\n" 00550 "InnoDB: tables and recreate the whole InnoDB tablespace.\n" 00551 "InnoDB: If the mysqld server crashes after the startup or when\n" 00552 "InnoDB: you dump the tables, look at\n" 00553 "InnoDB: http://dev.mysql.com/doc/mysql/en/Forcing_recovery.html" 00554 " for help.\n", stderr); 00555 00556 } else { 00557 fprintf(stderr, "InnoDB: unknown error code %lu\n", 00558 (ulong) err); 00559 ut_error; 00560 } 00561 00562 if (trx->error_state != DB_SUCCESS) { 00563 *new_err = trx->error_state; 00564 } else { 00565 *new_err = err; 00566 } 00567 00568 trx->error_state = DB_SUCCESS; 00569 00570 return(FALSE); 00571 #else /* UNIV_HOTBACKUP */ 00572 /* This function depends on MySQL code that is not included in 00573 InnoDB Hot Backup builds. Besides, this function should never 00574 be called in InnoDB Hot Backup. */ 00575 ut_error; 00576 return(FALSE); 00577 #endif /* UNIV_HOTBACKUP */ 00578 } 00579 00580 /************************************************************************ 00581 Create a prebuilt struct for a MySQL table handle. */ 00582 00583 row_prebuilt_t* 00584 row_create_prebuilt( 00585 /*================*/ 00586 /* out, own: a prebuilt struct */ 00587 dict_table_t* table) /* in: Innobase table handle */ 00588 { 00589 row_prebuilt_t* prebuilt; 00590 mem_heap_t* heap; 00591 dict_index_t* clust_index; 00592 dtuple_t* ref; 00593 ulint ref_len; 00594 ulint i; 00595 00596 heap = mem_heap_create(128); 00597 00598 prebuilt = mem_heap_alloc(heap, sizeof(row_prebuilt_t)); 00599 00600 prebuilt->magic_n = ROW_PREBUILT_ALLOCATED; 00601 prebuilt->magic_n2 = ROW_PREBUILT_ALLOCATED; 00602 00603 prebuilt->table = table; 00604 00605 prebuilt->trx = NULL; 00606 00607 prebuilt->sql_stat_start = TRUE; 00608 00609 prebuilt->mysql_has_locked = FALSE; 00610 00611 prebuilt->index = NULL; 00612 00613 prebuilt->used_in_HANDLER = FALSE; 00614 00615 prebuilt->n_template = 0; 00616 prebuilt->mysql_template = NULL; 00617 00618 prebuilt->heap = heap; 00619 prebuilt->ins_node = NULL; 00620 00621 prebuilt->ins_upd_rec_buff = NULL; 00622 00623 prebuilt->upd_node = NULL; 00624 prebuilt->ins_graph = NULL; 00625 prebuilt->upd_graph = NULL; 00626 00627 prebuilt->pcur = btr_pcur_create_for_mysql(); 00628 prebuilt->clust_pcur = btr_pcur_create_for_mysql(); 00629 00630 prebuilt->select_lock_type = LOCK_NONE; 00631 prebuilt->stored_select_lock_type = 99999999; 00632 00633 prebuilt->row_read_type = ROW_READ_WITH_LOCKS; 00634 00635 prebuilt->sel_graph = NULL; 00636 00637 prebuilt->search_tuple = dtuple_create(heap, 00638 2 * dict_table_get_n_cols(table)); 00639 00640 clust_index = dict_table_get_first_index(table); 00641 00642 /* Make sure that search_tuple is long enough for clustered index */ 00643 ut_a(2 * dict_table_get_n_cols(table) >= clust_index->n_fields); 00644 00645 ref_len = dict_index_get_n_unique(clust_index); 00646 00647 ref = dtuple_create(heap, ref_len); 00648 00649 dict_index_copy_types(ref, clust_index, ref_len); 00650 00651 prebuilt->clust_ref = ref; 00652 00653 for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) { 00654 prebuilt->fetch_cache[i] = NULL; 00655 } 00656 00657 prebuilt->n_fetch_cached = 0; 00658 00659 prebuilt->blob_heap = NULL; 00660 00661 prebuilt->old_vers_heap = NULL; 00662 00663 return(prebuilt); 00664 } 00665 00666 /************************************************************************ 00667 Free a prebuilt struct for a MySQL table handle. */ 00668 00669 void 00670 row_prebuilt_free( 00671 /*==============*/ 00672 row_prebuilt_t* prebuilt) /* in, own: prebuilt struct */ 00673 { 00674 ulint i; 00675 00676 if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED 00677 || prebuilt->magic_n2 != ROW_PREBUILT_ALLOCATED) { 00678 fprintf(stderr, 00679 "InnoDB: Error: trying to free a corrupt\n" 00680 "InnoDB: table handle. Magic n %lu, magic n2 %lu, table name", 00681 (ulong) prebuilt->magic_n, 00682 (ulong) prebuilt->magic_n2); 00683 ut_print_name(stderr, NULL, TRUE, prebuilt->table->name); 00684 putc('\n', stderr); 00685 00686 mem_analyze_corruption(prebuilt); 00687 00688 ut_error; 00689 } 00690 00691 prebuilt->magic_n = ROW_PREBUILT_FREED; 00692 prebuilt->magic_n2 = ROW_PREBUILT_FREED; 00693 00694 btr_pcur_free_for_mysql(prebuilt->pcur); 00695 btr_pcur_free_for_mysql(prebuilt->clust_pcur); 00696 00697 if (prebuilt->mysql_template) { 00698 mem_free(prebuilt->mysql_template); 00699 } 00700 00701 if (prebuilt->ins_graph) { 00702 que_graph_free_recursive(prebuilt->ins_graph); 00703 } 00704 00705 if (prebuilt->sel_graph) { 00706 que_graph_free_recursive(prebuilt->sel_graph); 00707 } 00708 00709 if (prebuilt->upd_graph) { 00710 que_graph_free_recursive(prebuilt->upd_graph); 00711 } 00712 00713 if (prebuilt->blob_heap) { 00714 mem_heap_free(prebuilt->blob_heap); 00715 } 00716 00717 if (prebuilt->old_vers_heap) { 00718 mem_heap_free(prebuilt->old_vers_heap); 00719 } 00720 00721 for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) { 00722 if (prebuilt->fetch_cache[i] != NULL) { 00723 00724 if ((ROW_PREBUILT_FETCH_MAGIC_N != 00725 mach_read_from_4((prebuilt->fetch_cache[i]) - 4)) 00726 || (ROW_PREBUILT_FETCH_MAGIC_N != 00727 mach_read_from_4((prebuilt->fetch_cache[i]) 00728 + prebuilt->mysql_row_len))) { 00729 fputs( 00730 "InnoDB: Error: trying to free a corrupt\n" 00731 "InnoDB: fetch buffer.\n", stderr); 00732 00733 mem_analyze_corruption( 00734 prebuilt->fetch_cache[i]); 00735 00736 ut_error; 00737 } 00738 00739 mem_free((prebuilt->fetch_cache[i]) - 4); 00740 } 00741 } 00742 00743 dict_table_decrement_handle_count(prebuilt->table); 00744 00745 mem_heap_free(prebuilt->heap); 00746 } 00747 00748 /************************************************************************* 00749 Updates the transaction pointers in query graphs stored in the prebuilt 00750 struct. */ 00751 00752 void 00753 row_update_prebuilt_trx( 00754 /*====================*/ 00755 /* out: prebuilt dtuple */ 00756 row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL 00757 handle */ 00758 trx_t* trx) /* in: transaction handle */ 00759 { 00760 if (trx->magic_n != TRX_MAGIC_N) { 00761 fprintf(stderr, 00762 "InnoDB: Error: trying to use a corrupt\n" 00763 "InnoDB: trx handle. Magic n %lu\n", 00764 (ulong) trx->magic_n); 00765 00766 mem_analyze_corruption(trx); 00767 00768 ut_error; 00769 } 00770 00771 if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { 00772 fprintf(stderr, 00773 "InnoDB: Error: trying to use a corrupt\n" 00774 "InnoDB: table handle. Magic n %lu, table name", 00775 (ulong) prebuilt->magic_n); 00776 ut_print_name(stderr, NULL, TRUE, prebuilt->table->name); 00777 putc('\n', stderr); 00778 00779 mem_analyze_corruption(prebuilt); 00780 00781 ut_error; 00782 } 00783 00784 prebuilt->trx = trx; 00785 00786 if (prebuilt->ins_graph) { 00787 prebuilt->ins_graph->trx = trx; 00788 } 00789 00790 if (prebuilt->upd_graph) { 00791 prebuilt->upd_graph->trx = trx; 00792 } 00793 00794 if (prebuilt->sel_graph) { 00795 prebuilt->sel_graph->trx = trx; 00796 } 00797 } 00798 00799 /************************************************************************* 00800 Gets pointer to a prebuilt dtuple used in insertions. If the insert graph 00801 has not yet been built in the prebuilt struct, then this function first 00802 builds it. */ 00803 static 00804 dtuple_t* 00805 row_get_prebuilt_insert_row( 00806 /*========================*/ 00807 /* out: prebuilt dtuple; the column 00808 type information is also set in it */ 00809 row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL 00810 handle */ 00811 { 00812 ins_node_t* node; 00813 dtuple_t* row; 00814 dict_table_t* table = prebuilt->table; 00815 ulint i; 00816 00817 ut_ad(prebuilt && table && prebuilt->trx); 00818 00819 if (prebuilt->ins_node == NULL) { 00820 00821 /* Not called before for this handle: create an insert node 00822 and query graph to the prebuilt struct */ 00823 00824 node = ins_node_create(INS_DIRECT, table, prebuilt->heap); 00825 00826 prebuilt->ins_node = node; 00827 00828 if (prebuilt->ins_upd_rec_buff == NULL) { 00829 prebuilt->ins_upd_rec_buff = mem_heap_alloc( 00830 prebuilt->heap, 00831 prebuilt->mysql_row_len); 00832 } 00833 00834 row = dtuple_create(prebuilt->heap, 00835 dict_table_get_n_cols(table)); 00836 00837 dict_table_copy_types(row, table); 00838 00839 /* We init the value of every field to the SQL NULL to avoid 00840 a debug assertion from failing */ 00841 00842 for (i = 0; i < dtuple_get_n_fields(row); i++) { 00843 00844 dtuple_get_nth_field(row, i)->len = UNIV_SQL_NULL; 00845 } 00846 00847 ins_node_set_new_row(node, row); 00848 00849 prebuilt->ins_graph = 00850 que_node_get_parent( 00851 pars_complete_graph_for_exec(node, 00852 prebuilt->trx, 00853 prebuilt->heap)); 00854 prebuilt->ins_graph->state = QUE_FORK_ACTIVE; 00855 } 00856 00857 return(prebuilt->ins_node->row); 00858 } 00859 00860 /************************************************************************* 00861 Updates the table modification counter and calculates new estimates 00862 for table and index statistics if necessary. */ 00863 UNIV_INLINE 00864 void 00865 row_update_statistics_if_needed( 00866 /*============================*/ 00867 dict_table_t* table) /* in: table */ 00868 { 00869 ulint counter; 00870 00871 counter = table->stat_modified_counter; 00872 00873 table->stat_modified_counter = counter + 1; 00874 00875 /* Calculate new statistics if 1 / 16 of table has been modified 00876 since the last time a statistics batch was run, or if 00877 stat_modified_counter > 2 000 000 000 (to avoid wrap-around). 00878 We calculate statistics at most every 16th round, since we may have 00879 a counter table which is very small and updated very often. */ 00880 00881 if (counter > 2000000000 00882 || ((ib_longlong)counter > 16 + table->stat_n_rows / 16)) { 00883 00884 dict_update_statistics(table); 00885 } 00886 } 00887 00888 /************************************************************************* 00889 Unlocks an AUTO_INC type lock possibly reserved by trx. */ 00890 00891 void 00892 row_unlock_table_autoinc_for_mysql( 00893 /*===============================*/ 00894 trx_t* trx) /* in: transaction */ 00895 { 00896 if (!trx->auto_inc_lock) { 00897 00898 return; 00899 } 00900 00901 lock_table_unlock_auto_inc(trx); 00902 } 00903 00904 /************************************************************************* 00905 Sets an AUTO_INC type lock on the table mentioned in prebuilt. The 00906 AUTO_INC lock gives exclusive access to the auto-inc counter of the 00907 table. The lock is reserved only for the duration of an SQL statement. 00908 It is not compatible with another AUTO_INC or exclusive lock on the 00909 table. */ 00910 00911 int 00912 row_lock_table_autoinc_for_mysql( 00913 /*=============================*/ 00914 /* out: error code or DB_SUCCESS */ 00915 row_prebuilt_t* prebuilt) /* in: prebuilt struct in the MySQL 00916 table handle */ 00917 { 00918 trx_t* trx = prebuilt->trx; 00919 ins_node_t* node = prebuilt->ins_node; 00920 que_thr_t* thr; 00921 ulint err; 00922 ibool was_lock_wait; 00923 00924 ut_ad(trx); 00925 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 00926 00927 if (trx->auto_inc_lock) { 00928 00929 return(DB_SUCCESS); 00930 } 00931 00932 trx->op_info = "setting auto-inc lock"; 00933 00934 if (node == NULL) { 00935 row_get_prebuilt_insert_row(prebuilt); 00936 node = prebuilt->ins_node; 00937 } 00938 00939 /* We use the insert query graph as the dummy graph needed 00940 in the lock module call */ 00941 00942 thr = que_fork_get_first_thr(prebuilt->ins_graph); 00943 00944 que_thr_move_to_run_state_for_mysql(thr, trx); 00945 00946 run_again: 00947 thr->run_node = node; 00948 thr->prev_node = node; 00949 00950 /* It may be that the current session has not yet started 00951 its transaction, or it has been committed: */ 00952 00953 trx_start_if_not_started(trx); 00954 00955 err = lock_table(0, prebuilt->table, LOCK_AUTO_INC, thr); 00956 00957 trx->error_state = err; 00958 00959 if (err != DB_SUCCESS) { 00960 que_thr_stop_for_mysql(thr); 00961 00962 was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL); 00963 00964 if (was_lock_wait) { 00965 goto run_again; 00966 } 00967 00968 trx->op_info = ""; 00969 00970 return((int) err); 00971 } 00972 00973 que_thr_stop_for_mysql_no_error(thr, trx); 00974 00975 trx->op_info = ""; 00976 00977 return((int) err); 00978 } 00979 00980 /************************************************************************* 00981 Sets a table lock on the table mentioned in prebuilt. */ 00982 00983 int 00984 row_lock_table_for_mysql( 00985 /*=====================*/ 00986 /* out: error code or DB_SUCCESS */ 00987 row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL 00988 table handle */ 00989 dict_table_t* table, /* in: table to lock, or NULL 00990 if prebuilt->table should be 00991 locked as 00992 prebuilt->select_lock_type */ 00993 ulint mode) /* in: lock mode of table 00994 (ignored if table==NULL) */ 00995 { 00996 trx_t* trx = prebuilt->trx; 00997 que_thr_t* thr; 00998 ulint err; 00999 ibool was_lock_wait; 01000 01001 ut_ad(trx); 01002 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 01003 01004 trx->op_info = "setting table lock"; 01005 01006 if (prebuilt->sel_graph == NULL) { 01007 /* Build a dummy select query graph */ 01008 row_prebuild_sel_graph(prebuilt); 01009 } 01010 01011 /* We use the select query graph as the dummy graph needed 01012 in the lock module call */ 01013 01014 thr = que_fork_get_first_thr(prebuilt->sel_graph); 01015 01016 que_thr_move_to_run_state_for_mysql(thr, trx); 01017 01018 run_again: 01019 thr->run_node = thr; 01020 thr->prev_node = thr->common.parent; 01021 01022 /* It may be that the current session has not yet started 01023 its transaction, or it has been committed: */ 01024 01025 trx_start_if_not_started(trx); 01026 01027 if (table) { 01028 err = lock_table(0, table, mode, thr); 01029 } else { 01030 err = lock_table(0, prebuilt->table, 01031 prebuilt->select_lock_type, thr); 01032 } 01033 01034 trx->error_state = err; 01035 01036 if (err != DB_SUCCESS) { 01037 que_thr_stop_for_mysql(thr); 01038 01039 was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL); 01040 01041 if (was_lock_wait) { 01042 goto run_again; 01043 } 01044 01045 trx->op_info = ""; 01046 01047 return((int) err); 01048 } 01049 01050 que_thr_stop_for_mysql_no_error(thr, trx); 01051 01052 trx->op_info = ""; 01053 01054 return((int) err); 01055 } 01056 01057 /************************************************************************* 01058 Does an insert for MySQL. */ 01059 01060 int 01061 row_insert_for_mysql( 01062 /*=================*/ 01063 /* out: error code or DB_SUCCESS */ 01064 byte* mysql_rec, /* in: row in the MySQL format */ 01065 row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL 01066 handle */ 01067 { 01068 trx_savept_t savept; 01069 que_thr_t* thr; 01070 ulint err; 01071 ibool was_lock_wait; 01072 trx_t* trx = prebuilt->trx; 01073 ins_node_t* node = prebuilt->ins_node; 01074 01075 ut_ad(trx); 01076 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 01077 01078 if (prebuilt->table->ibd_file_missing) { 01079 ut_print_timestamp(stderr); 01080 fprintf(stderr, " InnoDB: Error:\n" 01081 "InnoDB: MySQL is trying to use a table handle but the .ibd file for\n" 01082 "InnoDB: table %s does not exist.\n" 01083 "InnoDB: Have you deleted the .ibd file from the database directory under\n" 01084 "InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n" 01085 "InnoDB: Look from\n" 01086 "http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" 01087 "InnoDB: how you can resolve the problem.\n", 01088 prebuilt->table->name); 01089 return(DB_ERROR); 01090 } 01091 01092 if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { 01093 fprintf(stderr, 01094 "InnoDB: Error: trying to free a corrupt\n" 01095 "InnoDB: table handle. Magic n %lu, table name", 01096 (ulong) prebuilt->magic_n); 01097 ut_print_name(stderr, prebuilt->trx, TRUE, 01098 prebuilt->table->name); 01099 putc('\n', stderr); 01100 01101 mem_analyze_corruption(prebuilt); 01102 01103 ut_error; 01104 } 01105 01106 if (srv_created_new_raw || srv_force_recovery) { 01107 fputs( 01108 "InnoDB: A new raw disk partition was initialized or\n" 01109 "InnoDB: innodb_force_recovery is on: we do not allow\n" 01110 "InnoDB: database modifications by the user. Shut down\n" 01111 "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n" 01112 "InnoDB: with raw, and innodb_force_... is removed.\n", 01113 stderr); 01114 01115 return(DB_ERROR); 01116 } 01117 01118 trx->op_info = "inserting"; 01119 01120 row_mysql_delay_if_needed(); 01121 01122 trx_start_if_not_started(trx); 01123 01124 if (node == NULL) { 01125 row_get_prebuilt_insert_row(prebuilt); 01126 node = prebuilt->ins_node; 01127 } 01128 01129 row_mysql_convert_row_to_innobase(node->row, prebuilt, mysql_rec); 01130 01131 savept = trx_savept_take(trx); 01132 01133 thr = que_fork_get_first_thr(prebuilt->ins_graph); 01134 01135 if (prebuilt->sql_stat_start) { 01136 node->state = INS_NODE_SET_IX_LOCK; 01137 prebuilt->sql_stat_start = FALSE; 01138 } else { 01139 node->state = INS_NODE_ALLOC_ROW_ID; 01140 } 01141 01142 que_thr_move_to_run_state_for_mysql(thr, trx); 01143 01144 run_again: 01145 thr->run_node = node; 01146 thr->prev_node = node; 01147 01148 row_ins_step(thr); 01149 01150 err = trx->error_state; 01151 01152 if (err != DB_SUCCESS) { 01153 que_thr_stop_for_mysql(thr); 01154 01155 /* TODO: what is this? */ thr->lock_state= QUE_THR_LOCK_ROW; 01156 01157 was_lock_wait = row_mysql_handle_errors(&err, trx, thr, 01158 &savept); 01159 thr->lock_state= QUE_THR_LOCK_NOLOCK; 01160 01161 if (was_lock_wait) { 01162 goto run_again; 01163 } 01164 01165 trx->op_info = ""; 01166 01167 return((int) err); 01168 } 01169 01170 que_thr_stop_for_mysql_no_error(thr, trx); 01171 01172 prebuilt->table->stat_n_rows++; 01173 01174 srv_n_rows_inserted++; 01175 01176 if (prebuilt->table->stat_n_rows == 0) { 01177 /* Avoid wrap-over */ 01178 prebuilt->table->stat_n_rows--; 01179 } 01180 01181 row_update_statistics_if_needed(prebuilt->table); 01182 trx->op_info = ""; 01183 01184 return((int) err); 01185 } 01186 01187 /************************************************************************* 01188 Builds a dummy query graph used in selects. */ 01189 01190 void 01191 row_prebuild_sel_graph( 01192 /*===================*/ 01193 row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL 01194 handle */ 01195 { 01196 sel_node_t* node; 01197 01198 ut_ad(prebuilt && prebuilt->trx); 01199 01200 if (prebuilt->sel_graph == NULL) { 01201 01202 node = sel_node_create(prebuilt->heap); 01203 01204 prebuilt->sel_graph = 01205 que_node_get_parent( 01206 pars_complete_graph_for_exec(node, 01207 prebuilt->trx, 01208 prebuilt->heap)); 01209 01210 prebuilt->sel_graph->state = QUE_FORK_ACTIVE; 01211 } 01212 } 01213 01214 /************************************************************************* 01215 Creates an query graph node of 'update' type to be used in the MySQL 01216 interface. */ 01217 01218 upd_node_t* 01219 row_create_update_node_for_mysql( 01220 /*=============================*/ 01221 /* out, own: update node */ 01222 dict_table_t* table, /* in: table to update */ 01223 mem_heap_t* heap) /* in: mem heap from which allocated */ 01224 { 01225 upd_node_t* node; 01226 01227 node = upd_node_create(heap); 01228 01229 node->in_mysql_interface = TRUE; 01230 node->is_delete = FALSE; 01231 node->searched_update = FALSE; 01232 node->select_will_do_update = FALSE; 01233 node->select = NULL; 01234 node->pcur = btr_pcur_create_for_mysql(); 01235 node->table = table; 01236 01237 node->update = upd_create(dict_table_get_n_cols(table), heap); 01238 01239 node->update_n_fields = dict_table_get_n_cols(table); 01240 01241 UT_LIST_INIT(node->columns); 01242 node->has_clust_rec_x_lock = TRUE; 01243 node->cmpl_info = 0; 01244 01245 node->table_sym = NULL; 01246 node->col_assign_list = NULL; 01247 01248 return(node); 01249 } 01250 01251 /************************************************************************* 01252 Gets pointer to a prebuilt update vector used in updates. If the update 01253 graph has not yet been built in the prebuilt struct, then this function 01254 first builds it. */ 01255 01256 upd_t* 01257 row_get_prebuilt_update_vector( 01258 /*===========================*/ 01259 /* out: prebuilt update vector */ 01260 row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL 01261 handle */ 01262 { 01263 dict_table_t* table = prebuilt->table; 01264 upd_node_t* node; 01265 01266 ut_ad(prebuilt && table && prebuilt->trx); 01267 01268 if (prebuilt->upd_node == NULL) { 01269 01270 /* Not called before for this handle: create an update node 01271 and query graph to the prebuilt struct */ 01272 01273 node = row_create_update_node_for_mysql(table, prebuilt->heap); 01274 01275 prebuilt->upd_node = node; 01276 01277 prebuilt->upd_graph = 01278 que_node_get_parent( 01279 pars_complete_graph_for_exec(node, 01280 prebuilt->trx, 01281 prebuilt->heap)); 01282 prebuilt->upd_graph->state = QUE_FORK_ACTIVE; 01283 } 01284 01285 return(prebuilt->upd_node->update); 01286 } 01287 01288 /************************************************************************* 01289 Does an update or delete of a row for MySQL. */ 01290 01291 int 01292 row_update_for_mysql( 01293 /*=================*/ 01294 /* out: error code or DB_SUCCESS */ 01295 byte* mysql_rec, /* in: the row to be updated, in 01296 the MySQL format */ 01297 row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL 01298 handle */ 01299 { 01300 trx_savept_t savept; 01301 ulint err; 01302 que_thr_t* thr; 01303 ibool was_lock_wait; 01304 dict_index_t* clust_index; 01305 /* ulint ref_len; */ 01306 upd_node_t* node; 01307 dict_table_t* table = prebuilt->table; 01308 trx_t* trx = prebuilt->trx; 01309 01310 ut_ad(prebuilt && trx); 01311 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 01312 UT_NOT_USED(mysql_rec); 01313 01314 if (prebuilt->table->ibd_file_missing) { 01315 ut_print_timestamp(stderr); 01316 fprintf(stderr, " InnoDB: Error:\n" 01317 "InnoDB: MySQL is trying to use a table handle but the .ibd file for\n" 01318 "InnoDB: table %s does not exist.\n" 01319 "InnoDB: Have you deleted the .ibd file from the database directory under\n" 01320 "InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n" 01321 "InnoDB: Look from\n" 01322 "http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" 01323 "InnoDB: how you can resolve the problem.\n", 01324 prebuilt->table->name); 01325 return(DB_ERROR); 01326 } 01327 01328 if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { 01329 fprintf(stderr, 01330 "InnoDB: Error: trying to free a corrupt\n" 01331 "InnoDB: table handle. Magic n %lu, table name", 01332 (ulong) prebuilt->magic_n); 01333 ut_print_name(stderr, prebuilt->trx, TRUE, 01334 prebuilt->table->name); 01335 putc('\n', stderr); 01336 01337 mem_analyze_corruption(prebuilt); 01338 01339 ut_error; 01340 } 01341 01342 if (srv_created_new_raw || srv_force_recovery) { 01343 fputs( 01344 "InnoDB: A new raw disk partition was initialized or\n" 01345 "InnoDB: innodb_force_recovery is on: we do not allow\n" 01346 "InnoDB: database modifications by the user. Shut down\n" 01347 "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n" 01348 "InnoDB: with raw, and innodb_force_... is removed.\n", 01349 stderr); 01350 01351 return(DB_ERROR); 01352 } 01353 01354 trx->op_info = "updating or deleting"; 01355 01356 row_mysql_delay_if_needed(); 01357 01358 trx_start_if_not_started(trx); 01359 01360 node = prebuilt->upd_node; 01361 01362 clust_index = dict_table_get_first_index(table); 01363 01364 if (prebuilt->pcur->btr_cur.index == clust_index) { 01365 btr_pcur_copy_stored_position(node->pcur, prebuilt->pcur); 01366 } else { 01367 btr_pcur_copy_stored_position(node->pcur, 01368 prebuilt->clust_pcur); 01369 } 01370 01371 ut_a(node->pcur->rel_pos == BTR_PCUR_ON); 01372 01373 /* MySQL seems to call rnd_pos before updating each row it 01374 has cached: we can get the correct cursor position from 01375 prebuilt->pcur; NOTE that we cannot build the row reference 01376 from mysql_rec if the clustered index was automatically 01377 generated for the table: MySQL does not know anything about 01378 the row id used as the clustered index key */ 01379 01380 savept = trx_savept_take(trx); 01381 01382 thr = que_fork_get_first_thr(prebuilt->upd_graph); 01383 01384 node->state = UPD_NODE_UPDATE_CLUSTERED; 01385 01386 ut_ad(!prebuilt->sql_stat_start); 01387 01388 que_thr_move_to_run_state_for_mysql(thr, trx); 01389 01390 run_again: 01391 thr->run_node = node; 01392 thr->prev_node = node; 01393 01394 row_upd_step(thr); 01395 01396 err = trx->error_state; 01397 01398 if (err != DB_SUCCESS) { 01399 que_thr_stop_for_mysql(thr); 01400 01401 if (err == DB_RECORD_NOT_FOUND) { 01402 trx->error_state = DB_SUCCESS; 01403 trx->op_info = ""; 01404 01405 return((int) err); 01406 } 01407 01408 thr->lock_state= QUE_THR_LOCK_ROW; 01409 was_lock_wait = row_mysql_handle_errors(&err, trx, thr, 01410 &savept); 01411 thr->lock_state= QUE_THR_LOCK_NOLOCK; 01412 01413 if (was_lock_wait) { 01414 goto run_again; 01415 } 01416 01417 trx->op_info = ""; 01418 01419 return((int) err); 01420 } 01421 01422 que_thr_stop_for_mysql_no_error(thr, trx); 01423 01424 if (node->is_delete) { 01425 if (prebuilt->table->stat_n_rows > 0) { 01426 prebuilt->table->stat_n_rows--; 01427 } 01428 01429 srv_n_rows_deleted++; 01430 } else { 01431 srv_n_rows_updated++; 01432 } 01433 01434 row_update_statistics_if_needed(prebuilt->table); 01435 01436 trx->op_info = ""; 01437 01438 return((int) err); 01439 } 01440 01441 /************************************************************************* 01442 This can only be used when srv_locks_unsafe_for_binlog is TRUE or 01443 this session is using a READ COMMITTED isolation level. Before 01444 calling this function we must use trx_reset_new_rec_lock_info() and 01445 trx_register_new_rec_lock() to store the information which new record locks 01446 really were set. This function removes a newly set lock under prebuilt->pcur, 01447 and also under prebuilt->clust_pcur. Currently, this is only used and tested 01448 in the case of an UPDATE or a DELETE statement, where the row lock is of the 01449 LOCK_X type. 01450 Thus, this implements a 'mini-rollback' that releases the latest record 01451 locks we set. */ 01452 01453 int 01454 row_unlock_for_mysql( 01455 /*=================*/ 01456 /* out: error code or DB_SUCCESS */ 01457 row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL 01458 handle */ 01459 ibool has_latches_on_recs)/* TRUE if called so that we have 01460 the latches on the records under pcur 01461 and clust_pcur, and we do not need to 01462 reposition the cursors. */ 01463 { 01464 dict_index_t* index; 01465 btr_pcur_t* pcur = prebuilt->pcur; 01466 btr_pcur_t* clust_pcur = prebuilt->clust_pcur; 01467 trx_t* trx = prebuilt->trx; 01468 rec_t* rec; 01469 mtr_t mtr; 01470 01471 ut_ad(prebuilt && trx); 01472 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 01473 01474 if (!(srv_locks_unsafe_for_binlog 01475 || trx->isolation_level == TRX_ISO_READ_COMMITTED)) { 01476 01477 fprintf(stderr, 01478 "InnoDB: Error: calling row_unlock_for_mysql though\n" 01479 "InnoDB: srv_locks_unsafe_for_binlog is FALSE and\n" 01480 "InnoDB: this session is not using READ COMMITTED isolation level.\n"); 01481 01482 return(DB_SUCCESS); 01483 } 01484 01485 trx->op_info = "unlock_row"; 01486 01487 index = btr_pcur_get_btr_cur(pcur)->index; 01488 01489 if (index != NULL && trx_new_rec_locks_contain(trx, index)) { 01490 01491 mtr_start(&mtr); 01492 01493 /* Restore the cursor position and find the record */ 01494 01495 if (!has_latches_on_recs) { 01496 btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr); 01497 } 01498 01499 rec = btr_pcur_get_rec(pcur); 01500 01501 lock_rec_unlock(trx, rec, prebuilt->select_lock_type); 01502 01503 mtr_commit(&mtr); 01504 01505 /* If the search was done through the clustered index, then 01506 we have not used clust_pcur at all, and we must NOT try to 01507 reset locks on clust_pcur. The values in clust_pcur may be 01508 garbage! */ 01509 01510 if (index->type & DICT_CLUSTERED) { 01511 01512 goto func_exit; 01513 } 01514 } 01515 01516 index = btr_pcur_get_btr_cur(clust_pcur)->index; 01517 01518 if (index != NULL && trx_new_rec_locks_contain(trx, index)) { 01519 01520 mtr_start(&mtr); 01521 01522 /* Restore the cursor position and find the record */ 01523 01524 if (!has_latches_on_recs) { 01525 btr_pcur_restore_position(BTR_SEARCH_LEAF, clust_pcur, 01526 &mtr); 01527 } 01528 01529 rec = btr_pcur_get_rec(clust_pcur); 01530 01531 lock_rec_unlock(trx, rec, prebuilt->select_lock_type); 01532 01533 mtr_commit(&mtr); 01534 } 01535 01536 func_exit: 01537 trx->op_info = ""; 01538 01539 return(DB_SUCCESS); 01540 } 01541 01542 /************************************************************************** 01543 Does a cascaded delete or set null in a foreign key operation. */ 01544 01545 ulint 01546 row_update_cascade_for_mysql( 01547 /*=========================*/ 01548 /* out: error code or DB_SUCCESS */ 01549 que_thr_t* thr, /* in: query thread */ 01550 upd_node_t* node, /* in: update node used in the cascade 01551 or set null operation */ 01552 dict_table_t* table) /* in: table where we do the operation */ 01553 { 01554 ulint err; 01555 trx_t* trx; 01556 01557 trx = thr_get_trx(thr); 01558 run_again: 01559 thr->run_node = node; 01560 thr->prev_node = node; 01561 01562 row_upd_step(thr); 01563 01564 err = trx->error_state; 01565 01566 /* Note that the cascade node is a subnode of another InnoDB 01567 query graph node. We do a normal lock wait in this node, but 01568 all errors are handled by the parent node. */ 01569 01570 if (err == DB_LOCK_WAIT) { 01571 /* Handle lock wait here */ 01572 01573 que_thr_stop_for_mysql(thr); 01574 01575 srv_suspend_mysql_thread(thr); 01576 01577 /* Note that a lock wait may also end in a lock wait timeout, 01578 or this transaction is picked as a victim in selective 01579 deadlock resolution */ 01580 01581 if (trx->error_state != DB_SUCCESS) { 01582 01583 return(trx->error_state); 01584 } 01585 01586 /* Retry operation after a normal lock wait */ 01587 01588 goto run_again; 01589 } 01590 01591 if (err != DB_SUCCESS) { 01592 01593 return(err); 01594 } 01595 01596 if (node->is_delete) { 01597 if (table->stat_n_rows > 0) { 01598 table->stat_n_rows--; 01599 } 01600 01601 srv_n_rows_deleted++; 01602 } else { 01603 srv_n_rows_updated++; 01604 } 01605 01606 row_update_statistics_if_needed(table); 01607 01608 return(err); 01609 } 01610 01611 /************************************************************************* 01612 Checks if a table is such that we automatically created a clustered 01613 index on it (on row id). */ 01614 01615 ibool 01616 row_table_got_default_clust_index( 01617 /*==============================*/ 01618 dict_table_t* table) 01619 { 01620 dict_index_t* clust_index; 01621 01622 clust_index = dict_table_get_first_index(table); 01623 01624 if (dtype_get_mtype(dict_index_get_nth_type(clust_index, 0)) 01625 == DATA_SYS) { 01626 return(TRUE); 01627 } 01628 01629 return(FALSE); 01630 } 01631 01632 /************************************************************************* 01633 Calculates the key number used inside MySQL for an Innobase index. We have 01634 to take into account if we generated a default clustered index for the table */ 01635 01636 ulint 01637 row_get_mysql_key_number_for_index( 01638 /*===============================*/ 01639 dict_index_t* index) 01640 { 01641 dict_index_t* ind; 01642 ulint i; 01643 01644 ut_a(index); 01645 01646 i = 0; 01647 ind = dict_table_get_first_index(index->table); 01648 01649 while (index != ind) { 01650 ind = dict_table_get_next_index(ind); 01651 i++; 01652 } 01653 01654 if (row_table_got_default_clust_index(index->table)) { 01655 ut_a(i > 0); 01656 i--; 01657 } 01658 01659 return(i); 01660 } 01661 01662 /************************************************************************* 01663 Recovers an orphaned tmp table inside InnoDB by renaming it. In the table 01664 name #sql becomes rsql, and "_recover_innodb_tmp_table" is catenated to 01665 the end of name. table->name should be of the form 01666 "dbname/rsql..._recover_innodb_tmp_table". This renames a table whose 01667 name is "#sql..." */ 01668 static 01669 int 01670 row_mysql_recover_tmp_table( 01671 /*========================*/ 01672 /* out: error code or DB_SUCCESS */ 01673 dict_table_t* table, /* in: table definition */ 01674 trx_t* trx) /* in: transaction handle */ 01675 { 01676 const char* ptr = strstr(table->name, "/rsql"); 01677 01678 if (!ptr) { 01679 /* table name does not begin with "/rsql" */ 01680 dict_mem_table_free(table); 01681 trx_commit_for_mysql(trx); 01682 01683 return(DB_ERROR); 01684 } 01685 else { 01686 int status; 01687 int namelen = (int) strlen(table->name); 01688 char* old_name = mem_strdupl(table->name, namelen); 01689 /* replace "rsql" with "#sql" */ 01690 old_name[ptr - table->name + 1] = '#'; 01691 /* remove "_recover_innodb_tmp_table" suffix */ 01692 ut_ad(namelen > (int) sizeof S_recover_innodb_tmp_table); 01693 ut_ad(!strcmp(old_name + namelen + 1 - 01694 sizeof S_recover_innodb_tmp_table, 01695 S_recover_innodb_tmp_table)); 01696 old_name[namelen + 1 - sizeof S_recover_innodb_tmp_table] = 0; 01697 status = row_rename_table_for_mysql(old_name, 01698 table->name, trx); 01699 mem_free(old_name); 01700 return(status); 01701 } 01702 } 01703 01704 /************************************************************************* 01705 Locks the data dictionary in shared mode from modifications, for performing 01706 foreign key check, rollback, or other operation invisible to MySQL. */ 01707 01708 void 01709 row_mysql_freeze_data_dictionary( 01710 /*=============================*/ 01711 trx_t* trx) /* in: transaction */ 01712 { 01713 ut_a(trx->dict_operation_lock_mode == 0); 01714 01715 rw_lock_s_lock(&dict_operation_lock); 01716 01717 trx->dict_operation_lock_mode = RW_S_LATCH; 01718 } 01719 01720 /************************************************************************* 01721 Unlocks the data dictionary shared lock. */ 01722 01723 void 01724 row_mysql_unfreeze_data_dictionary( 01725 /*===============================*/ 01726 trx_t* trx) /* in: transaction */ 01727 { 01728 ut_a(trx->dict_operation_lock_mode == RW_S_LATCH); 01729 01730 rw_lock_s_unlock(&dict_operation_lock); 01731 01732 trx->dict_operation_lock_mode = 0; 01733 } 01734 01735 /************************************************************************* 01736 Locks the data dictionary exclusively for performing a table create or other 01737 data dictionary modification operation. */ 01738 01739 void 01740 row_mysql_lock_data_dictionary( 01741 /*===========================*/ 01742 trx_t* trx) /* in: transaction */ 01743 { 01744 ut_a(trx->dict_operation_lock_mode == 0 01745 || trx->dict_operation_lock_mode == RW_X_LATCH); 01746 01747 /* Serialize data dictionary operations with dictionary mutex: 01748 no deadlocks or lock waits can occur then in these operations */ 01749 01750 rw_lock_x_lock(&dict_operation_lock); 01751 trx->dict_operation_lock_mode = RW_X_LATCH; 01752 01753 mutex_enter(&(dict_sys->mutex)); 01754 } 01755 01756 /************************************************************************* 01757 Unlocks the data dictionary exclusive lock. */ 01758 01759 void 01760 row_mysql_unlock_data_dictionary( 01761 /*=============================*/ 01762 trx_t* trx) /* in: transaction */ 01763 { 01764 ut_a(trx->dict_operation_lock_mode == RW_X_LATCH); 01765 01766 /* Serialize data dictionary operations with dictionary mutex: 01767 no deadlocks can occur then in these operations */ 01768 01769 mutex_exit(&(dict_sys->mutex)); 01770 rw_lock_x_unlock(&dict_operation_lock); 01771 01772 trx->dict_operation_lock_mode = 0; 01773 } 01774 01775 /************************************************************************* 01776 Does a table creation operation for MySQL. If the name of the table 01777 to be created is equal with one of the predefined magic table names, 01778 then this also starts printing the corresponding monitor output by 01779 the master thread. */ 01780 01781 int 01782 row_create_table_for_mysql( 01783 /*=======================*/ 01784 /* out: error code or DB_SUCCESS */ 01785 dict_table_t* table, /* in: table definition */ 01786 trx_t* trx) /* in: transaction handle */ 01787 { 01788 tab_node_t* node; 01789 mem_heap_t* heap; 01790 que_thr_t* thr; 01791 const char* table_name; 01792 ulint table_name_len; 01793 ulint err; 01794 ulint i; 01795 01796 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 01797 #ifdef UNIV_SYNC_DEBUG 01798 ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); 01799 ut_ad(mutex_own(&(dict_sys->mutex))); 01800 #endif /* UNIV_SYNC_DEBUG */ 01801 ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH); 01802 01803 if (srv_created_new_raw) { 01804 fputs( 01805 "InnoDB: A new raw disk partition was initialized or\n" 01806 "InnoDB: innodb_force_recovery is on: we do not allow\n" 01807 "InnoDB: database modifications by the user. Shut down\n" 01808 "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n" 01809 "InnoDB: with raw, and innodb_force_... is removed.\n", 01810 stderr); 01811 01812 dict_mem_table_free(table); 01813 trx_commit_for_mysql(trx); 01814 01815 return(DB_ERROR); 01816 } 01817 01818 trx->op_info = "creating table"; 01819 01820 if (row_mysql_is_system_table(table->name)) { 01821 01822 fprintf(stderr, 01823 "InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n" 01824 "InnoDB: MySQL system tables must be of the MyISAM type!\n", 01825 table->name); 01826 01827 dict_mem_table_free(table); 01828 trx_commit_for_mysql(trx); 01829 01830 return(DB_ERROR); 01831 } 01832 01833 /* Check that no reserved column names are used. */ 01834 for (i = 0; i < dict_table_get_n_user_cols(table); i++) { 01835 dict_col_t* col = dict_table_get_nth_col(table, i); 01836 01837 if (dict_col_name_is_reserved(col->name)) { 01838 01839 dict_mem_table_free(table); 01840 trx_commit_for_mysql(trx); 01841 01842 return(DB_ERROR); 01843 } 01844 } 01845 01846 trx_start_if_not_started(trx); 01847 01848 if (row_mysql_is_recovered_tmp_table(table->name)) { 01849 01850 /* MySQL prevents accessing of tables whose name begins 01851 with #sql, that is temporary tables. If mysqld crashes in 01852 the middle of an ALTER TABLE, we may get an orphaned 01853 #sql-table in the tablespace. We have here a special 01854 mechanism to recover such tables by renaming them to 01855 rsql... */ 01856 01857 return(row_mysql_recover_tmp_table(table, trx)); 01858 } 01859 01860 /* The table name is prefixed with the database name and a '/'. 01861 Certain table names starting with 'innodb_' have their special 01862 meaning regardless of the database name. Thus, we need to 01863 ignore the database name prefix in the comparisons. */ 01864 table_name = strchr(table->name, '/'); 01865 ut_a(table_name); 01866 table_name++; 01867 table_name_len = strlen(table_name) + 1; 01868 01869 if (table_name_len == sizeof S_innodb_monitor 01870 && !memcmp(table_name, S_innodb_monitor, 01871 sizeof S_innodb_monitor)) { 01872 01873 /* Table equals "innodb_monitor": 01874 start monitor prints */ 01875 01876 srv_print_innodb_monitor = TRUE; 01877 01878 /* The lock timeout monitor thread also takes care 01879 of InnoDB monitor prints */ 01880 01881 os_event_set(srv_lock_timeout_thread_event); 01882 } else if (table_name_len == sizeof S_innodb_lock_monitor 01883 && !memcmp(table_name, S_innodb_lock_monitor, 01884 sizeof S_innodb_lock_monitor)) { 01885 01886 srv_print_innodb_monitor = TRUE; 01887 srv_print_innodb_lock_monitor = TRUE; 01888 os_event_set(srv_lock_timeout_thread_event); 01889 } else if (table_name_len == sizeof S_innodb_tablespace_monitor 01890 && !memcmp(table_name, S_innodb_tablespace_monitor, 01891 sizeof S_innodb_tablespace_monitor)) { 01892 01893 srv_print_innodb_tablespace_monitor = TRUE; 01894 os_event_set(srv_lock_timeout_thread_event); 01895 } else if (table_name_len == sizeof S_innodb_table_monitor 01896 && !memcmp(table_name, S_innodb_table_monitor, 01897 sizeof S_innodb_table_monitor)) { 01898 01899 srv_print_innodb_table_monitor = TRUE; 01900 os_event_set(srv_lock_timeout_thread_event); 01901 } else if (table_name_len == sizeof S_innodb_mem_validate 01902 && !memcmp(table_name, S_innodb_mem_validate, 01903 sizeof S_innodb_mem_validate)) { 01904 /* We define here a debugging feature intended for 01905 developers */ 01906 01907 fputs("Validating InnoDB memory:\n" 01908 "to use this feature you must compile InnoDB with\n" 01909 "UNIV_MEM_DEBUG defined in univ.i and the server must be\n" 01910 "quiet because allocation from a mem heap is not protected\n" 01911 "by any semaphore.\n", stderr); 01912 #ifdef UNIV_MEM_DEBUG 01913 ut_a(mem_validate()); 01914 fputs("Memory validated\n", stderr); 01915 #else /* UNIV_MEM_DEBUG */ 01916 fputs("Memory NOT validated (recompile with UNIV_MEM_DEBUG)\n", 01917 stderr); 01918 #endif /* UNIV_MEM_DEBUG */ 01919 } 01920 01921 heap = mem_heap_create(512); 01922 01923 trx->dict_operation = TRUE; 01924 01925 node = tab_create_graph_create(table, heap); 01926 01927 thr = pars_complete_graph_for_exec(node, trx, heap); 01928 01929 ut_a(thr == que_fork_start_command(que_node_get_parent(thr))); 01930 que_run_threads(thr); 01931 01932 err = trx->error_state; 01933 01934 if (err != DB_SUCCESS) { 01935 /* We have special error handling here */ 01936 01937 trx->error_state = DB_SUCCESS; 01938 01939 trx_general_rollback_for_mysql(trx, FALSE, NULL); 01940 01941 if (err == DB_OUT_OF_FILE_SPACE) { 01942 ut_print_timestamp(stderr); 01943 01944 fputs(" InnoDB: Warning: cannot create table ", 01945 stderr); 01946 ut_print_name(stderr, trx, TRUE, table->name); 01947 fputs(" because tablespace full\n", stderr); 01948 01949 if (dict_table_get_low(table->name)) { 01950 01951 row_drop_table_for_mysql(table->name, trx, 01952 FALSE); 01953 } 01954 01955 } else if (err == DB_DUPLICATE_KEY) { 01956 ut_print_timestamp(stderr); 01957 01958 fputs(" InnoDB: Error: table ", stderr); 01959 ut_print_name(stderr, trx, TRUE, table->name); 01960 fputs(" already exists in InnoDB internal\n" 01961 "InnoDB: data dictionary. Have you deleted the .frm file\n" 01962 "InnoDB: and not used DROP TABLE? Have you used DROP DATABASE\n" 01963 "InnoDB: for InnoDB tables in MySQL version <= 3.23.43?\n" 01964 "InnoDB: See the Restrictions section of the InnoDB manual.\n" 01965 "InnoDB: You can drop the orphaned table inside InnoDB by\n" 01966 "InnoDB: creating an InnoDB table with the same name in another\n" 01967 "InnoDB: database and copying the .frm file to the current database.\n" 01968 "InnoDB: Then MySQL thinks the table exists, and DROP TABLE will\n" 01969 "InnoDB: succeed.\n" 01970 "InnoDB: You can look for further help from\n" 01971 "InnoDB: http://dev.mysql.com/doc/mysql/en/" 01972 "InnoDB_troubleshooting_datadict.html\n", stderr); 01973 } 01974 01975 /* We may also get err == DB_ERROR if the .ibd file for the 01976 table already exists */ 01977 01978 trx->error_state = DB_SUCCESS; 01979 } 01980 01981 que_graph_free((que_t*) que_node_get_parent(thr)); 01982 01983 trx->op_info = ""; 01984 01985 return((int) err); 01986 } 01987 01988 /************************************************************************* 01989 Does an index creation operation for MySQL. TODO: currently failure 01990 to create an index results in dropping the whole table! This is no problem 01991 currently as all indexes must be created at the same time as the table. */ 01992 01993 int 01994 row_create_index_for_mysql( 01995 /*=======================*/ 01996 /* out: error number or DB_SUCCESS */ 01997 dict_index_t* index, /* in: index definition */ 01998 trx_t* trx, /* in: transaction handle */ 01999 const ulint* field_lengths) /* in: if not NULL, must contain 02000 dict_index_get_n_fields(index) 02001 actual field lengths for the 02002 index columns, which are 02003 then checked for not being too 02004 large. */ 02005 { 02006 ind_node_t* node; 02007 mem_heap_t* heap; 02008 que_thr_t* thr; 02009 ulint err; 02010 ulint i, j; 02011 ulint len; 02012 02013 #ifdef UNIV_SYNC_DEBUG 02014 ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); 02015 ut_ad(mutex_own(&(dict_sys->mutex))); 02016 #endif /* UNIV_SYNC_DEBUG */ 02017 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 02018 02019 trx->op_info = "creating index"; 02020 02021 trx_start_if_not_started(trx); 02022 02023 /* Check that the same column does not appear twice in the index. 02024 Starting from 4.0.14, InnoDB should be able to cope with that, but 02025 safer not to allow them. */ 02026 02027 for (i = 0; i < dict_index_get_n_fields(index); i++) { 02028 for (j = 0; j < i; j++) { 02029 if (0 == ut_strcmp( 02030 dict_index_get_nth_field(index, j)->name, 02031 dict_index_get_nth_field(index, i)->name)) { 02032 02033 ut_print_timestamp(stderr); 02034 02035 fputs(" InnoDB: Error: column ", stderr); 02036 ut_print_name(stderr, trx, FALSE, 02037 dict_index_get_nth_field(index, i)->name); 02038 fputs(" appears twice in ", stderr); 02039 dict_index_name_print(stderr, trx, index); 02040 fputs("\n" 02041 "InnoDB: This is not allowed in InnoDB.\n", 02042 stderr); 02043 02044 err = DB_COL_APPEARS_TWICE_IN_INDEX; 02045 02046 goto error_handling; 02047 } 02048 } 02049 02050 /* Check also that prefix_len and actual length 02051 < DICT_MAX_INDEX_COL_LEN */ 02052 02053 len = dict_index_get_nth_field(index, i)->prefix_len; 02054 02055 if (field_lengths) { 02056 len = ut_max(len, field_lengths[i]); 02057 } 02058 02059 if (len >= DICT_MAX_INDEX_COL_LEN) { 02060 err = DB_TOO_BIG_RECORD; 02061 02062 goto error_handling; 02063 } 02064 } 02065 02066 if (row_mysql_is_recovered_tmp_table(index->table_name)) { 02067 02068 return(DB_SUCCESS); 02069 } 02070 02071 heap = mem_heap_create(512); 02072 02073 trx->dict_operation = TRUE; 02074 02075 /* Note that the space id where we store the index is inherited from 02076 the table in dict_build_index_def_step() in dict0crea.c. */ 02077 02078 node = ind_create_graph_create(index, heap); 02079 02080 thr = pars_complete_graph_for_exec(node, trx, heap); 02081 02082 ut_a(thr == que_fork_start_command(que_node_get_parent(thr))); 02083 que_run_threads(thr); 02084 02085 err = trx->error_state; 02086 02087 que_graph_free((que_t*) que_node_get_parent(thr)); 02088 02089 error_handling: 02090 if (err != DB_SUCCESS) { 02091 /* We have special error handling here */ 02092 02093 trx->error_state = DB_SUCCESS; 02094 02095 trx_general_rollback_for_mysql(trx, FALSE, NULL); 02096 02097 row_drop_table_for_mysql(index->table_name, trx, FALSE); 02098 02099 trx->error_state = DB_SUCCESS; 02100 } 02101 02102 trx->op_info = ""; 02103 02104 return((int) err); 02105 } 02106 02107 /************************************************************************* 02108 Scans a table create SQL string and adds to the data dictionary 02109 the foreign key constraints declared in the string. This function 02110 should be called after the indexes for a table have been created. 02111 Each foreign key constraint must be accompanied with indexes in 02112 bot participating tables. The indexes are allowed to contain more 02113 fields than mentioned in the constraint. Check also that foreign key 02114 constraints which reference this table are ok. */ 02115 02116 int 02117 row_table_add_foreign_constraints( 02118 /*==============================*/ 02119 /* out: error code or DB_SUCCESS */ 02120 trx_t* trx, /* in: transaction */ 02121 const char* sql_string, /* in: table create statement where 02122 foreign keys are declared like: 02123 FOREIGN KEY (a, b) REFERENCES table2(c, d), 02124 table2 can be written also with the 02125 database name before it: test.table2 */ 02126 const char* name, /* in: table full name in the 02127 normalized form 02128 database_name/table_name */ 02129 ibool reject_fks) /* in: if TRUE, fail with error 02130 code DB_CANNOT_ADD_CONSTRAINT if 02131 any foreign keys are found. */ 02132 { 02133 ulint err; 02134 02135 #ifdef UNIV_SYNC_DEBUG 02136 ut_ad(mutex_own(&(dict_sys->mutex))); 02137 ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); 02138 #endif /* UNIV_SYNC_DEBUG */ 02139 ut_a(sql_string); 02140 02141 trx->op_info = "adding foreign keys"; 02142 02143 trx_start_if_not_started(trx); 02144 02145 if (row_mysql_is_recovered_tmp_table(name)) { 02146 02147 return(DB_SUCCESS); 02148 } 02149 02150 trx->dict_operation = TRUE; 02151 02152 err = dict_create_foreign_constraints(trx, sql_string, name, 02153 reject_fks); 02154 02155 if (err == DB_SUCCESS) { 02156 /* Check that also referencing constraints are ok */ 02157 err = dict_load_foreigns(name, TRUE); 02158 } 02159 02160 if (err != DB_SUCCESS) { 02161 /* We have special error handling here */ 02162 02163 trx->error_state = DB_SUCCESS; 02164 02165 trx_general_rollback_for_mysql(trx, FALSE, NULL); 02166 02167 row_drop_table_for_mysql(name, trx, FALSE); 02168 02169 trx->error_state = DB_SUCCESS; 02170 } 02171 02172 return((int) err); 02173 } 02174 02175 /************************************************************************* 02176 Drops a table for MySQL as a background operation. MySQL relies on Unix 02177 in ALTER TABLE to the fact that the table handler does not remove the 02178 table before all handles to it has been removed. Furhermore, the MySQL's 02179 call to drop table must be non-blocking. Therefore we do the drop table 02180 as a background operation, which is taken care of by the master thread 02181 in srv0srv.c. */ 02182 static 02183 int 02184 row_drop_table_for_mysql_in_background( 02185 /*===================================*/ 02186 /* out: error code or DB_SUCCESS */ 02187 const char* name) /* in: table name */ 02188 { 02189 ulint error; 02190 trx_t* trx; 02191 02192 trx = trx_allocate_for_background(); 02193 02194 /* If the original transaction was dropping a table referenced by 02195 foreign keys, we must set the following to be able to drop the 02196 table: */ 02197 02198 trx->check_foreigns = FALSE; 02199 02200 /* fputs("InnoDB: Error: Dropping table ", stderr); 02201 ut_print_name(stderr, trx, TRUE, name); 02202 fputs(" in background drop list\n", stderr); */ 02203 02204 /* Try to drop the table in InnoDB */ 02205 02206 error = row_drop_table_for_mysql(name, trx, FALSE); 02207 02208 /* Flush the log to reduce probability that the .frm files and 02209 the InnoDB data dictionary get out-of-sync if the user runs 02210 with innodb_flush_log_at_trx_commit = 0 */ 02211 02212 log_buffer_flush_to_disk(); 02213 02214 trx_commit_for_mysql(trx); 02215 02216 trx_free_for_background(trx); 02217 02218 return((int) error); 02219 } 02220 02221 /************************************************************************* 02222 The master thread in srv0srv.c calls this regularly to drop tables which 02223 we must drop in background after queries to them have ended. Such lazy 02224 dropping of tables is needed in ALTER TABLE on Unix. */ 02225 02226 ulint 02227 row_drop_tables_for_mysql_in_background(void) 02228 /*=========================================*/ 02229 /* out: how many tables dropped 02230 + remaining tables in list */ 02231 { 02232 row_mysql_drop_t* drop; 02233 dict_table_t* table; 02234 ulint n_tables; 02235 ulint n_tables_dropped = 0; 02236 loop: 02237 mutex_enter(&kernel_mutex); 02238 02239 if (!row_mysql_drop_list_inited) { 02240 02241 UT_LIST_INIT(row_mysql_drop_list); 02242 row_mysql_drop_list_inited = TRUE; 02243 } 02244 02245 drop = UT_LIST_GET_FIRST(row_mysql_drop_list); 02246 02247 n_tables = UT_LIST_GET_LEN(row_mysql_drop_list); 02248 02249 mutex_exit(&kernel_mutex); 02250 02251 if (drop == NULL) { 02252 /* All tables dropped */ 02253 02254 return(n_tables + n_tables_dropped); 02255 } 02256 02257 mutex_enter(&(dict_sys->mutex)); 02258 table = dict_table_get_low(drop->table_name); 02259 mutex_exit(&(dict_sys->mutex)); 02260 02261 if (table == NULL) { 02262 /* If for some reason the table has already been dropped 02263 through some other mechanism, do not try to drop it */ 02264 02265 goto already_dropped; 02266 } 02267 02268 if (DB_SUCCESS != row_drop_table_for_mysql_in_background( 02269 drop->table_name)) { 02270 /* If the DROP fails for some table, we return, and let the 02271 main thread retry later */ 02272 02273 return(n_tables + n_tables_dropped); 02274 } 02275 02276 n_tables_dropped++; 02277 02278 already_dropped: 02279 mutex_enter(&kernel_mutex); 02280 02281 UT_LIST_REMOVE(row_mysql_drop_list, row_mysql_drop_list, drop); 02282 02283 ut_print_timestamp(stderr); 02284 fprintf(stderr, 02285 " InnoDB: Dropped table %s in background drop queue.\n", 02286 drop->table_name); 02287 02288 mem_free(drop->table_name); 02289 02290 mem_free(drop); 02291 02292 mutex_exit(&kernel_mutex); 02293 02294 goto loop; 02295 } 02296 02297 /************************************************************************* 02298 Get the background drop list length. NOTE: the caller must own the kernel 02299 mutex! */ 02300 02301 ulint 02302 row_get_background_drop_list_len_low(void) 02303 /*======================================*/ 02304 /* out: how many tables in list */ 02305 { 02306 #ifdef UNIV_SYNC_DEBUG 02307 ut_ad(mutex_own(&kernel_mutex)); 02308 #endif /* UNIV_SYNC_DEBUG */ 02309 02310 if (!row_mysql_drop_list_inited) { 02311 02312 UT_LIST_INIT(row_mysql_drop_list); 02313 row_mysql_drop_list_inited = TRUE; 02314 } 02315 02316 return(UT_LIST_GET_LEN(row_mysql_drop_list)); 02317 } 02318 02319 /************************************************************************* 02320 If a table is not yet in the drop list, adds the table to the list of tables 02321 which the master thread drops in background. We need this on Unix because in 02322 ALTER TABLE MySQL may call drop table even if the table has running queries on 02323 it. Also, if there are running foreign key checks on the table, we drop the 02324 table lazily. */ 02325 static 02326 ibool 02327 row_add_table_to_background_drop_list( 02328 /*==================================*/ 02329 /* out: TRUE if the table was not yet in the 02330 drop list, and was added there */ 02331 dict_table_t* table) /* in: table */ 02332 { 02333 row_mysql_drop_t* drop; 02334 02335 mutex_enter(&kernel_mutex); 02336 02337 if (!row_mysql_drop_list_inited) { 02338 02339 UT_LIST_INIT(row_mysql_drop_list); 02340 row_mysql_drop_list_inited = TRUE; 02341 } 02342 02343 /* Look if the table already is in the drop list */ 02344 drop = UT_LIST_GET_FIRST(row_mysql_drop_list); 02345 02346 while (drop != NULL) { 02347 if (strcmp(drop->table_name, table->name) == 0) { 02348 /* Already in the list */ 02349 02350 mutex_exit(&kernel_mutex); 02351 02352 return(FALSE); 02353 } 02354 02355 drop = UT_LIST_GET_NEXT(row_mysql_drop_list, drop); 02356 } 02357 02358 drop = mem_alloc(sizeof(row_mysql_drop_t)); 02359 02360 drop->table_name = mem_strdup(table->name); 02361 02362 UT_LIST_ADD_LAST(row_mysql_drop_list, row_mysql_drop_list, drop); 02363 02364 /* fputs("InnoDB: Adding table ", stderr); 02365 ut_print_name(stderr, trx, TRUE, drop->table_name); 02366 fputs(" to background drop list\n", stderr); */ 02367 02368 mutex_exit(&kernel_mutex); 02369 02370 return(TRUE); 02371 } 02372 02373 #ifndef UNIV_HOTBACKUP 02374 /************************************************************************* 02375 Discards the tablespace of a table which stored in an .ibd file. Discarding 02376 means that this function deletes the .ibd file and assigns a new table id for 02377 the table. Also the flag table->ibd_file_missing is set TRUE. */ 02378 02379 int 02380 row_discard_tablespace_for_mysql( 02381 /*=============================*/ 02382 /* out: error code or DB_SUCCESS */ 02383 const char* name, /* in: table name */ 02384 trx_t* trx) /* in: transaction handle */ 02385 { 02386 dict_foreign_t* foreign; 02387 dulint new_id; 02388 dict_table_t* table; 02389 ibool success; 02390 ulint err; 02391 pars_info_t* info = NULL; 02392 02393 /* How do we prevent crashes caused by ongoing operations on the table? Old 02394 operations could try to access non-existent pages. 02395 02396 1) SQL queries, INSERT, SELECT, ...: we must get an exclusive MySQL table lock 02397 on the table before we can do DISCARD TABLESPACE. Then there are no running 02398 queries on the table. 02399 2) Purge and rollback: we assign a new table id for the table. Since purge and 02400 rollback look for the table based on the table id, they see the table as 02401 'dropped' and discard their operations. 02402 3) Insert buffer: we remove all entries for the tablespace in the insert 02403 buffer tree; as long as the tablespace mem object does not exist, ongoing 02404 insert buffer page merges are discarded in buf0rea.c. If we recreate the 02405 tablespace mem object with IMPORT TABLESPACE later, then the tablespace will 02406 have the same id, but the tablespace_version field in the mem object is 02407 different, and ongoing old insert buffer page merges get discarded. 02408 4) Linear readahead and random readahead: we use the same method as in 3) to 02409 discard ongoing operations. 02410 5) FOREIGN KEY operations: if table->n_foreign_key_checks_running > 0, we 02411 do not allow the discard. We also reserve the data dictionary latch. */ 02412 02413 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 02414 02415 trx->op_info = "discarding tablespace"; 02416 trx_start_if_not_started(trx); 02417 02418 /* Serialize data dictionary operations with dictionary mutex: 02419 no deadlocks can occur then in these operations */ 02420 02421 row_mysql_lock_data_dictionary(trx); 02422 02423 table = dict_table_get_low(name); 02424 02425 if (!table) { 02426 err = DB_TABLE_NOT_FOUND; 02427 02428 goto funct_exit; 02429 } 02430 02431 if (table->space == 0) { 02432 ut_print_timestamp(stderr); 02433 fputs(" InnoDB: Error: table ", stderr); 02434 ut_print_name(stderr, trx, TRUE, name); 02435 fputs("\n" 02436 "InnoDB: is in the system tablespace 0 which cannot be discarded\n", stderr); 02437 err = DB_ERROR; 02438 02439 goto funct_exit; 02440 } 02441 02442 if (table->n_foreign_key_checks_running > 0) { 02443 02444 ut_print_timestamp(stderr); 02445 fputs(" InnoDB: You are trying to DISCARD table ", stderr); 02446 ut_print_name(stderr, trx, TRUE, table->name); 02447 fputs("\n" 02448 "InnoDB: though there is a foreign key check running on it.\n" 02449 "InnoDB: Cannot discard the table.\n", 02450 stderr); 02451 02452 err = DB_ERROR; 02453 02454 goto funct_exit; 02455 } 02456 02457 /* Check if the table is referenced by foreign key constraints from 02458 some other table (not the table itself) */ 02459 02460 foreign = UT_LIST_GET_FIRST(table->referenced_list); 02461 02462 while (foreign && foreign->foreign_table == table) { 02463 foreign = UT_LIST_GET_NEXT(referenced_list, foreign); 02464 } 02465 02466 if (foreign && trx->check_foreigns) { 02467 02468 FILE* ef = dict_foreign_err_file; 02469 02470 /* We only allow discarding a referenced table if 02471 FOREIGN_KEY_CHECKS is set to 0 */ 02472 02473 err = DB_CANNOT_DROP_CONSTRAINT; 02474 02475 mutex_enter(&dict_foreign_err_mutex); 02476 rewind(ef); 02477 ut_print_timestamp(ef); 02478 02479 fputs(" Cannot DISCARD table ", ef); 02480 ut_print_name(ef, trx, TRUE, name); 02481 fputs("\n" 02482 "because it is referenced by ", ef); 02483 ut_print_name(ef, trx, TRUE, foreign->foreign_table_name); 02484 putc('\n', ef); 02485 mutex_exit(&dict_foreign_err_mutex); 02486 02487 goto funct_exit; 02488 } 02489 02490 new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); 02491 02492 /* Remove any locks there are on the table or its records */ 02493 lock_reset_all_on_table(table); 02494 02495 info = pars_info_create(); 02496 02497 pars_info_add_str_literal(info, "table_name", name); 02498 pars_info_add_dulint_literal(info, "new_id", new_id); 02499 02500 err = que_eval_sql(info, 02501 "PROCEDURE DISCARD_TABLESPACE_PROC () IS\n" 02502 "old_id CHAR;\n" 02503 "BEGIN\n" 02504 "SELECT ID INTO old_id\n" 02505 "FROM SYS_TABLES\n" 02506 "WHERE NAME = :table_name\n" 02507 "LOCK IN SHARE MODE;\n" 02508 "IF (SQL % NOTFOUND) THEN\n" 02509 " COMMIT WORK;\n" 02510 " RETURN;\n" 02511 "END IF;\n" 02512 "UPDATE SYS_TABLES SET ID = :new_id\n" 02513 " WHERE ID = old_id;\n" 02514 "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n" 02515 " WHERE TABLE_ID = old_id;\n" 02516 "UPDATE SYS_INDEXES SET TABLE_ID = :new_id\n" 02517 " WHERE TABLE_ID = old_id;\n" 02518 "COMMIT WORK;\n" 02519 "END;\n" 02520 , FALSE, trx); 02521 02522 if (err != DB_SUCCESS) { 02523 trx->error_state = DB_SUCCESS; 02524 trx_general_rollback_for_mysql(trx, FALSE, NULL); 02525 trx->error_state = DB_SUCCESS; 02526 } else { 02527 dict_table_change_id_in_cache(table, new_id); 02528 02529 success = fil_discard_tablespace(table->space); 02530 02531 if (!success) { 02532 trx->error_state = DB_SUCCESS; 02533 trx_general_rollback_for_mysql(trx, FALSE, NULL); 02534 trx->error_state = DB_SUCCESS; 02535 02536 err = DB_ERROR; 02537 } else { 02538 /* Set the flag which tells that now it is legal to 02539 IMPORT a tablespace for this table */ 02540 table->tablespace_discarded = TRUE; 02541 table->ibd_file_missing = TRUE; 02542 } 02543 } 02544 02545 funct_exit: 02546 trx_commit_for_mysql(trx); 02547 02548 row_mysql_unlock_data_dictionary(trx); 02549 02550 trx->op_info = ""; 02551 02552 return((int) err); 02553 } 02554 02555 /********************************************************************* 02556 Imports a tablespace. The space id in the .ibd file must match the space id 02557 of the table in the data dictionary. */ 02558 02559 int 02560 row_import_tablespace_for_mysql( 02561 /*============================*/ 02562 /* out: error code or DB_SUCCESS */ 02563 const char* name, /* in: table name */ 02564 trx_t* trx) /* in: transaction handle */ 02565 { 02566 dict_table_t* table; 02567 ibool success; 02568 dulint current_lsn; 02569 ulint err = DB_SUCCESS; 02570 02571 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 02572 02573 trx_start_if_not_started(trx); 02574 02575 trx->op_info = "importing tablespace"; 02576 02577 current_lsn = log_get_lsn(); 02578 02579 /* It is possible, though very improbable, that the lsn's in the 02580 tablespace to be imported have risen above the current system lsn, if 02581 a lengthy purge, ibuf merge, or rollback was performed on a backup 02582 taken with ibbackup. If that is the case, reset page lsn's in the 02583 file. We assume that mysqld was shut down after it performed these 02584 cleanup operations on the .ibd file, so that it stamped the latest lsn 02585 to the FIL_PAGE_FILE_FLUSH_LSN in the first page of the .ibd file. 02586 02587 TODO: reset also the trx id's in clustered index records and write 02588 a new space id to each data page. That would allow us to import clean 02589 .ibd files from another MySQL installation. */ 02590 02591 success = fil_reset_too_high_lsns(name, current_lsn); 02592 02593 if (!success) { 02594 ut_print_timestamp(stderr); 02595 fputs(" InnoDB: Error: cannot reset lsn's in table ", stderr); 02596 ut_print_name(stderr, trx, TRUE, name); 02597 fputs("\n" 02598 "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n", stderr); 02599 02600 err = DB_ERROR; 02601 02602 row_mysql_lock_data_dictionary(trx); 02603 02604 goto funct_exit; 02605 } 02606 02607 /* Serialize data dictionary operations with dictionary mutex: 02608 no deadlocks can occur then in these operations */ 02609 02610 row_mysql_lock_data_dictionary(trx); 02611 02612 table = dict_table_get_low(name); 02613 02614 if (!table) { 02615 ut_print_timestamp(stderr); 02616 fputs(" InnoDB: table ", stderr); 02617 ut_print_name(stderr, trx, TRUE, name); 02618 fputs("\n" 02619 "InnoDB: does not exist in the InnoDB data dictionary\n" 02620 "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n", 02621 stderr); 02622 02623 err = DB_TABLE_NOT_FOUND; 02624 02625 goto funct_exit; 02626 } 02627 02628 if (table->space == 0) { 02629 ut_print_timestamp(stderr); 02630 fputs(" InnoDB: Error: table ", stderr); 02631 ut_print_name(stderr, trx, TRUE, name); 02632 fputs("\n" 02633 "InnoDB: is in the system tablespace 0 which cannot be imported\n", stderr); 02634 err = DB_ERROR; 02635 02636 goto funct_exit; 02637 } 02638 02639 if (!table->tablespace_discarded) { 02640 ut_print_timestamp(stderr); 02641 fputs( 02642 " InnoDB: Error: you are trying to IMPORT a tablespace\n" 02643 "InnoDB: ", stderr); 02644 ut_print_name(stderr, trx, TRUE, name); 02645 fputs(", though you have not called DISCARD on it yet\n" 02646 "InnoDB: during the lifetime of the mysqld process!\n", stderr); 02647 02648 err = DB_ERROR; 02649 02650 goto funct_exit; 02651 } 02652 02653 /* Play safe and remove all insert buffer entries, though we should 02654 have removed them already when DISCARD TABLESPACE was called */ 02655 02656 ibuf_delete_for_discarded_space(table->space); 02657 02658 success = fil_open_single_table_tablespace(TRUE, table->space, 02659 table->name); 02660 if (success) { 02661 table->ibd_file_missing = FALSE; 02662 table->tablespace_discarded = FALSE; 02663 } else { 02664 if (table->ibd_file_missing) { 02665 ut_print_timestamp(stderr); 02666 fputs( 02667 " InnoDB: cannot find or open in the database directory the .ibd file of\n" 02668 "InnoDB: table ", stderr); 02669 ut_print_name(stderr, trx, TRUE, name); 02670 fputs("\n" 02671 "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n", 02672 stderr); 02673 } 02674 02675 err = DB_ERROR; 02676 } 02677 02678 funct_exit: 02679 trx_commit_for_mysql(trx); 02680 02681 row_mysql_unlock_data_dictionary(trx); 02682 02683 trx->op_info = ""; 02684 02685 return((int) err); 02686 } 02687 02688 /************************************************************************* 02689 Truncates a table for MySQL. */ 02690 02691 int 02692 row_truncate_table_for_mysql( 02693 /*=========================*/ 02694 /* out: error code or DB_SUCCESS */ 02695 dict_table_t* table, /* in: table handle */ 02696 trx_t* trx) /* in: transaction handle */ 02697 { 02698 dict_foreign_t* foreign; 02699 ulint err; 02700 mem_heap_t* heap; 02701 byte* buf; 02702 dtuple_t* tuple; 02703 dfield_t* dfield; 02704 dict_index_t* sys_index; 02705 btr_pcur_t pcur; 02706 mtr_t mtr; 02707 dulint new_id; 02708 pars_info_t* info = NULL; 02709 02710 /* How do we prevent crashes caused by ongoing operations on the table? Old 02711 operations could try to access non-existent pages. 02712 02713 1) SQL queries, INSERT, SELECT, ...: we must get an exclusive MySQL table lock 02714 on the table before we can do TRUNCATE TABLE. Then there are no running 02715 queries on the table. This is guaranteed, because in 02716 ha_innobase::store_lock(), we do not weaken the TL_WRITE lock requested 02717 by MySQL when executing SQLCOM_TRUNCATE. 02718 2) Purge and rollback: we assign a new table id for the table. Since purge and 02719 rollback look for the table based on the table id, they see the table as 02720 'dropped' and discard their operations. 02721 3) Insert buffer: TRUNCATE TABLE is analogous to DROP TABLE, so we do not 02722 have to remove insert buffer records, as the insert buffer works at a low 02723 level. If a freed page is later reallocated, the allocator will remove 02724 the ibuf entries for it. 02725 02726 TODO: when we truncate *.ibd files (analogous to DISCARD TABLESPACE), we 02727 will have to remove we remove all entries for the table in the insert 02728 buffer tree! 02729 02730 4) Linear readahead and random readahead: we use the same method as in 3) to 02731 discard ongoing operations. (This will only be relevant for TRUNCATE TABLE 02732 by DISCARD TABLESPACE.) 02733 5) FOREIGN KEY operations: if table->n_foreign_key_checks_running > 0, we 02734 do not allow the TRUNCATE. We also reserve the data dictionary latch. */ 02735 02736 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 02737 ut_ad(table); 02738 02739 if (srv_created_new_raw) { 02740 fputs( 02741 "InnoDB: A new raw disk partition was initialized or\n" 02742 "InnoDB: innodb_force_recovery is on: we do not allow\n" 02743 "InnoDB: database modifications by the user. Shut down\n" 02744 "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n" 02745 "InnoDB: with raw, and innodb_force_... is removed.\n", 02746 stderr); 02747 02748 return(DB_ERROR); 02749 } 02750 02751 trx->op_info = "truncating table"; 02752 02753 trx_start_if_not_started(trx); 02754 02755 /* Serialize data dictionary operations with dictionary mutex: 02756 no deadlocks can occur then in these operations */ 02757 02758 ut_a(trx->dict_operation_lock_mode == 0); 02759 /* Prevent foreign key checks etc. while we are truncating the 02760 table */ 02761 02762 row_mysql_lock_data_dictionary(trx); 02763 02764 #ifdef UNIV_SYNC_DEBUG 02765 ut_ad(mutex_own(&(dict_sys->mutex))); 02766 ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); 02767 #endif /* UNIV_SYNC_DEBUG */ 02768 02769 /* Check if the table is referenced by foreign key constraints from 02770 some other table (not the table itself) */ 02771 02772 foreign = UT_LIST_GET_FIRST(table->referenced_list); 02773 02774 while (foreign && foreign->foreign_table == table) { 02775 foreign = UT_LIST_GET_NEXT(referenced_list, foreign); 02776 } 02777 02778 if (foreign && trx->check_foreigns) { 02779 FILE* ef = dict_foreign_err_file; 02780 02781 /* We only allow truncating a referenced table if 02782 FOREIGN_KEY_CHECKS is set to 0 */ 02783 02784 mutex_enter(&dict_foreign_err_mutex); 02785 rewind(ef); 02786 ut_print_timestamp(ef); 02787 02788 fputs(" Cannot truncate table ", ef); 02789 ut_print_name(ef, trx, TRUE, table->name); 02790 fputs(" by DROP+CREATE\n" 02791 "InnoDB: because it is referenced by ", ef); 02792 ut_print_name(ef, trx, TRUE, foreign->foreign_table_name); 02793 putc('\n', ef); 02794 mutex_exit(&dict_foreign_err_mutex); 02795 02796 err = DB_ERROR; 02797 goto funct_exit; 02798 } 02799 02800 /* TODO: could we replace the counter n_foreign_key_checks_running 02801 with lock checks on the table? Acquire here an exclusive lock on the 02802 table, and rewrite lock0lock.c and the lock wait in srv0srv.c so that 02803 they can cope with the table having been truncated here? Foreign key 02804 checks take an IS or IX lock on the table. */ 02805 02806 if (table->n_foreign_key_checks_running > 0) { 02807 ut_print_timestamp(stderr); 02808 fputs(" InnoDB: Cannot truncate table ", stderr); 02809 ut_print_name(stderr, trx, TRUE, table->name); 02810 fputs(" by DROP+CREATE\n" 02811 "InnoDB: because there is a foreign key check running on it.\n", 02812 stderr); 02813 err = DB_ERROR; 02814 02815 goto funct_exit; 02816 } 02817 02818 /* Remove any locks there are on the table or its records */ 02819 02820 lock_reset_all_on_table(table); 02821 02822 trx->table_id = table->id; 02823 02824 /* scan SYS_INDEXES for all indexes of the table */ 02825 heap = mem_heap_create(800); 02826 02827 tuple = dtuple_create(heap, 1); 02828 dfield = dtuple_get_nth_field(tuple, 0); 02829 02830 buf = mem_heap_alloc(heap, 8); 02831 mach_write_to_8(buf, table->id); 02832 02833 dfield_set_data(dfield, buf, 8); 02834 sys_index = dict_table_get_first_index(dict_sys->sys_indexes); 02835 dict_index_copy_types(tuple, sys_index, 1); 02836 02837 mtr_start(&mtr); 02838 btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, 02839 BTR_MODIFY_LEAF, &pcur, &mtr); 02840 for (;;) { 02841 rec_t* rec; 02842 const byte* field; 02843 ulint len; 02844 ulint root_page_no; 02845 02846 if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) { 02847 /* The end of SYS_INDEXES has been reached. */ 02848 break; 02849 } 02850 02851 rec = btr_pcur_get_rec(&pcur); 02852 02853 field = rec_get_nth_field_old(rec, 0, &len); 02854 ut_ad(len == 8); 02855 02856 if (memcmp(buf, field, len) != 0) { 02857 /* End of indexes for the table (TABLE_ID mismatch). */ 02858 break; 02859 } 02860 02861 if (rec_get_deleted_flag(rec, FALSE)) { 02862 /* The index has been dropped. */ 02863 goto next_rec; 02864 } 02865 02866 btr_pcur_store_position(&pcur, &mtr); 02867 02868 /* This call may commit and restart mtr. */ 02869 root_page_no = dict_truncate_index_tree(table, rec, &mtr); 02870 02871 btr_pcur_restore_position(BTR_MODIFY_LEAF, &pcur, &mtr); 02872 rec = btr_pcur_get_rec(&pcur); 02873 02874 if (root_page_no != FIL_NULL) { 02875 page_rec_write_index_page_no(rec, 02876 DICT_SYS_INDEXES_PAGE_NO_FIELD, 02877 root_page_no, &mtr); 02878 /* We will need to commit and restart the 02879 mini-transaction in order to avoid deadlocks. 02880 The dict_truncate_index_tree() call has allocated 02881 a page in this mini-transaction, and the rest of 02882 this loop could latch another index page. */ 02883 mtr_commit(&mtr); 02884 mtr_start(&mtr); 02885 btr_pcur_restore_position(BTR_MODIFY_LEAF, 02886 &pcur, &mtr); 02887 } 02888 02889 next_rec: 02890 btr_pcur_move_to_next_user_rec(&pcur, &mtr); 02891 } 02892 02893 btr_pcur_close(&pcur); 02894 mtr_commit(&mtr); 02895 02896 mem_heap_free(heap); 02897 02898 new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); 02899 02900 info = pars_info_create(); 02901 02902 pars_info_add_dulint_literal(info, "old_id", table->id); 02903 pars_info_add_dulint_literal(info, "new_id", new_id); 02904 02905 err = que_eval_sql(info, 02906 "PROCEDURE RENUMBER_TABLESPACE_PROC () IS\n" 02907 "BEGIN\n" 02908 "UPDATE SYS_TABLES SET ID = :new_id\n" 02909 " WHERE ID = :old_id;\n" 02910 "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n" 02911 " WHERE TABLE_ID = :old_id;\n" 02912 "UPDATE SYS_INDEXES SET TABLE_ID = :new_id\n" 02913 " WHERE TABLE_ID = :old_id;\n" 02914 "COMMIT WORK;\n" 02915 "END;\n" 02916 , FALSE, trx); 02917 02918 if (err != DB_SUCCESS) { 02919 trx->error_state = DB_SUCCESS; 02920 trx_general_rollback_for_mysql(trx, FALSE, NULL); 02921 trx->error_state = DB_SUCCESS; 02922 ut_print_timestamp(stderr); 02923 fputs(" InnoDB: Unable to assign a new identifier to table ", stderr); 02924 ut_print_name(stderr, trx, TRUE, table->name); 02925 fputs("\n" 02926 "InnoDB: after truncating it. Background processes may corrupt the table!\n", 02927 stderr); 02928 err = DB_ERROR; 02929 } else { 02930 dict_table_change_id_in_cache(table, new_id); 02931 } 02932 02933 dict_table_autoinc_initialize(table, 0); 02934 dict_update_statistics(table); 02935 02936 trx_commit_for_mysql(trx); 02937 02938 funct_exit: 02939 02940 row_mysql_unlock_data_dictionary(trx); 02941 02942 trx->op_info = ""; 02943 02944 srv_wake_master_thread(); 02945 02946 return((int) err); 02947 } 02948 #endif /* !UNIV_HOTBACKUP */ 02949 02950 /************************************************************************* 02951 Drops a table for MySQL. If the name of the table to be dropped is equal 02952 with one of the predefined magic table names, then this also stops printing 02953 the corresponding monitor output by the master thread. */ 02954 02955 int 02956 row_drop_table_for_mysql( 02957 /*=====================*/ 02958 /* out: error code or DB_SUCCESS */ 02959 const char* name, /* in: table name */ 02960 trx_t* trx, /* in: transaction handle */ 02961 ibool drop_db)/* in: TRUE=dropping whole database */ 02962 { 02963 dict_foreign_t* foreign; 02964 dict_table_t* table; 02965 ulint space_id; 02966 ulint err; 02967 const char* table_name; 02968 ulint namelen; 02969 char* dir_path_of_temp_table = NULL; 02970 ibool success; 02971 ibool locked_dictionary = FALSE; 02972 pars_info_t* info = NULL; 02973 02974 ut_a(name != NULL); 02975 02976 if (srv_created_new_raw) { 02977 fputs( 02978 "InnoDB: A new raw disk partition was initialized or\n" 02979 "InnoDB: innodb_force_recovery is on: we do not allow\n" 02980 "InnoDB: database modifications by the user. Shut down\n" 02981 "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n" 02982 "InnoDB: with raw, and innodb_force_... is removed.\n", 02983 stderr); 02984 02985 return(DB_ERROR); 02986 } 02987 02988 trx->op_info = "dropping table"; 02989 02990 trx_start_if_not_started(trx); 02991 02992 /* The table name is prefixed with the database name and a '/'. 02993 Certain table names starting with 'innodb_' have their special 02994 meaning regardless of the database name. Thus, we need to 02995 ignore the database name prefix in the comparisons. */ 02996 table_name = strchr(name, '/'); 02997 ut_a(table_name); 02998 table_name++; 02999 namelen = strlen(table_name) + 1; 03000 03001 if (namelen == sizeof S_innodb_monitor 03002 && !memcmp(table_name, S_innodb_monitor, 03003 sizeof S_innodb_monitor)) { 03004 03005 /* Table name equals "innodb_monitor": 03006 stop monitor prints */ 03007 03008 srv_print_innodb_monitor = FALSE; 03009 srv_print_innodb_lock_monitor = FALSE; 03010 } else if (namelen == sizeof S_innodb_lock_monitor 03011 && !memcmp(table_name, S_innodb_lock_monitor, 03012 sizeof S_innodb_lock_monitor)) { 03013 srv_print_innodb_monitor = FALSE; 03014 srv_print_innodb_lock_monitor = FALSE; 03015 } else if (namelen == sizeof S_innodb_tablespace_monitor 03016 && !memcmp(table_name, S_innodb_tablespace_monitor, 03017 sizeof S_innodb_tablespace_monitor)) { 03018 03019 srv_print_innodb_tablespace_monitor = FALSE; 03020 } else if (namelen == sizeof S_innodb_table_monitor 03021 && !memcmp(table_name, S_innodb_table_monitor, 03022 sizeof S_innodb_table_monitor)) { 03023 03024 srv_print_innodb_table_monitor = FALSE; 03025 } 03026 03027 /* Serialize data dictionary operations with dictionary mutex: 03028 no deadlocks can occur then in these operations */ 03029 03030 if (trx->dict_operation_lock_mode != RW_X_LATCH) { 03031 /* Prevent foreign key checks etc. while we are dropping the 03032 table */ 03033 03034 row_mysql_lock_data_dictionary(trx); 03035 03036 locked_dictionary = TRUE; 03037 } 03038 03039 #ifdef UNIV_SYNC_DEBUG 03040 ut_ad(mutex_own(&(dict_sys->mutex))); 03041 ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); 03042 #endif /* UNIV_SYNC_DEBUG */ 03043 03044 table = dict_table_get_low(name); 03045 03046 if (!table) { 03047 err = DB_TABLE_NOT_FOUND; 03048 ut_print_timestamp(stderr); 03049 03050 fputs(" InnoDB: Error: table ", stderr); 03051 ut_print_name(stderr, trx, TRUE, name); 03052 fputs(" does not exist in the InnoDB internal\n" 03053 "InnoDB: data dictionary though MySQL is trying to drop it.\n" 03054 "InnoDB: Have you copied the .frm file of the table to the\n" 03055 "InnoDB: MySQL database directory from another database?\n" 03056 "InnoDB: You can look for further help from\n" 03057 "InnoDB: http://dev.mysql.com/doc/mysql/en/" 03058 "InnoDB_troubleshooting_datadict.html\n", stderr); 03059 goto funct_exit; 03060 } 03061 03062 /* Check if the table is referenced by foreign key constraints from 03063 some other table (not the table itself) */ 03064 03065 foreign = UT_LIST_GET_FIRST(table->referenced_list); 03066 03067 while (foreign && foreign->foreign_table == table) { 03068 check_next_foreign: 03069 foreign = UT_LIST_GET_NEXT(referenced_list, foreign); 03070 } 03071 03072 if (foreign && trx->check_foreigns && 03073 !(drop_db && dict_tables_have_same_db( 03074 name, foreign->foreign_table_name))) { 03075 FILE* ef = dict_foreign_err_file; 03076 03077 /* We only allow dropping a referenced table if 03078 FOREIGN_KEY_CHECKS is set to 0 */ 03079 03080 err = DB_CANNOT_DROP_CONSTRAINT; 03081 03082 mutex_enter(&dict_foreign_err_mutex); 03083 rewind(ef); 03084 ut_print_timestamp(ef); 03085 03086 fputs(" Cannot drop table ", ef); 03087 ut_print_name(ef, trx, TRUE, name); 03088 fputs("\n" 03089 "because it is referenced by ", ef); 03090 ut_print_name(ef, trx, TRUE, foreign->foreign_table_name); 03091 putc('\n', ef); 03092 mutex_exit(&dict_foreign_err_mutex); 03093 03094 goto funct_exit; 03095 } 03096 03097 if (foreign && trx->check_foreigns) { 03098 goto check_next_foreign; 03099 } 03100 03101 if (table->n_mysql_handles_opened > 0) { 03102 ibool added; 03103 03104 added = row_add_table_to_background_drop_list(table); 03105 03106 if (added) { 03107 ut_print_timestamp(stderr); 03108 fputs(" InnoDB: Warning: MySQL is trying to drop table ", stderr); 03109 ut_print_name(stderr, trx, TRUE, table->name); 03110 fputs("\n" 03111 "InnoDB: though there are still open handles to it.\n" 03112 "InnoDB: Adding the table to the background drop queue.\n", 03113 stderr); 03114 03115 /* We return DB_SUCCESS to MySQL though the drop will 03116 happen lazily later */ 03117 03118 err = DB_SUCCESS; 03119 } else { 03120 /* The table is already in the background drop list */ 03121 err = DB_ERROR; 03122 } 03123 03124 goto funct_exit; 03125 } 03126 03127 /* TODO: could we replace the counter n_foreign_key_checks_running 03128 with lock checks on the table? Acquire here an exclusive lock on the 03129 table, and rewrite lock0lock.c and the lock wait in srv0srv.c so that 03130 they can cope with the table having been dropped here? Foreign key 03131 checks take an IS or IX lock on the table. */ 03132 03133 if (table->n_foreign_key_checks_running > 0) { 03134 03135 ibool added; 03136 03137 added = row_add_table_to_background_drop_list(table); 03138 03139 if (added) { 03140 ut_print_timestamp(stderr); 03141 fputs(" InnoDB: You are trying to drop table ", stderr); 03142 ut_print_name(stderr, trx, TRUE, table->name); 03143 fputs("\n" 03144 "InnoDB: though there is a foreign key check running on it.\n" 03145 "InnoDB: Adding the table to the background drop queue.\n", 03146 stderr); 03147 03148 /* We return DB_SUCCESS to MySQL though the drop will 03149 happen lazily later */ 03150 03151 err = DB_SUCCESS; 03152 } else { 03153 /* The table is already in the background drop list */ 03154 err = DB_ERROR; 03155 } 03156 03157 goto funct_exit; 03158 } 03159 03160 /* Remove any locks there are on the table or its records */ 03161 03162 lock_reset_all_on_table(table); 03163 03164 trx->dict_operation = TRUE; 03165 trx->table_id = table->id; 03166 03167 /* We use the private SQL parser of Innobase to generate the 03168 query graphs needed in deleting the dictionary data from system 03169 tables in Innobase. Deleting a row from SYS_INDEXES table also 03170 frees the file segments of the B-tree associated with the index. */ 03171 03172 info = pars_info_create(); 03173 03174 pars_info_add_str_literal(info, "table_name", name); 03175 03176 err = que_eval_sql(info, 03177 "PROCEDURE DROP_TABLE_PROC () IS\n" 03178 "sys_foreign_id CHAR;\n" 03179 "table_id CHAR;\n" 03180 "index_id CHAR;\n" 03181 "foreign_id CHAR;\n" 03182 "found INT;\n" 03183 "BEGIN\n" 03184 "SELECT ID INTO table_id\n" 03185 "FROM SYS_TABLES\n" 03186 "WHERE NAME = :table_name\n" 03187 "LOCK IN SHARE MODE;\n" 03188 "IF (SQL % NOTFOUND) THEN\n" 03189 " COMMIT WORK;\n" 03190 " RETURN;\n" 03191 "END IF;\n" 03192 "found := 1;\n" 03193 "SELECT ID INTO sys_foreign_id\n" 03194 "FROM SYS_TABLES\n" 03195 "WHERE NAME = 'SYS_FOREIGN'\n" 03196 "LOCK IN SHARE MODE;\n" 03197 "IF (SQL % NOTFOUND) THEN\n" 03198 " found := 0;\n" 03199 "END IF;\n" 03200 "IF (:table_name = 'SYS_FOREIGN') THEN\n" 03201 " found := 0;\n" 03202 "END IF;\n" 03203 "IF (:table_name = 'SYS_FOREIGN_COLS') THEN\n" 03204 " found := 0;\n" 03205 "END IF;\n" 03206 "WHILE found = 1 LOOP\n" 03207 " SELECT ID INTO foreign_id\n" 03208 " FROM SYS_FOREIGN\n" 03209 " WHERE FOR_NAME = :table_name\n" 03210 " AND TO_BINARY(FOR_NAME) = TO_BINARY(:table_name)\n" 03211 " LOCK IN SHARE MODE;\n" 03212 " IF (SQL % NOTFOUND) THEN\n" 03213 " found := 0;\n" 03214 " ELSE" 03215 " DELETE FROM SYS_FOREIGN_COLS WHERE ID = foreign_id;\n" 03216 " DELETE FROM SYS_FOREIGN WHERE ID = foreign_id;\n" 03217 " END IF;\n" 03218 "END LOOP;\n" 03219 "found := 1;\n" 03220 "WHILE found = 1 LOOP\n" 03221 " SELECT ID INTO index_id\n" 03222 " FROM SYS_INDEXES\n" 03223 " WHERE TABLE_ID = table_id\n" 03224 " LOCK IN SHARE MODE;\n" 03225 " IF (SQL % NOTFOUND) THEN\n" 03226 " found := 0;\n" 03227 " ELSE" 03228 " DELETE FROM SYS_FIELDS WHERE INDEX_ID = index_id;\n" 03229 " DELETE FROM SYS_INDEXES WHERE ID = index_id\n" 03230 " AND TABLE_ID = table_id;\n" 03231 " END IF;\n" 03232 "END LOOP;\n" 03233 "DELETE FROM SYS_COLUMNS WHERE TABLE_ID = table_id;\n" 03234 "DELETE FROM SYS_TABLES WHERE ID = table_id;\n" 03235 "COMMIT WORK;\n" 03236 "END;\n" 03237 , FALSE, trx); 03238 03239 if (err != DB_SUCCESS) { 03240 ut_a(err == DB_OUT_OF_FILE_SPACE); 03241 03242 err = DB_MUST_GET_MORE_FILE_SPACE; 03243 03244 row_mysql_handle_errors(&err, trx, NULL, NULL); 03245 03246 ut_error; 03247 } else { 03248 ibool is_path; 03249 const char* name_or_path; 03250 03251 space_id = table->space; 03252 03253 if (table->dir_path_of_temp_table != NULL) { 03254 dir_path_of_temp_table = 03255 mem_strdup(table->dir_path_of_temp_table); 03256 is_path = TRUE; 03257 name_or_path = dir_path_of_temp_table; 03258 } else { 03259 is_path = FALSE; 03260 name_or_path = name; 03261 } 03262 03263 dict_table_remove_from_cache(table); 03264 03265 if (dict_load_table(name) != NULL) { 03266 ut_print_timestamp(stderr); 03267 fputs(" InnoDB: Error: not able to remove table ", 03268 stderr); 03269 ut_print_name(stderr, trx, TRUE, name); 03270 fputs(" from the dictionary cache!\n", stderr); 03271 err = DB_ERROR; 03272 } 03273 03274 /* Do not drop possible .ibd tablespace if something went 03275 wrong: we do not want to delete valuable data of the user */ 03276 03277 if (err == DB_SUCCESS && space_id > 0) { 03278 if (!fil_space_for_table_exists_in_mem(space_id, 03279 name_or_path, 03280 is_path, 03281 FALSE, TRUE)) { 03282 err = DB_SUCCESS; 03283 03284 fprintf(stderr, 03285 "InnoDB: We removed now the InnoDB internal data dictionary entry\n" 03286 "InnoDB: of table "); 03287 ut_print_name(stderr, trx, TRUE, name); 03288 fprintf(stderr, ".\n"); 03289 03290 goto funct_exit; 03291 } 03292 03293 success = fil_delete_tablespace(space_id); 03294 03295 if (!success) { 03296 fprintf(stderr, 03297 "InnoDB: We removed now the InnoDB internal data dictionary entry\n" 03298 "InnoDB: of table "); 03299 ut_print_name(stderr, trx, TRUE, name); 03300 fprintf(stderr, ".\n"); 03301 03302 ut_print_timestamp(stderr); 03303 fprintf(stderr, 03304 " InnoDB: Error: not able to delete tablespace %lu of table ", 03305 (ulong) space_id); 03306 ut_print_name(stderr, trx, TRUE, name); 03307 fputs("!\n", stderr); 03308 err = DB_ERROR; 03309 } 03310 } 03311 } 03312 funct_exit: 03313 03314 trx_commit_for_mysql(trx); 03315 03316 if (locked_dictionary) { 03317 row_mysql_unlock_data_dictionary(trx); 03318 } 03319 03320 if (dir_path_of_temp_table) { 03321 mem_free(dir_path_of_temp_table); 03322 } 03323 03324 trx->op_info = ""; 03325 03326 #ifndef UNIV_HOTBACKUP 03327 srv_wake_master_thread(); 03328 #endif /* !UNIV_HOTBACKUP */ 03329 03330 return((int) err); 03331 } 03332 03333 /************************************************************************* 03334 Drops a database for MySQL. */ 03335 03336 int 03337 row_drop_database_for_mysql( 03338 /*========================*/ 03339 /* out: error code or DB_SUCCESS */ 03340 const char* name, /* in: database name which ends to '/' */ 03341 trx_t* trx) /* in: transaction handle */ 03342 { 03343 dict_table_t* table; 03344 char* table_name; 03345 int err = DB_SUCCESS; 03346 ulint namelen = strlen(name); 03347 03348 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 03349 ut_a(name != NULL); 03350 ut_a(name[namelen - 1] == '/'); 03351 03352 trx->op_info = "dropping database"; 03353 03354 trx_start_if_not_started(trx); 03355 loop: 03356 row_mysql_lock_data_dictionary(trx); 03357 03358 while ((table_name = dict_get_first_table_name_in_db(name))) { 03359 ut_a(memcmp(table_name, name, namelen) == 0); 03360 03361 table = dict_table_get_low(table_name); 03362 03363 ut_a(table); 03364 03365 /* Wait until MySQL does not have any queries running on 03366 the table */ 03367 03368 if (table->n_mysql_handles_opened > 0) { 03369 row_mysql_unlock_data_dictionary(trx); 03370 03371 ut_print_timestamp(stderr); 03372 fputs( 03373 " InnoDB: Warning: MySQL is trying to drop database ", stderr); 03374 ut_print_name(stderr, trx, TRUE, name); 03375 fputs("\n" 03376 "InnoDB: though there are still open handles to table ", stderr); 03377 ut_print_name(stderr, trx, TRUE, table_name); 03378 fputs(".\n", stderr); 03379 03380 os_thread_sleep(1000000); 03381 03382 mem_free(table_name); 03383 03384 goto loop; 03385 } 03386 03387 err = row_drop_table_for_mysql(table_name, trx, TRUE); 03388 03389 mem_free(table_name); 03390 03391 if (err != DB_SUCCESS) { 03392 fputs("InnoDB: DROP DATABASE ", stderr); 03393 ut_print_name(stderr, trx, TRUE, name); 03394 fprintf(stderr, " failed with error %lu for table ", 03395 (ulint) err); 03396 ut_print_name(stderr, trx, TRUE, table_name); 03397 putc('\n', stderr); 03398 break; 03399 } 03400 } 03401 03402 trx_commit_for_mysql(trx); 03403 03404 row_mysql_unlock_data_dictionary(trx); 03405 03406 trx->op_info = ""; 03407 03408 return(err); 03409 } 03410 03411 /************************************************************************* 03412 Checks if a table name contains the string "/#sql" which denotes temporary 03413 tables in MySQL. */ 03414 static 03415 ibool 03416 row_is_mysql_tmp_table_name( 03417 /*========================*/ 03418 /* out: TRUE if temporary table */ 03419 const char* name) /* in: table name in the form 03420 'database/tablename' */ 03421 { 03422 return(strstr(name, "/#sql") != NULL); 03423 /* return(strstr(name, "/@0023sql") != NULL); */ 03424 } 03425 03426 /******************************************************************** 03427 Delete a single constraint. */ 03428 static 03429 int 03430 row_delete_constraint_low( 03431 /*======================*/ 03432 /* out: error code or DB_SUCCESS */ 03433 const char* id, /* in: constraint id */ 03434 trx_t* trx) /* in: transaction handle */ 03435 { 03436 pars_info_t* info = pars_info_create(); 03437 03438 pars_info_add_str_literal(info, "id", id); 03439 03440 return(que_eval_sql(info, 03441 "PROCEDURE DELETE_CONSTRAINT () IS\n" 03442 "BEGIN\n" 03443 "DELETE FROM SYS_FOREIGN_COLS WHERE ID = :id;\n" 03444 "DELETE FROM SYS_FOREIGN WHERE ID = :id;\n" 03445 "END;\n" 03446 , FALSE, trx)); 03447 } 03448 03449 /******************************************************************** 03450 Delete a single constraint. */ 03451 static 03452 int 03453 row_delete_constraint( 03454 /*==================*/ 03455 /* out: error code or DB_SUCCESS */ 03456 const char* id, /* in: constraint id */ 03457 const char* database_name, /* in: database name, with the 03458 trailing '/' */ 03459 mem_heap_t* heap, /* in: memory heap */ 03460 trx_t* trx) /* in: transaction handle */ 03461 { 03462 ulint err; 03463 03464 /* New format constraints have ids <databasename>/<constraintname>. */ 03465 err = row_delete_constraint_low( 03466 mem_heap_strcat(heap, database_name, id), trx); 03467 03468 if ((err == DB_SUCCESS) && !strchr(id, '/')) { 03469 /* Old format < 4.0.18 constraints have constraint ids 03470 <number>_<number>. We only try deleting them if the 03471 constraint name does not contain a '/' character, otherwise 03472 deleting a new format constraint named 'foo/bar' from 03473 database 'baz' would remove constraint 'bar' from database 03474 'foo', if it existed. */ 03475 03476 err = row_delete_constraint_low(id, trx); 03477 } 03478 03479 return(err); 03480 } 03481 03482 /************************************************************************* 03483 Renames a table for MySQL. */ 03484 03485 int 03486 row_rename_table_for_mysql( 03487 /*=======================*/ 03488 /* out: error code or DB_SUCCESS */ 03489 const char* old_name, /* in: old table name */ 03490 const char* new_name, /* in: new table name */ 03491 trx_t* trx) /* in: transaction handle */ 03492 { 03493 dict_table_t* table; 03494 ulint err; 03495 mem_heap_t* heap = NULL; 03496 const char** constraints_to_drop = NULL; 03497 ulint n_constraints_to_drop = 0; 03498 ibool recovering_temp_table = FALSE; 03499 ibool old_is_tmp, new_is_tmp; 03500 pars_info_t* info = NULL; 03501 03502 ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); 03503 ut_a(old_name != NULL); 03504 ut_a(new_name != NULL); 03505 03506 if (srv_created_new_raw || srv_force_recovery) { 03507 fputs( 03508 "InnoDB: A new raw disk partition was initialized or\n" 03509 "InnoDB: innodb_force_recovery is on: we do not allow\n" 03510 "InnoDB: database modifications by the user. Shut down\n" 03511 "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n" 03512 "InnoDB: with raw, and innodb_force_... is removed.\n", 03513 stderr); 03514 03515 trx_commit_for_mysql(trx); 03516 return(DB_ERROR); 03517 } 03518 03519 if (row_mysql_is_system_table(new_name)) { 03520 03521 fprintf(stderr, 03522 "InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n" 03523 "InnoDB: MySQL system tables must be of the MyISAM type!\n", 03524 new_name); 03525 03526 trx_commit_for_mysql(trx); 03527 return(DB_ERROR); 03528 } 03529 03530 trx->op_info = "renaming table"; 03531 trx_start_if_not_started(trx); 03532 03533 old_is_tmp = row_is_mysql_tmp_table_name(old_name); 03534 new_is_tmp = row_is_mysql_tmp_table_name(new_name); 03535 03536 if (row_mysql_is_recovered_tmp_table(new_name)) { 03537 03538 recovering_temp_table = TRUE; 03539 } else { 03540 /* Serialize data dictionary operations with dictionary mutex: 03541 no deadlocks can occur then in these operations */ 03542 03543 row_mysql_lock_data_dictionary(trx); 03544 } 03545 03546 table = dict_table_get_low(old_name); 03547 03548 if (!table) { 03549 err = DB_TABLE_NOT_FOUND; 03550 ut_print_timestamp(stderr); 03551 03552 fputs(" InnoDB: Error: table ", stderr); 03553 ut_print_name(stderr, trx, TRUE, old_name); 03554 fputs(" does not exist in the InnoDB internal\n" 03555 "InnoDB: data dictionary though MySQL is trying to rename the table.\n" 03556 "InnoDB: Have you copied the .frm file of the table to the\n" 03557 "InnoDB: MySQL database directory from another database?\n" 03558 "InnoDB: You can look for further help from\n" 03559 "InnoDB: http://dev.mysql.com/doc/mysql/en/" 03560 "InnoDB_troubleshooting_datadict.html\n", stderr); 03561 goto funct_exit; 03562 } 03563 03564 if (table->ibd_file_missing) { 03565 err = DB_TABLE_NOT_FOUND; 03566 ut_print_timestamp(stderr); 03567 03568 fputs(" InnoDB: Error: table ", stderr); 03569 ut_print_name(stderr, trx, TRUE, old_name); 03570 fputs( 03571 " does not have an .ibd file in the database directory.\n" 03572 "InnoDB: You can look for further help from\n" 03573 "InnoDB: http://dev.mysql.com/doc/mysql/en/" 03574 "InnoDB_troubleshooting_datadict.html\n", stderr); 03575 goto funct_exit; 03576 } 03577 03578 if (new_is_tmp) { 03579 /* MySQL is doing an ALTER TABLE command and it renames the 03580 original table to a temporary table name. We want to preserve 03581 the original foreign key constraint definitions despite the 03582 name change. An exception is those constraints for which 03583 the ALTER TABLE contained DROP FOREIGN KEY <foreign key id>.*/ 03584 03585 heap = mem_heap_create(100); 03586 03587 err = dict_foreign_parse_drop_constraints(heap, trx, 03588 table, &n_constraints_to_drop, &constraints_to_drop); 03589 03590 if (err != DB_SUCCESS) { 03591 03592 goto funct_exit; 03593 } 03594 } 03595 03596 /* We use the private SQL parser of Innobase to generate the query 03597 graphs needed in deleting the dictionary data from system tables in 03598 Innobase. Deleting a row from SYS_INDEXES table also frees the file 03599 segments of the B-tree associated with the index. */ 03600 03601 info = pars_info_create(); 03602 03603 pars_info_add_str_literal(info, "new_table_name", new_name); 03604 pars_info_add_str_literal(info, "old_table_name", old_name); 03605 03606 err = que_eval_sql(info, 03607 "PROCEDURE RENAME_TABLE () IS\n" 03608 "BEGIN\n" 03609 "UPDATE SYS_TABLES SET NAME = :new_table_name\n" 03610 " WHERE NAME = :old_table_name;\n" 03611 "END;\n" 03612 , FALSE, trx); 03613 03614 if (err != DB_SUCCESS) { 03615 03616 goto end; 03617 } 03618 03619 if (!new_is_tmp) { 03620 /* Rename all constraints. */ 03621 03622 info = pars_info_create(); 03623 03624 pars_info_add_str_literal(info, "new_table_name", new_name); 03625 pars_info_add_str_literal(info, "old_table_name", old_name); 03626 03627 err = que_eval_sql(info, 03628 "PROCEDURE RENAME_CONSTRAINT_IDS () IS\n" 03629 "gen_constr_prefix CHAR;\n" 03630 "new_db_name CHAR;\n" 03631 "foreign_id CHAR;\n" 03632 "new_foreign_id CHAR;\n" 03633 "old_db_name_len INT;\n" 03634 "old_t_name_len INT;\n" 03635 "new_db_name_len INT;\n" 03636 "id_len INT;\n" 03637 "found INT;\n" 03638 "BEGIN\n" 03639 "found := 1;\n" 03640 "old_db_name_len := INSTR(:old_table_name, '/') - 1;\n" 03641 "new_db_name_len := INSTR(:new_table_name, '/') - 1;\n" 03642 "new_db_name := SUBSTR(:new_table_name, 0, new_db_name_len);\n" 03643 "old_t_name_len := LENGTH(:old_table_name);\n" 03644 "gen_constr_prefix := CONCAT(:old_table_name, '_ibfk_');\n" 03645 "WHILE found = 1 LOOP\n" 03646 " SELECT ID INTO foreign_id\n" 03647 " FROM SYS_FOREIGN\n" 03648 " WHERE FOR_NAME = :old_table_name\n" 03649 " AND TO_BINARY(FOR_NAME) = TO_BINARY(:old_table_name)\n" 03650 " LOCK IN SHARE MODE;\n" 03651 " IF (SQL % NOTFOUND) THEN\n" 03652 " found := 0;\n" 03653 " ELSE\n" 03654 " UPDATE SYS_FOREIGN\n" 03655 " SET FOR_NAME = :new_table_name\n" 03656 " WHERE ID = foreign_id;\n" 03657 " id_len := LENGTH(foreign_id);\n" 03658 " IF (INSTR(foreign_id, '/') > 0) THEN\n" 03659 " IF (INSTR(foreign_id,\n" 03660 " gen_constr_prefix) > 0)\n" 03661 " THEN\n" 03662 " new_foreign_id :=\n" 03663 " CONCAT(:new_table_name,\n" 03664 " SUBSTR(foreign_id, old_t_name_len,\n" 03665 " id_len - old_t_name_len));\n" 03666 " ELSE\n" 03667 " new_foreign_id :=\n" 03668 " CONCAT(new_db_name,\n" 03669 " SUBSTR(foreign_id,\n" 03670 " old_db_name_len,\n" 03671 " id_len - old_db_name_len));\n" 03672 " END IF;\n" 03673 " UPDATE SYS_FOREIGN\n" 03674 " SET ID = new_foreign_id\n" 03675 " WHERE ID = foreign_id;\n" 03676 " UPDATE SYS_FOREIGN_COLS\n" 03677 " SET ID = new_foreign_id\n" 03678 " WHERE ID = foreign_id;\n" 03679 " END IF;\n" 03680 " END IF;\n" 03681 "END LOOP;\n" 03682 "UPDATE SYS_FOREIGN SET REF_NAME = :new_table_name\n" 03683 "WHERE REF_NAME = :old_table_name\n" 03684 " AND TO_BINARY(REF_NAME) = TO_BINARY(:old_table_name);\n" 03685 "END;\n" 03686 , FALSE, trx); 03687 03688 } else if (n_constraints_to_drop > 0) { 03689 /* Drop some constraints of tmp tables. */ 03690 03691 ulint db_name_len = dict_get_db_name_len(old_name) + 1; 03692 char* db_name = mem_heap_strdupl(heap, old_name, 03693 db_name_len); 03694 ulint i; 03695 03696 for (i = 0; i < n_constraints_to_drop; i++) { 03697 err = row_delete_constraint(constraints_to_drop[i], 03698 db_name, heap, trx); 03699 03700 if (err != DB_SUCCESS) { 03701 break; 03702 } 03703 } 03704 } 03705 03706 end: 03707 if (err != DB_SUCCESS) { 03708 if (err == DB_DUPLICATE_KEY) { 03709 ut_print_timestamp(stderr); 03710 fputs( 03711 " InnoDB: Error; possible reasons:\n" 03712 "InnoDB: 1) Table rename would cause two FOREIGN KEY constraints\n" 03713 "InnoDB: to have the same internal name in case-insensitive comparison.\n" 03714 "InnoDB: 2) table ", stderr); 03715 ut_print_name(stderr, trx, TRUE, new_name); 03716 fputs(" exists in the InnoDB internal data\n" 03717 "InnoDB: dictionary though MySQL is trying rename table ", stderr); 03718 ut_print_name(stderr, trx, TRUE, old_name); 03719 fputs(" to it.\n" 03720 "InnoDB: Have you deleted the .frm file and not used DROP TABLE?\n" 03721 "InnoDB: You can look for further help from\n" 03722 "InnoDB: http://dev.mysql.com/doc/mysql/en/" 03723 "InnoDB_troubleshooting_datadict.html\n" 03724 "InnoDB: If table ", stderr); 03725 ut_print_name(stderr, trx, TRUE, new_name); 03726 fputs( 03727 " is a temporary table #sql..., then it can be that\n" 03728 "InnoDB: there are still queries running on the table, and it will be\n" 03729 "InnoDB: dropped automatically when the queries end.\n" 03730 "InnoDB: You can drop the orphaned table inside InnoDB by\n" 03731 "InnoDB: creating an InnoDB table with the same name in another\n" 03732 "InnoDB: database and copying the .frm file to the current database.\n" 03733 "InnoDB: Then MySQL thinks the table exists, and DROP TABLE will\n" 03734 "InnoDB: succeed.\n", stderr); 03735 } 03736 trx->error_state = DB_SUCCESS; 03737 trx_general_rollback_for_mysql(trx, FALSE, NULL); 03738 trx->error_state = DB_SUCCESS; 03739 } else { 03740 /* The following call will also rename the .ibd data file if 03741 the table is stored in a single-table tablespace */ 03742 03743 ibool success = dict_table_rename_in_cache(table, new_name, 03744 !new_is_tmp); 03745 03746 if (!success) { 03747 trx->error_state = DB_SUCCESS; 03748 trx_general_rollback_for_mysql(trx, FALSE, NULL); 03749 trx->error_state = DB_SUCCESS; 03750 ut_print_timestamp(stderr); 03751 fputs(" InnoDB: Error in table rename, cannot rename ", 03752 stderr); 03753 ut_print_name(stderr, trx, TRUE, old_name); 03754 fputs(" to ", stderr); 03755 ut_print_name(stderr, trx, TRUE, new_name); 03756 putc('\n', stderr); 03757 err = DB_ERROR; 03758 03759 goto funct_exit; 03760 } 03761 03762 /* We only want to switch off some of the type checking in 03763 an ALTER, not in a RENAME. */ 03764 03765 err = dict_load_foreigns(new_name, 03766 old_is_tmp ? trx->check_foreigns : TRUE); 03767 03768 if (err != DB_SUCCESS) { 03769 ut_print_timestamp(stderr); 03770 03771 if (old_is_tmp) { 03772 fputs(" InnoDB: Error: in ALTER TABLE ", 03773 stderr); 03774 ut_print_name(stderr, trx, TRUE, new_name); 03775 fputs("\n" 03776 "InnoDB: has or is referenced in foreign key constraints\n" 03777 "InnoDB: which are not compatible with the new table definition.\n", 03778 stderr); 03779 } else { 03780 fputs( 03781 " InnoDB: Error: in RENAME TABLE table ", 03782 stderr); 03783 ut_print_name(stderr, trx, TRUE, new_name); 03784 fputs("\n" 03785 "InnoDB: is referenced in foreign key constraints\n" 03786 "InnoDB: which are not compatible with the new table definition.\n", 03787 stderr); 03788 } 03789 03790 ut_a(dict_table_rename_in_cache(table, 03791 old_name, FALSE)); 03792 trx->error_state = DB_SUCCESS; 03793 trx_general_rollback_for_mysql(trx, FALSE, NULL); 03794 trx->error_state = DB_SUCCESS; 03795 } 03796 } 03797 03798 funct_exit: 03799 trx_commit_for_mysql(trx); 03800 03801 if (!recovering_temp_table) { 03802 row_mysql_unlock_data_dictionary(trx); 03803 } 03804 03805 if (UNIV_LIKELY_NULL(heap)) { 03806 mem_heap_free(heap); 03807 } 03808 03809 trx->op_info = ""; 03810 03811 return((int) err); 03812 } 03813 03814 /************************************************************************* 03815 Checks that the index contains entries in an ascending order, unique 03816 constraint is not broken, and calculates the number of index entries 03817 in the read view of the current transaction. */ 03818 static 03819 ibool 03820 row_scan_and_check_index( 03821 /*=====================*/ 03822 /* out: TRUE if ok */ 03823 row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL */ 03824 dict_index_t* index, /* in: index */ 03825 ulint* n_rows) /* out: number of entries seen in the 03826 current consistent read */ 03827 { 03828 dtuple_t* prev_entry = NULL; 03829 ulint matched_fields; 03830 ulint matched_bytes; 03831 byte* buf; 03832 ulint ret; 03833 rec_t* rec; 03834 ibool is_ok = TRUE; 03835 int cmp; 03836 ibool contains_null; 03837 ulint i; 03838 ulint cnt; 03839 mem_heap_t* heap = NULL; 03840 ulint offsets_[REC_OFFS_NORMAL_SIZE]; 03841 ulint* offsets = offsets_; 03842 *offsets_ = (sizeof offsets_) / sizeof *offsets_; 03843 03844 *n_rows = 0; 03845 03846 buf = mem_alloc(UNIV_PAGE_SIZE); 03847 heap = mem_heap_create(100); 03848 03849 /* Make a dummy template in prebuilt, which we will use 03850 in scanning the index entries */ 03851 03852 prebuilt->index = index; 03853 prebuilt->sql_stat_start = TRUE; 03854 prebuilt->template_type = ROW_MYSQL_DUMMY_TEMPLATE; 03855 prebuilt->n_template = 0; 03856 prebuilt->need_to_access_clustered = FALSE; 03857 03858 dtuple_set_n_fields(prebuilt->search_tuple, 0); 03859 03860 prebuilt->select_lock_type = LOCK_NONE; 03861 cnt = 1000; 03862 03863 ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, 0); 03864 loop: 03865 /* Check thd->killed every 1,000 scanned rows */ 03866 if (--cnt == 0) { 03867 if (trx_is_interrupted(prebuilt->trx)) { 03868 goto func_exit; 03869 } 03870 cnt = 1000; 03871 } 03872 if (ret != DB_SUCCESS) { 03873 func_exit: 03874 mem_free(buf); 03875 mem_heap_free(heap); 03876 03877 return(is_ok); 03878 } 03879 03880 *n_rows = *n_rows + 1; 03881 03882 /* row_search... returns the index record in buf, record origin offset 03883 within buf stored in the first 4 bytes, because we have built a dummy 03884 template */ 03885 03886 rec = buf + mach_read_from_4(buf); 03887 03888 if (prev_entry != NULL) { 03889 matched_fields = 0; 03890 matched_bytes = 0; 03891 03892 offsets = rec_get_offsets(rec, index, offsets, 03893 ULINT_UNDEFINED, &heap); 03894 cmp = cmp_dtuple_rec_with_match(prev_entry, rec, offsets, 03895 &matched_fields, 03896 &matched_bytes); 03897 contains_null = FALSE; 03898 03899 /* In a unique secondary index we allow equal key values if 03900 they contain SQL NULLs */ 03901 03902 for (i = 0; 03903 i < dict_index_get_n_ordering_defined_by_user(index); 03904 i++) { 03905 if (UNIV_SQL_NULL == dfield_get_len( 03906 dtuple_get_nth_field(prev_entry, i))) { 03907 03908 contains_null = TRUE; 03909 } 03910 } 03911 03912 if (cmp > 0) { 03913 fputs("InnoDB: index records in a wrong order in ", 03914 stderr); 03915 not_ok: 03916 dict_index_name_print(stderr, 03917 prebuilt->trx, index); 03918 fputs("\n" 03919 "InnoDB: prev record ", stderr); 03920 dtuple_print(stderr, prev_entry); 03921 fputs("\n" 03922 "InnoDB: record ", stderr); 03923 rec_print_new(stderr, rec, offsets); 03924 putc('\n', stderr); 03925 is_ok = FALSE; 03926 } else if ((index->type & DICT_UNIQUE) 03927 && !contains_null 03928 && matched_fields >= 03929 dict_index_get_n_ordering_defined_by_user(index)) { 03930 03931 fputs("InnoDB: duplicate key in ", stderr); 03932 goto not_ok; 03933 } 03934 } 03935 03936 mem_heap_empty(heap); 03937 offsets = offsets_; 03938 03939 prev_entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap); 03940 03941 ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, ROW_SEL_NEXT); 03942 03943 goto loop; 03944 } 03945 03946 /************************************************************************* 03947 Checks a table for corruption. */ 03948 03949 ulint 03950 row_check_table_for_mysql( 03951 /*======================*/ 03952 /* out: DB_ERROR or DB_SUCCESS */ 03953 row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL 03954 handle */ 03955 { 03956 dict_table_t* table = prebuilt->table; 03957 dict_index_t* index; 03958 ulint n_rows; 03959 ulint n_rows_in_table = ULINT_UNDEFINED; 03960 ulint ret = DB_SUCCESS; 03961 ulint old_isolation_level; 03962 03963 if (prebuilt->table->ibd_file_missing) { 03964 ut_print_timestamp(stderr); 03965 fprintf(stderr, " InnoDB: Error:\n" 03966 "InnoDB: MySQL is trying to use a table handle but the .ibd file for\n" 03967 "InnoDB: table %s does not exist.\n" 03968 "InnoDB: Have you deleted the .ibd file from the database directory under\n" 03969 "InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n" 03970 "InnoDB: Look from\n" 03971 "http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" 03972 "InnoDB: how you can resolve the problem.\n", 03973 prebuilt->table->name); 03974 return(DB_ERROR); 03975 } 03976 03977 prebuilt->trx->op_info = "checking table"; 03978 03979 old_isolation_level = prebuilt->trx->isolation_level; 03980 03981 /* We must run the index record counts at an isolation level 03982 >= READ COMMITTED, because a dirty read can see a wrong number 03983 of records in some index; to play safe, we use always 03984 REPEATABLE READ here */ 03985 03986 prebuilt->trx->isolation_level = TRX_ISO_REPEATABLE_READ; 03987 03988 /* Enlarge the fatal lock wait timeout during CHECK TABLE. */ 03989 mutex_enter(&kernel_mutex); 03990 srv_fatal_semaphore_wait_threshold += 7200; /* 2 hours */ 03991 mutex_exit(&kernel_mutex); 03992 03993 index = dict_table_get_first_index(table); 03994 03995 while (index != NULL) { 03996 /* fputs("Validating index ", stderr); 03997 ut_print_name(stderr, trx, FALSE, index->name); 03998 putc('\n', stderr); */ 03999 04000 if (!btr_validate_tree(index->tree, prebuilt->trx)) { 04001 ret = DB_ERROR; 04002 } else { 04003 if (!row_scan_and_check_index(prebuilt, 04004 index, &n_rows)) { 04005 ret = DB_ERROR; 04006 } 04007 04008 if (trx_is_interrupted(prebuilt->trx)) { 04009 break; 04010 } 04011 04012 /* fprintf(stderr, "%lu entries in index %s\n", n_rows, 04013 index->name); */ 04014 04015 if (index == dict_table_get_first_index(table)) { 04016 n_rows_in_table = n_rows; 04017 } else if (n_rows != n_rows_in_table) { 04018 04019 ret = DB_ERROR; 04020 04021 fputs("Error: ", stderr); 04022 dict_index_name_print(stderr, 04023 prebuilt->trx, index); 04024 fprintf(stderr, 04025 " contains %lu entries, should be %lu\n", 04026 (ulong) n_rows, 04027 (ulong) n_rows_in_table); 04028 } 04029 } 04030 04031 index = dict_table_get_next_index(index); 04032 } 04033 04034 /* Restore the original isolation level */ 04035 prebuilt->trx->isolation_level = old_isolation_level; 04036 04037 /* We validate also the whole adaptive hash index for all tables 04038 at every CHECK TABLE */ 04039 04040 if (!btr_search_validate()) { 04041 04042 ret = DB_ERROR; 04043 } 04044 04045 /* Restore the fatal lock wait timeout after CHECK TABLE. */ 04046 mutex_enter(&kernel_mutex); 04047 srv_fatal_semaphore_wait_threshold -= 7200; /* 2 hours */ 04048 mutex_exit(&kernel_mutex); 04049 04050 prebuilt->trx->op_info = ""; 04051 04052 return(ret); 04053 }
1.4.7

