diff options
Diffstat (limited to 'storage/xtradb/row/row0purge.cc')
-rw-r--r-- | storage/xtradb/row/row0purge.cc | 1057 |
1 files changed, 0 insertions, 1057 deletions
diff --git a/storage/xtradb/row/row0purge.cc b/storage/xtradb/row/row0purge.cc deleted file mode 100644 index 0cd0941987d..00000000000 --- a/storage/xtradb/row/row0purge.cc +++ /dev/null @@ -1,1057 +0,0 @@ -/***************************************************************************** - -Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2018, MariaDB Corporation. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA - -*****************************************************************************/ - -/**************************************************//** -@file row/row0purge.cc -Purge obsolete records - -Created 3/14/1997 Heikki Tuuri -*******************************************************/ - -#include "row0purge.h" - -#ifdef UNIV_NONINL -#include "row0purge.ic" -#endif - -#include "fsp0fsp.h" -#include "mach0data.h" -#include "trx0rseg.h" -#include "trx0trx.h" -#include "trx0roll.h" -#include "trx0undo.h" -#include "trx0purge.h" -#include "trx0rec.h" -#include "que0que.h" -#include "row0row.h" -#include "row0upd.h" -#include "row0vers.h" -#include "row0mysql.h" -#include "row0log.h" -#include "log0log.h" -#include "srv0mon.h" -#include "srv0start.h" - -/************************************************************************* -IMPORTANT NOTE: Any operation that generates redo MUST check that there -is enough space in the redo log before for that operation. This is -done by calling log_free_check(). The reason for checking the -availability of the redo log space before the start of the operation is -that we MUST not hold any synchonization objects when performing the -check. -If you make a change in this module make sure that no codepath is -introduced where a call to log_free_check() is bypassed. */ - -/********************************************************************//** -Creates a purge node to a query graph. -@return own: purge node */ -UNIV_INTERN -purge_node_t* -row_purge_node_create( -/*==================*/ - que_thr_t* parent, /*!< in: parent node */ - mem_heap_t* heap) /*!< in: memory heap where created */ -{ - purge_node_t* node; - - ut_ad(parent != NULL); - ut_ad(heap != NULL); - - node = static_cast<purge_node_t*>( - mem_heap_zalloc(heap, sizeof(*node))); - - node->common.type = QUE_NODE_PURGE; - node->common.parent = parent; - node->done = TRUE; - node->heap = mem_heap_create(256); - - return(node); -} - -/***********************************************************//** -Repositions the pcur in the purge node on the clustered index record, -if found. If the record is not found, close pcur. -@return TRUE if the record was found */ -static -ibool -row_purge_reposition_pcur( -/*======================*/ - ulint mode, /*!< in: latching mode */ - purge_node_t* node, /*!< in: row purge node */ - mtr_t* mtr) /*!< in: mtr */ -{ - if (node->found_clust) { - ut_ad(node->validate_pcur()); - - node->found_clust = btr_pcur_restore_position(mode, &node->pcur, mtr); - - } else { - node->found_clust = row_search_on_row_ref( - &node->pcur, mode, node->table, node->ref, mtr); - - if (node->found_clust) { - btr_pcur_store_position(&node->pcur, mtr); - } - } - - /* Close the current cursor if we fail to position it correctly. */ - if (!node->found_clust) { - btr_pcur_close(&node->pcur); - } - - return(node->found_clust); -} - -/***********************************************************//** -Removes a delete marked clustered index record if possible. -@retval true if the row was not found, or it was successfully removed -@retval false if the row was modified after the delete marking */ -static MY_ATTRIBUTE((nonnull, warn_unused_result)) -bool -row_purge_remove_clust_if_poss_low( -/*===============================*/ - purge_node_t* node, /*!< in/out: row purge node */ - ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ -{ - dict_index_t* index; - bool success = true; - mtr_t mtr; - rec_t* rec; - mem_heap_t* heap = NULL; - ulint* offsets; - ulint offsets_[REC_OFFS_NORMAL_SIZE]; - rec_offs_init(offsets_); - -#ifdef UNIV_SYNC_DEBUG - ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED)); -#endif /* UNIV_SYNC_DEBUG */ - - index = dict_table_get_first_index(node->table); - - log_free_check(); - mtr_start(&mtr); - - if (!row_purge_reposition_pcur(mode, node, &mtr)) { - /* The record was already removed. */ - goto func_exit; - } - - rec = btr_pcur_get_rec(&node->pcur); - - offsets = rec_get_offsets( - rec, index, offsets_, ULINT_UNDEFINED, &heap); - - if (node->roll_ptr != row_get_rec_roll_ptr(rec, index, offsets)) { - /* Someone else has modified the record later: do not remove */ - goto func_exit; - } - - if (mode == BTR_MODIFY_LEAF) { - success = btr_cur_optimistic_delete( - btr_pcur_get_btr_cur(&node->pcur), 0, &mtr); - } else { - dberr_t err; - ut_ad(mode == BTR_MODIFY_TREE); - btr_cur_pessimistic_delete( - &err, FALSE, btr_pcur_get_btr_cur(&node->pcur), 0, - RB_NONE, &mtr); - - switch (err) { - case DB_SUCCESS: - break; - case DB_OUT_OF_FILE_SPACE: - success = false; - break; - default: - ut_error; - } - } - -func_exit: - if (heap) { - mem_heap_free(heap); - } - - /* Persistent cursor is closed if reposition fails. */ - if (node->found_clust) { - btr_pcur_commit_specify_mtr(&node->pcur, &mtr); - } else { - mtr_commit(&mtr); - } - - return(success); -} - -/***********************************************************//** -Removes a clustered index record if it has not been modified after the delete -marking. -@retval true if the row was not found, or it was successfully removed -@retval false the purge needs to be suspended because of running out -of file space. */ -static MY_ATTRIBUTE((nonnull, warn_unused_result)) -bool -row_purge_remove_clust_if_poss( -/*===========================*/ - purge_node_t* node) /*!< in/out: row purge node */ -{ - if (row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_LEAF)) { - return(true); - } - - for (ulint n_tries = 0; - n_tries < BTR_CUR_RETRY_DELETE_N_TIMES; - n_tries++) { - if (row_purge_remove_clust_if_poss_low( - node, BTR_MODIFY_TREE)) { - return(true); - } - - os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME); - } - - return(false); -} - -/***********************************************************//** -Determines if it is possible to remove a secondary index entry. -Removal is possible if the secondary index entry does not refer to any -not delete marked version of a clustered index record where DB_TRX_ID -is newer than the purge view. - -NOTE: This function should only be called by the purge thread, only -while holding a latch on the leaf page of the secondary index entry -(or keeping the buffer pool watch on the page). It is possible that -this function first returns true and then false, if a user transaction -inserts a record that the secondary index entry would refer to. -However, in that case, the user transaction would also re-insert the -secondary index entry after purge has removed it and released the leaf -page latch. -@return true if the secondary index record can be purged */ -UNIV_INTERN -bool -row_purge_poss_sec( -/*===============*/ - purge_node_t* node, /*!< in/out: row purge node */ - dict_index_t* index, /*!< in: secondary index */ - const dtuple_t* entry) /*!< in: secondary index entry */ -{ - bool can_delete; - mtr_t mtr; - - ut_ad(!dict_index_is_clust(index)); - mtr_start(&mtr); - - can_delete = !row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr) - || !row_vers_old_has_index_entry(TRUE, - btr_pcur_get_rec(&node->pcur), - &mtr, index, entry); - - /* Persistent cursor is closed if reposition fails. */ - if (node->found_clust) { - btr_pcur_commit_specify_mtr(&node->pcur, &mtr); - } else { - mtr_commit(&mtr); - } - - return(can_delete); -} - -/*************************************************************** -Removes a secondary index entry if possible, by modifying the -index tree. Does not try to buffer the delete. -@return TRUE if success or if not found */ -static MY_ATTRIBUTE((nonnull, warn_unused_result)) -ibool -row_purge_remove_sec_if_poss_tree( -/*==============================*/ - purge_node_t* node, /*!< in: row purge node */ - dict_index_t* index, /*!< in: index */ - const dtuple_t* entry) /*!< in: index entry */ -{ - btr_pcur_t pcur; - btr_cur_t* btr_cur; - ibool success = TRUE; - dberr_t err; - mtr_t mtr; - enum row_search_result search_result; - - log_free_check(); - mtr_start(&mtr); - - if (*index->name == TEMP_INDEX_PREFIX) { - /* The index->online_status may change if the - index->name starts with TEMP_INDEX_PREFIX (meaning - that the index is or was being created online). It is - protected by index->lock. */ - mtr_x_lock(dict_index_get_lock(index), &mtr); - - if (dict_index_is_online_ddl(index)) { - /* Online secondary index creation will not - copy any delete-marked records. Therefore - there is nothing to be purged. We must also - skip the purge when a completed index is - dropped by rollback_inplace_alter_table(). */ - goto func_exit_no_pcur; - } - } else { - /* For secondary indexes, - index->online_status==ONLINE_INDEX_CREATION unless - index->name starts with TEMP_INDEX_PREFIX. */ - ut_ad(!dict_index_is_online_ddl(index)); - } - - search_result = row_search_index_entry(index, entry, BTR_MODIFY_TREE, - &pcur, &mtr); - - switch (search_result) { - case ROW_NOT_FOUND: - /* Not found. This is a legitimate condition. In a - rollback, InnoDB will remove secondary recs that would - be purged anyway. Then the actual purge will not find - the secondary index record. Also, the purge itself is - eager: if it comes to consider a secondary index - record, and notices it does not need to exist in the - index, it will remove it. Then if/when the purge - comes to consider the secondary index record a second - time, it will not exist any more in the index. */ - - /* fputs("PURGE:........sec entry not found\n", stderr); */ - /* dtuple_print(stderr, entry); */ - goto func_exit; - case ROW_FOUND: - break; - case ROW_BUFFERED: - case ROW_NOT_DELETED_REF: - /* These are invalid outcomes, because the mode passed - to row_search_index_entry() did not include any of the - flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */ - ut_error; - } - - btr_cur = btr_pcur_get_btr_cur(&pcur); - - /* We should remove the index record if no later version of the row, - which cannot be purged yet, requires its existence. If some requires, - we should do nothing. */ - - if (row_purge_poss_sec(node, index, entry)) { - /* Remove the index record, which should have been - marked for deletion. */ - if (!rec_get_deleted_flag(btr_cur_get_rec(btr_cur), - dict_table_is_comp(index->table))) { - fputs("InnoDB: tried to purge sec index entry not" - " marked for deletion in\n" - "InnoDB: ", stderr); - dict_index_name_print(stderr, NULL, index); - fputs("\n" - "InnoDB: tuple ", stderr); - dtuple_print(stderr, entry); - fputs("\n" - "InnoDB: record ", stderr); - rec_print(stderr, btr_cur_get_rec(btr_cur), index); - putc('\n', stderr); - - ut_ad(0); - - goto func_exit; - } - - btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0, - RB_NONE, &mtr); - switch (UNIV_EXPECT(err, DB_SUCCESS)) { - case DB_SUCCESS: - break; - case DB_OUT_OF_FILE_SPACE: - success = FALSE; - break; - default: - ut_error; - } - } - -func_exit: - btr_pcur_close(&pcur); -func_exit_no_pcur: - mtr_commit(&mtr); - - return(success); -} - -/*************************************************************** -Removes a secondary index entry without modifying the index tree, -if possible. -@retval true if success or if not found -@retval false if row_purge_remove_sec_if_poss_tree() should be invoked */ -static MY_ATTRIBUTE((nonnull, warn_unused_result)) -bool -row_purge_remove_sec_if_poss_leaf( -/*==============================*/ - purge_node_t* node, /*!< in: row purge node */ - dict_index_t* index, /*!< in: index */ - const dtuple_t* entry) /*!< in: index entry */ -{ - mtr_t mtr; - btr_pcur_t pcur; - ulint mode; - enum row_search_result search_result; - bool success = true; - - log_free_check(); - - mtr_start(&mtr); - - if (*index->name == TEMP_INDEX_PREFIX) { - /* The index->online_status may change if the - index->name starts with TEMP_INDEX_PREFIX (meaning - that the index is or was being created online). It is - protected by index->lock. */ - mtr_s_lock(dict_index_get_lock(index), &mtr); - - if (dict_index_is_online_ddl(index)) { - /* Online secondary index creation will not - copy any delete-marked records. Therefore - there is nothing to be purged. We must also - skip the purge when a completed index is - dropped by rollback_inplace_alter_table(). */ - goto func_exit_no_pcur; - } - - mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED | BTR_DELETE; - } else { - /* For secondary indexes, - index->online_status==ONLINE_INDEX_CREATION unless - index->name starts with TEMP_INDEX_PREFIX. */ - ut_ad(!dict_index_is_online_ddl(index)); - - mode = BTR_MODIFY_LEAF | BTR_DELETE; - } - - /* Set the purge node for the call to row_purge_poss_sec(). */ - pcur.btr_cur.purge_node = node; - /* Set the query thread, so that ibuf_insert_low() will be - able to invoke thd_get_trx(). */ - pcur.btr_cur.thr = static_cast<que_thr_t*>(que_node_get_parent(node)); - - search_result = row_search_index_entry( - index, entry, mode, &pcur, &mtr); - - switch (search_result) { - case ROW_FOUND: - /* Before attempting to purge a record, check - if it is safe to do so. */ - if (row_purge_poss_sec(node, index, entry)) { - btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur); - - /* Only delete-marked records should be purged. */ - if (!rec_get_deleted_flag( - btr_cur_get_rec(btr_cur), - dict_table_is_comp(index->table))) { - - fputs("InnoDB: tried to purge sec index" - " entry not marked for deletion in\n" - "InnoDB: ", stderr); - dict_index_name_print(stderr, NULL, index); - fputs("\n" - "InnoDB: tuple ", stderr); - dtuple_print(stderr, entry); - fputs("\n" - "InnoDB: record ", stderr); - rec_print(stderr, btr_cur_get_rec(btr_cur), - index); - putc('\n', stderr); - - ut_ad(0); - - btr_pcur_close(&pcur); - - goto func_exit_no_pcur; - } - - if (!btr_cur_optimistic_delete(btr_cur, 0, &mtr)) { - - /* The index entry could not be deleted. */ - success = false; - } - } - /* (The index entry is still needed, - or the deletion succeeded) */ - /* fall through */ - case ROW_NOT_DELETED_REF: - /* The index entry is still needed. */ - case ROW_BUFFERED: - /* The deletion was buffered. */ - case ROW_NOT_FOUND: - /* The index entry does not exist, nothing to do. */ - btr_pcur_close(&pcur); - func_exit_no_pcur: - mtr_commit(&mtr); - return(success); - } - - ut_error; - return(FALSE); -} - -/***********************************************************//** -Removes a secondary index entry if possible. */ -UNIV_INLINE MY_ATTRIBUTE((nonnull(1,2))) -void -row_purge_remove_sec_if_poss( -/*=========================*/ - purge_node_t* node, /*!< in: row purge node */ - dict_index_t* index, /*!< in: index */ - const dtuple_t* entry) /*!< in: index entry */ -{ - ibool success; - ulint n_tries = 0; - - /* fputs("Purge: Removing secondary record\n", stderr); */ - - if (!entry) { - /* The node->row must have lacked some fields of this - index. This is possible when the undo log record was - written before this index was created. */ - return; - } - - if (row_purge_remove_sec_if_poss_leaf(node, index, entry)) { - - return; - } -retry: - success = row_purge_remove_sec_if_poss_tree(node, index, entry); - /* The delete operation may fail if we have little - file space left: TODO: easiest to crash the database - and restart with more file space */ - - if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) { - - n_tries++; - - os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME); - - goto retry; - } - - ut_a(success); -} - -/***********************************************************//** -Purges a delete marking of a record. -@retval true if the row was not found, or it was successfully removed -@retval false the purge needs to be suspended because of -running out of file space */ -static MY_ATTRIBUTE((nonnull, warn_unused_result)) -bool -row_purge_del_mark( -/*===============*/ - purge_node_t* node) /*!< in/out: row purge node */ -{ - mem_heap_t* heap; - - heap = mem_heap_create(1024); - - while (node->index != NULL) { - /* skip corrupted secondary index */ - dict_table_skip_corrupt_index(node->index); - - if (!node->index) { - break; - } - - if (node->index->type != DICT_FTS) { - dtuple_t* entry = row_build_index_entry_low( - node->row, NULL, node->index, heap); - row_purge_remove_sec_if_poss(node, node->index, entry); - mem_heap_empty(heap); - } - - node->index = dict_table_get_next_index(node->index); - } - - mem_heap_free(heap); - - return(row_purge_remove_clust_if_poss(node)); -} - -/***********************************************************//** -Purges an update of an existing record. Also purges an update of a delete -marked record if that record contained an externally stored field. */ -static -void -row_purge_upd_exist_or_extern_func( -/*===============================*/ -#ifdef UNIV_DEBUG - const que_thr_t*thr, /*!< in: query thread */ -#endif /* UNIV_DEBUG */ - purge_node_t* node, /*!< in: row purge node */ - trx_undo_rec_t* undo_rec) /*!< in: record to purge */ -{ - mem_heap_t* heap; - -#ifdef UNIV_SYNC_DEBUG - ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED)); -#endif /* UNIV_SYNC_DEBUG */ - - if (node->rec_type == TRX_UNDO_UPD_DEL_REC - || (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { - - goto skip_secondaries; - } - - heap = mem_heap_create(1024); - - while (node->index != NULL) { - dict_table_skip_corrupt_index(node->index); - - if (!node->index) { - break; - } - - if (row_upd_changes_ord_field_binary(node->index, node->update, - thr, NULL, NULL)) { - /* Build the older version of the index entry */ - dtuple_t* entry = row_build_index_entry_low( - node->row, NULL, node->index, heap); - row_purge_remove_sec_if_poss(node, node->index, entry); - mem_heap_empty(heap); - } - - node->index = dict_table_get_next_index(node->index); - } - - mem_heap_free(heap); - -skip_secondaries: - /* Free possible externally stored fields */ - for (ulint i = 0; i < upd_get_n_fields(node->update); i++) { - - const upd_field_t* ufield - = upd_get_nth_field(node->update, i); - - if (dfield_is_ext(&ufield->new_val)) { - trx_rseg_t* rseg; - buf_block_t* block; - ulint internal_offset; - byte* data_field; - dict_index_t* index; - ibool is_insert; - ulint rseg_id; - ulint page_no; - ulint offset; - mtr_t mtr; - - /* We use the fact that new_val points to - undo_rec and get thus the offset of - dfield data inside the undo record. Then we - can calculate from node->roll_ptr the file - address of the new_val data */ - - internal_offset - = ((const byte*) - dfield_get_data(&ufield->new_val)) - - undo_rec; - - ut_a(internal_offset < UNIV_PAGE_SIZE); - - trx_undo_decode_roll_ptr(node->roll_ptr, - &is_insert, &rseg_id, - &page_no, &offset); - - rseg = trx_sys_get_nth_rseg(trx_sys, rseg_id); - ut_a(rseg != NULL); - ut_a(rseg->id == rseg_id); - - mtr_start(&mtr); - - /* We have to acquire an X-latch to the clustered - index tree */ - - index = dict_table_get_first_index(node->table); - mtr_x_lock(dict_index_get_lock(index), &mtr); - - /* NOTE: we must also acquire an X-latch to the - root page of the tree. We will need it when we - free pages from the tree. If the tree is of height 1, - the tree X-latch does NOT protect the root page, - because it is also a leaf page. Since we will have a - latch on an undo log page, we would break the - latching order if we would only later latch the - root page of such a tree! */ - - btr_root_get(index, &mtr); - - block = buf_page_get( - rseg->space, 0, page_no, RW_X_LATCH, &mtr); - - buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); - - data_field = buf_block_get_frame(block) - + offset + internal_offset; - - ut_a(dfield_get_len(&ufield->new_val) - >= BTR_EXTERN_FIELD_REF_SIZE); - btr_free_externally_stored_field( - index, - data_field + dfield_get_len(&ufield->new_val) - - BTR_EXTERN_FIELD_REF_SIZE, - NULL, NULL, NULL, 0, RB_NONE, &mtr); - mtr_commit(&mtr); - } - } -} - -#ifdef UNIV_DEBUG -# define row_purge_upd_exist_or_extern(thr,node,undo_rec) \ - row_purge_upd_exist_or_extern_func(thr,node,undo_rec) -#else /* UNIV_DEBUG */ -# define row_purge_upd_exist_or_extern(thr,node,undo_rec) \ - row_purge_upd_exist_or_extern_func(node,undo_rec) -#endif /* UNIV_DEBUG */ - -/***********************************************************//** -Parses the row reference and other info in a modify undo log record. -@return true if purge operation required */ -static -bool -row_purge_parse_undo_rec( -/*=====================*/ - purge_node_t* node, /*!< in: row undo node */ - trx_undo_rec_t* undo_rec, /*!< in: record to purge */ - bool* updated_extern, /*!< out: true if an externally - stored field was updated */ - que_thr_t* thr) /*!< in: query thread */ -{ - dict_index_t* clust_index; - byte* ptr; - trx_t* trx; - undo_no_t undo_no; - table_id_t table_id; - trx_id_t trx_id; - roll_ptr_t roll_ptr; - ulint info_bits; - ulint type; - - ut_ad(node != NULL); - ut_ad(thr != NULL); - - ptr = trx_undo_rec_get_pars( - undo_rec, &type, &node->cmpl_info, - updated_extern, &undo_no, &table_id); - - node->rec_type = type; - - if (type == TRX_UNDO_UPD_DEL_REC && !*updated_extern) { - - return(false); - } - - ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr, - &info_bits); - node->table = NULL; - - /* Prevent DROP TABLE etc. from running when we are doing the purge - for this row */ - - rw_lock_s_lock_inline(&dict_operation_lock, 0, __FILE__, __LINE__); - - node->table = dict_table_open_on_id( - table_id, FALSE, DICT_TABLE_OP_NORMAL); - - if (node->table == NULL) { - /* The table has been dropped: no need to do purge */ - goto err_exit; - } - - if (node->table->file_unreadable) { - /* We skip purge of missing .ibd files */ - - dict_table_close(node->table, FALSE, FALSE); - - node->table = NULL; - - goto err_exit; - } - - clust_index = dict_table_get_first_index(node->table); - - if (clust_index == NULL) { - /* The table was corrupt in the data dictionary. - dict_set_corrupted() works on an index, and - we do not have an index to call it with. */ -close_exit: - dict_table_close(node->table, FALSE, FALSE); -err_exit: - rw_lock_s_unlock(&dict_operation_lock); - return(false); - } - - if (type == TRX_UNDO_UPD_EXIST_REC - && (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) - && !*updated_extern) { - - /* Purge requires no changes to indexes: we may return */ - goto close_exit; - } - - ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref), - node->heap); - - trx = thr_get_trx(thr); - - ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id, - roll_ptr, info_bits, trx, - node->heap, &(node->update)); - - /* Read to the partial row the fields that occur in indexes */ - - if (!(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { - ptr = trx_undo_rec_get_partial_row( - ptr, clust_index, node->update, &node->row, - type == TRX_UNDO_UPD_DEL_REC, - node->heap); - } - - return(true); -} - -/***********************************************************//** -Purges the parsed record. -@return true if purged, false if skipped */ -static MY_ATTRIBUTE((nonnull, warn_unused_result)) -bool -row_purge_record_func( -/*==================*/ - purge_node_t* node, /*!< in: row purge node */ - trx_undo_rec_t* undo_rec, /*!< in: record to purge */ -#ifdef UNIV_DEBUG - const que_thr_t*thr, /*!< in: query thread */ -#endif /* UNIV_DEBUG */ - bool updated_extern) /*!< in: whether external columns - were updated */ -{ - dict_index_t* clust_index; - bool purged = true; - - ut_ad(!node->found_clust); - - clust_index = dict_table_get_first_index(node->table); - - node->index = dict_table_get_next_index(clust_index); - - switch (node->rec_type) { - case TRX_UNDO_DEL_MARK_REC: - purged = row_purge_del_mark(node); - if (!purged) { - break; - } - MONITOR_INC(MONITOR_N_DEL_ROW_PURGE); - break; - default: - if (!updated_extern) { - break; - } - /* fall through */ - case TRX_UNDO_UPD_EXIST_REC: - row_purge_upd_exist_or_extern(thr, node, undo_rec); - MONITOR_INC(MONITOR_N_UPD_EXIST_EXTERN); - break; - } - - if (node->found_clust) { - btr_pcur_close(&node->pcur); - node->found_clust = FALSE; - } - - if (node->table != NULL) { - dict_table_close(node->table, FALSE, FALSE); - node->table = NULL; - } - - return(purged); -} - -#ifdef UNIV_DEBUG -# define row_purge_record(node,undo_rec,thr,updated_extern) \ - row_purge_record_func(node,undo_rec,thr,updated_extern) -#else /* UNIV_DEBUG */ -# define row_purge_record(node,undo_rec,thr,updated_extern) \ - row_purge_record_func(node,undo_rec,updated_extern) -#endif /* UNIV_DEBUG */ - -/***********************************************************//** -Fetches an undo log record and does the purge for the recorded operation. -If none left, or the current purge completed, returns the control to the -parent node, which is always a query thread node. */ -static -void -row_purge( -/*======*/ - purge_node_t* node, /*!< in: row purge node */ - trx_undo_rec_t* undo_rec, /*!< in: record to purge */ - que_thr_t* thr) /*!< in: query thread */ -{ - if (undo_rec != &trx_purge_dummy_rec) { - bool updated_extern; - - while (row_purge_parse_undo_rec( - node, undo_rec, &updated_extern, thr)) { - - bool purged = row_purge_record( - node, undo_rec, thr, updated_extern); - - rw_lock_s_unlock(&dict_operation_lock); - - if (purged - || srv_shutdown_state != SRV_SHUTDOWN_NONE) { - return; - } - - /* Retry the purge in a second. */ - os_thread_sleep(1000000); - } - } -} - -/***********************************************************//** -Reset the purge query thread. */ -UNIV_INLINE -void -row_purge_end( -/*==========*/ - que_thr_t* thr) /*!< in: query thread */ -{ - purge_node_t* node; - - ut_ad(thr); - - node = static_cast<purge_node_t*>(thr->run_node); - - ut_ad(que_node_get_type(node) == QUE_NODE_PURGE); - - thr->run_node = que_node_get_parent(node); - - node->undo_recs = NULL; - - node->done = TRUE; - - ut_a(thr->run_node != NULL); - - mem_heap_empty(node->heap); -} - -/***********************************************************//** -Does the purge operation for a single undo log record. This is a high-level -function used in an SQL execution graph. -@return query thread to run next or NULL */ -UNIV_INTERN -que_thr_t* -row_purge_step( -/*===========*/ - que_thr_t* thr) /*!< in: query thread */ -{ - purge_node_t* node; - - ut_ad(thr); - - node = static_cast<purge_node_t*>(thr->run_node); - - node->table = NULL; - node->row = NULL; - node->ref = NULL; - node->index = NULL; - node->update = NULL; - node->found_clust = FALSE; - node->rec_type = ULINT_UNDEFINED; - node->cmpl_info = ULINT_UNDEFINED; - - ut_a(!node->done); - - ut_ad(que_node_get_type(node) == QUE_NODE_PURGE); - - if (!(node->undo_recs == NULL || ib_vector_is_empty(node->undo_recs))) { - trx_purge_rec_t*purge_rec; - - purge_rec = static_cast<trx_purge_rec_t*>( - ib_vector_pop(node->undo_recs)); - - node->roll_ptr = purge_rec->roll_ptr; - - row_purge(node, purge_rec->undo_rec, thr); - - if (ib_vector_is_empty(node->undo_recs)) { - row_purge_end(thr); - } else { - thr->run_node = node; - } - } else { - row_purge_end(thr); - } - - return(thr); -} - -#ifdef UNIV_DEBUG -/***********************************************************//** -Validate the persisent cursor. The purge node has two references -to the clustered index record - one via the ref member, and the -other via the persistent cursor. These two references must match -each other if the found_clust flag is set. -@return true if the stored copy of persistent cursor is consistent -with the ref member.*/ -bool -purge_node_t::validate_pcur() -{ - if (!found_clust) { - return(true); - } - - if (index == NULL) { - return(true); - } - - if (index->type == DICT_FTS) { - return(true); - } - - if (pcur.old_stored != BTR_PCUR_OLD_STORED) { - return(true); - } - - dict_index_t* clust_index = pcur.btr_cur.index; - - ulint* offsets = rec_get_offsets( - pcur.old_rec, clust_index, NULL, pcur.old_n_fields, &heap); - - /* Here we are comparing the purge ref record and the stored initial - part in persistent cursor. Both cases we store n_uniq fields of the - cluster index and so it is fine to do the comparison. We note this - dependency here as pcur and ref belong to different modules. */ - int st = cmp_dtuple_rec(ref, pcur.old_rec, offsets); - - if (st != 0) { - fprintf(stderr, "Purge node pcur validation failed\n"); - dtuple_print(stderr, ref); - rec_print(stderr, pcur.old_rec, clust_index); - return(false); - } - - return(true); -} -#endif /* UNIV_DEBUG */ |