📄 row0ins.c
字号:
ufield->field_no = dict_table_get_nth_col_pos(table, dict_index_get_nth_col_no(index, i)); ufield->exp = NULL; ufield->new_val = parent_ufield->new_val; type = dict_index_get_nth_type(index, i); /* Do not allow a NOT NULL column to be updated as NULL */ if (ufield->new_val.len == UNIV_SQL_NULL && (type->prtype & DATA_NOT_NULL)) { return(ULINT_UNDEFINED); } /* If the new value would not fit in the column, do not allow the update */ if (ufield->new_val.len != UNIV_SQL_NULL && dtype_get_at_most_n_mbchars( type, dtype_get_len(type), ufield->new_val.len, ufield->new_val.data) < ufield->new_val.len) { return(ULINT_UNDEFINED); } /* If the parent column type has a different length than the child column type, we may need to pad with spaces the new value of the child column */ min_size = dtype_get_min_size(type); if (min_size && ufield->new_val.len != UNIV_SQL_NULL && ufield->new_val.len < min_size) { char* pad_start; const char* pad_end; ufield->new_val.data = mem_heap_alloc(heap, min_size); pad_start = ((char*) ufield->new_val.data) + ufield->new_val.len; pad_end = ((char*) ufield->new_val.data) + min_size; ufield->new_val.len = min_size; ut_memcpy(ufield->new_val.data, parent_ufield->new_val.data, parent_ufield->new_val.len); switch (UNIV_EXPECT( dtype_get_mbminlen(type), 1)) { default: ut_error; case 1: if (UNIV_UNLIKELY( dtype_get_charset_coll( dtype_get_prtype(type)) == DATA_MYSQL_BINARY_CHARSET_COLL)) { /* Do not pad BINARY columns. */ return(ULINT_UNDEFINED); } /* space=0x20 */ memset(pad_start, 0x20, pad_end - pad_start); break; case 2: /* space=0x0020 */ ut_a(!(ufield->new_val.len % 2)); ut_a(!(min_size % 2)); do { *pad_start++ = 0x00; *pad_start++ = 0x20; } while (pad_start < pad_end); break; } } ufield->extern_storage = FALSE; n_fields_updated++; } } } update->n_fields = n_fields_updated; return(n_fields_updated);}/*************************************************************************Set detailed error message associated with foreign key errors forthe given transaction. */staticvoidrow_ins_set_detailed(/*=================*/ trx_t* trx, /* in: transaction */ dict_foreign_t* foreign) /* in: foreign key constraint */{ mutex_enter(&srv_misc_tmpfile_mutex); rewind(srv_misc_tmpfile); if (os_file_set_eof(srv_misc_tmpfile)) { ut_print_name(srv_misc_tmpfile, trx, foreign->foreign_table_name); dict_print_info_on_foreign_key_in_create_format( srv_misc_tmpfile, trx, foreign, FALSE); trx_set_detailed_error_from_file(trx, srv_misc_tmpfile); } else { trx_set_detailed_error(trx, "temp file operation failed"); } mutex_exit(&srv_misc_tmpfile_mutex);}/*************************************************************************Reports a foreign key error associated with an update or a delete of aparent table index entry. */staticvoidrow_ins_foreign_report_err(/*=======================*/ 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 */ dict_foreign_t* foreign, /* in: foreign key constraint */ rec_t* rec, /* in: a matching index record in the child table */ dtuple_t* entry) /* in: index entry in the parent table */{ FILE* ef = dict_foreign_err_file; trx_t* trx = thr_get_trx(thr); 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); putc('\n', ef); fputs(errstr, ef); fputs(" in parent table, in index ", ef); 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, trx, foreign->foreign_table_name); fputs(", in index ", ef); ut_print_name(ef, trx, foreign->foreign_index->name); if (rec) { fputs(", there is a record:\n", ef); rec_print(ef, rec, foreign->foreign_index); } else { fputs(", the record is not available\n", ef); } putc('\n', ef); mutex_exit(&dict_foreign_err_mutex);}/*************************************************************************Reports a foreign key error to dict_foreign_err_buf when we are tryingto add an index entry to a child table. Note that the adding may be the resultof an update, too. */staticvoidrow_ins_foreign_report_add_err(/*===========================*/ trx_t* trx, /* in: transaction */ dict_foreign_t* foreign, /* in: foreign key constraint */ rec_t* rec, /* in: a record in the parent table: it does not match entry because we have an error! */ dtuple_t* entry) /* in: index entry to insert in the child table */{ 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 in child table, in index ", ef); 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, trx, foreign->referenced_table_name); fputs(", in index ", ef); 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 to report the previous record in the error message, so that the user gets a more descriptive error message. */ rec = page_rec_get_prev(rec); } if (rec) { rec_print(ef, rec, foreign->referenced_index); } putc('\n', ef); mutex_exit(&dict_foreign_err_mutex);}/*************************************************************************Invalidate the query cache for the given table. */staticvoidrow_ins_invalidate_query_cache(/*===========================*/ que_thr_t* thr, /* in: query thread whose run_node is an update node */ const char* name) /* in: table name prefixed with database name and a '/' character */{ char* buf; char* ptr; ulint len = strlen(name) + 1; buf = mem_strdupl(name, len); ptr = strchr(buf, '/'); ut_a(ptr); *ptr = '\0'; /* We call a function in ha_innodb.cc */#ifndef UNIV_HOTBACKUP innobase_invalidate_query_cache(thr_get_trx(thr), buf, len);#endif mem_free(buf);}/*************************************************************************Perform referential actions or checks when a parent row is deleted or updatedand the constraint had an ON DELETE or ON UPDATE condition which was notRESTRICT. */staticulintrow_ins_foreign_check_on_constraint(/*================================*/ /* 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 */ dtuple_t* entry, /* in: index entry in the parent 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* upd_vec_heap = NULL; rec_t* rec; rec_t* clust_rec; upd_t* update; ulint n_to_update; ulint err; ulint i; trx_t* trx; mem_heap_t* tmp_heap = NULL; 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. A deadlock of threads is not possible here because the caller of this function does not hold any latches with the sync0sync.h rank above the kernel mutex. The query cache mutex has a rank just above the kernel mutex. */ row_ins_invalidate_query_cache(thr, table->name); node = thr->run_node; if (node->is_delete && 0 == (foreign->type & (DICT_FOREIGN_ON_DELETE_CASCADE | DICT_FOREIGN_ON_DELETE_SET_NULL))) { row_ins_foreign_report_err("Trying to delete", thr, foreign, btr_pcur_get_rec(pcur), entry); return(DB_ROW_IS_REFERENCED); } if (!node->is_delete && 0 == (foreign->type & (DICT_FOREIGN_ON_UPDATE_CASCADE | DICT_FOREIGN_ON_UPDATE_SET_NULL))) { /* This is an UPDATE */ row_ins_foreign_report_err("Trying to update", thr, foreign, btr_pcur_get_rec(pcur), entry); return(DB_ROW_IS_REFERENCED); } 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; cascade->foreign = foreign; if (node->is_delete && (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; } } /* We do not allow cyclic cascaded updating (DELETE is allowed, but not UPDATE) of the same table, as this can lead to an infinite cycle. Check that we are not updating the same table which is already being modified in this cascade chain. We have to check this also because the modification of the indexes of a 'parent' table may still be incomplete, and we must avoid seeing the indexes of the parent table in an inconsistent state! */ if (!cascade->is_delete && row_ins_cascade_ancestor_updates_table(cascade, table)) { /* We do not know if this would break foreign key constraints, but play safe and return an error */ err = DB_ROW_IS_REFERENCED; row_ins_foreign_report_err("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; } if (row_ins_cascade_n_ancestors(cascade) >= 15) { err = DB_ROW_IS_REFERENCED; row_ins_foreign_report_err("Trying a too deep cascaded delete or update\n", thr, foreign, btr_pcur_get_rec(pcur), entry); goto nonstandard_exit_func; } index = btr_pcur_get_btr_cur(pcur)->index; ut_a(index == foreign->foreign_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); clust_rec = btr_pcur_get_rec(cascade->pcur); if (!page_rec_is_user_rec(clust_rec) || btr_pcur_get_low_match(cascade->pcur) < dict_index_get_n_unique(clust_index)) { fputs( "InnoDB: error in cascade of a foreign key op\n" "InnoDB: ", stderr); dict_index_name_print(stderr, trx, index); fputs("\n" "InnoDB: record ", stderr); rec_print(stderr, rec, index); fputs("\n" "InnoDB: clustered record ", stderr); rec_print(stderr, clust_rec, clust_index); fputs("\n""InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n", stderr); 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) { /* Here it suffices to use a LOCK_REC_NOT_GAP type lock; we already have a normal shared lock on the appropriate gap if the search criterion was not unique */ err = lock_clust_rec_read_check_and_lock_alt(0, clust_rec, clust_index, LOCK_X, LOCK_REC_NOT_GAP, thr); } if (err != DB_SUCCESS) { goto nonstandard_exit_func; } if (rec_get_deleted_flag(clust_rec, table->comp)) { /* This can happen if there is a circular reference of rows such that cascading delete comes to delete a row already in the process of being delete marked */ err = DB_SUCCESS; goto nonstandard_exit_func; } if ((node->is_delete && (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)) || (!node->is_delete && (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL))) { /* Build the appropriate update vector which sets foreign->n_fields first fields in rec to SQL NULL */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -