diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2021-03-25 15:06:19 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2021-03-25 15:06:19 +0200 |
commit | cd2d79aff440a40d4b64b96a34f193a056ea5312 (patch) | |
tree | 7e9294d4218dbe76aed8db251f4ec392a8ee9e4b | |
parent | cb545f11169d2425316d96feafc78ac841950e43 (diff) | |
download | mariadb-git-bb-10.6-MDEV-24786.tar.gz |
MDEV-24786: row_upd_clust_step() skips mtr_t::commit() on virtual column errorbb-10.6-MDEV-24786
The function row_upd_clust_step() is invoking several static functions,
some of which used to commit the mini-transaction in some cases.
If innobase_get_computed_value() would fail due to some reason,
we would fail to invoke mtr_t::commit() and release buffer pool
page latches. This would likely lead to a hanging server later.
This regression was introduced in
commit 97db6c15ea3e83a21df137c222dbd5a40fbe7c82 (MDEV-20618).
row_upd_index_is_referenced(), row_upd_sec_index_entry(),
row_upd_sec_index_entry(): Cleanup: Replace some ibool with bool.
row_upd_clust_rec_by_insert(), row_upd_clust_rec(): Guarantee that
the mini-transaction will always remain in active state.
row_upd_del_mark_clust_rec(): Guarantee that
the mini-transaction will always remain in active state.
This fixes one "leak" of mini-transaction on DB_COMPUTE_VALUE_FAILED.
row_upd_clust_step(): Use only one return path, which will always
invoke mtr.commit(). After a failed row_upd_store_row() call, we
will no longer "leak" the mini-transaction.
This fix was verified by RQG. Unfortunately, it is challenging to
create a regression test for this, and a test case could soon become
invalid as more bugs in virtual column evaluation are fixed.
-rw-r--r-- | storage/innobase/row/row0upd.cc | 130 |
1 files changed, 48 insertions, 82 deletions
diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 336c3d29342..ee826b95420 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -126,25 +126,23 @@ NOTE that since we do not hold dict_sys.latch when leaving the function, it may be that the referencing table has been dropped when we leave this function: this function is only for heuristic use! -@return TRUE if referenced */ +@return true if referenced */ static -ibool +bool row_upd_index_is_referenced( /*========================*/ dict_index_t* index, /*!< in: index */ trx_t* trx) /*!< in: transaction */ { dict_table_t* table = index->table; - ibool froze_data_dict = FALSE; - ibool is_referenced = FALSE; if (table->referenced_set.empty()) { - return(FALSE); + return false; } - if (trx->dict_operation_lock_mode == 0) { + const bool froze_data_dict = !trx->dict_operation_lock_mode; + if (froze_data_dict) { row_mysql_freeze_data_dictionary(trx); - froze_data_dict = TRUE; } dict_foreign_set::iterator it @@ -152,13 +150,13 @@ row_upd_index_is_referenced( table->referenced_set.end(), dict_foreign_with_index(index)); - is_referenced = (it != table->referenced_set.end()); + const bool is_referenced = (it != table->referenced_set.end()); if (froze_data_dict) { row_mysql_unfreeze_data_dictionary(trx); } - return(is_referenced); + return is_referenced; } #ifdef WITH_WSREP @@ -1957,7 +1955,6 @@ row_upd_sec_index_entry( dtuple_t* entry; dict_index_t* index; btr_cur_t* btr_cur; - ibool referenced; dberr_t err = DB_SUCCESS; trx_t* trx = thr_get_trx(thr); ulint mode; @@ -1968,7 +1965,7 @@ row_upd_sec_index_entry( index = node->index; - referenced = row_upd_index_is_referenced(index, trx); + const bool referenced = row_upd_index_is_referenced(index, trx); #ifdef WITH_WSREP bool foreign = wsrep_row_upd_index_is_foreign(index, trx); #endif /* WITH_WSREP */ @@ -2380,12 +2377,13 @@ row_upd_clust_rec_by_insert( upd_node_t* node, /*!< in/out: row update node */ dict_index_t* index, /*!< in: clustered index of the record */ que_thr_t* thr, /*!< in: query thread */ - ibool referenced,/*!< in: TRUE if index may be referenced in + bool referenced,/*!< in: whether index may be referenced in a foreign key constraint */ #ifdef WITH_WSREP bool foreign,/*!< in: whether this is a foreign key */ #endif - mtr_t* mtr) /*!< in/out: mtr; gets committed here */ + mtr_t* mtr) /*!< in/out: mini-transaction, + may be committed and restarted */ { mem_heap_t* heap; btr_pcur_t* pcur; @@ -2455,10 +2453,7 @@ row_upd_clust_rec_by_insert( btr_cur_get_block(btr_cur), rec, index, offsets, thr, node->row, mtr); if (err != DB_SUCCESS) { -err_exit: - mtr_commit(mtr); - mem_heap_free(heap); - return(err); + goto err_exit; } /* If the the new row inherits externally stored @@ -2518,14 +2513,14 @@ check_fk: } } - mtr_commit(mtr); + mtr->commit(); + mtr->start(); + node->state = UPD_NODE_INSERT_CLUSTERED; err = row_ins_clust_index_entry(index, entry, thr, dtuple_get_n_ext(entry)); - node->state = UPD_NODE_INSERT_CLUSTERED; - +err_exit: mem_heap_free(heap); - return(err); } @@ -2545,7 +2540,8 @@ row_upd_clust_rec( mem_heap_t** offsets_heap, /*!< in/out: memory heap, can be emptied */ que_thr_t* thr, /*!< in: query thread */ - mtr_t* mtr) /*!< in: mtr; gets committed here */ + mtr_t* mtr) /*!< in,out: mini-transaction; may be + committed and restarted here */ { mem_heap_t* heap = NULL; big_rec_t* big_rec = NULL; @@ -2591,15 +2587,15 @@ row_upd_clust_rec( goto success; } - mtr_commit(mtr); - if (buf_pool.running_out()) { err = DB_LOCK_TABLE_FULL; goto func_exit; } + /* We may have to modify the tree structure: do a pessimistic descent down the index tree */ + mtr->commit(); mtr->start(); if (index->table->is_temporary()) { @@ -2649,7 +2645,6 @@ success: } } - mtr_commit(mtr); func_exit: if (heap) { mem_heap_free(heap); @@ -2674,17 +2669,17 @@ row_upd_del_mark_clust_rec( rec_offs* offsets,/*!< in/out: rec_get_offsets() for the record under the cursor */ que_thr_t* thr, /*!< in: query thread */ - ibool referenced, - /*!< in: TRUE if index may be referenced in + bool referenced, + /*!< in: whether index may be referenced in a foreign key constraint */ #ifdef WITH_WSREP bool foreign,/*!< in: whether this is a foreign key */ #endif - mtr_t* mtr) /*!< in: mtr; gets committed here */ + mtr_t* mtr) /*!< in,out: mini-transaction; + will be committed and restarted */ { btr_pcur_t* pcur; btr_cur_t* btr_cur; - dberr_t err; rec_t* rec; trx_t* trx = thr_get_trx(thr); @@ -2700,8 +2695,7 @@ row_upd_del_mark_clust_rec( if (!row_upd_store_row(node, trx->mysql_thd, thr->prebuilt && thr->prebuilt->table == node->table ? thr->prebuilt->m_mysql_table : NULL)) { - err = DB_COMPUTE_VALUE_FAILED; - return err; + return DB_COMPUTE_VALUE_FAILED; } /* Mark the clustered index record deleted; we do not have to check @@ -2709,7 +2703,7 @@ row_upd_del_mark_clust_rec( rec = btr_cur_get_rec(btr_cur); - err = btr_cur_del_mark_set_clust_rec( + dberr_t err = btr_cur_del_mark_set_clust_rec( btr_cur_get_block(btr_cur), rec, index, offsets, thr, node->row, mtr); @@ -2746,8 +2740,6 @@ row_upd_del_mark_clust_rec( #endif /* WITH_WSREP */ } - mtr_commit(mtr); - return(err); } @@ -2764,14 +2756,12 @@ row_upd_clust_step( { dict_index_t* index; btr_pcur_t* pcur; - ibool success; dberr_t err; mtr_t mtr; rec_t* rec; mem_heap_t* heap = NULL; rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs* offsets; - ibool referenced; ulint flags; trx_t* trx = thr_get_trx(thr); @@ -2779,8 +2769,7 @@ row_upd_clust_step( index = dict_table_get_first_index(node->table); - referenced = row_upd_index_is_referenced(index, trx); - + const bool referenced = row_upd_index_is_referenced(index, trx); #ifdef WITH_WSREP const bool foreign = wsrep_row_upd_index_is_foreign(index, trx); #endif @@ -2826,14 +2815,9 @@ row_upd_clust_step( mode = BTR_MODIFY_LEAF; } - success = btr_pcur_restore_position(mode, pcur, &mtr); - - if (!success) { + if (!btr_pcur_restore_position(mode, pcur, &mtr)) { err = DB_RECORD_NOT_FOUND; - - mtr_commit(&mtr); - - return(err); + goto exit_func; } /* If this is a row in SYS_INDEXES table of the data dictionary, @@ -2852,14 +2836,9 @@ row_upd_clust_step( mtr.start(); index->set_modified(mtr); - success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, - &mtr); - if (!success) { + if (!btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, &mtr)) { err = DB_ERROR; - - mtr.commit(); - - return(err); + goto exit_func; } } @@ -2872,7 +2851,6 @@ row_upd_clust_step( btr_pcur_get_block(pcur), rec, index, offsets, thr); if (err != DB_SUCCESS) { - mtr.commit(); goto exit_func; } } @@ -2883,8 +2861,6 @@ row_upd_clust_step( btr_pcur_get_block(pcur)->page.id(), page_rec_get_heap_no(rec))); - /* NOTE: the following function calls will also commit mtr */ - if (node->is_delete == PLAIN_DELETE) { err = row_upd_del_mark_clust_rec( node, index, offsets, thr, referenced, @@ -2892,13 +2868,7 @@ row_upd_clust_step( foreign, #endif &mtr); - - if (err == DB_SUCCESS) { - node->state = UPD_NODE_UPDATE_ALL_SEC; - node->index = dict_table_get_next_index(index); - } - - goto exit_func; + goto all_done; } /* If the update is made for MySQL, we already have the update vector @@ -2913,14 +2883,13 @@ row_upd_clust_step( } if (!node->is_delete && node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) { - err = row_upd_clust_rec( flags, node, index, offsets, &heap, thr, &mtr); goto exit_func; } - if(!row_upd_store_row(node, trx->mysql_thd, - thr->prebuilt ? thr->prebuilt->m_mysql_table : NULL)) { + if (!row_upd_store_row(node, trx->mysql_thd, thr->prebuilt + ? thr->prebuilt->m_mysql_table : NULL)) { err = DB_COMPUTE_VALUE_FAILED; goto exit_func; } @@ -2945,34 +2914,31 @@ row_upd_clust_step( foreign, #endif &mtr); - if (err != DB_SUCCESS) { - - goto exit_func; +all_done: + if (err == DB_SUCCESS) { + node->state = UPD_NODE_UPDATE_ALL_SEC; +success: + node->index = dict_table_get_next_index(index); } - - node->state = UPD_NODE_UPDATE_ALL_SEC; } else { err = row_upd_clust_rec( flags, node, index, offsets, &heap, thr, &mtr); - if (err != DB_SUCCESS) { - - goto exit_func; + if (err == DB_SUCCESS) { + ut_ad(node->is_delete != PLAIN_DELETE); + node->state = node->is_delete ? + UPD_NODE_UPDATE_ALL_SEC : + UPD_NODE_UPDATE_SOME_SEC; + goto success; } - - ut_ad(node->is_delete != PLAIN_DELETE); - node->state = node->is_delete ? - UPD_NODE_UPDATE_ALL_SEC : - UPD_NODE_UPDATE_SOME_SEC; } - node->index = dict_table_get_next_index(index); - exit_func: - if (heap) { + mtr.commit(); + if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } - return(err); + return err; } /***********************************************************//** |