mirror of
https://github.com/MariaDB/server.git
synced 2025-02-01 11:31:51 +01:00
b6679b1d14
------------------------------------------------------------------------ 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. ------------------------------------------------------------------------
337 lines
8.3 KiB
C
337 lines
8.3 KiB
C
/******************************************************
|
|
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;
|
|
ibool success;
|
|
ulint err;
|
|
ulint n_tries = 0;
|
|
mtr_t mtr;
|
|
|
|
mtr_start(&mtr);
|
|
|
|
success = btr_pcur_restore_position(BTR_MODIFY_LEAF, &(node->pcur),
|
|
&mtr);
|
|
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);
|
|
|
|
/* Drop the index tree associated with the row in
|
|
SYS_INDEXES table: */
|
|
|
|
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);
|
|
ut_a(success);
|
|
}
|
|
|
|
btr_cur = btr_pcur_get_btr_cur(&(node->pcur));
|
|
|
|
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);
|
|
|
|
success = btr_pcur_restore_position(BTR_MODIFY_TREE,
|
|
&(node->pcur), &mtr);
|
|
ut_a(success);
|
|
|
|
btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
|
|
trx_is_recv(node->trx)
|
|
? RB_RECOVERY
|
|
: RB_NORMAL, &mtr);
|
|
|
|
/* 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) {
|
|
|
|
btr_pcur_commit_specify_mtr(&(node->pcur), &mtr);
|
|
|
|
n_tries++;
|
|
|
|
os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
|
|
|
|
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;
|
|
|
|
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:
|
|
case ROW_NOT_IN_POOL:
|
|
/* These are invalid outcomes, because the mode passed
|
|
to row_search_index_entry() did not include any of the
|
|
flags BTR_INSERT, BTR_DELETE, BTR_DELETE_MARK, or
|
|
BTR_WATCH_LEAF. */
|
|
ut_error;
|
|
}
|
|
|
|
if (mode == BTR_MODIFY_LEAF) {
|
|
err = btr_cur_optimistic_delete(btr_cur, &mtr)
|
|
? DB_SUCCESS : DB_FAIL;
|
|
} 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);
|
|
}
|
|
func_exit:
|
|
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;
|
|
|
|
/* Try first optimistic descent to the B-tree */
|
|
|
|
err = row_undo_ins_remove_sec_low(BTR_MODIFY_LEAF, index, entry);
|
|
|
|
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);
|
|
|
|
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(
|
|
/*========================*/
|
|
undo_node_t* node) /* in/out: row undo node */
|
|
{
|
|
dict_index_t* clust_index;
|
|
byte* ptr;
|
|
dulint undo_no;
|
|
dulint table_id;
|
|
ulint type;
|
|
ulint dummy;
|
|
ibool dummy_extern;
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************
|
|
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
|
|
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
|
|
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);
|
|
|
|
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);
|
|
|
|
return(DB_SUCCESS);
|
|
}
|
|
|
|
/* Iterate over all the indexes and undo the insert.*/
|
|
|
|
/* Skip the clustered index (the first index) */
|
|
node->index = dict_table_get_next_index(
|
|
dict_table_get_first_index(node->table));
|
|
|
|
while (node->index != NULL) {
|
|
dtuple_t* entry;
|
|
ulint err;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
node->index = dict_table_get_next_index(node->index);
|
|
}
|
|
|
|
return(row_undo_ins_remove_clust_rec(node));
|
|
}
|