diff options
Diffstat (limited to 'innobase/row')
-rw-r--r-- | innobase/row/row0ins.c | 195 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 793 | ||||
-rw-r--r-- | innobase/row/row0purge.c | 14 | ||||
-rw-r--r-- | innobase/row/row0row.c | 50 | ||||
-rw-r--r-- | innobase/row/row0sel.c | 403 | ||||
-rw-r--r-- | innobase/row/row0uins.c | 7 | ||||
-rw-r--r-- | innobase/row/row0umod.c | 25 | ||||
-rw-r--r-- | innobase/row/row0undo.c | 3 | ||||
-rw-r--r-- | innobase/row/row0upd.c | 46 |
9 files changed, 1222 insertions, 314 deletions
diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index 60dbf059673..f8a98f74c09 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -50,6 +50,15 @@ innobase_invalidate_query_cache( ulint full_name_len); /* in: full name length where also the null chars count */ +/********************************************************************** +This function returns true if SQL-query in the current thread +is either REPLACE or LOAD DATA INFILE REPLACE. +NOTE that /mysql/innobase/row/row0ins.c must contain the +prototype for this function ! */ + +ibool +innobase_query_is_replace(void); +/*===========================*/ /************************************************************************* Creates an insert node struct. */ @@ -252,7 +261,7 @@ row_ins_sec_index_entry_by_modify( heap = mem_heap_create(1024); update = row_upd_build_sec_rec_difference_binary(cursor->index, - entry, rec, heap); + entry, rec, thr_get_trx(thr), heap); if (mode == BTR_MODIFY_LEAF) { /* Try an optimistic updating of the record, keeping changes within the page */ @@ -316,7 +325,7 @@ row_ins_clust_index_entry_by_modify( roll_ptr */ update = row_upd_build_difference_binary(cursor->index, entry, ext_vec, - n_ext_vec, rec, heap); + n_ext_vec, rec, thr_get_trx(thr), heap); if (mode == BTR_MODIFY_LEAF) { /* Try optimistic updating of the record, keeping changes within the page */ @@ -543,7 +552,7 @@ static void row_ins_foreign_report_err( /*=======================*/ - char* errstr, /* in: error string from the viewpoint + const char* errstr, /* in: error string from the viewpoint of the parent table */ que_thr_t* thr, /* in: query thread whose run_node is an update node */ @@ -554,29 +563,30 @@ row_ins_foreign_report_err( table */ { FILE* ef = dict_foreign_err_file; + trx_t* trx = thr_get_trx(thr); mutex_enter(&dict_foreign_err_mutex); rewind(ef); ut_print_timestamp(ef); fputs(" Transaction:\n", ef); - trx_print(ef, thr_get_trx(thr)); + trx_print(ef, trx); fputs("Foreign key constraint fails for table ", ef); - ut_print_name(ef, foreign->foreign_table_name); + ut_print_name(ef, trx, foreign->foreign_table_name); fputs(":\n", ef); - dict_print_info_on_foreign_key_in_create_format(ef, foreign); + dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign); putc('\n', ef); fputs(errstr, ef); fputs(" in parent table, in index ", ef); - ut_print_name(ef, foreign->referenced_index->name); + ut_print_name(ef, trx, foreign->referenced_index->name); if (entry) { fputs(" tuple:\n", ef); dtuple_print(ef, entry); } fputs("\nBut in child table ", ef); - ut_print_name(ef, foreign->foreign_table_name); + ut_print_name(ef, trx, foreign->foreign_table_name); fputs(", in index ", ef); - ut_print_name(ef, foreign->foreign_index->name); + ut_print_name(ef, trx, foreign->foreign_index->name); if (rec) { fputs(", there is a record:\n", ef); rec_print(ef, rec); @@ -612,19 +622,19 @@ row_ins_foreign_report_add_err( fputs(" Transaction:\n", ef); trx_print(ef, trx); fputs("Foreign key constraint fails for table ", ef); - ut_print_name(ef, foreign->foreign_table_name); + ut_print_name(ef, trx, foreign->foreign_table_name); fputs(":\n", ef); - dict_print_info_on_foreign_key_in_create_format(ef, foreign); + dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign); fputs("\nTrying to add in child table, in index ", ef); - ut_print_name(ef, foreign->foreign_index->name); + ut_print_name(ef, trx, foreign->foreign_index->name); if (entry) { fputs(" tuple:\n", ef); dtuple_print(ef, entry); } fputs("\nBut in parent table ", ef); - ut_print_name(ef, foreign->referenced_table_name); + ut_print_name(ef, trx, foreign->referenced_table_name); fputs(", in index ", ef); - ut_print_name(ef, foreign->referenced_index->name); + ut_print_name(ef, trx, foreign->referenced_index->name); fputs(",\nthe closest match we can find is record:\n", ef); if (rec && page_rec_is_supremum(rec)) { /* If the cursor ended on a supremum record, it is better @@ -704,9 +714,13 @@ row_ins_foreign_check_on_constraint( ulint n_to_update; ulint err; ulint i; + trx_t* trx; + ut_a(thr && foreign && pcur && mtr); + trx = thr_get_trx(thr); + /* Since we are going to delete or update a row, we have to invalidate the MySQL query cache for table */ @@ -718,7 +732,7 @@ row_ins_foreign_check_on_constraint( (DICT_FOREIGN_ON_DELETE_CASCADE | DICT_FOREIGN_ON_DELETE_SET_NULL))) { - row_ins_foreign_report_err((char*)"Trying to delete", + row_ins_foreign_report_err("Trying to delete", thr, foreign, btr_pcur_get_rec(pcur), entry); @@ -731,7 +745,7 @@ row_ins_foreign_check_on_constraint( /* This is an UPDATE */ - row_ins_foreign_report_err((char*)"Trying to update", + row_ins_foreign_report_err("Trying to update", thr, foreign, btr_pcur_get_rec(pcur), entry); @@ -792,7 +806,7 @@ row_ins_foreign_check_on_constraint( err = DB_ROW_IS_REFERENCED; row_ins_foreign_report_err( -(char*)"Trying an update, possibly causing a cyclic cascaded update\n" +"Trying an update, possibly causing a cyclic cascaded update\n" "in the child table,", thr, foreign, btr_pcur_get_rec(pcur), entry); goto nonstandard_exit_func; @@ -845,7 +859,7 @@ row_ins_foreign_check_on_constraint( fputs( "InnoDB: error in cascade of a foreign key op\n" "InnoDB: ", stderr); - dict_index_name_print(stderr, index); + dict_index_name_print(stderr, trx, index); fputs("\n" "InnoDB: record ", stderr); @@ -927,7 +941,7 @@ row_ins_foreign_check_on_constraint( err = DB_ROW_IS_REFERENCED; row_ins_foreign_report_err( -(char*)"Trying a cascaded update where the updated value in the child\n" +"Trying a cascaded update where the updated value in the child\n" "table would not fit in the length of the column, or the value would\n" "be NULL and the column is declared as not NULL in the child table,", thr, foreign, btr_pcur_get_rec(pcur), entry); @@ -1037,6 +1051,33 @@ row_ins_set_shared_rec_lock( return(err); } + +/************************************************************************* +Sets a exclusive lock on a record. Used in locking possible duplicate key +records */ +static +ulint +row_ins_set_exclusive_rec_lock( +/*============================*/ + /* out: DB_SUCCESS or error code */ + ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or + LOCK_REC_NOT_GAP type lock */ + rec_t* rec, /* in: record */ + dict_index_t* index, /* in: index */ + que_thr_t* thr) /* in: query thread */ +{ + ulint err; + + if (index->type & DICT_CLUSTERED) { + err = lock_clust_rec_read_check_and_lock(0, rec, index, LOCK_X, + type, thr); + } else { + err = lock_sec_rec_read_check_and_lock(0, rec, index, LOCK_X, + type, thr); + } + + return(err); +} /******************************************************************* Checks if foreign key constraint fails for an index entry. Sets shared locks @@ -1072,6 +1113,7 @@ row_ins_check_foreign_constraint( ulint err; ulint i; mtr_t mtr; + trx_t* trx = thr_get_trx(thr); run_again: #ifdef UNIV_SYNC_DEBUG @@ -1080,7 +1122,7 @@ run_again: err = DB_SUCCESS; - if (thr_get_trx(thr)->check_foreigns == FALSE) { + if (trx->check_foreigns == FALSE) { /* The user has suppressed foreign key checks currently for this session */ @@ -1131,26 +1173,26 @@ run_again: check_index = foreign->foreign_index; } - if (check_table == NULL) { + if (check_table == NULL || check_table->ibd_file_missing) { if (check_ref) { FILE* ef = dict_foreign_err_file; mutex_enter(&dict_foreign_err_mutex); rewind(ef); ut_print_timestamp(ef); fputs(" Transaction:\n", ef); - trx_print(ef, thr_get_trx(thr)); + trx_print(ef, trx); fputs("Foreign key constraint fails for table ", ef); - ut_print_name(ef, foreign->foreign_table_name); + ut_print_name(ef, trx, foreign->foreign_table_name); fputs(":\n", ef); dict_print_info_on_foreign_key_in_create_format(ef, - foreign); + trx, foreign); fputs("\nTrying to add to index ", ef); - ut_print_name(ef, foreign->foreign_index->name); + ut_print_name(ef, trx, foreign->foreign_index->name); fputs(" tuple:\n", ef); dtuple_print(ef, entry); fputs("\nBut the parent table ", ef); - ut_print_name(ef, foreign->referenced_table_name); - fputs(" does not currently exist!\n", ef); + ut_print_name(ef, trx, foreign->referenced_table_name); + fputs("\nor its .ibd file does not currently exist!\n", ef); mutex_exit(&dict_foreign_err_mutex); return(DB_NO_REFERENCED_ROW); @@ -1262,7 +1304,7 @@ run_again: } } else { row_ins_foreign_report_err( - (char*)"Trying to delete or update", + "Trying to delete or update", thr, foreign, rec, entry); err = DB_ROW_IS_REFERENCED; @@ -1282,7 +1324,7 @@ run_again: if (check_ref) { err = DB_NO_REFERENCED_ROW; row_ins_foreign_report_add_err( - thr_get_trx(thr), foreign, rec, entry); + trx, foreign, rec, entry); } else { err = DB_SUCCESS; } @@ -1298,7 +1340,7 @@ next_rec: if (check_ref) { rec = btr_pcur_get_rec(&pcur); row_ins_foreign_report_add_err( - thr_get_trx(thr), foreign, rec, entry); + trx, foreign, rec, entry); err = DB_NO_REFERENCED_ROW; } else { err = DB_SUCCESS; @@ -1317,18 +1359,18 @@ next_rec: do_possible_lock_wait: if (err == DB_LOCK_WAIT) { - thr_get_trx(thr)->error_state = err; + trx->error_state = err; que_thr_stop_for_mysql(thr); srv_suspend_mysql_thread(thr); - if (thr_get_trx(thr)->error_state == DB_SUCCESS) { + if (trx->error_state == DB_SUCCESS) { goto run_again; } - err = thr_get_trx(thr)->error_state; + err = trx->error_state; } return(err); @@ -1492,7 +1534,8 @@ row_ins_scan_sec_index_for_duplicate( ulint err = DB_SUCCESS; ibool moved; mtr_t mtr; - + trx_t* trx; + n_unique = dict_index_get_n_unique(index); /* If the secondary index is unique, but one of the fields in the @@ -1529,8 +1572,23 @@ row_ins_scan_sec_index_for_duplicate( /* Try to place a lock on the index record */ - err = row_ins_set_shared_rec_lock(LOCK_ORDINARY, rec, index, - thr); + trx = thr_get_trx(thr); + ut_ad(trx); + + if (innobase_query_is_replace()) { + + /* The manual defines the REPLACE semantics that it + is either an INSERT or DELETE(s) for duplicate key + + INSERT. Therefore, we should take X-lock for + duplicates */ + + err = row_ins_set_exclusive_rec_lock( + LOCK_ORDINARY,rec,index,thr); + } else { + + err = row_ins_set_shared_rec_lock( + LOCK_ORDINARY, rec, index,thr); + } if (err != DB_SUCCESS) { @@ -1629,9 +1687,24 @@ row_ins_duplicate_error_in_clust( is needed in logical logging of MySQL to make sure that in roll-forward we get the same duplicate errors as in original execution */ - - err = row_ins_set_shared_rec_lock(LOCK_REC_NOT_GAP, - rec, cursor->index, thr); + + if (innobase_query_is_replace()) { + + /* The manual defines the REPLACE semantics + that it is either an INSERT or DELETE(s) + for duplicate key + INSERT. Therefore, we + should take X-lock for duplicates */ + + err = row_ins_set_exclusive_rec_lock( + LOCK_REC_NOT_GAP,rec,cursor->index, + thr); + } else { + + err = row_ins_set_shared_rec_lock( + LOCK_REC_NOT_GAP,rec, cursor->index, + thr); + } + if (err != DB_SUCCESS) { return(err); @@ -1652,8 +1725,24 @@ row_ins_duplicate_error_in_clust( if (rec != page_get_supremum_rec(page)) { - err = row_ins_set_shared_rec_lock(LOCK_REC_NOT_GAP, - rec, cursor->index, thr); + + /* The manual defines the REPLACE semantics that it + is either an INSERT or DELETE(s) for duplicate key + + INSERT. Therefore, we should take X-lock for + duplicates. */ + + if (innobase_query_is_replace()) { + + err = row_ins_set_exclusive_rec_lock( + LOCK_REC_NOT_GAP, + rec,cursor->index,thr); + } else { + + err = row_ins_set_shared_rec_lock( + LOCK_REC_NOT_GAP,rec, + cursor->index, thr); + } + if (err != DB_SUCCESS) { return(err); @@ -1747,6 +1836,7 @@ row_ins_index_entry_low( ulint modify = 0; /* remove warning */ rec_t* insert_rec; rec_t* rec; + rec_t* first_rec; ulint err; ulint n_unique; big_rec_t* big_rec = NULL; @@ -1779,6 +1869,14 @@ row_ins_index_entry_low( goto function_exit; } + first_rec = page_rec_get_next(page_get_infimum_rec( + buf_frame_align(btr_cur_get_rec(&cursor)))); + + if (!page_rec_is_supremum(first_rec)) { + ut_a((rec_get_n_fields(first_rec)) + == dtuple_get_n_fields(entry)); + } + n_unique = dict_index_get_n_unique(index); if (index->type & DICT_UNIQUE && (cursor.up_match >= n_unique @@ -1945,6 +2043,7 @@ row_ins_index_entry_set_vals( dfield_t* row_field; ulint n_fields; ulint i; + dtype_t* cur_type; ut_ad(entry && row); @@ -1958,10 +2057,14 @@ row_ins_index_entry_set_vals( /* Check column prefix indexes */ if (ind_field->prefix_len > 0 - && dfield_get_len(row_field) != UNIV_SQL_NULL - && dfield_get_len(row_field) > ind_field->prefix_len) { - - field->len = ind_field->prefix_len; + && dfield_get_len(row_field) != UNIV_SQL_NULL) { + + cur_type = dict_col_get_type( + dict_field_get_col(ind_field)); + + field->len = dtype_get_at_most_n_mbchars(cur_type, + ind_field->prefix_len, + dfield_get_len(row_field), row_field->data); } else { field->len = row_field->len; } @@ -2246,4 +2349,4 @@ error_handling: } return(thr); -} +} diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index ad5efc160c8..47f1f4c444c 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -22,12 +22,15 @@ Created 9/17/2000 Heikki Tuuri #include "dict0dict.h" #include "dict0crea.h" #include "dict0load.h" +#include "dict0boot.h" #include "trx0roll.h" #include "trx0purge.h" #include "lock0lock.h" #include "rem0cmp.h" #include "log0log.h" #include "btr0sea.h" +#include "fil0fil.h" +#include "ibuf0ibuf.h" /* A dummy variable used to fool the compiler */ ibool row_mysql_identically_false = FALSE; @@ -117,6 +120,19 @@ row_mysql_read_var_ref_noninline( } /*********************************************************************** +Frees the blob heap in prebuilt when no longer needed. */ + +void +row_mysql_prebuilt_free_blob_heap( +/*==============================*/ + row_prebuilt_t* prebuilt) /* in: prebuilt struct of a + ha_innobase:: table handle */ +{ + mem_heap_free(prebuilt->blob_heap); + prebuilt->blob_heap = NULL; +} + +/*********************************************************************** Stores a reference to a BLOB in the MySQL format. */ void @@ -292,7 +308,8 @@ handle_new_error: return(TRUE); - } else if (err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT) { + } else if (err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT + || err == DB_LOCK_TABLE_FULL) { /* Roll back the whole transaction; this resolution was added to version 3.23.43 */ @@ -317,7 +334,7 @@ handle_new_error: exit(1); } else if (err == DB_CORRUPTION) { - fputs( + fputs( "InnoDB: We detected index corruption in an InnoDB type table.\n" "InnoDB: You have to dump + drop + reimport the table or, in\n" "InnoDB: a case of widespread corruption, dump all InnoDB\n" @@ -328,7 +345,8 @@ handle_new_error: " for help.\n", stderr); } else { - fprintf(stderr, "InnoDB: unknown error code %lu\n", err); + fprintf(stderr, "InnoDB: unknown error code %lu\n", + (ulong) err); ut_error; } @@ -438,9 +456,10 @@ row_prebuilt_free( || prebuilt->magic_n2 != ROW_PREBUILT_ALLOCATED) { fprintf(stderr, "InnoDB: Error: trying to free a corrupt\n" -"InnoDB: table handle. Magic n %lu, magic n2 %lu, table name ", - prebuilt->magic_n, prebuilt->magic_n2); - ut_print_name(stderr, prebuilt->table->name); +"InnoDB: table handle. Magic n %lu, magic n2 %lu, table name", + (ulong) prebuilt->magic_n, + (ulong) prebuilt->magic_n2); + ut_print_name(stderr, NULL, prebuilt->table->name); putc('\n', stderr); mem_analyze_corruption((byte*)prebuilt); @@ -521,7 +540,7 @@ row_update_prebuilt_trx( fprintf(stderr, "InnoDB: Error: trying to use a corrupt\n" "InnoDB: trx handle. Magic n %lu\n", - trx->magic_n); + (ulong) trx->magic_n); mem_analyze_corruption((byte*)trx); @@ -531,9 +550,9 @@ row_update_prebuilt_trx( if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { fprintf(stderr, "InnoDB: Error: trying to use a corrupt\n" - "InnoDB: table handle. Magic n %lu, table name ", - prebuilt->magic_n); - ut_print_name(stderr, prebuilt->table->name); + "InnoDB: table handle. Magic n %lu, table name", + (ulong) prebuilt->magic_n); + ut_print_name(stderr, NULL, prebuilt->table->name); putc('\n', stderr); mem_analyze_corruption((byte*)prebuilt); @@ -688,7 +707,7 @@ row_lock_table_autoinc_for_mysql( return(DB_SUCCESS); } - trx->op_info = (char *) "setting auto-inc lock"; + trx->op_info = "setting auto-inc lock"; if (node == NULL) { row_get_prebuilt_insert_row(prebuilt); @@ -724,14 +743,14 @@ run_again: goto run_again; } - trx->op_info = (char *) ""; + trx->op_info = ""; return(err); } que_thr_stop_for_mysql_no_error(thr, trx); - trx->op_info = (char *) ""; + trx->op_info = ""; return((int) err); } @@ -761,8 +780,13 @@ int row_lock_table_for_mysql( /*=====================*/ /* out: error code or DB_SUCCESS */ - row_prebuilt_t* prebuilt) /* in: prebuilt struct in the MySQL + row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL table handle */ + dict_table_t* table, /* in: table to lock, or NULL + if prebuilt->table should be + locked as LOCK_TABLE_EXP | + prebuilt->select_lock_type */ + ulint mode) /* in: lock mode of table */ { trx_t* trx = prebuilt->trx; que_thr_t* thr; @@ -772,7 +796,7 @@ row_lock_table_for_mysql( ut_ad(trx); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); - trx->op_info = (char *) "setting table lock"; + trx->op_info = "setting table lock"; if (prebuilt->sel_graph == NULL) { /* Build a dummy select query graph */ @@ -795,8 +819,12 @@ run_again: trx_start_if_not_started(trx); - err = lock_table(LOCK_TABLE_EXP, prebuilt->table, - prebuilt->select_lock_type, thr); + if (table) { + err = lock_table(0, table, mode, thr); + } else { + err = lock_table(LOCK_TABLE_EXP, prebuilt->table, + prebuilt->select_lock_type, thr); + } trx->error_state = err; @@ -809,14 +837,14 @@ run_again: goto run_again; } - trx->op_info = (char *) ""; + trx->op_info = ""; return(err); } que_thr_stop_for_mysql_no_error(thr, trx); - trx->op_info = (char *) ""; + trx->op_info = ""; return((int) err); } @@ -841,13 +869,27 @@ row_insert_for_mysql( ut_ad(trx); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); - + + if (prebuilt->table->ibd_file_missing) { + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Error:\n" +"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n" +"InnoDB: table %s does not exist.\n" +"InnoDB: Have you deleted the .ibd file from the database directory under\n" +"InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n" +"InnoDB: Look from\n" +"http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" +"InnoDB: how you can resolve the problem.\n", + prebuilt->table->name); + return(DB_ERROR); + } + if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { fprintf(stderr, "InnoDB: Error: trying to free a corrupt\n" - "InnoDB: table handle. Magic n %lu, table name ", - prebuilt->magic_n); - ut_print_name(stderr, prebuilt->table->name); + "InnoDB: table handle. Magic n %lu, table name", + (ulong) prebuilt->magic_n); + ut_print_name(stderr, prebuilt->trx, prebuilt->table->name); putc('\n', stderr); mem_analyze_corruption((byte*)prebuilt); @@ -867,7 +909,7 @@ row_insert_for_mysql( return(DB_ERROR); } - trx->op_info = (char *) "inserting"; + trx->op_info = "inserting"; row_mysql_delay_if_needed(); @@ -910,7 +952,7 @@ run_again: goto run_again; } - trx->op_info = (char *) ""; + trx->op_info = ""; return(err); } @@ -927,7 +969,7 @@ run_again: } row_update_statistics_if_needed(prebuilt->table); - trx->op_info = (char *) ""; + trx->op_info = ""; return((int) err); } @@ -1059,12 +1101,26 @@ row_update_for_mysql( ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); UT_NOT_USED(mysql_rec); + if (prebuilt->table->ibd_file_missing) { + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Error:\n" +"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n" +"InnoDB: table %s does not exist.\n" +"InnoDB: Have you deleted the .ibd file from the database directory under\n" +"InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n" +"InnoDB: Look from\n" +"http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" +"InnoDB: how you can resolve the problem.\n", + prebuilt->table->name); + return(DB_ERROR); + } + if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { fprintf(stderr, "InnoDB: Error: trying to free a corrupt\n" - "InnoDB: table handle. Magic n %lu, table name ", - prebuilt->magic_n); - ut_print_name(stderr, prebuilt->table->name); + "InnoDB: table handle. Magic n %lu, table name", + (ulong) prebuilt->magic_n); + ut_print_name(stderr, prebuilt->trx, prebuilt->table->name); putc('\n', stderr); mem_analyze_corruption((byte*)prebuilt); @@ -1084,7 +1140,7 @@ row_update_for_mysql( return(DB_ERROR); } - trx->op_info = (char *) "updating or deleting"; + trx->op_info = "updating or deleting"; row_mysql_delay_if_needed(); @@ -1133,7 +1189,7 @@ run_again: if (err == DB_RECORD_NOT_FOUND) { trx->error_state = DB_SUCCESS; - trx->op_info = (char *) ""; + trx->op_info = ""; return((int) err); } @@ -1144,7 +1200,7 @@ run_again: goto run_again; } - trx->op_info = (char *) ""; + trx->op_info = ""; return(err); } @@ -1163,7 +1219,7 @@ run_again: row_update_statistics_if_needed(prebuilt->table); - trx->op_info = (char *) ""; + trx->op_info = ""; return((int) err); } @@ -1368,7 +1424,8 @@ row_mysql_lock_data_dictionary( /*===========================*/ trx_t* trx) /* in: transaction */ { - ut_a(trx->dict_operation_lock_mode == 0); + ut_a(trx->dict_operation_lock_mode == 0 + || trx->dict_operation_lock_mode == RW_X_LATCH); /* Serialize data dictionary operations with dictionary mutex: no deadlocks or lock waits can occur then in these operations */ @@ -1403,6 +1460,7 @@ Does a table creation operation for MySQL. If the name of the table to be created is equal with one of the predefined magic table names, then this also starts printing the corresponding monitor output by the master thread. */ + int row_create_table_for_mysql( /*=======================*/ @@ -1438,7 +1496,7 @@ row_create_table_for_mysql( return(DB_ERROR); } - trx->op_info = (char *) "creating table"; + trx->op_info = "creating table"; if (row_mysql_is_system_table(table->name)) { @@ -1549,16 +1607,15 @@ row_create_table_for_mysql( if (err == DB_OUT_OF_FILE_SPACE) { fputs("InnoDB: Warning: cannot create table ", stderr); - ut_print_name(stderr, table->name); + ut_print_name(stderr, trx, table->name); fputs(" because tablespace full\n", stderr); row_drop_table_for_mysql(table->name, trx, FALSE); - } else { - ut_a(err == DB_DUPLICATE_KEY); + } else if (err == DB_DUPLICATE_KEY) { ut_print_timestamp(stderr); fputs(" InnoDB: Error: table ", stderr); - ut_print_name(stderr, table->name); + ut_print_name(stderr, trx, table->name); fputs(" already exists in InnoDB internal\n" "InnoDB: data dictionary. Have you deleted the .frm file\n" "InnoDB: and not used DROP TABLE? Have you used DROP DATABASE\n" @@ -1566,20 +1623,23 @@ row_create_table_for_mysql( "InnoDB: See the Restrictions section of the InnoDB manual.\n" "InnoDB: You can drop the orphaned table inside InnoDB by\n" "InnoDB: creating an InnoDB table with the same name in another\n" - "InnoDB: database and moving the .frm file to the current database.\n" + "InnoDB: database and copying the .frm file to the current database.\n" "InnoDB: Then MySQL thinks the table exists, and DROP TABLE will\n" "InnoDB: succeed.\n" "InnoDB: You can look for further help from\n" "InnoDB: http://dev.mysql.com/doc/mysql/en/" "InnoDB_troubleshooting_datadict.html\n", stderr); } + + /* We may also get err == DB_ERROR if the .ibd file for the + table already exists */ trx->error_state = DB_SUCCESS; } que_graph_free((que_t*) que_node_get_parent(thr)); - trx->op_info = (char *) ""; + trx->op_info = ""; return((int) err); } @@ -1608,7 +1668,9 @@ row_create_index_for_mysql( #endif /* UNIV_SYNC_DEBUG */ ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); - trx->op_info = (char *) "creating index"; + trx->op_info = "creating index"; + + trx_start_if_not_started(trx); /* Check that the same column does not appear twice in the index. Starting from 4.0.14, InnoDB should be able to cope with that, but @@ -1623,10 +1685,10 @@ row_create_index_for_mysql( ut_print_timestamp(stderr); fputs(" InnoDB: Error: column ", stderr); - ut_print_name(stderr, + ut_print_name(stderr, trx, dict_index_get_nth_field(index, i)->name); fputs(" appears twice in ", stderr); - dict_index_name_print(stderr, index); + dict_index_name_print(stderr, trx, index); fputs("\n" "InnoDB: This is not allowed in InnoDB.\n", stderr); @@ -1636,9 +1698,16 @@ row_create_index_for_mysql( goto error_handling; } } - } + + /* Check also that prefix_len < DICT_MAX_COL_PREFIX_LEN */ - trx_start_if_not_started(trx); + if (dict_index_get_nth_field(index, i)->prefix_len + >= DICT_MAX_COL_PREFIX_LEN) { + err = DB_TOO_BIG_RECORD; + + goto error_handling; + } + } if (row_mysql_is_recovered_tmp_table(index->table_name)) { @@ -1649,6 +1718,9 @@ row_create_index_for_mysql( trx->dict_operation = TRUE; + /* Note that the space id where we store the index is inherited from + the table in dict_build_index_def_step() in dict0crea.c. */ + node = ind_create_graph_create(index, heap); thr = pars_complete_graph_for_exec(node, trx, heap); @@ -1661,7 +1733,6 @@ row_create_index_for_mysql( que_graph_free((que_t*) que_node_get_parent(thr)); error_handling: - if (err != DB_SUCCESS) { /* We have special error handling here */ @@ -1674,7 +1745,7 @@ error_handling: trx->error_state = DB_SUCCESS; } - trx->op_info = (char *) ""; + trx->op_info = ""; return((int) err); } @@ -1691,15 +1762,16 @@ constraints which reference this table are ok. */ int row_table_add_foreign_constraints( /*==============================*/ - /* out: error code or DB_SUCCESS */ - trx_t* trx, /* in: transaction */ - char* sql_string, /* in: table create statement where - foreign keys are declared like: + /* out: error code or DB_SUCCESS */ + trx_t* trx, /* in: transaction */ + const char* sql_string, /* in: table create statement where + foreign keys are declared like: FOREIGN KEY (a, b) REFERENCES table2(c, d), - table2 can be written also with the database - name before it: test.table2 */ - char* name) /* in: table full name in the normalized form - database_name/table_name */ + table2 can be written also with the + database name before it: test.table2 */ + const char* name) /* in: table full name in the + normalized form + database_name/table_name */ { ulint err; @@ -1709,7 +1781,7 @@ row_table_add_foreign_constraints( #endif /* UNIV_SYNC_DEBUG */ ut_a(sql_string); - trx->op_info = (char *) "adding foreign keys"; + trx->op_info = "adding foreign keys"; trx_start_if_not_started(trx); @@ -1753,8 +1825,8 @@ static int row_drop_table_for_mysql_in_background( /*===================================*/ - /* out: error code or DB_SUCCESS */ - char* name) /* in: table name */ + /* out: error code or DB_SUCCESS */ + const char* name) /* in: table name */ { ulint error; trx_t* trx; @@ -1778,7 +1850,7 @@ row_drop_table_for_mysql_in_background( if (error != DB_SUCCESS) { ut_print_timestamp(stderr); fputs(" InnoDB: Error: Dropping table ", stderr); - ut_print_name(stderr, name); + ut_print_name(stderr, trx, name); fputs(" in background drop list failed\n", stderr); } @@ -1857,9 +1929,9 @@ already_dropped: UT_LIST_REMOVE(row_mysql_drop_list, row_mysql_drop_list, drop); ut_print_timestamp(stderr); - fputs(" InnoDB: Dropped table ", stderr); - ut_print_name(stderr, drop->table_name); - fputs(" in background drop queue.\n", stderr); + fprintf(stderr, + " InnoDB: Dropped table %s in background drop queue.\n", + drop->table_name); mem_free(drop->table_name); @@ -1926,6 +1998,355 @@ row_add_table_to_background_drop_list( } /************************************************************************* +Discards the tablespace of a table which stored in an .ibd file. Discarding +means that this function deletes the .ibd file and assigns a new table id for +the table. Also the flag table->ibd_file_missing is set TRUE. */ + +int +row_discard_tablespace_for_mysql( +/*=============================*/ + /* out: error code or DB_SUCCESS */ + const char* name, /* in: table name */ + trx_t* trx) /* in: transaction handle */ +{ + dict_foreign_t* foreign; + dulint new_id; + dict_table_t* table; + que_thr_t* thr; + que_t* graph = NULL; + ibool success; + ulint err; + char* buf; + +/* How do we prevent crashes caused by ongoing operations on the table? Old +operations could try to access non-existent pages. + +1) SQL queries, INSERT, SELECT, ...: we must get an exclusive MySQL table lock +on the table before we can do DISCARD TABLESPACE. Then there are no running +queries on the table. +2) Purge and rollback: we assign a new table id for the table. Since purge and +rollback look for the table based on the table id, they see the table as +'dropped' and discard their operations. +3) Insert buffer: we remove all entries for the tablespace in the insert +buffer tree; as long as the tablespace mem object does not exist, ongoing +insert buffer page merges are discarded in buf0rea.c. If we recreate the +tablespace mem object with IMPORT TABLESPACE later, then the tablespace will +have the same id, but the tablespace_version field in the mem object is +different, and ongoing old insert buffer page merges get discarded. +4) Linear readahead and random readahead: we use the same method as in 3) to +discard ongoing operations. +5) FOREIGN KEY operations: if table->n_foreign_key_checks_running > 0, we +do not allow the discard. We also reserve the data dictionary latch. */ + + static const char discard_tablespace_proc1[] = + "PROCEDURE DISCARD_TABLESPACE_PROC () IS\n" + "old_id CHAR;\n" + "new_id CHAR;\n" + "new_id_low INT;\n" + "new_id_high INT;\n" + "table_name CHAR;\n" + "BEGIN\n" + "table_name := '"; + static const char discard_tablespace_proc2[] = + "';\n" + "new_id_high := %lu;\n" + "new_id_low := %lu;\n" + "new_id := CONCAT(TO_BINARY(new_id_high, 4), TO_BINARY(new_id_low, 4));\n" + "SELECT ID INTO old_id\n" + "FROM SYS_TABLES\n" + "WHERE NAME = table_name;\n" + "IF (SQL %% NOTFOUND) THEN\n" + " COMMIT WORK;\n" + " RETURN;\n" + "END IF;\n" + "UPDATE SYS_TABLES SET ID = new_id\n" + "WHERE ID = old_id;\n" + "UPDATE SYS_COLUMNS SET TABLE_ID = new_id\n" + "WHERE TABLE_ID = old_id;\n" + "UPDATE SYS_INDEXES SET TABLE_ID = new_id\n" + "WHERE TABLE_ID = old_id;\n" + "COMMIT WORK;\n" + "END;\n"; + + ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); + + trx->op_info = "discarding tablespace"; + trx_start_if_not_started(trx); + + /* Serialize data dictionary operations with dictionary mutex: + no deadlocks can occur then in these operations */ + + row_mysql_lock_data_dictionary(trx); + + table = dict_table_get_low(name); + + if (!table) { + err = DB_TABLE_NOT_FOUND; + + goto funct_exit; + } + + if (table->space == 0) { + ut_print_timestamp(stderr); + fputs(" InnoDB: Error: table ", stderr); + ut_print_name(stderr, trx, name); + fputs("\n" +"InnoDB: is in the system tablespace 0 which cannot be discarded\n", stderr); + err = DB_ERROR; + + goto funct_exit; + } + + if (table->n_foreign_key_checks_running > 0) { + + ut_print_timestamp(stderr); + fputs(" InnoDB: You are trying to DISCARD table ", stderr); + ut_print_name(stderr, trx, table->name); + fputs("\n" + "InnoDB: though there is a foreign key check running on it.\n" + "InnoDB: Cannot discard the table.\n", + stderr); + + err = DB_ERROR; + + goto funct_exit; + } + + /* Check if the table is referenced by foreign key constraints from + some other table (not the table itself) */ + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign && foreign->foreign_table == table) { + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } + + if (foreign && trx->check_foreigns) { + + FILE* ef = dict_foreign_err_file; + + /* We only allow discarding a referenced table if + FOREIGN_KEY_CHECKS is set to 0 */ + + err = DB_CANNOT_DROP_CONSTRAINT; + + mutex_enter(&dict_foreign_err_mutex); + rewind(ef); + ut_print_timestamp(ef); + + fputs(" Cannot DISCARD table ", ef); + ut_print_name(ef, trx, name); + fputs("\n" + "because it is referenced by ", ef); + ut_print_name(ef, trx, foreign->foreign_table_name); + putc('\n', ef); + mutex_exit(&dict_foreign_err_mutex); + + goto funct_exit; + } + + new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); + + buf = mem_alloc((sizeof discard_tablespace_proc1) + + (sizeof discard_tablespace_proc2) + + 20 + ut_strlenq(name, '\'')); + + memcpy(buf, discard_tablespace_proc1, sizeof discard_tablespace_proc1); + sprintf(ut_strcpyq(buf + (sizeof discard_tablespace_proc1 - 1), + '\'', name), + discard_tablespace_proc2, + (ulong) ut_dulint_get_high(new_id), + (ulong) ut_dulint_get_low(new_id)); + + graph = pars_sql(buf); + + ut_a(graph); + + /* Remove any locks there are on the table or its records */ + + lock_reset_all_on_table(table); + + graph->trx = trx; + trx->graph = NULL; + + graph->fork_type = QUE_FORK_MYSQL_INTERFACE; + + ut_a(thr = que_fork_start_command(graph)); + + que_run_threads(thr); + + err = trx->error_state; + + if (err != DB_SUCCESS) { + trx->error_state = DB_SUCCESS; + trx_general_rollback_for_mysql(trx, FALSE, NULL); + trx->error_state = DB_SUCCESS; + } else { + dict_table_change_id_in_cache(table, new_id); + + success = fil_discard_tablespace(table->space); + + if (!success) { + trx->error_state = DB_SUCCESS; + trx_general_rollback_for_mysql(trx, FALSE, NULL); + trx->error_state = DB_SUCCESS; + + err = DB_ERROR; + } else { + /* Set the flag which tells that now it is legal to + IMPORT a tablespace for this table */ + table->tablespace_discarded = TRUE; + table->ibd_file_missing = TRUE; + } + } +funct_exit: + row_mysql_unlock_data_dictionary(trx); + + if (graph) { + que_graph_free(graph); + } + + trx_commit_for_mysql(trx); + + trx->op_info = ""; + + return((int) err); +} + +/********************************************************************* +Imports a tablespace. The space id in the .ibd file must match the space id +of the table in the data dictionary. */ + +int +row_import_tablespace_for_mysql( +/*============================*/ + /* out: error code or DB_SUCCESS */ + const char* name, /* in: table name */ + trx_t* trx) /* in: transaction handle */ +{ + dict_table_t* table; + ibool success; + dulint current_lsn; + ulint err = DB_SUCCESS; + + ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); + + trx_start_if_not_started(trx); + + trx->op_info = "importing tablespace"; + + current_lsn = log_get_lsn(); + + /* It is possible, though very improbable, that the lsn's in the + tablespace to be imported have risen above the current system lsn, if + a lengthy purge, ibuf merge, or rollback was performed on a backup + taken with ibbackup. If that is the case, reset page lsn's in the + file. We assume that mysqld was shut down after it performed these + cleanup operations on the .ibd file, so that it stamped the latest lsn + to the FIL_PAGE_FILE_FLUSH_LSN in the first page of the .ibd file. + + TODO: reset also the trx id's in clustered index records and write + a new space id to each data page. That would allow us to import clean + .ibd files from another MySQL installation. */ + + success = fil_reset_too_high_lsns(name, current_lsn); + + if (!success) { + ut_print_timestamp(stderr); + fputs(" InnoDB: Error: cannot reset lsn's in table ", stderr); + ut_print_name(stderr, trx, name); + fputs("\n" + "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n", stderr); + + err = DB_ERROR; + + row_mysql_lock_data_dictionary(trx); + + goto funct_exit; + } + + /* Serialize data dictionary operations with dictionary mutex: + no deadlocks can occur then in these operations */ + + row_mysql_lock_data_dictionary(trx); + + table = dict_table_get_low(name); + + if (!table) { + ut_print_timestamp(stderr); + fputs(" InnoDB: table ", stderr); + ut_print_name(stderr, trx, name); + fputs("\n" +"InnoDB: does not exist in the InnoDB data dictionary\n" +"InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n", + stderr); + + err = DB_TABLE_NOT_FOUND; + + goto funct_exit; + } + + if (table->space == 0) { + ut_print_timestamp(stderr); + fputs(" InnoDB: Error: table ", stderr); + ut_print_name(stderr, trx, name); + fputs("\n" +"InnoDB: is in the system tablespace 0 which cannot be imported\n", stderr); + err = DB_ERROR; + + goto funct_exit; + } + + if (!table->tablespace_discarded) { + ut_print_timestamp(stderr); + fputs( +" InnoDB: Error: you are trying to IMPORT a tablespace\n" +"InnoDB: ", stderr); + ut_print_name(stderr, trx, name); + fputs(", though you have not called DISCARD on it yet\n" +"InnoDB: during the lifetime of the mysqld process!\n", stderr); + + err = DB_ERROR; + + goto funct_exit; + } + + /* Play safe and remove all insert buffer entries, though we should + have removed them already when DISCARD TABLESPACE was called */ + + ibuf_delete_for_discarded_space(table->space); + + success = fil_open_single_table_tablespace(TRUE, table->space, + table->name); + if (success) { + table->ibd_file_missing = FALSE; + table->tablespace_discarded = FALSE; + } else { + if (table->ibd_file_missing) { + ut_print_timestamp(stderr); + fputs( +" InnoDB: cannot find or open in the database directory the .ibd file of\n" +"InnoDB: table ", stderr); + ut_print_name(stderr, trx, name); + fputs("\n" +"InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n", + stderr); + } + + err = DB_ERROR; + } + +funct_exit: + row_mysql_unlock_data_dictionary(trx); + + trx_commit_for_mysql(trx); + + trx->op_info = ""; + + return((int) err); +} + +/************************************************************************* Drops a table for MySQL. If the name of the table to be dropped is equal with one of the predefined magic table names, then this also stops printing the corresponding monitor output by the master thread. */ @@ -1933,25 +2354,30 @@ the corresponding monitor output by the master thread. */ int row_drop_table_for_mysql( /*=====================*/ - /* out: error code or DB_SUCCESS */ - char* name, /* in: table name */ - trx_t* trx, /* in: transaction handle */ - ibool drop_db)/* in: TRUE=dropping whole database */ + /* out: error code or DB_SUCCESS */ + const char* name, /* in: table name */ + trx_t* trx, /* in: transaction handle */ + ibool drop_db)/* in: TRUE=dropping whole database */ { dict_foreign_t* foreign; dict_table_t* table; + ulint space_id; que_thr_t* thr; que_t* graph; ulint err; const char* table_name; ulint namelen; + char* dir_path_of_temp_table = NULL; + ibool success; ibool locked_dictionary = FALSE; char* quoted_name; char* sql; + /* We use the private SQL parser of Innobase to generate the query graphs needed in deleting the dictionary data from system tables in Innobase. Deleting a row from SYS_INDEXES table also frees the file segments of the B-tree associated with the index. */ + static const char str1[] = "PROCEDURE DROP_TABLE_PROC () IS\n" "table_name CHAR;\n" @@ -2014,7 +2440,6 @@ row_drop_table_for_mysql( "COMMIT WORK;\n" "END;\n"; - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_a(name != NULL); if (srv_created_new_raw) { @@ -2024,12 +2449,12 @@ row_drop_table_for_mysql( "InnoDB: database modifications by the user. Shut down\n" "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n" "InnoDB: with raw, and innodb_force_... is removed.\n", - stderr); + stderr); return(DB_ERROR); } - trx->op_info = (char *) "dropping table"; + trx->op_info = "dropping table"; trx_start_if_not_started(trx); @@ -2054,7 +2479,6 @@ row_drop_table_for_mysql( } else if (namelen == sizeof S_innodb_lock_monitor && !memcmp(table_name, S_innodb_lock_monitor, sizeof S_innodb_lock_monitor)) { - srv_print_innodb_monitor = FALSE; srv_print_innodb_lock_monitor = FALSE; } else if (namelen == sizeof S_innodb_tablespace_monitor @@ -2111,7 +2535,7 @@ row_drop_table_for_mysql( ut_print_timestamp(stderr); fputs(" InnoDB: Error: table ", stderr); - ut_print_name(stderr, name); + ut_print_name(stderr, trx, name); fputs(" does not exist in the InnoDB internal\n" "InnoDB: data dictionary though MySQL is trying to drop it.\n" "InnoDB: Have you copied the .frm file of the table to the\n" @@ -2146,10 +2570,10 @@ row_drop_table_for_mysql( ut_print_timestamp(ef); fputs(" Cannot drop table ", ef); - ut_print_name(ef, name); + ut_print_name(ef, trx, name); fputs("\n" "because it is referenced by ", ef); - ut_print_name(ef, foreign->foreign_table_name); + ut_print_name(ef, trx, foreign->foreign_table_name); putc('\n', ef); mutex_exit(&dict_foreign_err_mutex); @@ -2161,7 +2585,7 @@ row_drop_table_for_mysql( ut_print_timestamp(stderr); fputs(" InnoDB: Warning: MySQL is trying to drop table ", stderr); - ut_print_name(stderr, table->name); + ut_print_name(stderr, trx, table->name); fputs("\n" "InnoDB: though there are still open handles to it.\n" "InnoDB: Adding the table to the background drop queue.\n", @@ -2178,7 +2602,7 @@ row_drop_table_for_mysql( ut_print_timestamp(stderr); fputs(" InnoDB: You are trying to drop table ", stderr); - ut_print_name(stderr, table->name); + ut_print_name(stderr, trx, table->name); fputs("\n" "InnoDB: though there is a foreign key check running on it.\n" "InnoDB: Adding the table to the background drop queue.\n", @@ -2213,16 +2637,69 @@ row_drop_table_for_mysql( ut_error; } else { + ibool is_path; + const char* name_or_path; + + space_id = table->space; + + if (table->dir_path_of_temp_table != NULL) { + dir_path_of_temp_table = + mem_strdup(table->dir_path_of_temp_table); + is_path = TRUE; + name_or_path = dir_path_of_temp_table; + } else { + is_path = FALSE; + name_or_path = name; + } + dict_table_remove_from_cache(table); if (dict_load_table(name) != NULL) { ut_print_timestamp(stderr); - fputs(" InnoDB: Error: dropping of table ", + fputs(" InnoDB: Error: not able to remove table ", stderr); - ut_print_name(stderr, name); - fputs(" failed!\n", stderr); + ut_print_name(stderr, trx, name); + fputs(" from the dictionary cache!\n", stderr); err = DB_ERROR; } + + /* Do not drop possible .ibd tablespace if something went + wrong: we do not want to delete valuable data of the user */ + + if (err == DB_SUCCESS && space_id > 0) { + if (!fil_space_for_table_exists_in_mem(space_id, + name_or_path, + is_path, + FALSE, TRUE)) { + err = DB_SUCCESS; + + fprintf(stderr, +"InnoDB: We removed now the InnoDB internal data dictionary entry\n" +"InnoDB: of table "); + ut_print_name(stderr, trx, name); + fprintf(stderr, ".\n"); + + goto funct_exit; + } + + success = fil_delete_tablespace(space_id); + + if (!success) { + fprintf(stderr, +"InnoDB: We removed now the InnoDB internal data dictionary entry\n" +"InnoDB: of table "); + ut_print_name(stderr, trx, name); + fprintf(stderr, ".\n"); + + ut_print_timestamp(stderr); + fprintf(stderr, +" InnoDB: Error: not able to delete tablespace %lu of table ", + (ulong) space_id); + ut_print_name(stderr, trx, name); + fputs("!\n", stderr); + err = DB_ERROR; + } + } } funct_exit: @@ -2230,11 +2707,15 @@ funct_exit: row_mysql_unlock_data_dictionary(trx); } + if (dir_path_of_temp_table) { + mem_free(dir_path_of_temp_table); + } + que_graph_free(graph); trx_commit_for_mysql(trx); - trx->op_info = (char *) ""; + trx->op_info = ""; srv_wake_master_thread(); @@ -2247,9 +2728,9 @@ Drops a database for MySQL. */ int row_drop_database_for_mysql( /*========================*/ - /* out: error code or DB_SUCCESS */ - char* name, /* in: database name which ends to '/' */ - trx_t* trx) /* in: transaction handle */ + /* out: error code or DB_SUCCESS */ + const char* name, /* in: database name which ends to '/' */ + trx_t* trx) /* in: transaction handle */ { dict_table_t* table; char* table_name; @@ -2260,7 +2741,7 @@ row_drop_database_for_mysql( ut_a(name != NULL); ut_a(name[namelen - 1] == '/'); - trx->op_info = (char *) "dropping database"; + trx->op_info = "dropping database"; trx_start_if_not_started(trx); loop: @@ -2282,10 +2763,10 @@ loop: ut_print_timestamp(stderr); fputs( " InnoDB: Warning: MySQL is trying to drop database ", stderr); - ut_print_name(stderr, name); + ut_print_name(stderr, trx, name); fputs("\n" "InnoDB: though there are still open handles to table ", stderr); - ut_print_name(stderr, table_name); + ut_print_name(stderr, trx, table_name); fputs(".\n", stderr); os_thread_sleep(1000000); @@ -2301,10 +2782,10 @@ loop: if (err != DB_SUCCESS) { fputs("InnoDB: DROP DATABASE ", stderr); - ut_print_name(stderr, name); + ut_print_name(stderr, trx, name); fprintf(stderr, " failed with error %lu for table ", (ulint) err); - ut_print_name(stderr, table_name); + ut_print_name(stderr, trx, table_name); putc('\n', stderr); break; } @@ -2314,7 +2795,7 @@ loop: trx_commit_for_mysql(trx); - trx->op_info = (char *) ""; + trx->op_info = ""; return(err); } @@ -2339,10 +2820,10 @@ Renames a table for MySQL. */ int row_rename_table_for_mysql( /*=======================*/ - /* out: error code or DB_SUCCESS */ - char* old_name, /* in: old table name */ - char* new_name, /* in: new table name */ - trx_t* trx) /* in: transaction handle */ + /* out: error code or DB_SUCCESS */ + const char* old_name, /* in: old table name */ + const char* new_name, /* in: new table name */ + trx_t* trx) /* in: transaction handle */ { dict_table_t* table; que_thr_t* thr; @@ -2433,6 +2914,7 @@ row_rename_table_for_mysql( ibool recovering_temp_table = FALSE; ulint len; ulint i; + ibool success; /* length of database name; 0 if not renaming to a temporary table */ ulint db_name_len; char* sql; @@ -2466,7 +2948,7 @@ row_rename_table_for_mysql( return(DB_ERROR); } - trx->op_info = (char *) "renaming table"; + trx->op_info = "renaming table"; trx_start_if_not_started(trx); if (row_mysql_is_recovered_tmp_table(new_name)) { @@ -2483,7 +2965,29 @@ row_rename_table_for_mysql( if (!table) { err = DB_TABLE_NOT_FOUND; + ut_print_timestamp(stderr); + + fputs(" InnoDB: Error: table ", stderr); + ut_print_name(stderr, trx, old_name); + fputs(" does not exist in the InnoDB internal\n" + "InnoDB: data dictionary though MySQL is trying to rename the table.\n" + "InnoDB: Have you copied the .frm file of the table to the\n" + "InnoDB: MySQL database directory from another database?\n" + "InnoDB: You can look for further help from section 15.1 of\n" + "InnoDB: http://www.innodb.com/ibman.php\n", stderr); + goto funct_exit; + } + + if (table->ibd_file_missing) { + err = DB_TABLE_NOT_FOUND; + ut_print_timestamp(stderr); + fputs(" InnoDB: Error: table ", stderr); + ut_print_name(stderr, trx, old_name); + fputs( + " does not have an .ibd file in the database directory.\n" + "InnoDB: You can look for further help from section 15.1 of\n" + "InnoDB: http://www.innodb.com/ibman.php\n", stderr); goto funct_exit; } @@ -2510,7 +3014,7 @@ row_rename_table_for_mysql( goto funct_exit; } - + /* reserve space for all database names */ len += 2 * n_constraints_to_drop * (ut_strlenq(old_name, '\'') @@ -2610,40 +3114,56 @@ row_rename_table_for_mysql( if (err != DB_SUCCESS) { if (err == DB_DUPLICATE_KEY) { ut_print_timestamp(stderr); - fputs( " InnoDB: Error; possible reasons:\n" "InnoDB: 1) Table rename would cause two FOREIGN KEY constraints\n" "InnoDB: to have the same internal name in case-insensitive comparison.\n" "InnoDB: 2) table ", stderr); - ut_print_name(stderr, new_name); - fputs(" exists in the InnoDB internal data\n" + ut_print_name(stderr, trx, new_name); + fputs(" exists in the InnoDB internal data\n" "InnoDB: dictionary though MySQL is trying rename table ", stderr); - ut_print_name(stderr, old_name); - fputs(" to it.\n" + ut_print_name(stderr, trx, old_name); + fputs(" to it.\n" "InnoDB: Have you deleted the .frm file and not used DROP TABLE?\n" "InnoDB: You can look for further help from\n" "InnoDB: http://dev.mysql.com/doc/mysql/en/" "InnoDB_troubleshooting_datadict.html\n" "InnoDB: If table ", stderr); - ut_print_name(stderr, new_name); + ut_print_name(stderr, trx, new_name); fputs( " is a temporary table #sql..., then it can be that\n" "InnoDB: there are still queries running on the table, and it will be\n" "InnoDB: dropped automatically when the queries end.\n" "InnoDB: You can drop the orphaned table inside InnoDB by\n" "InnoDB: creating an InnoDB table with the same name in another\n" - "InnoDB: database and moving the .frm file to the current database.\n" + "InnoDB: database and copying the .frm file to the current database.\n" "InnoDB: Then MySQL thinks the table exists, and DROP TABLE will\n" "InnoDB: succeed.\n", stderr); } - trx->error_state = DB_SUCCESS; trx_general_rollback_for_mysql(trx, FALSE, NULL); trx->error_state = DB_SUCCESS; } else { - ut_a(dict_table_rename_in_cache(table, new_name, - !row_is_mysql_tmp_table_name(new_name))); + /* The following call will also rename the .ibd data file if + the table is stored in a single-table tablespace */ + + success = dict_table_rename_in_cache(table, new_name, + !row_is_mysql_tmp_table_name(new_name)); + if (!success) { + trx->error_state = DB_SUCCESS; + trx_general_rollback_for_mysql(trx, FALSE, NULL); + trx->error_state = DB_SUCCESS; + ut_print_timestamp(stderr); + fputs(" InnoDB: Error in table rename, cannot rename ", + stderr); + ut_print_name(stderr, trx, old_name); + fputs(" to ", stderr); + ut_print_name(stderr, trx, new_name); + putc('\n', stderr); + err = DB_ERROR; + + goto funct_exit; + } if (row_is_mysql_tmp_table_name(old_name)) { @@ -2657,20 +3177,17 @@ row_rename_table_for_mysql( err = dict_load_foreigns(new_name); if (err != DB_SUCCESS) { - ut_print_timestamp(stderr); - - fputs(" InnoDB: Error: in ALTER TABLE table ", + fputs(" InnoDB: Error: in ALTER TABLE ", stderr); - ut_print_name(stderr, new_name); + ut_print_name(stderr, trx, new_name); fputs("\n" - "InnoDB: has or is referenced in foreign key constraints\n" - "InnoDB: which are not compatible with the new table definition.\n", + "InnoDB: has or is referenced in foreign key constraints\n" + "InnoDB: which are not compatible with the new table definition.\n", stderr); - + ut_a(dict_table_rename_in_cache(table, - old_name, FALSE)); - + old_name, FALSE)); trx->error_state = DB_SUCCESS; trx_general_rollback_for_mysql(trx, FALSE, NULL); @@ -2686,14 +3203,14 @@ row_rename_table_for_mysql( fputs( " InnoDB: Error: in RENAME TABLE table ", stderr); - ut_print_name(stderr, new_name); + ut_print_name(stderr, trx, new_name); fputs("\n" "InnoDB: is referenced in foreign key constraints\n" "InnoDB: which are not compatible with the new table definition.\n", stderr); ut_a(dict_table_rename_in_cache(table, - old_name, FALSE)); + old_name, FALSE)); trx->error_state = DB_SUCCESS; trx_general_rollback_for_mysql(trx, FALSE, @@ -2702,8 +3219,8 @@ row_rename_table_for_mysql( } } } -funct_exit: - if (!recovering_temp_table) { +funct_exit: + if (!recovering_temp_table) { row_mysql_unlock_data_dictionary(trx); } @@ -2717,7 +3234,7 @@ funct_exit: trx_commit_for_mysql(trx); - trx->op_info = (char *) ""; + trx->op_info = ""; return((int) err); } @@ -2810,7 +3327,8 @@ loop: fputs("InnoDB: index records in a wrong order in ", stderr); not_ok: - dict_index_name_print(stderr, index); + dict_index_name_print(stderr, + prebuilt->trx, index); fputs("\n" "InnoDB: prev record ", stderr); dtuple_print(stderr, prev_entry); @@ -2854,8 +3372,22 @@ row_check_table_for_mysql( ulint n_rows_in_table = ULINT_UNDEFINED; ulint ret = DB_SUCCESS; ulint old_isolation_level; - - prebuilt->trx->op_info = (char *) "checking table"; + + if (prebuilt->table->ibd_file_missing) { + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Error:\n" +"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n" +"InnoDB: table %s does not exist.\n" +"InnoDB: Have you deleted the .ibd file from the database directory under\n" +"InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n" +"InnoDB: Look from\n" +"http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" +"InnoDB: how you can resolve the problem.\n", + prebuilt->table->name); + return(DB_ERROR); + } + + prebuilt->trx->op_info = "checking table"; old_isolation_level = prebuilt->trx->isolation_level; @@ -2886,21 +3418,22 @@ row_check_table_for_mysql( ret = DB_ERROR; } - /* fprintf(stderr, "%lu entries in index ", n_rows); - ut_print_name(stderr, index->name); - putc('\n', stderr); */ + /* fprintf(stderr, "%lu entries in index %s\n", n_rows, + index->name); */ if (index == dict_table_get_first_index(table)) { n_rows_in_table = n_rows; } else if (n_rows != n_rows_in_table) { ret = DB_ERROR; - - fputs("InnoDB: Error: ", stderr); - dict_index_name_print(stderr, index); + + fputs("Error: ", stderr); + dict_index_name_print(stderr, + prebuilt->trx, index); fprintf(stderr, " contains %lu entries, should be %lu\n", - n_rows, n_rows_in_table); + (ulong) n_rows, + (ulong) n_rows_in_table); } } @@ -2923,7 +3456,7 @@ row_check_table_for_mysql( srv_fatal_semaphore_wait_threshold -= 7200; /* 2 hours */ mutex_exit(&kernel_mutex); - prebuilt->trx->op_info = (char *) ""; + prebuilt->trx->op_info = ""; return(ret); } diff --git a/innobase/row/row0purge.c b/innobase/row/row0purge.c index 2ddc60613fc..f7e01169b9d 100644 --- a/innobase/row/row0purge.c +++ b/innobase/row/row0purge.c @@ -519,6 +519,16 @@ row_purge_parse_undo_rec( return(FALSE); } + if (node->table->ibd_file_missing) { + /* We skip purge of missing .ibd files */ + + node->table = NULL; + + row_mysql_unfreeze_data_dictionary(trx); + + return(FALSE); + } + clust_index = dict_table_get_first_index(node->table); if (clust_index == NULL) { @@ -533,8 +543,8 @@ row_purge_parse_undo_rec( node->heap); ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id, - roll_ptr, info_bits, node->heap, - &(node->update)); + roll_ptr, info_bits, trx, + node->heap, &(node->update)); /* Read to the partial row the fields that occur in indexes */ diff --git a/innobase/row/row0row.c b/innobase/row/row0row.c index f075caa7d75..38714b0c49b 100644 --- a/innobase/row/row0row.c +++ b/innobase/row/row0row.c @@ -113,6 +113,8 @@ row_build_index_entry( dfield_t* dfield2; dict_col_t* col; ulint i; + ulint storage_len; + dtype_t* cur_type; ut_ad(row && index && heap); ut_ad(dtuple_check_typed(row)); @@ -139,10 +141,17 @@ row_build_index_entry( /* If a column prefix index, take only the prefix */ if (ind_field->prefix_len > 0 - && dfield_get_len(dfield2) != UNIV_SQL_NULL - && dfield_get_len(dfield2) > ind_field->prefix_len) { + && dfield_get_len(dfield2) != UNIV_SQL_NULL) { - dfield_set_len(dfield, ind_field->prefix_len); + cur_type = dict_col_get_type( + dict_field_get_col(ind_field)); + + storage_len = dtype_get_at_most_n_mbchars( + cur_type, + ind_field->prefix_len, + dfield_get_len(dfield2), dfield2->data); + + dfield_set_len(dfield, storage_len); } } @@ -377,10 +386,12 @@ row_build_row_ref( dict_index_get_nth_field(clust_index, i)->prefix_len; if (clust_col_prefix_len > 0) { - if (len != UNIV_SQL_NULL - && len > clust_col_prefix_len) { + if (len != UNIV_SQL_NULL) { - dfield_set_len(dfield, clust_col_prefix_len); + dfield_set_len(dfield, + dtype_get_at_most_n_mbchars( + dfield_get_type(dfield), + clust_col_prefix_len, len, field)); } } } @@ -400,12 +411,13 @@ row_build_row_ref_in_tuple( dtuple_t* ref, /* in/out: row reference built; see the NOTE below! */ dict_index_t* index, /* in: index */ - rec_t* rec) /* in: record in the index; + rec_t* rec, /* in: record in the index; NOTE: the data fields in ref will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row reference is used! */ + trx_t* trx) /* in: transaction */ { dict_index_t* clust_index; dfield_t* dfield; @@ -421,9 +433,9 @@ row_build_row_ref_in_tuple( if (!index->table) { fputs("InnoDB: table ", stderr); notfound: - ut_print_name(stderr, index->table_name); + ut_print_name(stderr, trx, index->table_name); fputs(" for index ", stderr); - ut_print_name(stderr, index->name); + ut_print_name(stderr, trx, index->name); fputs(" not found\n", stderr); ut_error; } @@ -461,10 +473,12 @@ row_build_row_ref_in_tuple( dict_index_get_nth_field(clust_index, i)->prefix_len; if (clust_col_prefix_len > 0) { - if (len != UNIV_SQL_NULL - && len > clust_col_prefix_len) { + if (len != UNIV_SQL_NULL) { - dfield_set_len(dfield, clust_col_prefix_len); + dfield_set_len(dfield, + dtype_get_at_most_n_mbchars( + dfield_get_type(dfield), + clust_col_prefix_len, len, field)); } } } @@ -494,6 +508,7 @@ row_build_row_ref_from_row( dict_col_t* col; ulint ref_len; ulint i; + dtype_t* cur_type; ut_ad(ref && table && row); @@ -515,10 +530,15 @@ row_build_row_ref_from_row( dfield_copy(dfield, dfield2); if (field->prefix_len > 0 - && dfield->len != UNIV_SQL_NULL - && dfield->len > field->prefix_len) { + && dfield->len != UNIV_SQL_NULL) { + + cur_type = dict_col_get_type( + dict_field_get_col(field)); - dfield->len = field->prefix_len; + dfield->len = dtype_get_at_most_n_mbchars( + cur_type, + field->prefix_len, + dfield->len, dfield->data); } } diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 6c62fed974c..8cdcdd569ac 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -31,6 +31,7 @@ Created 12/19/1997 Heikki Tuuri #include "pars0pars.h" #include "row0mysql.h" #include "read0read.h" +#include "buf0lru.h" /* Maximum number of rows to prefetch; MySQL interface has another parameter */ #define SEL_MAX_N_PREFETCH 16 @@ -76,6 +77,7 @@ row_sel_sec_rec_is_for_clust_rec( ulint clust_len; ulint n; ulint i; + dtype_t* cur_type; UT_NOT_USED(clust_index); @@ -91,10 +93,15 @@ row_sel_sec_rec_is_for_clust_rec( sec_field = rec_get_nth_field(sec_rec, i, &sec_len); if (ifield->prefix_len > 0 - && clust_len != UNIV_SQL_NULL - && clust_len > ifield->prefix_len) { + && clust_len != UNIV_SQL_NULL) { - clust_len = ifield->prefix_len; + cur_type = dict_col_get_type( + dict_field_get_col(ifield)); + + clust_len = dtype_get_at_most_n_mbchars( + cur_type, + ifield->prefix_len, + clust_len, clust_field); } if (0 != cmp_data_data(dict_col_get_type(col), @@ -631,9 +638,24 @@ row_sel_get_clust_rec( if (!node->read_view) { /* Try to place a lock on the index record */ - - err = lock_clust_rec_read_check_and_lock(0, clust_rec, index, - node->row_lock_mode, LOCK_ORDINARY, thr); + + /* If innodb_locks_unsafe_for_binlog option is used, + we lock only the record, i.e. next-key locking is + not used. + */ + + if (srv_locks_unsafe_for_binlog) { + err = lock_clust_rec_read_check_and_lock(0, + clust_rec, + index, node->row_lock_mode, + LOCK_REC_NOT_GAP, thr); + } else { + err = lock_clust_rec_read_check_and_lock(0, + clust_rec, + index, node->row_lock_mode, + LOCK_ORDINARY, thr); + } + if (err != DB_SUCCESS) { return(err); @@ -709,8 +731,18 @@ sel_set_rec_lock( ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or LOC_REC_NOT_GAP */ que_thr_t* thr) /* in: query thread */ { + trx_t* trx; ulint err; + trx = thr_get_trx(thr); + + if (UT_LIST_GET_LEN(trx->trx_locks) > 10000) { + if (buf_LRU_buf_pool_running_out()) { + + return(DB_LOCK_TABLE_FULL); + } + } + if (index->type & DICT_CLUSTERED) { err = lock_clust_rec_read_check_and_lock(0, rec, index, mode, type, thr); @@ -1184,8 +1216,24 @@ rec_loop: search result set, resulting in the phantom problem. */ if (!consistent_read) { - err = sel_set_rec_lock(page_rec_get_next(rec), index, - node->row_lock_mode, LOCK_ORDINARY, thr); + + /* If innodb_locks_unsafe_for_binlog option is used, + we lock only the record, i.e. next-key locking is + not used. + */ + + if (srv_locks_unsafe_for_binlog) { + err = sel_set_rec_lock(page_rec_get_next(rec), + index, + node->row_lock_mode, + LOCK_REC_NOT_GAP, thr); + } else { + err = sel_set_rec_lock(page_rec_get_next(rec), + index, + node->row_lock_mode, + LOCK_ORDINARY, thr); + } + if (err != DB_SUCCESS) { /* Note that in this case we will store in pcur the PREDECESSOR of the record we are waiting @@ -1211,8 +1259,19 @@ rec_loop: if (!consistent_read) { /* Try to place a lock on the index record */ - err = sel_set_rec_lock(rec, index, node->row_lock_mode, + /* If innodb_locks_unsafe_for_binlog option is used, + we lock only the record, i.e. next-key locking is + not used. + */ + + if (srv_locks_unsafe_for_binlog) { + err = sel_set_rec_lock(rec, index, node->row_lock_mode, + LOCK_REC_NOT_GAP, thr); + } else { + err = sel_set_rec_lock(rec, index, node->row_lock_mode, LOCK_ORDINARY, thr); + } + if (err != DB_SUCCESS) { goto lock_wait_or_error; @@ -1756,7 +1815,7 @@ row_sel_step( return(NULL); } else { /* SQL error detected */ - fprintf(stderr, "SQL error %lu\n", err); + fprintf(stderr, "SQL error %lu\n", (ulong) err); que_thr_handle_error(thr, DB_ERROR, NULL, 0); @@ -1806,7 +1865,7 @@ fetch_step( if (sel_node->state == SEL_NODE_CLOSED) { /* SQL error detected */ - fprintf(stderr, "SQL error %lu\n", (ulint)DB_ERROR); + fprintf(stderr, "SQL error %lu\n", (ulong)DB_ERROR); que_thr_handle_error(thr, DB_ERROR, NULL, 0); @@ -1900,9 +1959,11 @@ row_sel_convert_mysql_key_to_innobase( ulint buf_len, /* in: buffer length */ dict_index_t* index, /* in: index of the key value */ byte* key_ptr, /* in: MySQL key value */ - ulint key_len) /* in: MySQL key value length */ + ulint key_len, /* in: MySQL key value length */ + trx_t* trx) /* in: transaction */ { byte* original_buf = buf; + byte* original_key_ptr = key_ptr; dict_field_t* field; dfield_t* dfield; ulint data_offset; @@ -1974,19 +2035,15 @@ row_sel_convert_mysql_key_to_innobase( /* MySQL stores the actual data length to the first 2 bytes after the optional SQL NULL marker byte. The - storage format is little-endian. */ - - /* There are no key fields > 255 bytes currently in - MySQL */ - if (key_ptr[data_offset + 1] != 0) { - ut_print_timestamp(stderr); - fputs( -" InnoDB: Error: BLOB or TEXT prefix > 255 bytes in query to table ", stderr); - ut_print_name(stderr, index->table_name); - putc('\n', stderr); - } - - data_len = key_ptr[data_offset]; + storage format is little-endian, that is, the most + significant byte at a higher address. In UTF-8, MySQL + seems to reserve field->prefix_len bytes for + storing this field in the key value buffer, even + though the actual value only takes data_len bytes + from the start. */ + + data_len = key_ptr[data_offset] + + 256 * key_ptr[data_offset + 1]; data_field_len = data_offset + 2 + field->prefix_len; data_offset += 2; @@ -1994,6 +2051,17 @@ row_sel_convert_mysql_key_to_innobase( store the column value like it would be a fixed char field */ } else if (field->prefix_len > 0) { + /* Looks like MySQL pads unused end bytes in the + prefix with space. Therefore, also in UTF-8, it is ok + to compare with a prefix containing full prefix_len + bytes, and no need to take at most prefix_len / 3 + UTF-8 characters from the start. + If the prefix is used as the upper end of a LIKE + 'abc%' query, then MySQL pads the end with chars + 0xff. TODO: in that case does it any harm to compare + with the full prefix_len bytes. How do characters + 0xff in UTF-8 behave? */ + data_len = field->prefix_len; data_field_len = data_offset + data_len; } else { @@ -2026,8 +2094,18 @@ row_sel_convert_mysql_key_to_innobase( ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Warning: using a partial-field key prefix in search\n"); + fputs( + " InnoDB: Warning: using a partial-field key prefix in search.\n" + "InnoDB: ", stderr); + dict_index_name_print(stderr, trx, index); + fprintf(stderr, ". Last data field length %lu bytes,\n" + "InnoDB: key ptr now exceeds key end by %lu bytes.\n" + "InnoDB: Key value in the MySQL format:\n", + (ulong) data_field_len, + (ulong) (key_ptr - key_end)); + fflush(stderr); + ut_print_buf(stderr, original_key_ptr, key_len); + fprintf(stderr, "\n"); if (!is_null) { dfield->len -= (ulint)(key_ptr - key_end); @@ -2064,11 +2142,11 @@ row_sel_store_row_id_to_prebuilt( if (len != DATA_ROW_ID_LEN) { fprintf(stderr, -"InnoDB: Error: Row id field is wrong length %lu in ", len); - dict_index_name_print(stderr, index); +"InnoDB: Error: Row id field is wrong length %lu in ", (ulong) len); + dict_index_name_print(stderr, prebuilt->trx, index); fprintf(stderr, "\n" "InnoDB: Field number %lu, record:\n", - dict_index_get_sys_col_pos(index, DATA_ROW_ID)); + (ulong) dict_index_get_sys_col_pos(index, DATA_ROW_ID)); rec_print(stderr, index_rec); putc('\n', stderr); ut_error; @@ -2149,9 +2227,13 @@ Note that the template in prebuilt may advise us to copy only a few columns to mysql_rec, other columns are left blank. All columns may not be needed in the query. */ static -void +ibool row_sel_store_mysql_rec( /*====================*/ + /* out: TRUE if success, FALSE if + could not allocate memory for a BLOB + (though we may also assert in that + case) */ byte* mysql_rec, /* out: row in the MySQL format */ row_prebuilt_t* prebuilt, /* in: prebuilt struct */ rec_t* rec) /* in: Innobase record in the index @@ -2163,6 +2245,7 @@ row_sel_store_mysql_rec( byte* data; ulint len; byte* blob_buf; + int pad_char; ulint i; ut_ad(prebuilt->mysql_template); @@ -2172,9 +2255,10 @@ row_sel_store_mysql_rec( prebuilt->blob_heap = NULL; } - /* Mark all columns as not SQL NULL */ + /* MySQL assumes that all columns have the SQL NULL bit set unless it + is a nullable column with a non-NULL value */ - memset(mysql_rec, '\0', prebuilt->null_bitmap_len); + memset(mysql_rec, 0xFF, prebuilt->null_bitmap_len); for (i = 0; i < prebuilt->n_template; i++) { @@ -2191,6 +2275,10 @@ row_sel_store_mysql_rec( extern_field_heap = mem_heap_create(UNIV_PAGE_SIZE); + /* NOTE: if we are retrieving a big BLOB, we may + already run out of memory in the next call, which + causes an assert */ + data = btr_rec_copy_externally_stored_field(rec, templ->rec_field_no, &len, extern_field_heap); @@ -2202,9 +2290,33 @@ row_sel_store_mysql_rec( if (templ->type == DATA_BLOB) { ut_a(prebuilt->templ_contains_blob); - - /* Copy the BLOB data to the BLOB - heap of prebuilt */ + + /* A heuristic test that we can allocate the + memory for a big BLOB. We have a safety margin + of 1000000 bytes. Since the test takes some + CPU time, we do not use it for small BLOBs. */ + + if (len > 2000000 + && !ut_test_malloc(len + 1000000)) { + + ut_print_timestamp(stderr); + fprintf(stderr, +" InnoDB: Warning: could not allocate %lu + 1000000 bytes to retrieve\n" +"InnoDB: a big column. Table name ", (ulong) len); + ut_print_name(stderr, + prebuilt->trx, + prebuilt->table->name); + putc('\n', stderr); + + if (extern_field_heap) { + mem_heap_free( + extern_field_heap); + } + return(FALSE); + } + + /* Copy the BLOB data to the BLOB heap of + prebuilt */ if (prebuilt->blob_heap == NULL) { prebuilt->blob_heap = @@ -2222,33 +2334,46 @@ row_sel_store_mysql_rec( mysql_rec + templ->mysql_col_offset, templ->mysql_col_len, data, len, templ->type, templ->is_unsigned); - + + /* Cleanup */ if (extern_field_heap) { mem_heap_free(extern_field_heap); extern_field_heap = NULL; } + + if (templ->mysql_null_bit_mask) { + /* It is a nullable column with a non-NULL + value */ + mysql_rec[templ->mysql_null_byte_offset] &= + ~(byte) (templ->mysql_null_bit_mask); + } } else { /* MySQL seems to assume the field for an SQL NULL - value is set to zero. Not taking this into account - caused seg faults with NULL BLOB fields, and + value is set to zero or space. Not taking this into + account caused seg faults with NULL BLOB fields, and bug number 154 in the MySQL bug database: GROUP BY and DISTINCT could treat NULL values inequal. */ - memset(mysql_rec + templ->mysql_col_offset, '\0', - templ->mysql_col_len); - - if (!templ->mysql_null_bit_mask) { - fputs( -"InnoDB: Error: trying to return an SQL NULL field in a non-null\n" -"innoDB: column! Table name ", stderr); - ut_print_name(stderr, prebuilt->table->name); - putc('\n', stderr); + if (templ->type == DATA_VARCHAR + || templ->type == DATA_CHAR + || templ->type == DATA_BINARY + || templ->type == DATA_FIXBINARY + || templ->type == DATA_MYSQL + || templ->type == DATA_VARMYSQL) { + /* MySQL pads all non-BLOB and non-TEXT + string types with space ' ' */ + + pad_char = ' '; } else { - mysql_rec[templ->mysql_null_byte_offset] |= - (byte) (templ->mysql_null_bit_mask); + pad_char = '\0'; } + + memset(mysql_rec + templ->mysql_col_offset, pad_char, + templ->mysql_col_len); } } + + return(TRUE); } /************************************************************************* @@ -2313,8 +2438,9 @@ row_sel_get_clust_rec_for_mysql( trx_t* trx; *out_rec = NULL; + trx = thr_get_trx(thr); - row_build_row_ref_in_tuple(prebuilt->clust_ref, sec_index, rec); + row_build_row_ref_in_tuple(prebuilt->clust_ref, sec_index, rec, trx); clust_index = dict_table_get_first_index(sec_index->table); @@ -2347,7 +2473,7 @@ row_sel_get_clust_rec_for_mysql( fputs(" InnoDB: error clustered record" " for sec rec not found\n" "InnoDB: ", stderr); - dict_index_name_print(stderr, sec_index); + dict_index_name_print(stderr, trx, sec_index); fputs("\n" "InnoDB: sec index record ", stderr); rec_print(stderr, rec); @@ -2355,7 +2481,7 @@ row_sel_get_clust_rec_for_mysql( "InnoDB: clust index record ", stderr); rec_print(stderr, clust_rec); putc('\n', stderr); - trx_print(stderr, thr_get_trx(thr)); + trx_print(stderr, trx); fputs("\n" "InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n", stderr); @@ -2383,8 +2509,6 @@ row_sel_get_clust_rec_for_mysql( /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ - trx = thr_get_trx(thr); - old_vers = NULL; /* If the isolation level allows reading of uncommitted data, @@ -2567,9 +2691,9 @@ row_sel_push_cache_row_for_mysql( ut_ad(prebuilt->fetch_cache_first == 0); - row_sel_store_mysql_rec( + ut_a(row_sel_store_mysql_rec( prebuilt->fetch_cache[prebuilt->n_fetch_cached], - prebuilt, rec); + prebuilt, rec)); prebuilt->n_fetch_cached++; } @@ -2651,7 +2775,9 @@ row_search_for_mysql( /*=================*/ /* out: DB_SUCCESS, DB_RECORD_NOT_FOUND, - DB_END_OF_INDEX, or DB_DEADLOCK */ + DB_END_OF_INDEX, DB_DEADLOCK, + DB_LOCK_TABLE_FULL, DB_CORRUPTION, + or DB_TOO_BIG_RECORD */ byte* buf, /* in/out: buffer for the fetched row in the MySQL format */ ulint mode, /* in: search mode PAGE_CUR_L, ... */ @@ -2680,7 +2806,7 @@ row_search_for_mysql( rec_t* index_rec; rec_t* clust_rec; rec_t* old_vers; - ulint err; + ulint err = DB_SUCCESS; ibool moved; ibool cons_read_requires_clust_rec; ibool was_lock_wait; @@ -2702,13 +2828,27 @@ row_search_for_mysql( ut_ad(index && pcur && search_tuple); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); - + + if (prebuilt->table->ibd_file_missing) { + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Error:\n" +"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n" +"InnoDB: table %s does not exist.\n" +"InnoDB: Have you deleted the .ibd file from the database directory under\n" +"InnoDB: the MySQL datadir, or have you used DISCARD TABLESPACE?\n" +"InnoDB: Look from\n" +"http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" +"InnoDB: how you can resolve the problem.\n", + prebuilt->table->name); + return(DB_ERROR); + } + if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { fprintf(stderr, "InnoDB: Error: trying to free a corrupt\n" "InnoDB: table handle. Magic n %lu, table name ", - prebuilt->magic_n); - ut_print_name(stderr, prebuilt->table->name); + (ulong) prebuilt->magic_n); + ut_print_name(stderr, trx, prebuilt->table->name); putc('\n', stderr); mem_analyze_corruption((byte*)prebuilt); @@ -2716,7 +2856,17 @@ row_search_for_mysql( ut_error; } -/* fprintf(stderr, "Match mode %lu\n search tuple ", match_mode); + if (trx->n_mysql_tables_in_use == 0) { + fputs( +"InnoDB: Error: MySQL is trying to perform a SELECT\n" +"InnoDB: but it has not locked any tables in ::external_lock()!\n", + stderr); + trx_print(stderr, trx); + fputc('\n', stderr); + ut_a(0); + } + +/* fprintf(stderr, "Match mode %lu\n search tuple ", (ulong) match_mode); dtuple_print(search_tuple); fprintf(stderr, "N tables locked %lu\n", trx->mysql_n_tables_locked); @@ -2743,7 +2893,7 @@ row_search_for_mysql( /* PHASE 1: Try to pop the row from the prefetch cache */ if (direction == 0) { - trx->op_info = (char *) "starting index read"; + trx->op_info = "starting index read"; prebuilt->n_rows_fetched = 0; prebuilt->n_fetch_cached = 0; @@ -2754,7 +2904,7 @@ row_search_for_mysql( row_prebuild_sel_graph(prebuilt); } } else { - trx->op_info = (char *) "fetching rows"; + trx->op_info = "fetching rows"; if (prebuilt->n_rows_fetched == 0) { prebuilt->fetch_direction = direction; @@ -2779,7 +2929,7 @@ row_search_for_mysql( prebuilt->n_rows_fetched++; srv_n_rows_read++; - trx->op_info = (char *) ""; + trx->op_info = ""; return(DB_SUCCESS); } @@ -2791,7 +2941,7 @@ row_search_for_mysql( cache, but the cache was not full at the time of the popping: no more rows can exist in the result set */ - trx->op_info = (char *) ""; + trx->op_info = ""; return(DB_RECORD_NOT_FOUND); } @@ -2833,10 +2983,10 @@ row_search_for_mysql( retrieve also a second row if a primary key contains more than 1 column. Return immediately if this is not a HANDLER command. */ - + if (direction != 0 && !prebuilt->used_in_HANDLER) { - - trx->op_info = (char*)""; + + trx->op_info = ""; return(DB_RECORD_NOT_FOUND); } } @@ -2892,7 +3042,14 @@ row_search_for_mysql( #ifdef UNIV_SEARCH_DEBUG ut_a(0 == cmp_dtuple_rec(search_tuple, rec)); #endif - row_sel_store_mysql_rec(buf, prebuilt, rec); + if (!row_sel_store_mysql_rec(buf, prebuilt, + rec)) { + err = DB_TOO_BIG_RECORD; + + /* We let the main loop to do the + error handling */ + goto shortcut_fails_too_big_rec; + } mtr_commit(&mtr); @@ -2910,7 +3067,7 @@ row_search_for_mysql( trx->has_search_latch = FALSE; } - trx->op_info = (char *) ""; + trx->op_info = ""; /* NOTE that we do NOT store the cursor position */ @@ -2933,14 +3090,14 @@ row_search_for_mysql( trx->has_search_latch = FALSE; } - trx->op_info = (char *) ""; + trx->op_info = ""; /* NOTE that we do NOT store the cursor position */ return(DB_RECORD_NOT_FOUND); } - +shortcut_fails_too_big_rec: mtr_commit(&mtr); mtr_start(&mtr); } @@ -3016,6 +3173,16 @@ row_search_for_mysql( if (!prebuilt->sql_stat_start) { /* No need to set an intention lock or assign a read view */ + if (trx->read_view == NULL + && prebuilt->select_lock_type == LOCK_NONE) { + + fputs( +"InnoDB: Error: MySQL is trying to perform a consistent read\n" +"InnoDB: but the read view is not assigned!\n", stderr); + trx_print(stderr, trx); + fputc('\n', stderr); + ut_a(0); + } } else if (prebuilt->select_lock_type == LOCK_NONE) { /* This is a consistent read */ /* Assign a read view for the query */ @@ -3062,11 +3229,18 @@ rec_loop: if (prebuilt->select_lock_type != LOCK_NONE && set_also_gap_locks) { - /* Try to place a lock on the index record */ + /* Try to place a lock on the index record */ - err = sel_set_rec_lock(rec, index, + /* If innodb_locks_unsafe_for_binlog option is used, + we do not lock gaps. Supremum record is really + a gap and therefore we do not set locks there. */ + + if (srv_locks_unsafe_for_binlog == FALSE) { + err = sel_set_rec_lock(rec, index, prebuilt->select_lock_type, LOCK_ORDINARY, thr); + } + if (err != DB_SUCCESS) { goto lock_wait_or_error; @@ -3098,12 +3272,13 @@ rec_loop: fprintf(stderr, "InnoDB: Index corruption: rec offs %lu next offs %lu, page no %lu,\n" "InnoDB: ", - (ulint)(rec - buf_frame_align(rec)), next_offs, - buf_frame_get_page_no(rec)); - dict_index_name_print(stderr, index); + (ulong) (rec - buf_frame_align(rec)), + (ulong) next_offs, + (ulong) buf_frame_get_page_no(rec)); + dict_index_name_print(stderr, trx, index); fputs(". Run CHECK TABLE. You may need to\n" "InnoDB: restore from a backup, or dump + drop + reimport the table.\n", - stderr); + stderr); err = DB_CORRUPTION; @@ -3115,9 +3290,10 @@ rec_loop: fprintf(stderr, "InnoDB: Index corruption: rec offs %lu next offs %lu, page no %lu,\n" "InnoDB: ", - (ulint)(rec - buf_frame_align(rec)), next_offs, - buf_frame_get_page_no(rec)); - dict_index_name_print(stderr, index); + (ulong) (rec - buf_frame_align(rec)), + (ulong) next_offs, + (ulong) buf_frame_get_page_no(rec)); + dict_index_name_print(stderr, trx, index); fputs(". We try to skip the rest of the page.\n", stderr); @@ -3133,9 +3309,10 @@ rec_loop: fprintf(stderr, "InnoDB: Index corruption: rec offs %lu next offs %lu, page no %lu,\n" "InnoDB: ", - (ulint)(rec - buf_frame_align(rec)), next_offs, - buf_frame_get_page_no(rec)); - dict_index_name_print(stderr, index); + (ulong) (rec - buf_frame_align(rec)), + (ulong) next_offs, + (ulong) buf_frame_get_page_no(rec)); + dict_index_name_print(stderr, trx, index); fputs(". We try to skip the record.\n", stderr); @@ -3160,11 +3337,18 @@ rec_loop: if (prebuilt->select_lock_type != LOCK_NONE && set_also_gap_locks) { - /* Try to place a lock on the index record */ - err = sel_set_rec_lock(rec, index, + /* Try to place a gap lock on the index + record only if innodb_locks_unsafe_for_binlog + option is not set */ + + if (srv_locks_unsafe_for_binlog == FALSE) { + + err = sel_set_rec_lock(rec, index, prebuilt->select_lock_type, LOCK_GAP, thr); + } + if (err != DB_SUCCESS) { goto lock_wait_or_error; @@ -3186,11 +3370,18 @@ rec_loop: if (prebuilt->select_lock_type != LOCK_NONE && set_also_gap_locks) { - /* Try to place a lock on the index record */ - err = sel_set_rec_lock(rec, index, + /* Try to place a gap lock on the index + record only if innodb_locks_unsafe_for_binlog + option is not set */ + + if (srv_locks_unsafe_for_binlog == FALSE) { + + err = sel_set_rec_lock(rec, index, prebuilt->select_lock_type, LOCK_GAP, thr); + } + if (err != DB_SUCCESS) { goto lock_wait_or_error; @@ -3224,9 +3415,19 @@ rec_loop: prebuilt->select_lock_type, LOCK_REC_NOT_GAP, thr); } else { - err = sel_set_rec_lock(rec, index, + /* If innodb_locks_unsafe_for_binlog option is used, + we lock only the record, i.e. next-key locking is + not used. */ + + if (srv_locks_unsafe_for_binlog) { + err = sel_set_rec_lock(rec, index, + prebuilt->select_lock_type, + LOCK_REC_NOT_GAP, thr); + } else { + err = sel_set_rec_lock(rec, index, prebuilt->select_lock_type, LOCK_ORDINARY, thr); + } } if (err != DB_SUCCESS) { @@ -3365,7 +3566,11 @@ rec_loop: rec_get_size(rec)); mach_write_to_4(buf, rec_get_extra_size(rec) + 4); } else { - row_sel_store_mysql_rec(buf, prebuilt, rec); + if (!row_sel_store_mysql_rec(buf, prebuilt, rec)) { + err = DB_TOO_BIG_RECORD; + + goto lock_wait_or_error; + } } if (prebuilt->clust_index_was_generated) { @@ -3470,7 +3675,7 @@ lock_wait_or_error: /* fputs("Using ", stderr); dict_index_name_print(stderr, index); fprintf(stderr, " cnt %lu ret value %lu err\n", cnt, err); */ - trx->op_info = (char *) ""; + trx->op_info = ""; return(err); @@ -3493,7 +3698,7 @@ normal_return: srv_n_rows_read++; } - trx->op_info = (char *) ""; + trx->op_info = ""; return(ret); } @@ -3505,11 +3710,11 @@ consistent read result, or store it to the query cache. */ ibool row_search_check_if_query_cache_permitted( /*======================================*/ - /* out: TRUE if storing or retrieving from - the query cache is permitted */ - trx_t* trx, /* in: transaction object */ - char* norm_name) /* in: concatenation of database name, '/' - char, table name */ + /* out: TRUE if storing or retrieving + from the query cache is permitted */ + trx_t* trx, /* in: transaction object */ + const char* norm_name) /* in: concatenation of database name, + '/' char, table name */ { dict_table_t* table; ibool ret = FALSE; diff --git a/innobase/row/row0uins.c b/innobase/row/row0uins.c index df2cdb6359d..9dc860d70b1 100644 --- a/innobase/row/row0uins.c +++ b/innobase/row/row0uins.c @@ -241,6 +241,13 @@ row_undo_ins_parse_undo_rec( return; } + if (node->table->ibd_file_missing) { + /* We skip undo operations to missing .ibd files */ + node->table = NULL; + + return; + } + clust_index = dict_table_get_first_index(node->table); ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref), diff --git a/innobase/row/row0umod.c b/innobase/row/row0umod.c index 1f74cfb52be..e16d696314b 100644 --- a/innobase/row/row0umod.c +++ b/innobase/row/row0umod.c @@ -422,6 +422,7 @@ row_undo_mod_del_unmark_sec_and_undo_update( ibool found; big_rec_t* dummy_big_rec; mtr_t mtr; + trx_t* trx = thr_get_trx(thr); log_free_check(); mtr_start(&mtr); @@ -431,7 +432,7 @@ row_undo_mod_del_unmark_sec_and_undo_update( if (!found) { fputs("InnoDB: error in sec index entry del undo in\n" "InnoDB: ", stderr); - dict_index_name_print(stderr, index); + dict_index_name_print(stderr, trx, index); fputs("\n" "InnoDB: tuple ", stderr); dtuple_print(stderr, entry); @@ -439,7 +440,7 @@ row_undo_mod_del_unmark_sec_and_undo_update( "InnoDB: record ", stderr); rec_print(stderr, btr_pcur_get_rec(&pcur)); putc('\n', stderr); - trx_print(stderr, thr_get_trx(thr)); + trx_print(stderr, trx); fputs("\n" "InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n", stderr); } else { @@ -451,7 +452,7 @@ row_undo_mod_del_unmark_sec_and_undo_update( heap = mem_heap_create(100); update = row_upd_build_sec_rec_difference_binary(index, entry, - btr_cur_get_rec(btr_cur), heap); + btr_cur_get_rec(btr_cur), trx, heap); if (upd_get_n_fields(update) == 0) { /* Do nothing */ @@ -671,14 +672,15 @@ row_undo_mod_parse_undo_rec( ulint type; ulint cmpl_info; ibool dummy_extern; - + trx_t* trx; + ut_ad(node && thr); - + trx = thr_get_trx(thr); ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info, &dummy_extern, &undo_no, &table_id); node->rec_type = type; - node->table = dict_table_get_on_id(table_id, thr_get_trx(thr)); + node->table = dict_table_get_on_id(table_id, trx); /* TODO: other fixes associated with DROP TABLE + rollback in the same table by another user */ @@ -688,6 +690,13 @@ row_undo_mod_parse_undo_rec( return; } + if (node->table->ibd_file_missing) { + /* We skip undo operations to missing .ibd files */ + node->table = NULL; + + return; + } + clust_index = dict_table_get_first_index(node->table); ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr, @@ -697,8 +706,8 @@ row_undo_mod_parse_undo_rec( node->heap); trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id, - roll_ptr, info_bits, node->heap, - &(node->update)); + roll_ptr, info_bits, trx, + node->heap, &(node->update)); node->new_roll_ptr = roll_ptr; node->new_trx_id = trx_id; node->cmpl_info = cmpl_info; diff --git a/innobase/row/row0undo.c b/innobase/row/row0undo.c index e1e44724752..bc3cc8ea9f3 100644 --- a/innobase/row/row0undo.c +++ b/innobase/row/row0undo.c @@ -320,7 +320,8 @@ row_undo_step( if (err != DB_SUCCESS) { /* SQL error detected */ - fprintf(stderr, "InnoDB: Fatal error %lu in rollback.\n", err); + fprintf(stderr, "InnoDB: Fatal error %lu in rollback.\n", + (ulong) err); if (err == DB_OUT_OF_FILE_SPACE) { fprintf(stderr, diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c index 22f7ba60ca4..a449b9f1736 100644 --- a/innobase/row/row0upd.c +++ b/innobase/row/row0upd.c @@ -685,6 +685,7 @@ row_upd_build_sec_rec_difference_binary( dict_index_t* index, /* in: index */ dtuple_t* entry, /* in: entry to insert */ rec_t* rec, /* in: secondary index record */ + trx_t* trx, /* in: transaction */ mem_heap_t* heap) /* in: memory heap from which allocated */ { upd_field_t* upd_field; @@ -725,7 +726,7 @@ row_upd_build_sec_rec_difference_binary( dfield_copy(&(upd_field->new_val), dfield); - upd_field_set_field_no(upd_field, i, index); + upd_field_set_field_no(upd_field, i, index, trx); upd_field->extern_storage = FALSE; @@ -754,6 +755,7 @@ row_upd_build_difference_binary( externally stored fields in entry, or NULL */ ulint n_ext_vec,/* in: number of fields in ext_vec */ rec_t* rec, /* in: clustered index record */ + trx_t* trx, /* in: transaction */ mem_heap_t* heap) /* in: memory heap from which allocated */ { upd_field_t* upd_field; @@ -800,7 +802,7 @@ row_upd_build_difference_binary( dfield_copy(&(upd_field->new_val), dfield); - upd_field_set_field_no(upd_field, i, index); + upd_field_set_field_no(upd_field, i, index, trx); if (upd_ext_vec_contains(ext_vec, n_ext_vec, i)) { upd_field->extern_storage = TRUE; @@ -842,6 +844,7 @@ row_upd_index_replace_new_col_vals_index_pos( dfield_t* new_val; ulint j; ulint i; + dtype_t* cur_type; ut_ad(index); @@ -871,10 +874,17 @@ row_upd_index_replace_new_col_vals_index_pos( } if (field->prefix_len > 0 - && new_val->len != UNIV_SQL_NULL - && new_val->len > field->prefix_len) { + && new_val->len != UNIV_SQL_NULL) { - dfield->len = field->prefix_len; + cur_type = dict_col_get_type( + dict_field_get_col(field)); + + dfield->len = + dtype_get_at_most_n_mbchars( + cur_type, + field->prefix_len, + new_val->len, + new_val->data); } } } @@ -904,6 +914,7 @@ row_upd_index_replace_new_col_vals( dfield_t* new_val; ulint j; ulint i; + dtype_t* cur_type; ut_ad(index); @@ -933,10 +944,17 @@ row_upd_index_replace_new_col_vals( } if (field->prefix_len > 0 - && new_val->len != UNIV_SQL_NULL - && new_val->len > field->prefix_len) { + && new_val->len != UNIV_SQL_NULL) { + + cur_type = dict_col_get_type( + dict_field_get_col(field)); - dfield->len = field->prefix_len; + dfield->len = + dtype_get_at_most_n_mbchars( + cur_type, + field->prefix_len, + new_val->len, + new_val->data); } } } @@ -1200,10 +1218,11 @@ row_upd_sec_index_entry( rec_t* rec; ulint err = DB_SUCCESS; mtr_t mtr; - + trx_t* trx = thr_get_trx(thr); + index = node->index; - check_ref = row_upd_index_is_referenced(index, thr_get_trx(thr)); + check_ref = row_upd_index_is_referenced(index, trx); heap = mem_heap_create(1024); @@ -1222,7 +1241,7 @@ row_upd_sec_index_entry( if (!found) { fputs("InnoDB: error in sec index entry update in\n" "InnoDB: ", stderr); - dict_index_name_print(stderr, index); + dict_index_name_print(stderr, trx, index); fputs("\n" "InnoDB: tuple ", stderr); dtuple_print(stderr, entry); @@ -1231,7 +1250,7 @@ row_upd_sec_index_entry( rec_print(stderr, rec); putc('\n', stderr); - trx_print(stderr, thr_get_trx(thr)); + trx_print(stderr, trx); fputs("\n" "InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n", stderr); @@ -1602,7 +1621,8 @@ row_upd_clust_step( then we have to free the file segments of the index tree associated with the index */ - if (ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) { + if (node->is_delete + && ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) { dict_drop_index_tree(btr_pcur_get_rec(pcur), mtr); |