📄 row0ins.c
字号:
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; } } if (!node->is_delete && (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)) { /* Build the appropriate update vector which sets changing foreign->n_fields first fields in rec to new values */ upd_vec_heap = mem_heap_create(256); n_to_update = row_ins_cascade_calc_update_vec(node, foreign, upd_vec_heap); if (n_to_update == ULINT_UNDEFINED) { err = DB_ROW_IS_REFERENCED; row_ins_foreign_report_err("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); goto nonstandard_exit_func; } if (cascade->update->n_fields == 0) { /* The update does not change any columns referred to in this foreign key constraint: no need to do anything */ err = DB_SUCCESS; goto nonstandard_exit_func; } } /* 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); 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 */ btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr); if (tmp_heap) { mem_heap_free(tmp_heap); } if (upd_vec_heap) { mem_heap_free(upd_vec_heap); } return(err);nonstandard_exit_func: if (tmp_heap) { mem_heap_free(tmp_heap); } if (upd_vec_heap) { mem_heap_free(upd_vec_heap); } 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 keyrecords and also in checking foreign key constraints. */staticulintrow_ins_set_shared_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 */ const ulint* offsets,/* in: rec_get_offsets(rec, index) */ que_thr_t* thr) /* in: query thread */ { ulint err; ut_ad(rec_offs_validate(rec, index, offsets)); if (index->type & DICT_CLUSTERED) { err = lock_clust_rec_read_check_and_lock(0, rec, index, offsets, LOCK_S, type, thr); } else { err = lock_sec_rec_read_check_and_lock(0, rec, index, offsets, LOCK_S, type, thr); } return(err);}/*************************************************************************Sets a exclusive lock on a record. Used in locking possible duplicate keyrecords */staticulintrow_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 */ const ulint* offsets,/* in: rec_get_offsets(rec, index) */ que_thr_t* thr) /* in: query thread */ { ulint err; ut_ad(rec_offs_validate(rec, index, offsets)); if (index->type & DICT_CLUSTERED) { err = lock_clust_rec_read_check_and_lock(0, rec, index, offsets, LOCK_X, type, thr); } else { err = lock_sec_rec_read_check_and_lock(0, rec, index, offsets, LOCK_X, type, thr); } return(err);} /*******************************************************************Checks if foreign key constraint fails for an index entry. Sets shared lockswhich lock either the success or the failure of the constraint. NOTE thatthe caller must have a shared latch on dict_operation_lock. */ulintrow_ins_check_foreign_constraint(/*=============================*/ /* out: DB_SUCCESS, DB_NO_REFERENCED_ROW, or DB_ROW_IS_REFERENCED */ ibool check_ref,/* in: TRUE if we want to check that the referenced table is ok, FALSE if we want to to check the foreign key table */ dict_foreign_t* foreign,/* in: foreign constraint; NOTE that the tables mentioned in it must be in the dictionary cache if they exist at all */ dict_table_t* table, /* in: if check_ref is TRUE, then the foreign table, else the referenced table */ dtuple_t* entry, /* in: index entry for index */ que_thr_t* thr) /* in: query thread */{ upd_node_t* upd_node; dict_table_t* check_table; dict_index_t* check_index; ulint n_fields_cmp; rec_t* rec; btr_pcur_t pcur; ibool moved; int cmp; ulint err; ulint i; mtr_t mtr; trx_t* trx = thr_get_trx(thr); mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; *offsets_ = (sizeof offsets_) / sizeof *offsets_;run_again:#ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED));#endif /* UNIV_SYNC_DEBUG */ err = DB_SUCCESS; if (trx->check_foreigns == FALSE) { /* The user has suppressed foreign key checks currently for this session */ goto exit_func; } /* 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 */ for (i = 0; i < foreign->n_fields; i++) { if (UNIV_SQL_NULL == dfield_get_len( dtuple_get_nth_field(entry, i))) { goto exit_func; } } if (que_node_get_type(thr->run_node) == QUE_NODE_UPDATE) { upd_node = thr->run_node; if (!(upd_node->is_delete) && upd_node->foreign == foreign) { /* If a cascaded update is done as defined by a foreign key constraint, do not check that constraint for the child row. In ON UPDATE CASCADE the update of the parent row is only half done when we come here: if we would check the constraint here for the child row it would fail. A QUESTION remains: if in the child table there are several constraints which refer to the same parent table, we should merge all updates to the child as one update? And the updates can be contradictory! Currently we just perform the update associated with each foreign key constraint, one after another, and the user has problems predicting in which order they are performed. */ goto exit_func; } } if (check_ref) { check_table = foreign->referenced_table; check_index = foreign->referenced_index; } else { check_table = foreign->foreign_table; check_index = foreign->foreign_index; } if (check_table == NULL || check_table->ibd_file_missing) { if (check_ref) { FILE* ef = dict_foreign_err_file; row_ins_set_detailed(trx, foreign); mutex_enter(&dict_foreign_err_mutex); rewind(ef); ut_print_timestamp(ef); fputs(" Transaction:\n", ef); trx_print(ef, trx, 600); fputs("Foreign key constraint fails for table ", ef); ut_print_name(ef, trx, foreign->foreign_table_name); fputs(":\n", ef); dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign, TRUE); fputs("\nTrying to add to index ", ef); 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, trx, foreign->referenced_table_name); fputs("\nor its .ibd file does not currently exist!\n", ef); mutex_exit(&dict_foreign_err_mutex); err = DB_NO_REFERENCED_ROW; } goto exit_func; } ut_a(check_table && check_index); if (check_table != table) { /* We already have a LOCK_IX on table, but not necessarily on check_table */ err = lock_table(0, check_table, LOCK_IS, thr); if (err != DB_SUCCESS) { goto do_possible_lock_wait; } } mtr_start(&mtr); /* Store old value on n_fields_cmp */ n_fields_cmp = dtuple_get_n_fields_cmp(entry); dtuple_set_n_fields_cmp(entry, foreign->n_fields); btr_pcur_open(check_index, entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); /* Scan index records and check if there is a matching record */ for (;;) { page_t* page; rec = btr_pcur_get_rec(&pcur); page = buf_frame_align(rec); if (rec == page_get_infimum_rec(page)) { goto next_rec; } offsets = rec_get_offsets(rec, check_index, offsets, ULINT_UNDEFINED, &heap); if (rec == page_get_supremum_rec(page)) { err = row_ins_set_shared_rec_lock(LOCK_ORDINARY, rec, check_index, offsets, thr); if (err != DB_SUCCESS) { break; } goto next_rec; } cmp = cmp_dtuple_rec(entry, rec, offsets); if (cmp == 0) { if (rec_get_deleted_flag(rec, rec_offs_comp(offsets))) { err = row_ins_set_shared_rec_lock( LOCK_ORDINARY, rec, check_index, offsets, thr); if (err != DB_SUCCESS) { break; } } else { /* Found a matching record. Lock only a record because we can allow inserts into gaps */ err = row_ins_set_shared_rec_lock( LOCK_REC_NOT_GAP, rec, check_index, offsets, thr); if (err != DB_SUCCESS) { break; } if (check_ref) { err = DB_SUCCESS; break; } else if (foreign->type != 0) { /* There is an ON UPDATE or ON DELETE condition: check them in a separate function */ err = row_ins_foreign_check_on_constraint( thr, foreign, &pcur, entry, &mtr); if (err != DB_SUCCESS) { break; } } else { row_ins_foreign_report_err( "Trying to delete or update", thr, foreign, rec, entry); err = DB_ROW_IS_REFERENCED; break; } } } if (cmp < 0) { err = row_ins_set_shared_rec_lock(LOCK_GAP, rec, check_index, offsets, thr); if (err != DB_SUCCESS) { break; } if (check_ref) { err = DB_NO_REFERENCED_ROW; row_ins_foreign_report_add_err( trx, foreign, rec, entry); } else { err = DB_SUCCESS; } break; } ut_a(cmp == 0);next_rec: moved = btr_pcur_move_to_next(&pcur, &mtr); if (!moved) { if (check_ref) { rec = btr_pcur_get_rec(&pcur); row_ins_foreign_report_add_err( trx, foreign, rec, entry); err = DB_NO_REFERENCED_ROW; } else { err = DB_SUCCESS; } break; } } btr_pcur_close(&pcur); mtr_commit(&mtr); /* Restore old value */ dtuple_set_n_fields_cmp(entry, n_fields_cmp);do_possible_lock_wait: if (err == DB_LOCK_WAIT) { trx->error_state = err; que_thr_stop_for_mysql(thr); srv_suspend_mysql_thread(thr); if (trx->error_state == DB_SUCCESS) { goto run_again; } err = trx->error_state; }exit_func: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err);}/*******************************************************************Checks if foreign key constraints fail for an index entry. If indexis not mentioned in any constraint, this function does nothing,Otherwise does searches to the indexes of referenced tables andsets shared locks which lock either the success or the failure ofa constraint. */staticulintrow_ins_check_foreign_constraints(/*==============================*/ /* out: DB_SUCCESS or error code */ dict_table_t* table, /* in: table */ dict_index_t* index, /* in: index */ dtuple_t* entry, /* in: index entry for index */ que_thr_t* thr) /* in: query thread */{ dict_foreign_t* foreign; ulint err; trx_t* trx; ibool got_s_lock = FALSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -