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 */
86
ulint mode) /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
88
big_rec_t* dummy_big_rec;
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
ut_ad(mode == BTR_MODIFY_TREE);
111
err = btr_cur_pessimistic_update(
113
| BTR_NO_UNDO_LOG_FLAG
115
btr_cur, &dummy_big_rec, node->update,
116
node->cmpl_info, thr, mtr);
122
/***************************************************************
123
Removes a clustered index record after undo if possible. */
126
row_undo_mod_remove_clust_low(
127
/*==========================*/
128
/* out: DB_SUCCESS, DB_FAIL, or error code:
129
we may run out of file space */
130
undo_node_t* node, /* in: row undo node */
131
que_thr_t* thr __attribute__((unused)), /* in: query thread */
132
mtr_t* mtr, /* in: mtr */
133
ulint mode) /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
140
pcur = &(node->pcur);
141
btr_cur = btr_pcur_get_btr_cur(pcur);
143
success = btr_pcur_restore_position(mode, pcur, mtr);
150
/* Find out if we can remove the whole clustered index record */
152
if (node->rec_type == TRX_UNDO_UPD_DEL_REC
153
&& !row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
155
/* Ok, we can remove */
160
if (mode == BTR_MODIFY_LEAF) {
161
success = btr_cur_optimistic_delete(btr_cur, mtr);
169
ut_ad(mode == BTR_MODIFY_TREE);
171
/* Note that since this operation is analogous to purge,
172
we can free also inherited externally stored fields:
173
hence the last FALSE in the call below */
175
btr_cur_pessimistic_delete(&err, FALSE, btr_cur, FALSE, mtr);
177
/* The delete operation may fail if we have little
178
file space left: TODO: easiest to crash the database
179
and restart with more file space */
185
/***************************************************************
186
Undoes a modify in a clustered index record. Sets also the node state for the
187
next round of undo. */
192
/* out: DB_SUCCESS or error code: we may run
194
undo_node_t* node, /* in: row undo node */
195
que_thr_t* thr) /* in: query thread */
206
/* Check if also the previous version of the clustered index record
207
should be undone in this same rollback operation */
209
more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no);
211
pcur = &(node->pcur);
215
/* Try optimistic processing of the record, keeping changes within
218
err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);
220
if (err != DB_SUCCESS) {
221
btr_pcur_commit_specify_mtr(pcur, &mtr);
223
/* We may have to modify tree structure: do a pessimistic
224
descent down the index tree */
228
err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
231
btr_pcur_commit_specify_mtr(pcur, &mtr);
233
if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
237
err = row_undo_mod_remove_clust_low(node, thr, &mtr,
239
if (err != DB_SUCCESS) {
240
btr_pcur_commit_specify_mtr(pcur, &mtr);
242
/* We may have to modify tree structure: do a
243
pessimistic descent down the index tree */
247
err = row_undo_mod_remove_clust_low(node, thr, &mtr,
251
btr_pcur_commit_specify_mtr(pcur, &mtr);
254
node->state = UNDO_NODE_FETCH_NEXT;
256
trx_undo_rec_release(node->trx, node->undo_no);
258
if (more_vers && err == DB_SUCCESS) {
260
/* Reserve the undo log record to the prior version after
261
committing &mtr: this is necessary to comply with the latching
262
order, as &mtr may contain the fsp latch which is lower in
263
the latch hierarchy than trx->undo_mutex. */
265
success = trx_undo_rec_reserve(node->trx, new_undo_no);
268
node->state = UNDO_NODE_PREV_VERS;
275
/***************************************************************
276
Delete marks or removes a secondary index entry if found. */
279
row_undo_mod_del_mark_or_remove_sec_low(
280
/*====================================*/
281
/* out: DB_SUCCESS, DB_FAIL, or
282
DB_OUT_OF_FILE_SPACE */
283
undo_node_t* node, /* in: row undo node */
284
que_thr_t* thr, /* in: query thread */
285
dict_index_t* index, /* in: index */
286
dtuple_t* entry, /* in: index entry */
287
ulint mode) /* in: latch mode BTR_MODIFY_LEAF or
302
found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
304
btr_cur = btr_pcur_get_btr_cur(&pcur);
309
btr_pcur_close(&pcur);
315
/* We should remove the index record if no prior version of the row,
316
which cannot be purged yet, requires its existence. If some requires,
317
we should delete mark the record. */
319
mtr_start(&mtr_vers);
321
success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
325
old_has = row_vers_old_has_index_entry(FALSE,
326
btr_pcur_get_rec(&(node->pcur)),
327
&mtr_vers, index, entry);
329
err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
330
btr_cur, TRUE, thr, &mtr);
331
ut_ad(err == DB_SUCCESS);
333
/* Remove the index record */
335
if (mode == BTR_MODIFY_LEAF) {
336
success = btr_cur_optimistic_delete(btr_cur, &mtr);
343
ut_ad(mode == BTR_MODIFY_TREE);
345
btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
348
/* The delete operation may fail if we have little
349
file space left: TODO: easiest to crash the database
350
and restart with more file space */
354
btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
355
btr_pcur_close(&pcur);
361
/***************************************************************
362
Delete marks or removes a secondary index entry if found.
363
NOTE that if we updated the fields of a delete-marked secondary index record
364
so that alphabetically they stayed the same, e.g., 'abc' -> 'aBc', we cannot
365
return to the original values because we do not know them. But this should
366
not cause problems because in row0sel.c, in queries we always retrieve the
367
clustered index record or an earlier version of it, if the secondary index
368
record through which we do the search is delete-marked. */
371
row_undo_mod_del_mark_or_remove_sec(
372
/*================================*/
373
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
374
undo_node_t* node, /* in: row undo node */
375
que_thr_t* thr, /* in: query thread */
376
dict_index_t* index, /* in: index */
377
dtuple_t* entry) /* in: index entry */
381
err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
382
entry, BTR_MODIFY_LEAF);
383
if (err == DB_SUCCESS) {
388
err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
389
entry, BTR_MODIFY_TREE);
393
/***************************************************************
394
Delete unmarks a secondary index entry which must be found. It might not be
395
delete-marked at the moment, but it does not harm to unmark it anyway. We also
396
need to update the fields of the secondary index record if we updated its
397
fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'. */
400
row_undo_mod_del_unmark_sec_and_undo_update(
401
/*========================================*/
402
/* out: DB_FAIL or DB_SUCCESS or
403
DB_OUT_OF_FILE_SPACE */
404
ulint mode, /* in: search mode: BTR_MODIFY_LEAF or
406
que_thr_t* thr, /* in: query thread */
407
dict_index_t* index, /* in: index */
408
dtuple_t* entry) /* in: index entry */
413
ulint err = DB_SUCCESS;
415
big_rec_t* dummy_big_rec;
417
trx_t* trx = thr_get_trx(thr);
422
found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
425
fputs("InnoDB: error in sec index entry del undo in\n"
427
dict_index_name_print(stderr, trx, index);
429
"InnoDB: tuple ", stderr);
430
dtuple_print(stderr, entry);
432
"InnoDB: record ", stderr);
433
rec_print(stderr, btr_pcur_get_rec(&pcur), index);
435
trx_print(stderr, trx, 0);
437
"InnoDB: Submit a detailed bug report"
438
" to http://bugs.mysql.com\n", stderr);
440
btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur);
442
err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
443
btr_cur, FALSE, thr, &mtr);
444
ut_a(err == DB_SUCCESS);
445
heap = mem_heap_create(100);
447
update = row_upd_build_sec_rec_difference_binary(
448
index, entry, btr_cur_get_rec(btr_cur), trx, heap);
449
if (upd_get_n_fields(update) == 0) {
453
} else if (mode == BTR_MODIFY_LEAF) {
454
/* Try an optimistic updating of the record, keeping
455
changes within the page */
457
err = btr_cur_optimistic_update(
458
BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
459
btr_cur, update, 0, thr, &mtr);
460
if (err == DB_OVERFLOW || err == DB_UNDERFLOW) {
464
ut_a(mode == BTR_MODIFY_TREE);
465
err = btr_cur_pessimistic_update(
466
BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
467
btr_cur, &dummy_big_rec,
468
update, 0, thr, &mtr);
474
btr_pcur_close(&pcur);
480
/***************************************************************
481
Undoes a modify in secondary indexes when undo record type is UPD_DEL. */
484
row_undo_mod_upd_del_sec(
485
/*=====================*/
486
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
487
undo_node_t* node, /* in: row undo node */
488
que_thr_t* thr) /* in: query thread */
495
heap = mem_heap_create(1024);
497
while (node->index != NULL) {
500
entry = row_build_index_entry(node->row, index, heap);
502
err = row_undo_mod_del_mark_or_remove_sec(node, thr, index,
504
if (err != DB_SUCCESS) {
511
node->index = dict_table_get_next_index(node->index);
519
/***************************************************************
520
Undoes a modify in secondary indexes when undo record type is DEL_MARK. */
523
row_undo_mod_del_mark_sec(
524
/*======================*/
525
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
526
undo_node_t* node, /* in: row undo node */
527
que_thr_t* thr) /* in: query thread */
534
heap = mem_heap_create(1024);
536
while (node->index != NULL) {
539
entry = row_build_index_entry(node->row, index, heap);
541
err = row_undo_mod_del_unmark_sec_and_undo_update(
542
BTR_MODIFY_LEAF, thr, index, entry);
543
if (err == DB_FAIL) {
544
err = row_undo_mod_del_unmark_sec_and_undo_update(
545
BTR_MODIFY_TREE, thr, index, entry);
548
if (err != DB_SUCCESS) {
555
node->index = dict_table_get_next_index(node->index);
563
/***************************************************************
564
Undoes a modify in secondary indexes when undo record type is UPD_EXIST. */
567
row_undo_mod_upd_exist_sec(
568
/*=======================*/
569
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
570
undo_node_t* node, /* in: row undo node */
571
que_thr_t* thr) /* in: query thread */
578
if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
579
/* No change in secondary indexes */
584
heap = mem_heap_create(1024);
586
while (node->index != NULL) {
589
if (row_upd_changes_ord_field_binary(node->row, node->index,
592
/* Build the newest version of the index entry */
593
entry = row_build_index_entry(node->row, index, heap);
595
/* NOTE that if we updated the fields of a
596
delete-marked secondary index record so that
597
alphabetically they stayed the same, e.g.,
598
'abc' -> 'aBc', we cannot return to the original
599
values because we do not know them. But this should
600
not cause problems because in row0sel.c, in queries
601
we always retrieve the clustered index record or an
602
earlier version of it, if the secondary index record
603
through which we do the search is delete-marked. */
605
err = row_undo_mod_del_mark_or_remove_sec(node, thr,
608
if (err != DB_SUCCESS) {
614
/* We may have to update the delete mark in the
615
secondary index record of the previous version of
616
the row. We also need to update the fields of
617
the secondary index record if we updated its fields
618
but alphabetically they stayed the same, e.g.,
621
row_upd_index_replace_new_col_vals(entry, index,
623
err = row_undo_mod_del_unmark_sec_and_undo_update(
624
BTR_MODIFY_LEAF, thr, index, entry);
625
if (err == DB_FAIL) {
626
err = row_undo_mod_del_unmark_sec_and_undo_update(
627
BTR_MODIFY_TREE, thr, index, entry);
630
if (err != DB_SUCCESS) {
637
node->index = dict_table_get_next_index(node->index);
645
/***************************************************************
646
Parses the row reference and other info in a modify undo log record. */
649
row_undo_mod_parse_undo_rec(
650
/*========================*/
651
undo_node_t* node, /* in: row undo node */
652
que_thr_t* thr) /* in: query thread */
654
dict_index_t* clust_index;
667
trx = thr_get_trx(thr);
668
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
669
&dummy_extern, &undo_no, &table_id);
670
node->rec_type = type;
672
node->table = dict_table_get_on_id(table_id, trx);
674
/* TODO: other fixes associated with DROP TABLE + rollback in the
675
same table by another user */
677
if (node->table == NULL) {
678
/* Table was dropped */
682
if (node->table->ibd_file_missing) {
683
/* We skip undo operations to missing .ibd files */
689
clust_index = dict_table_get_first_index(node->table);
691
ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
694
ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
697
trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
698
roll_ptr, info_bits, trx,
699
node->heap, &(node->update));
700
node->new_roll_ptr = roll_ptr;
701
node->new_trx_id = trx_id;
702
node->cmpl_info = cmpl_info;
705
/***************************************************************
706
Undoes a modify operation on a row of a table. */
711
/* out: DB_SUCCESS or error code */
712
undo_node_t* node, /* in: row undo node */
713
que_thr_t* thr) /* in: query thread */
719
ut_ad(node->state == UNDO_NODE_MODIFY);
721
row_undo_mod_parse_undo_rec(node, thr);
723
if (node->table == NULL) {
726
found = row_undo_search_clust_to_pcur(node);
730
/* It is already undone, or will be undone by another query
731
thread, or table was dropped */
733
trx_undo_rec_release(node->trx, node->undo_no);
734
node->state = UNDO_NODE_FETCH_NEXT;
739
node->index = dict_table_get_next_index(
740
dict_table_get_first_index(node->table));
742
if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
744
err = row_undo_mod_upd_exist_sec(node, thr);
746
} else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
748
err = row_undo_mod_del_mark_sec(node, thr);
750
ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
751
err = row_undo_mod_upd_del_sec(node, thr);
754
if (err != DB_SUCCESS) {
759
err = row_undo_mod_clust(node, thr);