summaryrefslogtreecommitdiff
path: root/storage/innobase/row/row0purge.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/row/row0purge.cc')
-rw-r--r--storage/innobase/row/row0purge.cc269
1 files changed, 177 insertions, 92 deletions
diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc
index bc2e0b0e1cb..6282fd9d4ec 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,34 @@ row_purge_remove_sec_if_poss_leaf(
goto func_exit_no_pcur;
}
+ if (dict_index_is_spatial(index)) {
+ const page_t* page;
+
+ page = btr_cur_get_page(btr_cur);
+
+ if (!lock_test_prdt_page_lock(
+ 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 +545,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);
}
/***********************************************************//**
@@ -575,7 +623,8 @@ row_purge_del_mark(
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 +652,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)) {
@@ -626,7 +673,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 +720,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 +753,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 +767,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 +821,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 +837,30 @@ row_purge_parse_undo_rec(
goto err_exit;
}
+ 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);
+ }
+
+ /* 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 +873,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 +937,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 +1000,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 +1043,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 +1113,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 +1129,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);
}