summaryrefslogtreecommitdiff
path: root/storage/innobase/row/row0umod.cc
diff options
context:
space:
mode:
authorMichael Widenius <monty@askmonty.org>2013-03-26 00:03:13 +0200
committerMichael Widenius <monty@askmonty.org>2013-03-26 00:03:13 +0200
commit068c61978e3a81836d52b8caf11e044290159ad1 (patch)
tree2cbca861ab2cebe3bd99379ca9668bb483ca0d2a /storage/innobase/row/row0umod.cc
parent35bc8f9f4353b64da215e52ff6f1612a8ce66f43 (diff)
downloadmariadb-git-068c61978e3a81836d52b8caf11e044290159ad1.tar.gz
Temporary commit of 10.0-merge
Diffstat (limited to 'storage/innobase/row/row0umod.cc')
-rw-r--r--storage/innobase/row/row0umod.cc510
1 files changed, 391 insertions, 119 deletions
diff --git a/storage/innobase/row/row0umod.cc b/storage/innobase/row/row0umod.cc
index 4869909f5a6..c1a4ba76052 100644
--- a/storage/innobase/row/row0umod.cc
+++ b/storage/innobase/row/row0umod.cc
@@ -37,6 +37,7 @@ Created 2/27/1997 Heikki Tuuri
#include "mach0data.h"
#include "row0undo.h"
#include "row0vers.h"
+#include "row0log.h"
#include "trx0trx.h"
#include "trx0rec.h"
#include "row0row.h"
@@ -71,11 +72,20 @@ introduced where a call to log_free_check() is bypassed. */
/***********************************************************//**
Undoes a modify in a clustered index record.
@return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
-static
-ulint
+static __attribute__((nonnull, warn_unused_result))
+dberr_t
row_undo_mod_clust_low(
/*===================*/
undo_node_t* node, /*!< in: row undo node */
+ ulint** offsets,/*!< out: rec_get_offsets() on the record */
+ mem_heap_t** offsets_heap,
+ /*!< in/out: memory heap that can be emptied */
+ mem_heap_t* heap, /*!< in/out: memory heap */
+ const dtuple_t**rebuilt_old_pk,
+ /*!< out: row_log_table_get_pk()
+ before the update, or NULL if
+ the table is not being rebuilt online or
+ the PRIMARY KEY definition does not change */
que_thr_t* thr, /*!< in: query thread */
mtr_t* mtr, /*!< in: mtr; must be committed before
latching any further pages */
@@ -83,12 +93,12 @@ row_undo_mod_clust_low(
{
btr_pcur_t* pcur;
btr_cur_t* btr_cur;
- ulint err;
+ dberr_t err;
#ifdef UNIV_DEBUG
ibool success;
#endif /* UNIV_DEBUG */
- pcur = &(node->pcur);
+ pcur = &node->pcur;
btr_cur = btr_pcur_get_btr_cur(pcur);
#ifdef UNIV_DEBUG
@@ -97,31 +107,40 @@ row_undo_mod_clust_low(
btr_pcur_restore_position(mode, pcur, mtr);
ut_ad(success);
+ ut_ad(rec_get_trx_id(btr_cur_get_rec(btr_cur),
+ btr_cur_get_index(btr_cur))
+ == thr_get_trx(thr)->id);
+
+ if (mode != BTR_MODIFY_LEAF
+ && dict_index_is_online_ddl(btr_cur_get_index(btr_cur))) {
+ *rebuilt_old_pk = row_log_table_get_pk(
+ btr_cur_get_rec(btr_cur),
+ btr_cur_get_index(btr_cur), NULL, &heap);
+ } else {
+ *rebuilt_old_pk = NULL;
+ }
- if (mode == BTR_MODIFY_LEAF) {
+ if (mode != BTR_MODIFY_TREE) {
+ ut_ad((mode & ~BTR_ALREADY_S_LATCHED) == BTR_MODIFY_LEAF);
- err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG
- | BTR_NO_UNDO_LOG_FLAG
- | BTR_KEEP_SYS_FLAG,
- btr_cur, node->update,
- node->cmpl_info, thr, mtr);
+ err = btr_cur_optimistic_update(
+ BTR_NO_LOCKING_FLAG | BTR_NO_UNDO_LOG_FLAG
+ | BTR_KEEP_SYS_FLAG,
+ btr_cur, offsets, offsets_heap,
+ node->update, node->cmpl_info,
+ thr, thr_get_trx(thr)->id, mtr);
} else {
- mem_heap_t* heap = NULL;
big_rec_t* dummy_big_rec;
- ut_ad(mode == BTR_MODIFY_TREE);
-
err = btr_cur_pessimistic_update(
BTR_NO_LOCKING_FLAG
| BTR_NO_UNDO_LOG_FLAG
| BTR_KEEP_SYS_FLAG,
- btr_cur, &heap, &dummy_big_rec, node->update,
- node->cmpl_info, thr, mtr);
+ btr_cur, offsets, offsets_heap, heap,
+ &dummy_big_rec, node->update,
+ node->cmpl_info, thr, thr_get_trx(thr)->id, mtr);
ut_a(!dummy_big_rec);
- if (UNIV_LIKELY_NULL(heap)) {
- mem_heap_free(heap);
- }
}
return(err);
@@ -134,8 +153,8 @@ delete-marked record and there no longer exist transactions
that would see the delete-marked record. In other words, we
roll back the insert by purging the record.
@return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
-static
-ulint
+static __attribute__((nonnull, warn_unused_result))
+dberr_t
row_undo_mod_remove_clust_low(
/*==========================*/
undo_node_t* node, /*!< in: row undo node */
@@ -144,7 +163,7 @@ row_undo_mod_remove_clust_low(
ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
{
btr_cur_t* btr_cur;
- ulint err;
+ dberr_t err;
ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
@@ -159,8 +178,14 @@ row_undo_mod_remove_clust_low(
btr_cur = btr_pcur_get_btr_cur(&node->pcur);
+ /* We are about to remove an old, delete-marked version of the
+ record that may have been delete-marked by a different transaction
+ than the rolling-back one. */
+ ut_ad(rec_get_deleted_flag(btr_cur_get_rec(btr_cur),
+ dict_table_is_comp(node->table)));
+
if (mode == BTR_MODIFY_LEAF) {
- err = btr_cur_optimistic_delete(btr_cur, mtr)
+ err = btr_cur_optimistic_delete(btr_cur, 0, mtr)
? DB_SUCCESS
: DB_FAIL;
} else {
@@ -169,7 +194,7 @@ row_undo_mod_remove_clust_low(
/* This operation is analogous to purge, we can free also
inherited externally stored fields */
- btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
+ btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0,
thr_is_recv(thr)
? RB_RECOVERY_PURGE_REC
: RB_NONE, mtr);
@@ -186,8 +211,8 @@ row_undo_mod_remove_clust_low(
Undoes a modify in a clustered index record. Sets also the node state for the
next round of undo.
@return DB_SUCCESS or error code: we may run out of file space */
-static
-ulint
+static __attribute__((nonnull, warn_unused_result))
+dberr_t
row_undo_mod_clust(
/*===============*/
undo_node_t* node, /*!< in: row undo node */
@@ -195,21 +220,42 @@ row_undo_mod_clust(
{
btr_pcur_t* pcur;
mtr_t mtr;
- ulint err;
+ dberr_t err;
+ dict_index_t* index;
+ bool online;
- ut_ad(node && thr);
+ ut_ad(thr_get_trx(thr) == node->trx);
+ ut_ad(node->trx->dict_operation_lock_mode);
+#ifdef UNIV_SYNC_DEBUG
+ ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED)
+ || rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
+#endif /* UNIV_SYNC_DEBUG */
log_free_check();
+ pcur = &node->pcur;
+ index = btr_cur_get_index(btr_pcur_get_btr_cur(pcur));
+ mtr_start(&mtr);
- pcur = &(node->pcur);
+ online = dict_index_is_online_ddl(index);
+ if (online) {
+ ut_ad(node->trx->dict_operation_lock_mode != RW_X_LATCH);
+ mtr_s_lock(dict_index_get_lock(index), &mtr);
+ }
- mtr_start(&mtr);
+ mem_heap_t* heap = mem_heap_create(1024);
+ mem_heap_t* offsets_heap = NULL;
+ ulint* offsets = NULL;
+ const dtuple_t* rebuilt_old_pk;
/* Try optimistic processing of the record, keeping changes within
the index page */
- err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);
+ err = row_undo_mod_clust_low(node, &offsets, &offsets_heap,
+ heap, &rebuilt_old_pk,
+ thr, &mtr, online
+ ? BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED
+ : BTR_MODIFY_LEAF);
if (err != DB_SUCCESS) {
btr_pcur_commit_specify_mtr(pcur, &mtr);
@@ -219,7 +265,40 @@ row_undo_mod_clust(
mtr_start(&mtr);
- err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
+ err = row_undo_mod_clust_low(
+ node, &offsets, &offsets_heap, heap, &rebuilt_old_pk,
+ thr, &mtr, BTR_MODIFY_TREE);
+ ut_ad(err == DB_SUCCESS || err == DB_OUT_OF_FILE_SPACE);
+ }
+
+ /* Online rebuild cannot be initiated while we are holding
+ dict_operation_lock and index->lock. (It can be aborted.) */
+ ut_ad(online || !dict_index_is_online_ddl(index));
+
+ if (err == DB_SUCCESS && online) {
+#ifdef UNIV_SYNC_DEBUG
+ ut_ad(rw_lock_own(&index->lock, RW_LOCK_SHARED)
+ || rw_lock_own(&index->lock, RW_LOCK_EX));
+#endif /* UNIV_SYNC_DEBUG */
+ switch (node->rec_type) {
+ case TRX_UNDO_DEL_MARK_REC:
+ row_log_table_insert(
+ btr_pcur_get_rec(pcur), index, offsets);
+ break;
+ case TRX_UNDO_UPD_EXIST_REC:
+ row_log_table_update(
+ btr_pcur_get_rec(pcur), index, offsets,
+ rebuilt_old_pk);
+ break;
+ case TRX_UNDO_UPD_DEL_REC:
+ row_log_table_delete(
+ btr_pcur_get_rec(pcur), index, offsets,
+ node->trx->id);
+ break;
+ default:
+ ut_ad(0);
+ break;
+ }
}
btr_pcur_commit_specify_mtr(pcur, &mtr);
@@ -228,8 +307,11 @@ row_undo_mod_clust(
mtr_start(&mtr);
- err = row_undo_mod_remove_clust_low(node, thr, &mtr,
- BTR_MODIFY_LEAF);
+ /* It is not necessary to call row_log_table,
+ because the record is delete-marked and would thus
+ be omitted from the rebuilt copy of the table. */
+ err = row_undo_mod_remove_clust_low(
+ node, thr, &mtr, BTR_MODIFY_LEAF);
if (err != DB_SUCCESS) {
btr_pcur_commit_specify_mtr(pcur, &mtr);
@@ -240,6 +322,9 @@ row_undo_mod_clust(
err = row_undo_mod_remove_clust_low(node, thr, &mtr,
BTR_MODIFY_TREE);
+
+ ut_ad(err == DB_SUCCESS
+ || err == DB_OUT_OF_FILE_SPACE);
}
btr_pcur_commit_specify_mtr(pcur, &mtr);
@@ -249,14 +334,18 @@ row_undo_mod_clust(
trx_undo_rec_release(node->trx, node->undo_no);
+ if (offsets_heap) {
+ mem_heap_free(offsets_heap);
+ }
+ mem_heap_free(heap);
return(err);
}
/***********************************************************//**
Delete marks or removes a secondary index entry if found.
@return DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */
-static
-ulint
+static __attribute__((nonnull, warn_unused_result))
+dberr_t
row_undo_mod_del_mark_or_remove_sec_low(
/*====================================*/
undo_node_t* node, /*!< in: row undo node */
@@ -270,7 +359,7 @@ row_undo_mod_del_mark_or_remove_sec_low(
btr_cur_t* btr_cur;
ibool success;
ibool old_has;
- ulint err;
+ dberr_t err = DB_SUCCESS;
mtr_t mtr;
mtr_t mtr_vers;
enum row_search_result search_result;
@@ -278,9 +367,30 @@ row_undo_mod_del_mark_or_remove_sec_low(
log_free_check();
mtr_start(&mtr);
- btr_cur = btr_pcur_get_btr_cur(&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. */
+ if (mode == BTR_MODIFY_LEAF) {
+ mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED;
+ mtr_s_lock(dict_index_get_lock(index), &mtr);
+ } else {
+ ut_ad(mode == BTR_MODIFY_TREE);
+ mtr_x_lock(dict_index_get_lock(index), &mtr);
+ }
+
+ if (row_log_online_op_try(index, entry, 0)) {
+ 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));
+ }
- ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
+ btr_cur = btr_pcur_get_btr_cur(&pcur);
search_result = row_search_index_entry(index, entry, mode,
&pcur, &mtr);
@@ -296,8 +406,6 @@ row_undo_mod_del_mark_or_remove_sec_low(
In normal processing, if an update ends in a deadlock
before it has inserted all updated secondary index
records, then the undo will not find those records. */
-
- err = DB_SUCCESS;
goto func_exit;
case ROW_FOUND:
break;
@@ -329,16 +437,14 @@ row_undo_mod_del_mark_or_remove_sec_low(
} else {
/* Remove the index record */
- if (mode == BTR_MODIFY_LEAF) {
- success = btr_cur_optimistic_delete(btr_cur, &mtr);
+ if (mode != BTR_MODIFY_TREE) {
+ success = btr_cur_optimistic_delete(btr_cur, 0, &mtr);
if (success) {
err = DB_SUCCESS;
} else {
err = DB_FAIL;
}
} else {
- ut_ad(mode == BTR_MODIFY_TREE);
-
/* No need to distinguish RB_RECOVERY_PURGE here,
because we are deleting a secondary index record:
the distinction between RB_NORMAL and
@@ -346,7 +452,7 @@ row_undo_mod_del_mark_or_remove_sec_low(
record that contains externally stored
columns. */
ut_ad(!dict_index_is_clust(index));
- btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
+ btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0,
RB_NORMAL, &mtr);
/* The delete operation may fail if we have little
@@ -359,6 +465,7 @@ row_undo_mod_del_mark_or_remove_sec_low(
func_exit:
btr_pcur_close(&pcur);
+func_exit_no_pcur:
mtr_commit(&mtr);
return(err);
@@ -373,8 +480,8 @@ not cause problems because in row0sel.cc, in queries we always retrieve the
clustered index record or an earlier version of it, if the secondary index
record through which we do the search is delete-marked.
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
-static
-ulint
+static __attribute__((nonnull, warn_unused_result))
+dberr_t
row_undo_mod_del_mark_or_remove_sec(
/*================================*/
undo_node_t* node, /*!< in: row undo node */
@@ -382,7 +489,7 @@ row_undo_mod_del_mark_or_remove_sec(
dict_index_t* index, /*!< in: index */
dtuple_t* entry) /*!< in: index entry */
{
- ulint err;
+ dberr_t err;
err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
entry, BTR_MODIFY_LEAF);
@@ -401,42 +508,67 @@ Delete unmarks a secondary index entry which must be found. It might not be
delete-marked at the moment, but it does not harm to unmark it anyway. We also
need to update the fields of the secondary index record if we updated its
fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'.
-@return DB_FAIL or DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
-static
-ulint
+@retval DB_SUCCESS on success
+@retval DB_FAIL if BTR_MODIFY_TREE should be tried
+@retval DB_OUT_OF_FILE_SPACE when running out of tablespace
+@retval DB_DUPLICATE_KEY if the value was missing
+ and an insert would lead to a duplicate exists */
+static __attribute__((nonnull, warn_unused_result))
+dberr_t
row_undo_mod_del_unmark_sec_and_undo_update(
/*========================================*/
ulint mode, /*!< in: search mode: BTR_MODIFY_LEAF or
BTR_MODIFY_TREE */
que_thr_t* thr, /*!< in: query thread */
dict_index_t* index, /*!< in: index */
- const dtuple_t* entry) /*!< in: index entry */
+ dtuple_t* entry) /*!< in: index entry */
{
- mem_heap_t* heap;
btr_pcur_t pcur;
- btr_cur_t* btr_cur;
+ btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur);
upd_t* update;
- ulint err = DB_SUCCESS;
+ dberr_t err = DB_SUCCESS;
big_rec_t* dummy_big_rec;
mtr_t mtr;
trx_t* trx = thr_get_trx(thr);
+ const ulint flags
+ = BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG;
enum row_search_result search_result;
- /* Ignore indexes that are being created. */
- if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {
-
- return(DB_SUCCESS);
- }
+ ut_ad(trx->id);
log_free_check();
mtr_start(&mtr);
- ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
+ 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. */
+ if (mode == BTR_MODIFY_LEAF) {
+ mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED;
+ mtr_s_lock(dict_index_get_lock(index), &mtr);
+ } else {
+ ut_ad(mode == BTR_MODIFY_TREE);
+ mtr_x_lock(dict_index_get_lock(index), &mtr);
+ }
+
+ if (row_log_online_op_try(index, entry, trx->id)) {
+ 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, mode,
&pcur, &mtr);
switch (search_result) {
+ mem_heap_t* heap;
+ mem_heap_t* offsets_heap;
+ ulint* offsets;
case ROW_BUFFERED:
case ROW_NOT_DELETED_REF:
/* These are invalid outcomes, because the mode passed
@@ -444,80 +576,183 @@ row_undo_mod_del_unmark_sec_and_undo_update(
flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
ut_error;
case ROW_NOT_FOUND:
- fputs("InnoDB: error in sec index entry del undo in\n"
- "InnoDB: ", stderr);
- dict_index_name_print(stderr, trx, index);
- fputs("\n"
- "InnoDB: tuple ", stderr);
- dtuple_print(stderr, entry);
- fputs("\n"
- "InnoDB: record ", stderr);
- rec_print(stderr, btr_pcur_get_rec(&pcur), index);
- putc('\n', stderr);
- trx_print(stderr, trx, 0);
- fputs("\n"
- "InnoDB: Submit a detailed bug report"
- " to http://bugs.mysql.com\n", stderr);
- ut_ad(0);
+ if (*index->name != TEMP_INDEX_PREFIX) {
+ /* During online secondary index creation, it
+ is possible that MySQL is waiting for a
+ meta-data lock upgrade before invoking
+ ha_innobase::commit_inplace_alter_table()
+ while this ROLLBACK is executing. InnoDB has
+ finished building the index, but it does not
+ yet exist in MySQL. In this case, we suppress
+ the printout to the error log. */
+ fputs("InnoDB: error in sec index entry del undo in\n"
+ "InnoDB: ", stderr);
+ dict_index_name_print(stderr, trx, index);
+ fputs("\n"
+ "InnoDB: tuple ", stderr);
+ dtuple_print(stderr, entry);
+ fputs("\n"
+ "InnoDB: record ", stderr);
+ rec_print(stderr, btr_pcur_get_rec(&pcur), index);
+ putc('\n', stderr);
+ trx_print(stderr, trx, 0);
+ fputs("\n"
+ "InnoDB: Submit a detailed bug report"
+ " to http://bugs.mysql.com\n", stderr);
+
+ ib_logf(IB_LOG_LEVEL_WARN,
+ "record in index %s was not found"
+ " on rollback, trying to insert",
+ index->name);
+ }
+
+ if (btr_cur->up_match >= dict_index_get_n_unique(index)
+ || btr_cur->low_match >= dict_index_get_n_unique(index)) {
+ if (*index->name != TEMP_INDEX_PREFIX) {
+ ib_logf(IB_LOG_LEVEL_WARN,
+ "record in index %s was not found on"
+ " rollback, and a duplicate exists",
+ index->name);
+ }
+ err = DB_DUPLICATE_KEY;
+ break;
+ }
+
+ /* Insert the missing record that we were trying to
+ delete-unmark. */
+ big_rec_t* big_rec;
+ rec_t* insert_rec;
+ offsets = NULL;
+ offsets_heap = NULL;
+
+ err = btr_cur_optimistic_insert(
+ flags, btr_cur, &offsets, &offsets_heap,
+ entry, &insert_rec, &big_rec,
+ 0, thr, &mtr);
+ ut_ad(!big_rec);
+
+ if (err == DB_FAIL && mode == BTR_MODIFY_TREE) {
+ err = btr_cur_pessimistic_insert(
+ flags, btr_cur,
+ &offsets, &offsets_heap,
+ entry, &insert_rec, &big_rec,
+ 0, thr, &mtr);
+ /* There are no off-page columns in
+ secondary indexes. */
+ ut_ad(!big_rec);
+ }
+
+ if (err == DB_SUCCESS) {
+ page_update_max_trx_id(
+ btr_cur_get_block(btr_cur),
+ btr_cur_get_page_zip(btr_cur),
+ trx->id, &mtr);
+ }
+
+ if (offsets_heap) {
+ mem_heap_free(offsets_heap);
+ }
+
break;
case ROW_FOUND:
- btr_cur = btr_pcur_get_btr_cur(&pcur);
- err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
- btr_cur, FALSE, thr, &mtr);
+ err = btr_cur_del_mark_set_sec_rec(
+ BTR_NO_LOCKING_FLAG,
+ btr_cur, FALSE, thr, &mtr);
ut_a(err == DB_SUCCESS);
- heap = mem_heap_create(100);
-
+ heap = mem_heap_create(
+ sizeof(upd_t)
+ + dtuple_get_n_fields(entry) * sizeof(upd_field_t));
+ offsets_heap = NULL;
+ offsets = rec_get_offsets(
+ btr_cur_get_rec(btr_cur),
+ index, NULL, ULINT_UNDEFINED, &offsets_heap);
update = row_upd_build_sec_rec_difference_binary(
- index, entry, btr_cur_get_rec(btr_cur), trx, heap);
+ btr_cur_get_rec(btr_cur), index, offsets, entry, heap);
if (upd_get_n_fields(update) == 0) {
/* Do nothing */
- } else if (mode == BTR_MODIFY_LEAF) {
+ } else if (mode != BTR_MODIFY_TREE) {
/* Try an optimistic updating of the record, keeping
changes within the page */
+ /* TODO: pass offsets, not &offsets */
err = btr_cur_optimistic_update(
- BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
- btr_cur, update, 0, thr, &mtr);
+ flags, btr_cur, &offsets, &offsets_heap,
+ update, 0, thr, thr_get_trx(thr)->id, &mtr);
switch (err) {
case DB_OVERFLOW:
case DB_UNDERFLOW:
case DB_ZIP_OVERFLOW:
err = DB_FAIL;
+ default:
+ break;
}
} else {
- ut_a(mode == BTR_MODIFY_TREE);
err = btr_cur_pessimistic_update(
- BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
- btr_cur, &heap, &dummy_big_rec,
- update, 0, thr, &mtr);
+ flags, btr_cur, &offsets, &offsets_heap,
+ heap, &dummy_big_rec,
+ update, 0, thr, thr_get_trx(thr)->id, &mtr);
ut_a(!dummy_big_rec);
}
mem_heap_free(heap);
+ mem_heap_free(offsets_heap);
}
btr_pcur_close(&pcur);
+func_exit_no_pcur:
mtr_commit(&mtr);
return(err);
}
/***********************************************************//**
+Flags a secondary index corrupted. */
+static __attribute__((nonnull))
+void
+row_undo_mod_sec_flag_corrupted(
+/*============================*/
+ trx_t* trx, /*!< in/out: transaction */
+ dict_index_t* index) /*!< in: secondary index */
+{
+ ut_ad(!dict_index_is_clust(index));
+
+ switch (trx->dict_operation_lock_mode) {
+ case RW_S_LATCH:
+ /* Because row_undo() is holding an S-latch
+ on the data dictionary during normal rollback,
+ we can only mark the index corrupted in the
+ data dictionary cache. TODO: fix this somehow.*/
+ mutex_enter(&dict_sys->mutex);
+ dict_set_corrupted_index_cache_only(index, index->table);
+ mutex_exit(&dict_sys->mutex);
+ break;
+ default:
+ ut_ad(0);
+ /* fall through */
+ case RW_X_LATCH:
+ /* This should be the rollback of a data dictionary
+ transaction. */
+ dict_set_corrupted(index, trx, "rollback");
+ }
+}
+
+/***********************************************************//**
Undoes a modify in secondary indexes when undo record type is UPD_DEL.
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
-static
-ulint
+static __attribute__((nonnull, warn_unused_result))
+dberr_t
row_undo_mod_upd_del_sec(
/*=====================*/
undo_node_t* node, /*!< in: row undo node */
que_thr_t* thr) /*!< in: query thread */
{
mem_heap_t* heap;
- ulint err = DB_SUCCESS;
+ dberr_t err = DB_SUCCESS;
ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
+ ut_ad(!node->undo_row);
heap = mem_heap_create(1024);
@@ -530,6 +765,13 @@ row_undo_mod_upd_del_sec(
continue;
}
+ /* During online index creation,
+ HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE should
+ guarantee that any active transaction has not modified
+ indexed columns such that col->ord_part was 0 at the
+ time when the undo log record was written. When we get
+ to roll back an undo log entry TRX_UNDO_DEL_MARK_REC,
+ it should always cover all affected indexes. */
entry = row_build_index_entry(
node->row, node->ext, index, heap);
@@ -566,15 +808,17 @@ row_undo_mod_upd_del_sec(
/***********************************************************//**
Undoes a modify in secondary indexes when undo record type is DEL_MARK.
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
-static
-ulint
+static __attribute__((nonnull, warn_unused_result))
+dberr_t
row_undo_mod_del_mark_sec(
/*======================*/
undo_node_t* node, /*!< in: row undo node */
que_thr_t* thr) /*!< in: query thread */
{
mem_heap_t* heap;
- ulint err = DB_SUCCESS;
+ dberr_t err = DB_SUCCESS;
+
+ ut_ad(!node->undo_row);
heap = mem_heap_create(1024);
@@ -587,6 +831,13 @@ row_undo_mod_del_mark_sec(
continue;
}
+ /* During online index creation,
+ HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE should
+ guarantee that any active transaction has not modified
+ indexed columns such that col->ord_part was 0 at the
+ time when the undo log record was written. When we get
+ to roll back an undo log entry TRX_UNDO_DEL_MARK_REC,
+ it should always cover all affected indexes. */
entry = row_build_index_entry(
node->row, node->ext, index, heap);
@@ -599,8 +850,17 @@ row_undo_mod_del_mark_sec(
BTR_MODIFY_TREE, thr, index, entry);
}
- if (UNIV_UNLIKELY(err != DB_SUCCESS)) {
-
+ if (err == DB_DUPLICATE_KEY) {
+ row_undo_mod_sec_flag_corrupted(
+ thr_get_trx(thr), index);
+ err = DB_SUCCESS;
+ /* Do not return any error to the caller. The
+ duplicate will be reported by ALTER TABLE or
+ CREATE UNIQUE INDEX. Unfortunately we cannot
+ report the duplicate key value to the DDL
+ thread, because the altered_table object is
+ private to its call stack. */
+ } else if (err != DB_SUCCESS) {
break;
}
@@ -616,18 +876,18 @@ row_undo_mod_del_mark_sec(
/***********************************************************//**
Undoes a modify in secondary indexes when undo record type is UPD_EXIST.
@return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
-static
-ulint
+static __attribute__((nonnull, warn_unused_result))
+dberr_t
row_undo_mod_upd_exist_sec(
/*=======================*/
undo_node_t* node, /*!< in: row undo node */
que_thr_t* thr) /*!< in: query thread */
{
mem_heap_t* heap;
- ulint err = DB_SUCCESS;
+ dberr_t err = DB_SUCCESS;
if (node->index == NULL
- || (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
+ || ((node->cmpl_info & UPD_NODE_NO_ORD_CHANGE))) {
/* No change in secondary indexes */
return(err);
@@ -713,7 +973,11 @@ row_undo_mod_upd_exist_sec(
BTR_MODIFY_TREE, thr, index, entry);
}
- if (UNIV_UNLIKELY(err != DB_SUCCESS)) {
+ if (err == DB_DUPLICATE_KEY) {
+ row_undo_mod_sec_flag_corrupted(
+ thr_get_trx(thr), index);
+ err = DB_SUCCESS;
+ } else if (err != DB_SUCCESS) {
break;
}
@@ -728,12 +992,11 @@ row_undo_mod_upd_exist_sec(
/***********************************************************//**
Parses the row reference and other info in a modify undo log record. */
-static
+static __attribute__((nonnull))
void
row_undo_mod_parse_undo_rec(
/*========================*/
undo_node_t* node, /*!< in: row undo node */
- que_thr_t* thr, /*!< in: query thread */
ibool dict_locked) /*!< in: TRUE if own dict_sys->mutex */
{
dict_index_t* clust_index;
@@ -745,16 +1008,13 @@ row_undo_mod_parse_undo_rec(
ulint info_bits;
ulint type;
ulint cmpl_info;
- ibool dummy_extern;
- trx_t* trx;
+ bool dummy_extern;
- ut_ad(node && thr);
- trx = thr_get_trx(thr);
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
&dummy_extern, &undo_no, &table_id);
node->rec_type = type;
- node->table = dict_table_open_on_id(table_id, dict_locked);
+ node->table = dict_table_open_on_id(table_id, dict_locked, FALSE);
/* TODO: other fixes associated with DROP TABLE + rollback in the
same table by another user */
@@ -765,7 +1025,7 @@ row_undo_mod_parse_undo_rec(
}
if (node->table->ibd_file_missing) {
- dict_table_close(node->table, dict_locked);
+ dict_table_close(node->table, dict_locked, FALSE);
/* We skip undo operations to missing .ibd files */
node->table = NULL;
@@ -782,14 +1042,14 @@ row_undo_mod_parse_undo_rec(
node->heap);
trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
- roll_ptr, info_bits, trx,
+ roll_ptr, info_bits, node->trx,
node->heap, &(node->update));
node->new_trx_id = trx_id;
node->cmpl_info = cmpl_info;
if (!row_undo_search_clust_to_pcur(node)) {
- dict_table_close(node->table, dict_locked);
+ dict_table_close(node->table, dict_locked, FALSE);
node->table = NULL;
}
@@ -799,21 +1059,23 @@ row_undo_mod_parse_undo_rec(
Undoes a modify operation on a row of a table.
@return DB_SUCCESS or error code */
UNIV_INTERN
-ulint
+dberr_t
row_undo_mod(
/*=========*/
undo_node_t* node, /*!< in: row undo node */
que_thr_t* thr) /*!< in: query thread */
{
- ulint err;
- ibool dict_locked;
+ dberr_t err;
+ ibool dict_locked;
ut_ad(node && thr);
ut_ad(node->state == UNDO_NODE_MODIFY);
dict_locked = thr_get_trx(thr)->dict_operation_lock_mode == RW_X_LATCH;
- row_undo_mod_parse_undo_rec(node, thr, dict_locked);
+ ut_ad(thr_get_trx(thr) == node->trx);
+
+ row_undo_mod_parse_undo_rec(node, dict_locked);
if (node->table == NULL) {
/* It is already undone, or will be undone by another query
@@ -825,8 +1087,18 @@ row_undo_mod(
return(DB_SUCCESS);
}
- node->index = dict_table_get_next_index(
- dict_table_get_first_index(node->table));
+ node->index = dict_table_get_first_index(node->table);
+ ut_ad(dict_index_is_clust(node->index));
+
+ if (dict_index_is_online_ddl(node->index)) {
+ /* Note that we are rolling back this transaction, so
+ that all inserts and updates with this DB_TRX_ID can
+ be skipped. */
+ row_log_table_rollback(node->index, node->trx->id);
+ }
+
+ /* Skip the clustered index (the first index) */
+ node->index = dict_table_get_next_index(node->index);
/* Skip all corrupted secondary index */
dict_table_skip_corrupt_index(node->index);
@@ -851,7 +1123,7 @@ row_undo_mod(
err = row_undo_mod_clust(node, thr);
}
- dict_table_close(node->table, dict_locked);
+ dict_table_close(node->table, dict_locked, FALSE);
node->table = NULL;