diff options
Diffstat (limited to 'innobase/row/row0ins.c')
-rw-r--r-- | innobase/row/row0ins.c | 189 |
1 files changed, 150 insertions, 39 deletions
diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index 0da749212d2..c45818ddd26 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 */ @@ -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,11 +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 */ @@ -847,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); @@ -968,6 +980,23 @@ row_ins_foreign_check_on_constraint( err = row_update_cascade_for_mysql(thr, cascade, foreign->foreign_table); + + if (foreign->foreign_table->n_foreign_key_checks_running == 0) { + fprintf(stderr, +"InnoDB: error: table %s has the counter 0 though there is\n" +"InnoDB: a FOREIGN KEY check running on it.\n", + foreign->foreign_table->name); + } + + /* Release the data dictionary latch for a while, so that we do not + starve other threads from doing CREATE TABLE etc. if we have a huge + cascaded operation running. The counter n_foreign_key_checks_running + will prevent other users from dropping or ALTERing the table when we + release the latch. */ + + row_mysql_unfreeze_data_dictionary(thr_get_trx(thr)); + row_mysql_freeze_data_dictionary(thr_get_trx(thr)); + mtr_start(mtr); /* Restore pcur position */ @@ -1022,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 @@ -1057,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 @@ -1065,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 */ @@ -1123,18 +1180,18 @@ run_again: 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); + ut_print_name(ef, trx, foreign->referenced_table_name); fputs(" does not currently exist!\n", ef); mutex_exit(&dict_foreign_err_mutex); @@ -1267,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; } @@ -1283,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; @@ -1302,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); @@ -1451,7 +1508,9 @@ row_ins_scan_sec_index_for_duplicate( ulint err = DB_SUCCESS; ibool moved; mtr_t mtr; - + trx_t* trx; + const char* ptr; + n_unique = dict_index_get_n_unique(index); /* If the secondary index is unique, but one of the fields in the @@ -1488,8 +1547,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) { @@ -1556,6 +1630,7 @@ row_ins_duplicate_error_in_clust( page_t* page; ulint n_unique; trx_t* trx = thr_get_trx(thr); + const char* ptr; UT_NOT_USED(mtr); @@ -1588,9 +1663,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); @@ -1611,8 +1701,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); @@ -1913,6 +2019,7 @@ row_ins_index_entry_set_vals( dfield_t* row_field; ulint n_fields; ulint i; + dtype_t* cur_type; ut_ad(entry && row); @@ -1926,10 +2033,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; } @@ -2214,4 +2325,4 @@ error_handling: } return(thr); -} +} |