mariadb/row/row0uins.c

338 lines
8.3 KiB
C
Raw Normal View History

2005-10-27 09:29:40 +02:00
/******************************************************
Fresh insert undo
(c) 1996 Innobase Oy
Created 2/25/1997 Heikki Tuuri
*******************************************************/
#include "row0uins.h"
#ifdef UNIV_NONINL
#include "row0uins.ic"
#endif
#include "dict0dict.h"
#include "dict0boot.h"
#include "dict0crea.h"
#include "trx0undo.h"
#include "trx0roll.h"
#include "btr0btr.h"
#include "mach0data.h"
#include "row0undo.h"
#include "row0vers.h"
#include "trx0trx.h"
#include "trx0rec.h"
#include "row0row.h"
#include "row0upd.h"
#include "que0que.h"
#include "ibuf0ibuf.h"
#include "log0log.h"
/*******************************************************************
Removes a clustered index record. The pcur in node was positioned on the
record, now it is detached. */
static
ulint
row_undo_ins_remove_clust_rec(
/*==========================*/
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
undo_node_t* node) /* in: undo node */
{
btr_cur_t* btr_cur;
2005-10-27 09:29:40 +02:00
ibool success;
ulint err;
ulint n_tries = 0;
mtr_t mtr;
2005-10-27 09:29:40 +02:00
mtr_start(&mtr);
2005-10-27 09:29:40 +02:00
success = btr_pcur_restore_position(BTR_MODIFY_LEAF, &(node->pcur),
&mtr);
2005-10-27 09:29:40 +02:00
ut_a(success);
if (ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) {
ut_ad(node->trx->dict_operation_lock_mode == RW_X_LATCH);
2005-10-27 09:29:40 +02:00
/* Drop the index tree associated with the row in
SYS_INDEXES table: */
2005-10-27 09:29:40 +02:00
dict_drop_index_tree(btr_pcur_get_rec(&(node->pcur)), &mtr);
mtr_commit(&mtr);
mtr_start(&mtr);
success = btr_pcur_restore_position(BTR_MODIFY_LEAF,
&(node->pcur), &mtr);
2005-10-27 09:29:40 +02:00
ut_a(success);
}
2005-10-27 09:29:40 +02:00
btr_cur = btr_pcur_get_btr_cur(&(node->pcur));
2005-10-27 09:29:40 +02:00
success = btr_cur_optimistic_delete(btr_cur, &mtr);
btr_pcur_commit_specify_mtr(&(node->pcur), &mtr);
if (success) {
trx_undo_rec_release(node->trx, node->undo_no);
return(DB_SUCCESS);
}
retry:
/* If did not succeed, try pessimistic descent to tree */
mtr_start(&mtr);
2005-10-27 09:29:40 +02:00
success = btr_pcur_restore_position(BTR_MODIFY_TREE,
&(node->pcur), &mtr);
2005-10-27 09:29:40 +02:00
ut_a(success);
btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
trx_is_recv(node->trx)
? RB_RECOVERY
: RB_NORMAL, &mtr);
2005-10-27 09:29:40 +02:00
/* 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 (err == DB_OUT_OF_FILE_SPACE
&& n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
2005-10-27 09:29:40 +02:00
btr_pcur_commit_specify_mtr(&(node->pcur), &mtr);
n_tries++;
os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
2005-10-27 09:29:40 +02:00
goto retry;
}
btr_pcur_commit_specify_mtr(&(node->pcur), &mtr);
trx_undo_rec_release(node->trx, node->undo_no);
return(err);
}
/*******************************************************************
Removes a secondary index entry if found. */
static
ulint
row_undo_ins_remove_sec_low(
/*========================*/
/* out: DB_SUCCESS, DB_FAIL, or
DB_OUT_OF_FILE_SPACE */
ulint mode, /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE,
depending on whether we wish optimistic or
pessimistic descent down the index tree */
dict_index_t* index, /* in: index */
dtuple_t* entry) /* in: index entry to remove */
{
btr_pcur_t pcur;
btr_cur_t* btr_cur;
ulint err;
mtr_t mtr;
enum row_search_result search_result;
2005-10-27 09:29:40 +02:00
log_free_check();
mtr_start(&mtr);
btr_cur = btr_pcur_get_btr_cur(&pcur);
ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
search_result = row_search_index_entry(index, entry, mode,
&pcur, &mtr);
switch (search_result) {
case ROW_NOT_FOUND:
err = DB_SUCCESS;
goto func_exit;
case ROW_FOUND:
break;
case ROW_BUFFERED:
branches/innodb+: Clean up the buffering of purges. Instead of traversing the index B-tree twice (first in BTR_WATCH_LEAF mode and then in BTR_DELETE mode), let BTR_DELETE take care of checking that the record can be purged, and either buffering or performing the purge. row_purge_poss_sec(): New function, to check if it is possible to purge a secondary index record. Refactored from row_purge_remove_sec_if_poss_low(). row_purge_remove_sec_if_poss_nonbuffered(): Rename to row_purge_remove_sec_if_poss_tree(). Remove the parameter mode (always use BTR_MODIFY_TREE). Use row_purge_poss_sec(). row_purge_remove_sec_if_poss_low(): Rename to row_purge_remove_sec_if_poss_leaf(). Remove the parameter mode (always use BTR_MODIFY_LEAF). Let row_search_index_entry() do all the hard work. btr_cur_t: Add purge_node, which will be needed by btr_cur_search_to_nth_level() for BTR_DELETE. Replace the flag value BTR_CUR_ABORTED with BTR_CUR_DELETE_REF and BTR_CUR_DELETE_FAILED. enum row_search_result, row_search_index_entry(): Replace ROW_NOT_IN_POOL with ROW_NOT_DELETED_REF and ROW_NOT_DELETED. btr_cur_search_to_nth_level(): Remove BTR_WATCH_LEAF. As a side effect, the adaptive hash index can be used in purge as well. If BTR_DELETE cannot be buffered, attempt btr_cur_optimistic_delete(). Either way, check row_purge_poss_sec(). Move the code to set cursor->ibuf_count to get rid of another if (height == 0) check. Eliminate the label loop_end. Do not call ibuf_should_try() twice. ibuf_should_try(): Now that the successful calls to this function will be halved, halve the magic constant that ibuf_flush_count will be compared to, accordingly. The changes regarding ibuf_should_try() were merged from branches/zip r3515. rb://60 approved by Heikki over IM
2008-12-12 13:59:48 +01:00
case ROW_NOT_DELETED:
case ROW_NOT_DELETED_REF:
/* These are invalid outcomes, because the mode passed
to row_search_index_entry() did not include any of the
branches/innodb+: Clean up the buffering of purges. Instead of traversing the index B-tree twice (first in BTR_WATCH_LEAF mode and then in BTR_DELETE mode), let BTR_DELETE take care of checking that the record can be purged, and either buffering or performing the purge. row_purge_poss_sec(): New function, to check if it is possible to purge a secondary index record. Refactored from row_purge_remove_sec_if_poss_low(). row_purge_remove_sec_if_poss_nonbuffered(): Rename to row_purge_remove_sec_if_poss_tree(). Remove the parameter mode (always use BTR_MODIFY_TREE). Use row_purge_poss_sec(). row_purge_remove_sec_if_poss_low(): Rename to row_purge_remove_sec_if_poss_leaf(). Remove the parameter mode (always use BTR_MODIFY_LEAF). Let row_search_index_entry() do all the hard work. btr_cur_t: Add purge_node, which will be needed by btr_cur_search_to_nth_level() for BTR_DELETE. Replace the flag value BTR_CUR_ABORTED with BTR_CUR_DELETE_REF and BTR_CUR_DELETE_FAILED. enum row_search_result, row_search_index_entry(): Replace ROW_NOT_IN_POOL with ROW_NOT_DELETED_REF and ROW_NOT_DELETED. btr_cur_search_to_nth_level(): Remove BTR_WATCH_LEAF. As a side effect, the adaptive hash index can be used in purge as well. If BTR_DELETE cannot be buffered, attempt btr_cur_optimistic_delete(). Either way, check row_purge_poss_sec(). Move the code to set cursor->ibuf_count to get rid of another if (height == 0) check. Eliminate the label loop_end. Do not call ibuf_should_try() twice. ibuf_should_try(): Now that the successful calls to this function will be halved, halve the magic constant that ibuf_flush_count will be compared to, accordingly. The changes regarding ibuf_should_try() were merged from branches/zip r3515. rb://60 approved by Heikki over IM
2008-12-12 13:59:48 +01:00
flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
ut_error;
2005-10-27 09:29:40 +02:00
}
if (mode == BTR_MODIFY_LEAF) {
err = btr_cur_optimistic_delete(btr_cur, &mtr)
? DB_SUCCESS : DB_FAIL;
2005-10-27 09:29:40 +02:00
} else {
ut_ad(mode == BTR_MODIFY_TREE);
/* No need to distinguish RB_RECOVERY here, because we
are deleting a secondary index record: the distinction
between RB_NORMAL and RB_RECOVERY only matters when
deleting a record that contains externally stored
columns. */
ut_ad(!dict_index_is_clust(index));
btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
RB_NORMAL, &mtr);
2005-10-27 09:29:40 +02:00
}
func_exit:
2005-10-27 09:29:40 +02:00
btr_pcur_close(&pcur);
mtr_commit(&mtr);
return(err);
}
/*******************************************************************
Removes a secondary index entry from the index if found. Tries first
optimistic, then pessimistic descent down the tree. */
static
ulint
row_undo_ins_remove_sec(
/*====================*/
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
dict_index_t* index, /* in: index */
dtuple_t* entry) /* in: index entry to insert */
{
ulint err;
ulint n_tries = 0;
2005-10-27 09:29:40 +02:00
/* Try first optimistic descent to the B-tree */
err = row_undo_ins_remove_sec_low(BTR_MODIFY_LEAF, index, entry);
2005-10-27 09:29:40 +02:00
if (err == DB_SUCCESS) {
return(err);
}
/* Try then pessimistic descent to the B-tree */
retry:
err = row_undo_ins_remove_sec_low(BTR_MODIFY_TREE, 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 (err != DB_SUCCESS && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
n_tries++;
os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
2005-10-27 09:29:40 +02:00
goto retry;
}
return(err);
}
/***************************************************************
Parses the row reference and other info in a fresh insert undo record. */
static
void
row_undo_ins_parse_undo_rec(
/*========================*/
branches/innodb+: Merge revisions 2799:2835 from branches/zip: ------------------------------------------------------------------------ r2809 | marko | 2008-10-16 09:41:13 +0300 (Thu, 16 Oct 2008) | 18 lines branches/zip: Skip the undo log size check on REDUNDANT and COMPACT tables. In ROW_FORMAT=DYNAMIC and ROW_FORMAT=COMPRESSED, column prefix indexes require that prefixes of externally stored columns be written to the undo log. This may make the undo log record bigger than the record on the B-tree page. The maximum size of an undo log record is the page size. That must be checked for, in dict_index_add_to_cache(). dict_index_add_to_cache(): Skip the undo log size check for REDUNDANT and COMPACT tables. These tables store prefixes of externally stored columns locally within the clustered index record. There are no special considerations for the undo log record size. innodb-index.test: Ensure that the check exists for ROW_FORMAT=DYNAMIC, but not for ROW_FORMAT=COMPACT. This fixes issue #99. rb://28 approved by Sunny. ------------------------------------------------------------------------ r2810 | vasil | 2008-10-16 19:57:58 +0300 (Thu, 16 Oct 2008) | 12 lines branches/zip: Fix Mantis issue#61: In row_undo_ins_parse_undo_rec(): if we find that a table has no indexes (dict_table_get_first_index() returns NULL) do not try to call trx_undo_rec_get_row_ref() with a NULL pointer because that would lead to a crash. Instead, print a warning and set node->table to NULL just like it is done if the .ibd file is missing. Approved by: Heikki (via IM) ------------------------------------------------------------------------ r2824 | marko | 2008-10-20 09:58:01 +0300 (Mon, 20 Oct 2008) | 2 lines branches/zip: rec_convert_dtuple_to_rec_comp(): Relax a too tight assertion. Spotted by Sunny. ------------------------------------------------------------------------ r2825 | vasil | 2008-10-20 13:41:04 +0300 (Mon, 20 Oct 2008) | 6 lines branches/zip: Print the table name via ut_print_name() and add two spaces before InnoDB. Suggested by: Marko ------------------------------------------------------------------------ r2833 | marko | 2008-10-21 10:16:45 +0300 (Tue, 21 Oct 2008) | 2 lines branches/zip: ibuf_insert_low(): Avoid unnecessarily acquiring and releasing ibuf_mutex. ------------------------------------------------------------------------ r2834 | marko | 2008-10-21 10:18:57 +0300 (Tue, 21 Oct 2008) | 1 line branches/zip: ibuf_delete_rec(): Add debug assertions suggested by Heikki. ------------------------------------------------------------------------ r2835 | marko | 2008-10-21 11:04:06 +0300 (Tue, 21 Oct 2008) | 1 line branches/zip: ibuf_insert_low(): Simplify a comparison. ------------------------------------------------------------------------
2008-10-21 09:30:53 +02:00
undo_node_t* node) /* in/out: row undo node */
2005-10-27 09:29:40 +02:00
{
dict_index_t* clust_index;
2005-10-27 09:29:40 +02:00
byte* ptr;
dulint undo_no;
2005-10-27 09:29:40 +02:00
dulint table_id;
ulint type;
ulint dummy;
ibool dummy_extern;
2005-10-27 09:29:40 +02:00
ut_ad(node);
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &dummy,
&dummy_extern, &undo_no, &table_id);
ut_ad(type == TRX_UNDO_INSERT_REC);
node->rec_type = type;
2005-10-27 09:29:40 +02:00
node->update = NULL;
node->table = dict_table_get_on_id(table_id, node->trx);
/* Skip the UNDO if we can't find the table or the .ibd file. */
if (UNIV_UNLIKELY(node->table == NULL)) {
} else if (UNIV_UNLIKELY(node->table->ibd_file_missing)) {
node->table = NULL;
} else {
clust_index = dict_table_get_first_index(node->table);
branches/innodb+: Merge revisions 2799:2835 from branches/zip: ------------------------------------------------------------------------ r2809 | marko | 2008-10-16 09:41:13 +0300 (Thu, 16 Oct 2008) | 18 lines branches/zip: Skip the undo log size check on REDUNDANT and COMPACT tables. In ROW_FORMAT=DYNAMIC and ROW_FORMAT=COMPRESSED, column prefix indexes require that prefixes of externally stored columns be written to the undo log. This may make the undo log record bigger than the record on the B-tree page. The maximum size of an undo log record is the page size. That must be checked for, in dict_index_add_to_cache(). dict_index_add_to_cache(): Skip the undo log size check for REDUNDANT and COMPACT tables. These tables store prefixes of externally stored columns locally within the clustered index record. There are no special considerations for the undo log record size. innodb-index.test: Ensure that the check exists for ROW_FORMAT=DYNAMIC, but not for ROW_FORMAT=COMPACT. This fixes issue #99. rb://28 approved by Sunny. ------------------------------------------------------------------------ r2810 | vasil | 2008-10-16 19:57:58 +0300 (Thu, 16 Oct 2008) | 12 lines branches/zip: Fix Mantis issue#61: In row_undo_ins_parse_undo_rec(): if we find that a table has no indexes (dict_table_get_first_index() returns NULL) do not try to call trx_undo_rec_get_row_ref() with a NULL pointer because that would lead to a crash. Instead, print a warning and set node->table to NULL just like it is done if the .ibd file is missing. Approved by: Heikki (via IM) ------------------------------------------------------------------------ r2824 | marko | 2008-10-20 09:58:01 +0300 (Mon, 20 Oct 2008) | 2 lines branches/zip: rec_convert_dtuple_to_rec_comp(): Relax a too tight assertion. Spotted by Sunny. ------------------------------------------------------------------------ r2825 | vasil | 2008-10-20 13:41:04 +0300 (Mon, 20 Oct 2008) | 6 lines branches/zip: Print the table name via ut_print_name() and add two spaces before InnoDB. Suggested by: Marko ------------------------------------------------------------------------ r2833 | marko | 2008-10-21 10:16:45 +0300 (Tue, 21 Oct 2008) | 2 lines branches/zip: ibuf_insert_low(): Avoid unnecessarily acquiring and releasing ibuf_mutex. ------------------------------------------------------------------------ r2834 | marko | 2008-10-21 10:18:57 +0300 (Tue, 21 Oct 2008) | 1 line branches/zip: ibuf_delete_rec(): Add debug assertions suggested by Heikki. ------------------------------------------------------------------------ r2835 | marko | 2008-10-21 11:04:06 +0300 (Tue, 21 Oct 2008) | 1 line branches/zip: ibuf_insert_low(): Simplify a comparison. ------------------------------------------------------------------------
2008-10-21 09:30:53 +02:00
if (clust_index != NULL) {
ptr = trx_undo_rec_get_row_ref(
ptr, clust_index, &node->ref, node->heap);
} else {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: table ");
ut_print_name(stderr, node->trx, TRUE,
node->table->name);
fprintf(stderr, " has no indexes, "
"ignoring the table\n");
node->table = NULL;
}
}
2005-10-27 09:29:40 +02:00
}
2005-10-27 09:29:40 +02:00
/***************************************************************
Undoes a fresh insert of a row to a table. A fresh insert means that
the same clustered index unique key did not have any record, even delete
branches/innodb+: Merge revisions 3180:3312 from branches/zip: ------------------------------------------------------------------------ r3254 | marko | 2008-11-24 18:01:42 +0200 (Mon, 24 Nov 2008) | 4 lines branches/zip: Note that it is legitimate for a secondary index record not to be found during purge. This tries to address Issue #129. The comments were supplied by Heikki. ------------------------------------------------------------------------ r3286 | marko | 2008-11-26 10:00:28 +0200 (Wed, 26 Nov 2008) | 18 lines branches/zip: row_merge_drop_temp_indexes(): Replace the WHILE 1 with WHILE 1=1 in the SQL procedure, so that the loop will actually be entered and temporary indexes be dropped during crash recovery. Thanks to Sunny Bains for pointing this out. Tested as follows: Set a breakpoint in row_merge_rename_indexes. CREATE TABLE t(a INT)ENGINE=InnoDB; CREATE INDEX a ON t(a); -- The breakpoint will be reached. Kill and restart mysqld. SHOW CREATE TABLE t; -- This shows the MySQL .frm file, without and index. CREATE TABLE innodb_table_monitor(a INT)ENGINE=InnoDB; -- This will dump the InnoDB dictionary to the error log, without the index. ------------------------------------------------------------------------ r3302 | vasil | 2008-11-27 23:26:39 +0200 (Thu, 27 Nov 2008) | 12 lines branches/zip: Fix Mantis issue#130 wdl: does not handle 64-bit address - Change the call from strtoul() to strtoull() - Change "%16X" to "%16llx" when scanning preferred load address rb://58 Submitted by: Calvin Approved by: Marko ------------------------------------------------------------------------ r3303 | vasil | 2008-11-27 23:31:18 +0200 (Thu, 27 Nov 2008) | 10 lines branches/zip: * Remove a change from win-plugin/win-plugin.diff about time_t because MySQL has used VS2005 for building 5.1.30. * Adjust the line numbers so the patch applies cleanly without fuzz and offset messages. Submitted by: Calvin ------------------------------------------------------------------------ r3304 | vasil | 2008-11-27 23:33:48 +0200 (Thu, 27 Nov 2008) | 6 lines branches/zip: Non-functional change in win-plugin/win-plugin.diff: fix the file name before the diff, this is irrelevant but it is nice to be the same as the file name on the following line. ------------------------------------------------------------------------ r3312 | marko | 2008-11-28 16:18:43 +0200 (Fri, 28 Nov 2008) | 5 lines branches/zip: row_undo_mod_del_mark_or_remove_sec_low(): Complain if the secondary index entry cannot be found, and this is not an incomplete transaction that is being rolled back in crash recovery. The source code comments were suggested by Heikki. ------------------------------------------------------------------------
2008-11-28 15:58:51 +01:00
marked, at the time of the insert. InnoDB is eager in a rollback:
if it figures out that an index record will be removed in the purge
anyway, it will remove it in the rollback. */
UNIV_INTERN
2005-10-27 09:29:40 +02:00
ulint
row_undo_ins(
/*=========*/
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
undo_node_t* node) /* in: row undo node */
{
ut_ad(node);
ut_ad(node->state == UNDO_NODE_INSERT);
2005-10-27 09:29:40 +02:00
row_undo_ins_parse_undo_rec(node);
if (!node->table || !row_undo_search_clust_to_pcur(node)) {
trx_undo_rec_release(node->trx, node->undo_no);
2005-10-27 09:29:40 +02:00
return(DB_SUCCESS);
}
2005-10-27 09:29:40 +02:00
/* Iterate over all the indexes and undo the insert.*/
2005-10-27 09:29:40 +02:00
/* Skip the clustered index (the first index) */
node->index = dict_table_get_next_index(
dict_table_get_first_index(node->table));
2005-10-27 09:29:40 +02:00
while (node->index != NULL) {
dtuple_t* entry;
ulint err;
2005-10-27 09:29:40 +02:00
entry = row_build_index_entry(node->row, node->ext,
node->index, node->heap);
if (UNIV_UNLIKELY(!entry)) {
/* The database must have crashed after
inserting a clustered index record but before
writing all the externally stored columns of
that record. Because secondary index entries
are inserted after the clustered index record,
we may assume that the secondary index record
does not exist. However, this situation may
only occur during the rollback of incomplete
transactions. */
ut_a(trx_is_recv(node->trx));
} else {
err = row_undo_ins_remove_sec(node->index, entry);
if (err != DB_SUCCESS) {
return(err);
}
2005-10-27 09:29:40 +02:00
}
node->index = dict_table_get_next_index(node->index);
2005-10-27 09:29:40 +02:00
}
return(row_undo_ins_remove_clust_rec(node));
2005-10-27 09:29:40 +02:00
}