1
/******************************************************
6
Created 2/27/1997 Heikki Tuuri
7
*******************************************************/
12
#include "row0umod.ic"
15
#include "dict0dict.h"
16
#include "dict0boot.h"
20
#include "mach0data.h"
30
/* Considerations on undoing a modify operation.
31
(1) Undoing a delete marking: all index records should be found. Some of
32
them may have delete mark already FALSE, if the delete mark operation was
33
stopped underway, or if the undo operation ended prematurely because of a
35
(2) Undoing an update of a delete unmarked record: the newer version of
36
an updated secondary index entry should be removed if no prior version
37
of the clustered index record requires its existence. Otherwise, it should
39
(3) Undoing an update of a delete marked record. In this kind of update a
40
delete marked clustered index record was delete unmarked and possibly also
41
some of its fields were changed. Now, it is possible that the delete marked
42
version has become obsolete at the time the undo is started. */
44
/***************************************************************
45
Checks if also the previous version of the clustered index record was
46
modified or inserted by the same transaction, and its undo number is such
47
that it should be undone in the same rollback. */
50
row_undo_mod_undo_also_prev_vers(
51
/*=============================*/
52
/* out: TRUE if also previous modify or
53
insert of this row should be undone */
54
undo_node_t* node, /* in: row undo node */
55
dulint* undo_no)/* out: the undo number */
57
trx_undo_rec_t* undo_rec;
62
if (0 != ut_dulint_cmp(node->new_trx_id, trx->id)) {
64
*undo_no = ut_dulint_zero;
68
undo_rec = trx_undo_get_undo_rec_low(node->new_roll_ptr, node->heap);
70
*undo_no = trx_undo_rec_get_undo_no(undo_rec);
72
return(ut_dulint_cmp(trx->roll_limit, *undo_no) <= 0);
75
/***************************************************************
76
Undoes a modify in a clustered index record. */
79
row_undo_mod_clust_low(
80
/*===================*/
81
/* out: DB_SUCCESS, DB_FAIL, or error code:
82
we may run out of file space */
83
undo_node_t* node, /* in: row undo node */
84
que_thr_t* thr, /* in: query thread */
85
mtr_t* mtr, /* in: mtr; must be committed before
86
latching any further pages */
87
ulint mode) /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
95
btr_cur = btr_pcur_get_btr_cur(pcur);
97
success = btr_pcur_restore_position(mode, pcur, mtr);
101
if (mode == BTR_MODIFY_LEAF) {
103
err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG
104
| BTR_NO_UNDO_LOG_FLAG
106
btr_cur, node->update,
107
node->cmpl_info, thr, mtr);
109
mem_heap_t* heap = NULL;
110
big_rec_t* dummy_big_rec;
112
ut_ad(mode == BTR_MODIFY_TREE);
114
err = btr_cur_pessimistic_update(
116
| BTR_NO_UNDO_LOG_FLAG
118
btr_cur, &heap, &dummy_big_rec, node->update,
119
node->cmpl_info, thr, mtr);
121
ut_a(!dummy_big_rec);
122
if (UNIV_LIKELY_NULL(heap)) {
130
/***************************************************************
131
Removes a clustered index record after undo if possible. */
134
row_undo_mod_remove_clust_low(
135
/*==========================*/
136
/* out: DB_SUCCESS, DB_FAIL, or error code:
137
we may run out of file space */
138
undo_node_t* node, /* in: row undo node */
139
que_thr_t* thr __attribute__((unused)), /* in: query thread */
140
mtr_t* mtr, /* in: mtr */
141
ulint mode) /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
148
pcur = &(node->pcur);
149
btr_cur = btr_pcur_get_btr_cur(pcur);
151
success = btr_pcur_restore_position(mode, pcur, mtr);
158
/* Find out if we can remove the whole clustered index record */
160
if (node->rec_type == TRX_UNDO_UPD_DEL_REC
161
&& !row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
163
/* Ok, we can remove */
168
if (mode == BTR_MODIFY_LEAF) {
169
success = btr_cur_optimistic_delete(btr_cur, mtr);
177
ut_ad(mode == BTR_MODIFY_TREE);
179
/* Note that since this operation is analogous to purge,
180
we can free also inherited externally stored fields:
181
hence the last FALSE in the call below */
183
btr_cur_pessimistic_delete(&err, FALSE, btr_cur, FALSE, mtr);
185
/* The delete operation may fail if we have little
186
file space left: TODO: easiest to crash the database
187
and restart with more file space */
193
/***************************************************************
194
Undoes a modify in a clustered index record. Sets also the node state for the
195
next round of undo. */
200
/* out: DB_SUCCESS or error code: we may run
202
undo_node_t* node, /* in: row undo node */
203
que_thr_t* thr) /* in: query thread */
214
/* Check if also the previous version of the clustered index record
215
should be undone in this same rollback operation */
217
more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no);
219
pcur = &(node->pcur);
223
/* Try optimistic processing of the record, keeping changes within
226
err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);
228
if (err != DB_SUCCESS) {
229
btr_pcur_commit_specify_mtr(pcur, &mtr);
231
/* We may have to modify tree structure: do a pessimistic
232
descent down the index tree */
236
err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
239
btr_pcur_commit_specify_mtr(pcur, &mtr);
241
if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
245
err = row_undo_mod_remove_clust_low(node, thr, &mtr,
247
if (err != DB_SUCCESS) {
248
btr_pcur_commit_specify_mtr(pcur, &mtr);
250
/* We may have to modify tree structure: do a
251
pessimistic descent down the index tree */
255
err = row_undo_mod_remove_clust_low(node, thr, &mtr,
259
btr_pcur_commit_specify_mtr(pcur, &mtr);
262
node->state = UNDO_NODE_FETCH_NEXT;
264
trx_undo_rec_release(node->trx, node->undo_no);
266
if (more_vers && err == DB_SUCCESS) {
268
/* Reserve the undo log record to the prior version after
269
committing &mtr: this is necessary to comply with the latching
270
order, as &mtr may contain the fsp latch which is lower in
271
the latch hierarchy than trx->undo_mutex. */
273
success = trx_undo_rec_reserve(node->trx, new_undo_no);
276
node->state = UNDO_NODE_PREV_VERS;
283
/***************************************************************
284
Delete marks or removes a secondary index entry if found. */
287
row_undo_mod_del_mark_or_remove_sec_low(
288
/*====================================*/
289
/* out: DB_SUCCESS, DB_FAIL, or
290
DB_OUT_OF_FILE_SPACE */
291
undo_node_t* node, /* in: row undo node */
292
que_thr_t* thr, /* in: query thread */
293
dict_index_t* index, /* in: index */
294
dtuple_t* entry, /* in: index entry */
295
ulint mode) /* in: latch mode BTR_MODIFY_LEAF or
310
found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
312
btr_cur = btr_pcur_get_btr_cur(&pcur);
317
btr_pcur_close(&pcur);
323
/* We should remove the index record if no prior version of the row,
324
which cannot be purged yet, requires its existence. If some requires,
325
we should delete mark the record. */
327
mtr_start(&mtr_vers);
329
success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
333
old_has = row_vers_old_has_index_entry(FALSE,
334
btr_pcur_get_rec(&(node->pcur)),
335
&mtr_vers, index, entry);
337
err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
338
btr_cur, TRUE, thr, &mtr);
339
ut_ad(err == DB_SUCCESS);
341
/* Remove the index record */
343
if (mode == BTR_MODIFY_LEAF) {
344
success = btr_cur_optimistic_delete(btr_cur, &mtr);
351
ut_ad(mode == BTR_MODIFY_TREE);
353
btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
356
/* The delete operation may fail if we have little
357
file space left: TODO: easiest to crash the database
358
and restart with more file space */
362
btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
363
btr_pcur_close(&pcur);
369
/***************************************************************
370
Delete marks or removes a secondary index entry if found.
371
NOTE that if we updated the fields of a delete-marked secondary index record
372
so that alphabetically they stayed the same, e.g., 'abc' -> 'aBc', we cannot
373
return to the original values because we do not know them. But this should
374
not cause problems because in row0sel.c, in queries we always retrieve the
375
clustered index record or an earlier version of it, if the secondary index
376
record through which we do the search is delete-marked. */
379
row_undo_mod_del_mark_or_remove_sec(
380
/*================================*/
381
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
382
undo_node_t* node, /* in: row undo node */
383
que_thr_t* thr, /* in: query thread */
384
dict_index_t* index, /* in: index */
385
dtuple_t* entry) /* in: index entry */
389
err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
390
entry, BTR_MODIFY_LEAF);
391
if (err == DB_SUCCESS) {
396
err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
397
entry, BTR_MODIFY_TREE);
401
/***************************************************************
402
Delete unmarks a secondary index entry which must be found. It might not be
403
delete-marked at the moment, but it does not harm to unmark it anyway. We also
404
need to update the fields of the secondary index record if we updated its
405
fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'. */
408
row_undo_mod_del_unmark_sec_and_undo_update(
409
/*========================================*/
410
/* out: DB_FAIL or DB_SUCCESS or
411
DB_OUT_OF_FILE_SPACE */
412
ulint mode, /* in: search mode: BTR_MODIFY_LEAF or
414
que_thr_t* thr, /* in: query thread */
415
dict_index_t* index, /* in: index */
416
dtuple_t* entry) /* in: index entry */
421
ulint err = DB_SUCCESS;
422
big_rec_t* dummy_big_rec;
424
trx_t* trx = thr_get_trx(thr);
429
/* Ignore indexes that are being created. */
430
if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {
435
if (UNIV_UNLIKELY(!row_search_index_entry(index, entry,
436
mode, &pcur, &mtr))) {
437
fputs("InnoDB: error in sec index entry del undo in\n"
439
dict_index_name_print(stderr, trx, index);
441
"InnoDB: tuple ", stderr);
442
dtuple_print(stderr, entry);
444
"InnoDB: record ", stderr);
445
rec_print(stderr, btr_pcur_get_rec(&pcur), index);
447
trx_print(stderr, trx, 0);
449
"InnoDB: Submit a detailed bug report"
450
" to http://bugs.mysql.com\n", stderr);
452
btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur);
454
err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
455
btr_cur, FALSE, thr, &mtr);
456
ut_a(err == DB_SUCCESS);
457
heap = mem_heap_create(100);
459
update = row_upd_build_sec_rec_difference_binary(
460
index, entry, btr_cur_get_rec(btr_cur), trx, heap);
461
if (upd_get_n_fields(update) == 0) {
465
} else if (mode == BTR_MODIFY_LEAF) {
466
/* Try an optimistic updating of the record, keeping
467
changes within the page */
469
err = btr_cur_optimistic_update(
470
BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
471
btr_cur, update, 0, thr, &mtr);
475
case DB_ZIP_OVERFLOW:
479
ut_a(mode == BTR_MODIFY_TREE);
480
err = btr_cur_pessimistic_update(
481
BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
482
btr_cur, &heap, &dummy_big_rec,
483
update, 0, thr, &mtr);
484
ut_a(!dummy_big_rec);
490
btr_pcur_close(&pcur);
496
/***************************************************************
497
Undoes a modify in secondary indexes when undo record type is UPD_DEL. */
500
row_undo_mod_upd_del_sec(
501
/*=====================*/
502
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
503
undo_node_t* node, /* in: row undo node */
504
que_thr_t* thr) /* in: query thread */
511
heap = mem_heap_create(1024);
513
while (node->index != NULL) {
516
entry = row_build_index_entry(node->row, node->ext,
519
err = row_undo_mod_del_mark_or_remove_sec(node, thr, index,
521
if (err != DB_SUCCESS) {
528
node->index = dict_table_get_next_index(node->index);
536
/***************************************************************
537
Undoes a modify in secondary indexes when undo record type is DEL_MARK. */
540
row_undo_mod_del_mark_sec(
541
/*======================*/
542
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
543
undo_node_t* node, /* in: row undo node */
544
que_thr_t* thr) /* in: query thread */
551
heap = mem_heap_create(1024);
553
while (node->index != NULL) {
556
entry = row_build_index_entry(node->row, node->ext,
559
err = row_undo_mod_del_unmark_sec_and_undo_update(
560
BTR_MODIFY_LEAF, thr, index, entry);
561
if (err == DB_FAIL) {
562
err = row_undo_mod_del_unmark_sec_and_undo_update(
563
BTR_MODIFY_TREE, thr, index, entry);
566
if (err != DB_SUCCESS) {
573
node->index = dict_table_get_next_index(node->index);
581
/***************************************************************
582
Undoes a modify in secondary indexes when undo record type is UPD_EXIST. */
585
row_undo_mod_upd_exist_sec(
586
/*=======================*/
587
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
588
undo_node_t* node, /* in: row undo node */
589
que_thr_t* thr) /* in: query thread */
596
if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
597
/* No change in secondary indexes */
602
heap = mem_heap_create(1024);
604
while (node->index != NULL) {
607
if (row_upd_changes_ord_field_binary(node->row, node->index,
610
/* Build the newest version of the index entry */
611
entry = row_build_index_entry(node->row, node->ext,
614
/* NOTE that if we updated the fields of a
615
delete-marked secondary index record so that
616
alphabetically they stayed the same, e.g.,
617
'abc' -> 'aBc', we cannot return to the original
618
values because we do not know them. But this should
619
not cause problems because in row0sel.c, in queries
620
we always retrieve the clustered index record or an
621
earlier version of it, if the secondary index record
622
through which we do the search is delete-marked. */
624
err = row_undo_mod_del_mark_or_remove_sec(node, thr,
627
if (err != DB_SUCCESS) {
633
/* We may have to update the delete mark in the
634
secondary index record of the previous version of
635
the row. We also need to update the fields of
636
the secondary index record if we updated its fields
637
but alphabetically they stayed the same, e.g.,
639
mem_heap_empty(heap);
640
entry = row_build_index_entry(node->undo_row,
645
err = row_undo_mod_del_unmark_sec_and_undo_update(
646
BTR_MODIFY_LEAF, thr, index, entry);
647
if (err == DB_FAIL) {
648
err = row_undo_mod_del_unmark_sec_and_undo_update(
649
BTR_MODIFY_TREE, thr, index, entry);
652
if (err != DB_SUCCESS) {
659
node->index = dict_table_get_next_index(node->index);
667
/***************************************************************
668
Parses the row reference and other info in a modify undo log record. */
671
row_undo_mod_parse_undo_rec(
672
/*========================*/
673
undo_node_t* node, /* in: row undo node */
674
que_thr_t* thr) /* in: query thread */
676
dict_index_t* clust_index;
689
trx = thr_get_trx(thr);
690
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
691
&dummy_extern, &undo_no, &table_id);
692
node->rec_type = type;
694
node->table = dict_table_get_on_id(table_id, trx);
696
/* TODO: other fixes associated with DROP TABLE + rollback in the
697
same table by another user */
699
if (node->table == NULL) {
700
/* Table was dropped */
704
if (node->table->ibd_file_missing) {
705
/* We skip undo operations to missing .ibd files */
711
clust_index = dict_table_get_first_index(node->table);
713
ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
716
ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
719
trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
720
roll_ptr, info_bits, trx,
721
node->heap, &(node->update));
722
node->new_roll_ptr = roll_ptr;
723
node->new_trx_id = trx_id;
724
node->cmpl_info = cmpl_info;
727
/***************************************************************
728
Undoes a modify operation on a row of a table. */
733
/* out: DB_SUCCESS or error code */
734
undo_node_t* node, /* in: row undo node */
735
que_thr_t* thr) /* in: query thread */
740
ut_ad(node->state == UNDO_NODE_MODIFY);
742
row_undo_mod_parse_undo_rec(node, thr);
744
if (!node->table || !row_undo_search_clust_to_pcur(node)) {
745
/* It is already undone, or will be undone by another query
746
thread, or table was dropped */
748
trx_undo_rec_release(node->trx, node->undo_no);
749
node->state = UNDO_NODE_FETCH_NEXT;
754
node->index = dict_table_get_next_index(
755
dict_table_get_first_index(node->table));
757
if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
759
err = row_undo_mod_upd_exist_sec(node, thr);
761
} else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
763
err = row_undo_mod_del_mark_sec(node, thr);
765
ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
766
err = row_undo_mod_upd_del_sec(node, thr);
769
if (err != DB_SUCCESS) {
774
err = row_undo_mod_clust(node, thr);