diff options
Diffstat (limited to 'innobase/row')
-rw-r--r-- | innobase/row/row0ins.c | 276 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 177 | ||||
-rw-r--r-- | innobase/row/row0sel.c | 8 | ||||
-rw-r--r-- | innobase/row/row0upd.c | 109 |
4 files changed, 484 insertions, 86 deletions
diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index 04df66c3bbd..0ba6db39e2a 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -356,6 +356,223 @@ row_ins_dupl_error_with_rec( } /************************************************************************* +Either deletes or sets the referencing columns SQL NULL in a child row. +Used in ON DELETE ... clause for foreign keys when a parent row is +deleted. */ +static +ulint +row_ins_foreign_delete_or_set_null( +/*===============================*/ + /* out: DB_SUCCESS, DB_LOCK_WAIT, + or error code */ + que_thr_t* thr, /* in: query thread whose run_node + is an update node */ + dict_foreign_t* foreign, /* in: foreign key constraint whose + type is != 0 */ + btr_pcur_t* pcur, /* in: cursor placed on a matching + index record in the child table */ + mtr_t* mtr) /* in: mtr holding the latch of pcur + page */ +{ + upd_node_t* node; + upd_node_t* cascade; + dict_table_t* table = foreign->foreign_table; + dict_index_t* index; + dict_index_t* clust_index; + dtuple_t* ref; + mem_heap_t* tmp_heap; + rec_t* rec; + rec_t* clust_rec; + upd_t* update; + ulint err; + ulint i; + char err_buf[1000]; + + ut_a(thr && foreign && pcur && mtr); + + node = thr->run_node; + + if (node->cascade_node == NULL) { + /* Extend our query graph by creating a child to current + update node. The child is used in the cascade or set null + operation. */ + + node->cascade_heap = mem_heap_create(128); + node->cascade_node = row_create_update_node_for_mysql( + table, node->cascade_heap); + que_node_set_parent(node->cascade_node, node); + } + + /* Initialize cascade_node to do the operation we want. Note that we + use the SAME cascade node to do all foreign key operations of the + SQL DELETE: the table of the cascade node may change if there are + several child tables to the table where the delete is done! */ + + cascade = node->cascade_node; + + cascade->table = table; + + if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE ) { + cascade->is_delete = TRUE; + } else { + cascade->is_delete = FALSE; + + if (foreign->n_fields > cascade->update_n_fields) { + /* We have to make the update vector longer */ + + cascade->update = upd_create(foreign->n_fields, + node->cascade_heap); + cascade->update_n_fields = foreign->n_fields; + } + } + + index = btr_pcur_get_btr_cur(pcur)->index; + + rec = btr_pcur_get_rec(pcur); + + if (index->type & DICT_CLUSTERED) { + /* pcur is already positioned in the clustered index of + the child table */ + + clust_index = index; + clust_rec = rec; + } else { + /* We have to look for the record in the clustered index + in the child table */ + + clust_index = dict_table_get_first_index(table); + + tmp_heap = mem_heap_create(256); + + ref = row_build_row_ref(ROW_COPY_POINTERS, index, rec, + tmp_heap); + btr_pcur_open_with_no_init(clust_index, ref, + PAGE_CUR_LE, BTR_SEARCH_LEAF, + cascade->pcur, 0, mtr); + + mem_heap_free(tmp_heap); + + clust_rec = btr_pcur_get_rec(cascade->pcur); + } + + if (!page_rec_is_user_rec(clust_rec)) { + fprintf(stderr, "InnoDB: error in cascade of a foreign key op\n" + "InnoDB: index %s table %s\n", index->name, + index->table->name); + + rec_sprintf(err_buf, 900, rec); + fprintf(stderr, "InnoDB: record %s\n", err_buf); + + rec_sprintf(err_buf, 900, clust_rec); + fprintf(stderr, "InnoDB: clustered record %s\n", err_buf); + + fprintf(stderr, + "InnoDB: Make a detailed bug report and send it\n"); + fprintf(stderr, "InnoDB: to mysql@lists.mysql.com\n"); + + err = DB_SUCCESS; + + goto nonstandard_exit_func; + } + + /* Set an X-lock on the row to delete or update in the child table */ + + err = lock_table(0, table, LOCK_IX, thr); + + if (err == DB_SUCCESS) { + err = lock_clust_rec_read_check_and_lock(0, clust_rec, + clust_index, LOCK_X, thr); + } + + if (err != DB_SUCCESS) { + + goto nonstandard_exit_func; + } + + if (rec_get_deleted_flag(clust_rec)) { + /* This should never happen since we already have an S-lock + on non-delete-marked clust_rec or secondary index record! */ + + fprintf(stderr, + "InnoDB: error 2 in cascade of a foreign key op\n" + "InnoDB: index %s table %s\n", index->name, + index->table->name); + + rec_sprintf(err_buf, 900, rec); + fprintf(stderr, "InnoDB: record %s\n", err_buf); + + rec_sprintf(err_buf, 900, clust_rec); + fprintf(stderr, "InnoDB: clustered record %s\n", err_buf); + + fprintf(stderr, + "InnoDB: Make a detailed bug report and send it\n"); + fprintf(stderr, "InnoDB: to mysql@lists.mysql.com\n"); + + ut_a(0); + + goto nonstandard_exit_func; + } + + if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) { + /* Build the appropriate update vector which sets + foreign->n_fields first fields in rec to SQL NULL */ + + update = cascade->update; + + update->info_bits = 0; + update->n_fields = foreign->n_fields; + + for (i = 0; i < foreign->n_fields; i++) { + (update->fields + i)->field_no + = dict_table_get_nth_col_pos(table, + dict_index_get_nth_col_no(index, i)); + (update->fields + i)->exp = NULL; + (update->fields + i)->new_val.len = UNIV_SQL_NULL; + (update->fields + i)->new_val.data = NULL; + (update->fields + i)->extern_storage = FALSE; + } + } + + /* Store pcur position and initialize or store the cascade node + pcur stored position */ + + btr_pcur_store_position(pcur, mtr); + + if (index == clust_index) { + btr_pcur_copy_stored_position(cascade->pcur, pcur); + } else { + btr_pcur_store_position(cascade->pcur, mtr); + } + + mtr_commit(mtr); + + ut_a(cascade->pcur->rel_pos == BTR_PCUR_ON); + + cascade->state = UPD_NODE_UPDATE_CLUSTERED; + + err = row_update_cascade_for_mysql(thr, cascade, + foreign->foreign_table); + mtr_start(mtr); + + /* Restore pcur position */ + + btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr); + + return(err); + +nonstandard_exit_func: + + btr_pcur_store_position(pcur, mtr); + + mtr_commit(mtr); + mtr_start(mtr); + + btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr); + + return(err); +} + +/************************************************************************* Sets a shared lock on a record. Used in locking possible duplicate key records. */ static @@ -416,6 +633,13 @@ row_ins_check_foreign_constraint( ut_ad(rw_lock_own(&dict_foreign_key_check_lock, RW_LOCK_SHARED)); + if (thr_get_trx(thr)->check_foreigns == FALSE) { + /* The user has suppressed foreign key checks currently for + this session */ + + return(DB_SUCCESS); + } + /* If any of the foreign key fields in entry is SQL NULL, we suppress the foreign key check: this is compatible with Oracle, for example */ @@ -478,8 +702,8 @@ row_ins_check_foreign_constraint( goto next_rec; } - - /* Try to place a lock on the index record */ + + /* Try to place a lock on the index record */ err = row_ins_set_shared_rec_lock(rec, check_index, thr); @@ -501,11 +725,21 @@ row_ins_check_foreign_constraint( if (check_ref) { err = DB_SUCCESS; + + break; + } else if (foreign->type != 0) { + err = + row_ins_foreign_delete_or_set_null( + thr, foreign, &pcur, &mtr); + + if (err != DB_SUCCESS) { + + break; + } } else { err = DB_ROW_IS_REFERENCED; + break; } - - break; } } @@ -534,6 +768,8 @@ next_rec: } } + btr_pcur_close(&pcur); + mtr_commit(&mtr); /* Restore old value */ @@ -561,6 +797,10 @@ row_ins_check_foreign_constraints( { dict_foreign_t* foreign; ulint err; + trx_t* trx; + ibool got_s_lock = FALSE; + + trx = thr_get_trx(thr); foreign = UT_LIST_GET_FIRST(table->foreign_list); @@ -569,16 +809,26 @@ row_ins_check_foreign_constraints( if (foreign->referenced_table == NULL) { dict_table_get(foreign->referenced_table_name, - thr_get_trx(thr)); + trx); } - rw_lock_s_lock(&dict_foreign_key_check_lock); + if (!trx->has_dict_foreign_key_check_lock) { + got_s_lock = TRUE; + + rw_lock_s_lock(&dict_foreign_key_check_lock); + + trx->has_dict_foreign_key_check_lock = TRUE; + } err = row_ins_check_foreign_constraint(TRUE, foreign, table, index, entry, thr); + if (got_s_lock) { - rw_lock_s_unlock(&dict_foreign_key_check_lock); + rw_lock_s_unlock(&dict_foreign_key_check_lock); + trx->has_dict_foreign_key_check_lock = FALSE; + } + if (err != DB_SUCCESS) { return(err); } @@ -868,13 +1118,14 @@ row_ins_index_entry_low( ulint n_ext_vec,/* in: number of fields in ext_vec */ que_thr_t* thr) /* in: query thread */ { - btr_cur_t cursor; + btr_cur_t cursor; + ulint ignore_sec_unique = 0; ulint modify; rec_t* insert_rec; rec_t* rec; ulint err; ulint n_unique; - big_rec_t* big_rec = NULL; + big_rec_t* big_rec = NULL; mtr_t mtr; log_free_check(); @@ -887,8 +1138,13 @@ row_ins_index_entry_low( the function will return in both low_match and up_match of the cursor sensible values */ + if (!(thr_get_trx(thr)->check_unique_secondary)) { + ignore_sec_unique = BTR_IGNORE_SEC_UNIQUE; + } + btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, - mode | BTR_INSERT, &cursor, 0, &mtr); + mode | BTR_INSERT | ignore_sec_unique, + &cursor, 0, &mtr); if (cursor.flag == BTR_CUR_INSERT_TO_IBUF) { /* The insertion was made to the insert buffer already during diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 2aca698eebd..7969394f5e4 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -499,29 +499,24 @@ UNIV_INLINE void row_update_statistics_if_needed( /*============================*/ - row_prebuilt_t* prebuilt) /* in: prebuilt struct */ + dict_table_t* table) /* in: table */ { ulint counter; - counter = prebuilt->table->stat_modified_counter; - - /* Since the physical size of an InnoDB row is bigger than the - MySQL row len, we put a safety factor 2 below */ - - counter += 2 * prebuilt->mysql_row_len; + counter = table->stat_modified_counter; - prebuilt->table->stat_modified_counter = counter; + table->stat_modified_counter = counter + 1; /* Calculate new statistics if 1 / 16 of table has been modified since the last time a statistics batch was run, or if - stat_modified_counter > 2 000 000 000 (to avoid wrap-around) */ + stat_modified_counter > 2 000 000 000 (to avoid wrap-around). + We calculate statistics at most every 16th round, since we may have + a counter table which is very small and updated very often. */ if (counter > 2000000000 - || ((ib_longlong)counter > - (UNIV_PAGE_SIZE * prebuilt->table->stat_clustered_index_size) - / 16)) { + || ((ib_longlong)counter > 16 + table->stat_n_rows / 16)) { - dict_update_statistics(prebuilt->table); + dict_update_statistics(table); } } @@ -712,7 +707,7 @@ run_again: prebuilt->table->stat_n_rows--; } - row_update_statistics_if_needed(prebuilt); + row_update_statistics_if_needed(prebuilt->table); trx->op_info = ""; return((int) err); @@ -746,6 +741,43 @@ row_prebuild_sel_graph( } /************************************************************************* +Creates an query graph node of 'update' type to be used in the MySQL +interface. */ + +upd_node_t* +row_create_update_node_for_mysql( +/*=============================*/ + /* out, own: update node */ + dict_table_t* table, /* in: table to update */ + mem_heap_t* heap) /* in: mem heap from which allocated */ +{ + upd_node_t* node; + + node = upd_node_create(heap); + + node->in_mysql_interface = TRUE; + node->is_delete = FALSE; + node->searched_update = FALSE; + node->select_will_do_update = FALSE; + node->select = NULL; + node->pcur = btr_pcur_create_for_mysql(); + node->table = table; + + node->update = upd_create(dict_table_get_n_cols(table), heap); + + node->update_n_fields = dict_table_get_n_cols(table); + + UT_LIST_INIT(node->columns); + node->has_clust_rec_x_lock = TRUE; + node->cmpl_info = 0; + + node->table_sym = NULL; + node->col_assign_list = NULL; + + return(node); +} + +/************************************************************************* Gets pointer to a prebuilt update vector used in updates. If the update graph has not yet been built in the prebuilt struct, then this function first builds it. */ @@ -767,26 +799,9 @@ row_get_prebuilt_update_vector( /* Not called before for this handle: create an update node and query graph to the prebuilt struct */ - node = upd_node_create(prebuilt->heap); - - prebuilt->upd_node = node; + node = row_create_update_node_for_mysql(table, prebuilt->heap); - node->in_mysql_interface = TRUE; - node->is_delete = FALSE; - node->searched_update = FALSE; - node->select_will_do_update = FALSE; - node->select = NULL; - node->pcur = btr_pcur_create_for_mysql(); - node->table = table; - - node->update = upd_create(dict_table_get_n_cols(table), - prebuilt->heap); - UT_LIST_INIT(node->columns); - node->has_clust_rec_x_lock = TRUE; - node->cmpl_info = 0; - - node->table_sym = NULL; - node->col_assign_list = NULL; + prebuilt->upd_node = node; prebuilt->upd_graph = que_node_get_parent( @@ -914,7 +929,7 @@ run_again: que_thr_stop_for_mysql_no_error(thr, trx); - if (prebuilt->upd_node->is_delete) { + if (node->is_delete) { if (prebuilt->table->stat_n_rows > 0) { prebuilt->table->stat_n_rows--; } @@ -924,13 +939,66 @@ run_again: srv_n_rows_updated++; } - row_update_statistics_if_needed(prebuilt); + row_update_statistics_if_needed(prebuilt->table); trx->op_info = ""; return((int) err); } +/************************************************************************** +Does a cascaded delete or set null in a foreign key operation. */ + +ulint +row_update_cascade_for_mysql( +/*=========================*/ + /* out: error code or DB_SUCCESS */ + que_thr_t* thr, /* in: query thread */ + upd_node_t* node, /* in: update node used in the cascade + or set null operation */ + dict_table_t* table) /* in: table where we do the operation */ +{ + ulint err; + trx_t* trx; + + trx = thr_get_trx(thr); + +run_again: + thr->run_node = node; + thr->prev_node = node; + + row_upd_step(thr); + + err = trx->error_state; + + if (err == DB_LOCK_WAIT) { + que_thr_stop_for_mysql(thr); + + row_mysql_handle_errors(&err, trx, thr, NULL); + + goto run_again; + } + + if (err != DB_SUCCESS) { + + return(err); + } + + if (node->is_delete) { + if (table->stat_n_rows > 0) { + table->stat_n_rows--; + } + + srv_n_rows_deleted++; + } else { + srv_n_rows_updated++; + } + + row_update_statistics_if_needed(table); + + return(err); +} + /************************************************************************* Checks if a table is such that we automatically created a clustered index on it (on row id). */ @@ -1169,6 +1237,7 @@ row_create_table_for_mysql( /* Serialize data dictionary operations with dictionary mutex: no deadlocks can occur then in these operations */ + rw_lock_x_lock(&(dict_foreign_key_check_lock)); mutex_enter(&(dict_sys->mutex)); heap = mem_heap_create(512); @@ -1221,6 +1290,8 @@ row_create_table_for_mysql( } mutex_exit(&(dict_sys->mutex)); + rw_lock_x_unlock(&(dict_foreign_key_check_lock)); + que_graph_free((que_t*) que_node_get_parent(thr)); trx->op_info = ""; @@ -1268,6 +1339,7 @@ row_create_index_for_mysql( /* Serialize data dictionary operations with dictionary mutex: no deadlocks can occur then in these operations */ + rw_lock_x_lock(&(dict_foreign_key_check_lock)); mutex_enter(&(dict_sys->mutex)); heap = mem_heap_create(512); @@ -1298,6 +1370,7 @@ row_create_index_for_mysql( } mutex_exit(&(dict_sys->mutex)); + rw_lock_x_unlock(&(dict_foreign_key_check_lock)); que_graph_free((que_t*) que_node_get_parent(thr)); @@ -1353,6 +1426,7 @@ row_table_add_foreign_constraints( /* Serialize data dictionary operations with dictionary mutex: no deadlocks can occur then in these operations */ + rw_lock_x_lock(&(dict_foreign_key_check_lock)); mutex_enter(&(dict_sys->mutex)); trx->dict_operation = TRUE; @@ -1377,6 +1451,7 @@ row_table_add_foreign_constraints( } mutex_exit(&(dict_sys->mutex)); + rw_lock_x_unlock(&(dict_foreign_key_check_lock)); return((int) err); } @@ -1471,7 +1546,8 @@ loop: goto already_dropped; } - if (table->n_mysql_handles_opened > 0) { + if (table->n_mysql_handles_opened > 0 + || table->n_foreign_key_checks_running > 0) { return(n_tables + n_tables_dropped); } @@ -1717,6 +1793,9 @@ row_drop_table_for_mysql( no deadlocks can occur then in these operations */ if (!has_dict_mutex) { + /* Prevent foreign key checks while we are dropping the table */ + rw_lock_x_lock(&(dict_foreign_key_check_lock)); + mutex_enter(&(dict_sys->mutex)); } @@ -1729,9 +1808,6 @@ row_drop_table_for_mysql( graph->fork_type = QUE_FORK_MYSQL_INTERFACE; - /* Prevent foreign key checks while we are dropping the table */ - rw_lock_x_lock(&(dict_foreign_key_check_lock)); - /* Prevent purge from running while we are dropping the table */ rw_lock_s_lock(&(purge_sys->purge_is_running)); @@ -1766,6 +1842,22 @@ row_drop_table_for_mysql( goto funct_exit; } + if (table->n_foreign_key_checks_running > 0) { + + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: You are trying to drop table %s\n" + "InnoDB: though there are foreign key check running on it.\n" + "InnoDB: Adding the table to the background drop queue.\n", + table->name); + + row_add_table_to_background_drop_list(table); + + err = DB_SUCCESS; + + goto funct_exit; + } + /* Remove any locks there are on the table or its records */ lock_reset_all_on_table(table); @@ -1793,10 +1885,9 @@ row_drop_table_for_mysql( funct_exit: rw_lock_s_unlock(&(purge_sys->purge_is_running)); - rw_lock_x_unlock(&(dict_foreign_key_check_lock)); - if (!has_dict_mutex) { mutex_exit(&(dict_sys->mutex)); + rw_lock_x_unlock(&(dict_foreign_key_check_lock)); } que_graph_free(graph); @@ -1832,6 +1923,7 @@ row_drop_database_for_mysql( trx_start_if_not_started(trx); loop: + rw_lock_x_lock(&(dict_foreign_key_check_lock)); mutex_enter(&(dict_sys->mutex)); while (table_name = dict_get_first_table_name_in_db(name)) { @@ -1873,6 +1965,7 @@ loop: } mutex_exit(&(dict_sys->mutex)); + rw_lock_x_unlock(&(dict_foreign_key_check_lock)); trx_commit_for_mysql(trx); @@ -2009,6 +2102,7 @@ row_rename_table_for_mysql( /* Serialize data dictionary operations with dictionary mutex: no deadlocks can occur then in these operations */ + rw_lock_x_lock(&(dict_foreign_key_check_lock)); mutex_enter(&(dict_sys->mutex)); table = dict_table_get_low(old_name); @@ -2090,6 +2184,7 @@ row_rename_table_for_mysql( } funct_exit: mutex_exit(&(dict_sys->mutex)); + rw_lock_x_unlock(&(dict_foreign_key_check_lock)); que_graph_free(graph); diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 9ebd47c25bf..a516699edf9 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2115,8 +2115,14 @@ row_sel_store_mysql_rec( extern_field_heap = NULL; } } else { - mysql_rec[templ->mysql_null_byte_offset] |= + if (!templ->mysql_null_bit_mask) { + fprintf(stderr, +"InnoDB: Error: trying to return an SQL NULL field in a non-null\n" +"innoDB: column! Table name %s\n", prebuilt->table->name); + } else { + mysql_rec[templ->mysql_null_byte_offset] |= (byte) (templ->mysql_null_bit_mask); + } } } } diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c index a566e29f2c3..457cb72aaad 100644 --- a/innobase/row/row0upd.c +++ b/innobase/row/row0upd.c @@ -73,8 +73,7 @@ steps of query graph execution. */ /************************************************************************* Checks if index currently is mentioned as a referenced index in a foreign -key constraint. This function also loads into the dictionary cache the -possible referencing table. */ +key constraint. */ static ibool row_upd_index_is_referenced( @@ -85,44 +84,28 @@ row_upd_index_is_referenced( the referencing table has been dropped when we leave this function: this function is only for heuristic use! */ - dict_index_t* index) /* in: index */ + dict_index_t* index, /* in: index */ + trx_t* trx) /* in: transaction */ { - dict_table_t* table = index->table; + dict_table_t* table = index->table; dict_foreign_t* foreign; - ulint phase = 1; -try_again: if (!UT_LIST_GET_FIRST(table->referenced_list)) { return(FALSE); } - if (phase == 2) { - mutex_enter(&(dict_sys->mutex)); + if (!trx->has_dict_foreign_key_check_lock) { + rw_lock_s_lock(&dict_foreign_key_check_lock); } - rw_lock_s_lock(&dict_foreign_key_check_lock); - foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign) { if (foreign->referenced_index == index) { - if (foreign->foreign_table == NULL) { - if (phase == 2) { - dict_table_get_low(foreign-> - foreign_table_name); - } else { - phase = 2; - rw_lock_s_unlock( - &dict_foreign_key_check_lock); - goto try_again; - } - } - - rw_lock_s_unlock(&dict_foreign_key_check_lock); - if (phase == 2) { - mutex_exit(&(dict_sys->mutex)); + if (!trx->has_dict_foreign_key_check_lock) { + rw_lock_s_unlock(&dict_foreign_key_check_lock); } return(TRUE); @@ -131,10 +114,8 @@ try_again: foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } - rw_lock_s_unlock(&dict_foreign_key_check_lock); - - if (phase == 2) { - mutex_exit(&(dict_sys->mutex)); + if (!trx->has_dict_foreign_key_check_lock) { + rw_lock_s_unlock(&dict_foreign_key_check_lock); } return(FALSE); @@ -160,8 +141,17 @@ row_upd_check_references_constraints( dict_foreign_t* foreign; mem_heap_t* heap; dtuple_t* entry; + trx_t* trx; rec_t* rec; ulint err; + ibool got_s_lock = FALSE; + + if (UT_LIST_GET_FIRST(table->referenced_list) == NULL) { + + return(DB_SUCCESS); + } + + trx = thr_get_trx(thr); rec = btr_pcur_get_rec(pcur); @@ -173,17 +163,61 @@ row_upd_check_references_constraints( mtr_start(mtr); - rw_lock_s_lock(&dict_foreign_key_check_lock); + if (!trx->has_dict_foreign_key_check_lock) { + got_s_lock = TRUE; + rw_lock_s_lock(&dict_foreign_key_check_lock); + + trx->has_dict_foreign_key_check_lock = TRUE; + } + foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign) { if (foreign->referenced_index == index) { + if (foreign->foreign_table == NULL) { + dict_table_get(foreign->foreign_table_name, + trx); + } + if (foreign->foreign_table) { + mutex_enter(&(dict_sys->mutex)); + + (foreign->foreign_table + ->n_foreign_key_checks_running)++; + + mutex_exit(&(dict_sys->mutex)); + } + + /* NOTE that if the thread ends up waiting for a lock + we will release dict_foreign_key_check_lock + temporarily! But the counter on the table + protects 'foreign' from being dropped while the check + is running. */ + err = row_ins_check_foreign_constraint(FALSE, foreign, table, index, entry, thr); + + if (foreign->foreign_table) { + mutex_enter(&(dict_sys->mutex)); + + ut_a(foreign->foreign_table + ->n_foreign_key_checks_running > 0); + + (foreign->foreign_table + ->n_foreign_key_checks_running)--; + + mutex_exit(&(dict_sys->mutex)); + } + if (err != DB_SUCCESS) { - rw_lock_s_unlock(&dict_foreign_key_check_lock); + if (got_s_lock) { + rw_lock_s_unlock( + &dict_foreign_key_check_lock); + trx->has_dict_foreign_key_check_lock + = FALSE; + } + mem_heap_free(heap); return(err); @@ -193,7 +227,11 @@ row_upd_check_references_constraints( foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } - rw_lock_s_unlock(&dict_foreign_key_check_lock); + if (got_s_lock) { + rw_lock_s_unlock(&dict_foreign_key_check_lock); + trx->has_dict_foreign_key_check_lock = FALSE; + } + mem_heap_free(heap); return(DB_SUCCESS); @@ -222,6 +260,9 @@ upd_node_create( node->index = NULL; node->update = NULL; + node->cascade_heap = NULL; + node->cascade_node = NULL; + node->select = NULL; node->heap = mem_heap_create(128); @@ -1027,7 +1068,7 @@ row_upd_sec_index_entry( index = node->index; - check_ref = row_upd_index_is_referenced(index); + check_ref = row_upd_index_is_referenced(index, thr_get_trx(thr)); heap = mem_heap_create(1024); @@ -1391,7 +1432,7 @@ row_upd_clust_step( index = dict_table_get_first_index(node->table); - check_ref = row_upd_index_is_referenced(index); + check_ref = row_upd_index_is_referenced(index, thr_get_trx(thr)); pcur = node->pcur; |