diff options
Diffstat (limited to 'storage/innobase/row/row0purge.cc')
-rw-r--r-- | storage/innobase/row/row0purge.cc | 302 |
1 files changed, 210 insertions, 92 deletions
diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index bc2e0b0e1cb..bac55694056 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -46,6 +46,8 @@ Created 3/14/1997 Heikki Tuuri #include "log0log.h" #include "srv0mon.h" #include "srv0start.h" +#include "handler.h" +#include "ha_innodb.h" /************************************************************************* IMPORTANT NOTE: Any operation that generates redo MUST check that there @@ -57,15 +59,14 @@ 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 +/** Create a purge node to a query graph. +@param[in] parent parent node, i.e., a thr node +@param[in] heap memory heap where created +@return own: purge node */ purge_node_t* row_purge_node_create( -/*==================*/ - que_thr_t* parent, /*!< in: parent node */ - mem_heap_t* heap) /*!< in: memory heap where created */ + que_thr_t* parent, + mem_heap_t* heap) { purge_node_t* node; @@ -86,7 +87,7 @@ row_purge_node_create( /***********************************************************//** 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 */ +@return TRUE if the record was found */ static ibool row_purge_reposition_pcur( @@ -137,14 +138,13 @@ row_purge_remove_clust_if_poss_low( 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 */ + ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_S)); index = dict_table_get_first_index(node->table); log_free_check(); mtr_start(&mtr); + mtr.set_named_space(index->space); if (!row_purge_reposition_pcur(mode, node, &mtr)) { /* The record was already removed. */ @@ -161,15 +161,17 @@ row_purge_remove_clust_if_poss_low( goto func_exit; } + ut_ad(rec_get_deleted_flag(rec, rec_offs_comp(offsets))); + 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); + ut_ad(mode == (BTR_MODIFY_TREE | BTR_LATCH_FOR_DELETE)); btr_cur_pessimistic_delete( &err, FALSE, btr_pcur_get_btr_cur(&node->pcur), 0, - RB_NONE, &mtr); + false, &mtr); switch (err) { case DB_SUCCESS: @@ -217,7 +219,7 @@ row_purge_remove_clust_if_poss( n_tries < BTR_CUR_RETRY_DELETE_N_TIMES; n_tries++) { if (row_purge_remove_clust_if_poss_low( - node, BTR_MODIFY_TREE)) { + node, BTR_MODIFY_TREE | BTR_LATCH_FOR_DELETE)) { return(true); } @@ -241,8 +243,7 @@ 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 +@return true if the secondary index record can be purged */ bool row_purge_poss_sec( /*===============*/ @@ -259,7 +260,8 @@ row_purge_poss_sec( 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); + &mtr, index, entry, + node->roll_ptr, node->trx_id); /* Persistent cursor is closed if reposition fails. */ if (node->found_clust) { @@ -274,7 +276,7 @@ row_purge_poss_sec( /*************************************************************** 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 */ +@return TRUE if success or if not found */ static MY_ATTRIBUTE((nonnull, warn_unused_result)) ibool row_purge_remove_sec_if_poss_tree( @@ -292,13 +294,13 @@ row_purge_remove_sec_if_poss_tree( log_free_check(); mtr_start(&mtr); + mtr.set_named_space(index->space); - 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 (!index->is_committed()) { + /* The index->online_status may change if the index is + or was being created online, but not committed yet. It + is protected by index->lock. */ + mtr_sx_lock(dict_index_get_lock(index), &mtr); if (dict_index_is_online_ddl(index)) { /* Online secondary index creation will not @@ -310,13 +312,15 @@ row_purge_remove_sec_if_poss_tree( } } else { /* For secondary indexes, - index->online_status==ONLINE_INDEX_CREATION unless - index->name starts with TEMP_INDEX_PREFIX. */ + index->online_status==ONLINE_INDEX_COMPLETE if + index->is_committed(). */ ut_ad(!dict_index_is_online_ddl(index)); } - search_result = row_search_index_entry(index, entry, BTR_MODIFY_TREE, - &pcur, &mtr); + search_result = row_search_index_entry( + index, entry, + BTR_MODIFY_TREE | BTR_LATCH_FOR_DELETE, + &pcur, &mtr); switch (search_result) { case ROW_NOT_FOUND: @@ -354,17 +358,13 @@ row_purge_remove_sec_if_poss_tree( 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); + ib::error() + << "tried to purge non-delete-marked record" + " in index " << index->name + << " of table " << index->table->name + << ": tuple: " << *entry + << ", record: " << rec_index_print( + btr_cur_get_rec(btr_cur), index); ut_ad(0); @@ -372,7 +372,7 @@ row_purge_remove_sec_if_poss_tree( } btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0, - RB_NONE, &mtr); + false, &mtr); switch (UNIV_EXPECT(err, DB_SUCCESS)) { case DB_SUCCESS: break; @@ -395,8 +395,8 @@ func_exit_no_pcur: /*************************************************************** 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 */ +@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( @@ -414,12 +414,17 @@ row_purge_remove_sec_if_poss_leaf( log_free_check(); mtr_start(&mtr); + mtr.set_named_space(index->space); + + if (!index->is_committed()) { + /* For uncommitted spatial index, we also skip the purge. */ + if (dict_index_is_spatial(index)) { + goto func_exit_no_pcur; + } - 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. */ + /* The index->online_status may change if the the + index is or was being created online, but not + committed yet. It is protected by index->lock. */ mtr_s_lock(dict_index_get_lock(index), &mtr); if (dict_index_is_online_ddl(index)) { @@ -431,25 +436,44 @@ row_purge_remove_sec_if_poss_leaf( goto func_exit_no_pcur; } - mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED | BTR_DELETE; + /* Change buffering is disabled for temporary tables. */ + mode = (dict_table_is_temporary(index->table)) + ? BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED + : 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. */ + index->online_status==ONLINE_INDEX_COMPLETE if + index->is_committed(). */ ut_ad(!dict_index_is_online_ddl(index)); - mode = BTR_MODIFY_LEAF | BTR_DELETE; + /* Change buffering is disabled for temporary tables + and spatial index. */ + mode = (dict_table_is_temporary(index->table) + || dict_index_is_spatial(index)) + ? BTR_MODIFY_LEAF + : 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)); + if (dict_index_is_spatial(index)) { + rw_lock_sx_lock(dict_index_get_lock(index)); + pcur.btr_cur.thr = NULL; + } else { + /* 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); + if (dict_index_is_spatial(index)) { + rw_lock_sx_unlock(dict_index_get_lock(index)); + } + switch (search_result) { case ROW_FOUND: /* Before attempting to purge a record, check @@ -462,19 +486,15 @@ row_purge_remove_sec_if_poss_leaf( 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); - + ib::error() + << "tried to purge non-delete-marked" + " record" " in index " << index->name + << " of table " << index->table->name + << ": tuple: " << *entry + << ", record: " + << rec_index_print( + btr_cur_get_rec(btr_cur), + index); ut_ad(0); btr_pcur_close(&pcur); @@ -482,6 +502,42 @@ row_purge_remove_sec_if_poss_leaf( goto func_exit_no_pcur; } + if (dict_index_is_spatial(index)) { + const page_t* page; + const trx_t* trx = NULL; + + if (btr_cur->rtr_info != NULL + && btr_cur->rtr_info->thr != NULL) { + trx = thr_get_trx( + btr_cur->rtr_info->thr); + } + + page = btr_cur_get_page(btr_cur); + + if (!lock_test_prdt_page_lock( + trx, + page_get_space_id(page), + page_get_page_no(page)) + && page_get_n_recs(page) < 2 + && page_get_page_no(page) != + dict_index_get_page(index)) { + /* this is the last record on page, + and it has a "page" lock on it, + which mean search is still depending + on it, so do not delete */ +#ifdef UNIV_DEBUG + ib::info() << "skip purging last" + " record on page " + << page_get_page_no(page) + << "."; +#endif /* UNIV_DEBUG */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + return(success); + } + } + if (!btr_cur_optimistic_delete(btr_cur, 0, &mtr)) { /* The index entry could not be deleted. */ @@ -497,13 +553,13 @@ row_purge_remove_sec_if_poss_leaf( case ROW_NOT_FOUND: /* The index entry does not exist, nothing to do. */ btr_pcur_close(&pcur); - func_exit_no_pcur: +func_exit_no_pcur: mtr_commit(&mtr); return(success); } ut_error; - return(FALSE); + return(false); } /***********************************************************//** @@ -550,6 +606,25 @@ retry: ut_a(success); } +/** Skip uncommitted virtual indexes on newly added virtual column. +@param[in,out] index dict index object */ +static +inline +void +row_purge_skip_uncommitted_virtual_index( + dict_index_t*& index) +{ + /* We need to skip virtual indexes which is not + committed yet. It's safe because these indexes are + newly created by alter table, and because we do + not support LOCK=NONE when adding an index on newly + added virtual column.*/ + while (index != NULL && dict_index_has_virtual(index) + && !index->is_committed() && index->has_new_v_col) { + index = dict_table_get_next_index(index); + } +} + /***********************************************************//** Purges a delete marking of a record. @retval true if the row was not found, or it was successfully removed @@ -569,13 +644,16 @@ row_purge_del_mark( /* skip corrupted secondary index */ dict_table_skip_corrupt_index(node->index); + row_purge_skip_uncommitted_virtual_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); + node->row, NULL, node->index, + heap, ROW_BUILD_FOR_PURGE); row_purge_remove_sec_if_poss(node, node->index, entry); mem_heap_empty(heap); } @@ -603,9 +681,7 @@ row_purge_upd_exist_or_extern_func( { mem_heap_t* heap; -#ifdef UNIV_SYNC_DEBUG - ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED)); -#endif /* UNIV_SYNC_DEBUG */ + ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_S)); if (node->rec_type == TRX_UNDO_UPD_DEL_REC || (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { @@ -618,6 +694,8 @@ row_purge_upd_exist_or_extern_func( while (node->index != NULL) { dict_table_skip_corrupt_index(node->index); + row_purge_skip_uncommitted_virtual_index(node->index); + if (!node->index) { break; } @@ -626,7 +704,8 @@ row_purge_upd_exist_or_extern_func( 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); + node->row, NULL, node->index, + heap, ROW_BUILD_FOR_PURGE); row_purge_remove_sec_if_poss(node, node->index, entry); mem_heap_empty(heap); } @@ -672,17 +751,26 @@ skip_secondaries: &is_insert, &rseg_id, &page_no, &offset); - rseg = trx_sys_get_nth_rseg(trx_sys, rseg_id); + /* If table is temp then it can't have its undo log + residing in rollback segment with REDO log enabled. */ + bool is_redo_rseg = + dict_table_is_temporary(node->table) + ? false : true; + rseg = trx_sys_get_nth_rseg( + trx_sys, rseg_id, is_redo_rseg); + 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 */ + /* We have to acquire an SX-latch to the clustered + index tree (exclude other tree changes) */ index = dict_table_get_first_index(node->table); - mtr_x_lock(dict_index_get_lock(index), &mtr); + mtr_sx_lock(dict_index_get_lock(index), &mtr); + + mtr.set_named_space(index->space); /* NOTE: we must also acquire an X-latch to the root page of the tree. We will need it when we @@ -696,7 +784,8 @@ skip_secondaries: btr_root_get(index, &mtr); block = buf_page_get( - rseg->space, 0, page_no, RW_X_LATCH, &mtr); + page_id_t(rseg->space, page_no), + univ_page_size, RW_X_LATCH, &mtr); buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); @@ -709,7 +798,7 @@ skip_secondaries: index, data_field + dfield_get_len(&ufield->new_val) - BTR_EXTERN_FIELD_REF_SIZE, - NULL, NULL, NULL, 0, RB_NONE, &mtr); + NULL, NULL, NULL, 0, false, &mtr); mtr_commit(&mtr); } } @@ -763,11 +852,13 @@ row_purge_parse_undo_rec( ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr, &info_bits); node->table = NULL; + node->trx_id = trx_id; /* 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__); +try_again: + 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); @@ -777,6 +868,32 @@ row_purge_parse_undo_rec( goto err_exit; } +#ifdef MYSQL_VIRTUAL_COLUMNS + if (node->table->n_v_cols && !node->table->vc_templ + && dict_table_has_indexed_v_cols(node->table)) { + /* Need server fully up for virtual column computation */ + if (!mysqld_server_started) { + + dict_table_close(node->table, FALSE, FALSE); + rw_lock_s_unlock(dict_operation_lock); + if (srv_shutdown_state != SRV_SHUTDOWN_NONE) { + return(false); + } + os_thread_sleep(1000000); + goto try_again; + } + + /* Initialize the template for the table */ + innobase_init_vc_templ(node->table); + } +#endif /* MYSQL_VIRTUAL_COLUMNS */ + + /* Disable purging for temp-tables as they are short-lived + and no point in re-organzing such short lived tables */ + if (dict_table_is_temporary(node->table)) { + goto close_exit; + } + if (node->table->ibd_file_missing) { /* We skip purge of missing .ibd files */ @@ -789,14 +906,15 @@ row_purge_parse_undo_rec( clust_index = dict_table_get_first_index(node->table); - if (clust_index == NULL) { + if (clust_index == NULL + || dict_index_is_corrupted(clust_index)) { /* 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); + rw_lock_s_unlock(dict_operation_lock); return(false); } @@ -852,6 +970,7 @@ row_purge_record_func( clust_index = dict_table_get_first_index(node->table); node->index = dict_table_get_next_index(clust_index); + ut_ad(!trx_undo_roll_ptr_is_insert(node->roll_ptr)); switch (node->rec_type) { case TRX_UNDO_DEL_MARK_REC: @@ -914,7 +1033,7 @@ row_purge( bool purged = row_purge_record( node, undo_rec, thr, updated_extern); - rw_lock_s_unlock(&dict_operation_lock); + rw_lock_s_unlock(dict_operation_lock); if (purged || srv_shutdown_state != SRV_SHUTDOWN_NONE) { @@ -957,8 +1076,7 @@ row_purge_end( /***********************************************************//** 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 +@return query thread to run next or NULL */ que_thr_t* row_purge_step( /*===========*/ @@ -1028,14 +1146,14 @@ purge_node_t::validate_pcur() return(true); } - if (pcur.old_stored != BTR_PCUR_OLD_STORED) { + if (!pcur.old_stored) { return(true); } - dict_index_t* clust_index = pcur.btr_cur.index; + 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); + 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 @@ -1044,9 +1162,9 @@ purge_node_t::validate_pcur() 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); + ib::error() << "Purge node pcur validation failed"; + ib::error() << rec_printer(ref).str(); + ib::error() << rec_printer(pcur.old_rec, offsets).str(); return(false); } |