1 /*****************************************************************************
2
3 Copyright (c) 1997, 2013, Oracle and/or its affiliates. All Rights Reserved.
4
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
8
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16
17 *****************************************************************************/
18
19 /**************************************************//**
20 @file row/row0umod.c
21 Undo modify of a row
22
23 Created 2/27/1997 Heikki Tuuri
24 *******************************************************/
25
26 #include "row0umod.h"
27
28 #ifdef UNIV_NONINL
29 #include "row0umod.ic"
30 #endif
31
32 #include "dict0dict.h"
33 #include "dict0boot.h"
34 #include "trx0undo.h"
35 #include "trx0roll.h"
36 #include "btr0btr.h"
37 #include "mach0data.h"
38 #include "row0undo.h"
39 #include "row0vers.h"
40 #include "trx0trx.h"
41 #include "trx0rec.h"
42 #include "row0row.h"
43 #include "row0upd.h"
44 #include "que0que.h"
45 #include "log0log.h"
46
47 /* Considerations on undoing a modify operation.
48 (1) Undoing a delete marking: all index records should be found. Some of
49 them may have delete mark already FALSE, if the delete mark operation was
50 stopped underway, or if the undo operation ended prematurely because of a
51 system crash.
52 (2) Undoing an update of a delete unmarked record: the newer version of
53 an updated secondary index entry should be removed if no prior version
54 of the clustered index record requires its existence. Otherwise, it should
55 be delete marked.
56 (3) Undoing an update of a delete marked record. In this kind of update a
57 delete marked clustered index record was delete unmarked and possibly also
58 some of its fields were changed. Now, it is possible that the delete marked
59 version has become obsolete at the time the undo is started. */
60
61 /*************************************************************************
62 IMPORTANT NOTE: Any operation that generates redo MUST check that there
63 is enough space in the redo log before for that operation. This is
64 done by calling log_free_check(). The reason for checking the
65 availability of the redo log space before the start of the operation is
66 that we MUST not hold any synchonization objects when performing the
67 check.
68 If you make a change in this module make sure that no codepath is
69 introduced where a call to log_free_check() is bypassed. */
70
71 /***********************************************************//**
72 Undoes a modify in a clustered index record.
73 @return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
74 static
75 ulint
row_undo_mod_clust_low(undo_node_t * node,que_thr_t * thr,mtr_t * mtr,ulint mode)76 row_undo_mod_clust_low(
77 /*===================*/
78 undo_node_t* node, /*!< in: row undo node */
79 que_thr_t* thr, /*!< in: query thread */
80 mtr_t* mtr, /*!< in: mtr; must be committed before
81 latching any further pages */
82 ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
83 {
84 btr_pcur_t* pcur;
85 btr_cur_t* btr_cur;
86 ulint err;
87 #ifdef UNIV_DEBUG
88 ibool success;
89 #endif /* UNIV_DEBUG */
90
91 pcur = &(node->pcur);
92 btr_cur = btr_pcur_get_btr_cur(pcur);
93
94 #ifdef UNIV_DEBUG
95 success =
96 #endif /* UNIV_DEBUG */
97 btr_pcur_restore_position(mode, pcur, mtr);
98
99 ut_ad(success);
100
101 if (mode == BTR_MODIFY_LEAF) {
102
103 err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG
104 | BTR_NO_UNDO_LOG_FLAG
105 | BTR_KEEP_SYS_FLAG,
106 btr_cur, node->update,
107 node->cmpl_info, thr, mtr);
108 } else {
109 mem_heap_t* heap = NULL;
110 big_rec_t* dummy_big_rec;
111
112 ut_ad(mode == BTR_MODIFY_TREE);
113
114 err = btr_cur_pessimistic_update(
115 BTR_NO_LOCKING_FLAG
116 | BTR_NO_UNDO_LOG_FLAG
117 | BTR_KEEP_SYS_FLAG,
118 btr_cur, &heap, &dummy_big_rec, node->update,
119 node->cmpl_info, thr, mtr);
120
121 ut_a(!dummy_big_rec);
122 if (UNIV_LIKELY_NULL(heap)) {
123 mem_heap_free(heap);
124 }
125 }
126
127 return(err);
128 }
129
130 /***********************************************************//**
131 Purges a clustered index record after undo if possible.
132 This is attempted when the record was inserted by updating a
133 delete-marked record and there no longer exist transactions
134 that would see the delete-marked record.
135 @return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
136 static
137 ulint
row_undo_mod_remove_clust_low(undo_node_t * node,que_thr_t * thr,mtr_t * mtr,ulint mode)138 row_undo_mod_remove_clust_low(
139 /*==========================*/
140 undo_node_t* node, /*!< in: row undo node */
141 que_thr_t* thr, /*!< in: query thread */
142 mtr_t* mtr, /*!< in/out: mini-transaction */
143 ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
144 {
145 btr_cur_t* btr_cur;
146 ulint err;
147 ulint trx_id_offset;
148
149 ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
150
151 /* Find out if the record has been purged already
152 or if we can remove it. */
153
154 if (!btr_pcur_restore_position(mode, &node->pcur, mtr)
155 || row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
156
157 return(DB_SUCCESS);
158 }
159
160 btr_cur = btr_pcur_get_btr_cur(&node->pcur);
161
162 trx_id_offset = btr_cur_get_index(btr_cur)->trx_id_offset;
163
164 if (!trx_id_offset) {
165 mem_heap_t* heap = NULL;
166 ulint trx_id_col;
167 ulint* offsets;
168 ulint len;
169
170 trx_id_col = dict_index_get_sys_col_pos(
171 btr_cur_get_index(btr_cur), DATA_TRX_ID);
172 ut_ad(trx_id_col > 0);
173 ut_ad(trx_id_col != ULINT_UNDEFINED);
174
175 offsets = rec_get_offsets(
176 btr_cur_get_rec(btr_cur), btr_cur_get_index(btr_cur),
177 NULL, trx_id_col + 1, &heap);
178
179 trx_id_offset = rec_get_nth_field_offs(
180 offsets, trx_id_col, &len);
181 ut_ad(len == DATA_TRX_ID_LEN);
182 mem_heap_free(heap);
183 }
184
185 if (trx_read_trx_id(btr_cur_get_rec(btr_cur) + trx_id_offset)
186 != node->new_trx_id) {
187 /* The record must have been purged and then replaced
188 with a different one. */
189 return(DB_SUCCESS);
190 }
191
192 /* We are about to remove an old, delete-marked version of the
193 record that may have been delete-marked by a different transaction
194 than the rolling-back one. */
195 ut_ad(rec_get_deleted_flag(btr_cur_get_rec(btr_cur),
196 dict_table_is_comp(node->table)));
197
198 if (mode == BTR_MODIFY_LEAF) {
199 err = btr_cur_optimistic_delete(btr_cur, mtr)
200 ? DB_SUCCESS
201 : DB_FAIL;
202 } else {
203 ut_ad(mode == BTR_MODIFY_TREE);
204
205 /* This operation is analogous to purge, we can free also
206 inherited externally stored fields */
207
208 btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
209 thr_is_recv(thr)
210 ? RB_RECOVERY_PURGE_REC
211 : RB_NONE, mtr);
212
213 /* The delete operation may fail if we have little
214 file space left: TODO: easiest to crash the database
215 and restart with more file space */
216 }
217
218 return(err);
219 }
220
221 /***********************************************************//**
222 Undoes a modify in a clustered index record. Sets also the node state for the
223 next round of undo.
224 @return DB_SUCCESS or error code: we may run out of file space */
225 static
226 ulint
row_undo_mod_clust(undo_node_t * node,que_thr_t * thr)227 row_undo_mod_clust(
228 /*===============*/
229 undo_node_t* node, /*!< in: row undo node */
230 que_thr_t* thr) /*!< in: query thread */
231 {
232 btr_pcur_t* pcur;
233 mtr_t mtr;
234 ulint err;
235
236 ut_ad(node && thr);
237
238 log_free_check();
239
240 pcur = &(node->pcur);
241
242 mtr_start(&mtr);
243
244 /* Try optimistic processing of the record, keeping changes within
245 the index page */
246
247 err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);
248
249 if (err != DB_SUCCESS) {
250 btr_pcur_commit_specify_mtr(pcur, &mtr);
251
252 /* We may have to modify tree structure: do a pessimistic
253 descent down the index tree */
254
255 mtr_start(&mtr);
256
257 err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
258 }
259
260 btr_pcur_commit_specify_mtr(pcur, &mtr);
261
262 if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
263
264 mtr_start(&mtr);
265
266 err = row_undo_mod_remove_clust_low(node, thr, &mtr,
267 BTR_MODIFY_LEAF);
268 if (err != DB_SUCCESS) {
269 btr_pcur_commit_specify_mtr(pcur, &mtr);
270
271 /* We may have to modify tree structure: do a
272 pessimistic descent down the index tree */
273
274 mtr_start(&mtr);
275
276 err = row_undo_mod_remove_clust_low(node, thr, &mtr,
277 BTR_MODIFY_TREE);
278 }
279
280 btr_pcur_commit_specify_mtr(pcur, &mtr);
281 }
282
283 node->state = UNDO_NODE_FETCH_NEXT;
284
285 trx_undo_rec_release(node->trx, node->undo_no);
286
287 return(err);
288 }
289
290 /***********************************************************//**
291 Delete marks or removes a secondary index entry if found.
292 @return DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */
293 static
294 ulint
row_undo_mod_del_mark_or_remove_sec_low(undo_node_t * node,que_thr_t * thr,dict_index_t * index,dtuple_t * entry,ulint mode)295 row_undo_mod_del_mark_or_remove_sec_low(
296 /*====================================*/
297 undo_node_t* node, /*!< in: row undo node */
298 que_thr_t* thr, /*!< in: query thread */
299 dict_index_t* index, /*!< in: index */
300 dtuple_t* entry, /*!< in: index entry */
301 ulint mode) /*!< in: latch mode BTR_MODIFY_LEAF or
302 BTR_MODIFY_TREE */
303 {
304 btr_pcur_t pcur;
305 btr_cur_t* btr_cur;
306 ibool success;
307 ibool old_has;
308 ulint err;
309 mtr_t mtr;
310 mtr_t mtr_vers;
311 enum row_search_result search_result;
312
313 log_free_check();
314 mtr_start(&mtr);
315
316 btr_cur = btr_pcur_get_btr_cur(&pcur);
317
318 ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
319
320 search_result = row_search_index_entry(index, entry, mode,
321 &pcur, &mtr);
322
323 switch (UNIV_EXPECT(search_result, ROW_FOUND)) {
324 case ROW_NOT_FOUND:
325 /* In crash recovery, the secondary index record may
326 be missing if the UPDATE did not have time to insert
327 the secondary index records before the crash. When we
328 are undoing that UPDATE in crash recovery, the record
329 may be missing.
330
331 In normal processing, if an update ends in a deadlock
332 before it has inserted all updated secondary index
333 records, then the undo will not find those records. */
334
335 err = DB_SUCCESS;
336 goto func_exit;
337 case ROW_FOUND:
338 break;
339 case ROW_BUFFERED:
340 case ROW_NOT_DELETED_REF:
341 /* These are invalid outcomes, because the mode passed
342 to row_search_index_entry() did not include any of the
343 flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
344 ut_error;
345 }
346
347 /* We should remove the index record if no prior version of the row,
348 which cannot be purged yet, requires its existence. If some requires,
349 we should delete mark the record. */
350
351 mtr_start(&mtr_vers);
352
353 success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
354 &mtr_vers);
355 ut_a(success);
356
357 old_has = row_vers_old_has_index_entry(FALSE,
358 btr_pcur_get_rec(&(node->pcur)),
359 &mtr_vers, index, entry);
360 if (old_has) {
361 err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
362 btr_cur, TRUE, thr, &mtr);
363 ut_ad(err == DB_SUCCESS);
364 } else {
365 /* Remove the index record */
366
367 if (mode == BTR_MODIFY_LEAF) {
368 success = btr_cur_optimistic_delete(btr_cur, &mtr);
369 if (success) {
370 err = DB_SUCCESS;
371 } else {
372 err = DB_FAIL;
373 }
374 } else {
375 ut_ad(mode == BTR_MODIFY_TREE);
376
377 /* No need to distinguish RB_RECOVERY_PURGE here,
378 because we are deleting a secondary index record:
379 the distinction between RB_NORMAL and
380 RB_RECOVERY_PURGE only matters when deleting a
381 record that contains externally stored
382 columns. */
383 ut_ad(!dict_index_is_clust(index));
384 btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
385 RB_NORMAL, &mtr);
386
387 /* The delete operation may fail if we have little
388 file space left: TODO: easiest to crash the database
389 and restart with more file space */
390 }
391 }
392
393 btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
394
395 func_exit:
396 btr_pcur_close(&pcur);
397 mtr_commit(&mtr);
398
399 return(err);
400 }
401
402 /***********************************************************//**
403 Delete marks or removes a secondary index entry if found.
404 NOTE that if we updated the fields of a delete-marked secondary index record
405 so that alphabetically they stayed the same, e.g., 'abc' -> 'aBc', we cannot
406 return to the original values because we do not know them. But this should
407 not cause problems because in row0sel.c, in queries we always retrieve the
408 clustered index record or an earlier version of it, if the secondary index
409 record through which we do the search is delete-marked.
410 @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
411 static
412 ulint
row_undo_mod_del_mark_or_remove_sec(undo_node_t * node,que_thr_t * thr,dict_index_t * index,dtuple_t * entry)413 row_undo_mod_del_mark_or_remove_sec(
414 /*================================*/
415 undo_node_t* node, /*!< in: row undo node */
416 que_thr_t* thr, /*!< in: query thread */
417 dict_index_t* index, /*!< in: index */
418 dtuple_t* entry) /*!< in: index entry */
419 {
420 ulint err;
421
422 err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
423 entry, BTR_MODIFY_LEAF);
424 if (err == DB_SUCCESS) {
425
426 return(err);
427 }
428
429 err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
430 entry, BTR_MODIFY_TREE);
431 return(err);
432 }
433
434 /***********************************************************//**
435 Delete unmarks a secondary index entry which must be found. It might not be
436 delete-marked at the moment, but it does not harm to unmark it anyway. We also
437 need to update the fields of the secondary index record if we updated its
438 fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'.
439 @return DB_FAIL or DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
440 static
441 ulint
row_undo_mod_del_unmark_sec_and_undo_update(ulint mode,que_thr_t * thr,dict_index_t * index,const dtuple_t * entry)442 row_undo_mod_del_unmark_sec_and_undo_update(
443 /*========================================*/
444 ulint mode, /*!< in: search mode: BTR_MODIFY_LEAF or
445 BTR_MODIFY_TREE */
446 que_thr_t* thr, /*!< in: query thread */
447 dict_index_t* index, /*!< in: index */
448 const dtuple_t* entry) /*!< in: index entry */
449 {
450 mem_heap_t* heap;
451 btr_pcur_t pcur;
452 btr_cur_t* btr_cur;
453 upd_t* update;
454 ulint err = DB_SUCCESS;
455 big_rec_t* dummy_big_rec;
456 mtr_t mtr;
457 trx_t* trx = thr_get_trx(thr);
458 enum row_search_result search_result;
459
460 /* Ignore indexes that are being created. */
461 if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {
462
463 return(DB_SUCCESS);
464 }
465
466 log_free_check();
467 mtr_start(&mtr);
468
469 ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
470
471 search_result = row_search_index_entry(index, entry, mode,
472 &pcur, &mtr);
473
474 switch (search_result) {
475 case ROW_BUFFERED:
476 case ROW_NOT_DELETED_REF:
477 /* These are invalid outcomes, because the mode passed
478 to row_search_index_entry() did not include any of the
479 flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
480 ut_error;
481 case ROW_NOT_FOUND:
482 fputs("InnoDB: error in sec index entry del undo in\n"
483 "InnoDB: ", stderr);
484 dict_index_name_print(stderr, trx, index);
485 fputs("\n"
486 "InnoDB: tuple ", stderr);
487 dtuple_print(stderr, entry);
488 fputs("\n"
489 "InnoDB: record ", stderr);
490 rec_print(stderr, btr_pcur_get_rec(&pcur), index);
491 putc('\n', stderr);
492 trx_print(stderr, trx, 0);
493 fputs("\n"
494 "InnoDB: Submit a detailed bug report"
495 " to http://bugs.mysql.com\n", stderr);
496 ut_ad(0);
497 break;
498 case ROW_FOUND:
499 btr_cur = btr_pcur_get_btr_cur(&pcur);
500 err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
501 btr_cur, FALSE, thr, &mtr);
502 ut_a(err == DB_SUCCESS);
503 heap = mem_heap_create(100);
504
505 update = row_upd_build_sec_rec_difference_binary(
506 index, entry, btr_cur_get_rec(btr_cur), trx, heap);
507 if (upd_get_n_fields(update) == 0) {
508
509 /* Do nothing */
510
511 } else if (mode == BTR_MODIFY_LEAF) {
512 /* Try an optimistic updating of the record, keeping
513 changes within the page */
514
515 err = btr_cur_optimistic_update(
516 BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
517 btr_cur, update, 0, thr, &mtr);
518 switch (err) {
519 case DB_OVERFLOW:
520 case DB_UNDERFLOW:
521 case DB_ZIP_OVERFLOW:
522 err = DB_FAIL;
523 }
524 } else {
525 ut_a(mode == BTR_MODIFY_TREE);
526 err = btr_cur_pessimistic_update(
527 BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
528 btr_cur, &heap, &dummy_big_rec,
529 update, 0, thr, &mtr);
530 ut_a(!dummy_big_rec);
531 }
532
533 mem_heap_free(heap);
534 }
535
536 btr_pcur_close(&pcur);
537 mtr_commit(&mtr);
538
539 return(err);
540 }
541
542 /***********************************************************//**
543 Undoes a modify in secondary indexes when undo record type is UPD_DEL.
544 @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
545 static
546 ulint
row_undo_mod_upd_del_sec(undo_node_t * node,que_thr_t * thr)547 row_undo_mod_upd_del_sec(
548 /*=====================*/
549 undo_node_t* node, /*!< in: row undo node */
550 que_thr_t* thr) /*!< in: query thread */
551 {
552 mem_heap_t* heap;
553 dtuple_t* entry;
554 dict_index_t* index;
555 ulint err = DB_SUCCESS;
556
557 ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
558 ut_ad(!node->undo_row);
559 heap = mem_heap_create(1024);
560
561 while (node->index != NULL) {
562
563 /* Skip all corrupted secondary index */
564 dict_table_skip_corrupt_index(node->index);
565
566 if (!node->index) {
567 break;
568 }
569
570 index = node->index;
571
572 entry = row_build_index_entry(node->row, node->ext,
573 index, heap);
574 if (UNIV_UNLIKELY(!entry)) {
575 /* The database must have crashed after
576 inserting a clustered index record but before
577 writing all the externally stored columns of
578 that record. Because secondary index entries
579 are inserted after the clustered index record,
580 we may assume that the secondary index record
581 does not exist. However, this situation may
582 only occur during the rollback of incomplete
583 transactions. */
584 ut_a(thr_is_recv(thr));
585 } else {
586 err = row_undo_mod_del_mark_or_remove_sec(
587 node, thr, index, entry);
588
589 if (err != DB_SUCCESS) {
590
591 break;
592 }
593 }
594
595 mem_heap_empty(heap);
596
597 node->index = dict_table_get_next_index(node->index);
598 }
599
600 mem_heap_free(heap);
601
602 return(err);
603 }
604
605 /***********************************************************//**
606 Undoes a modify in secondary indexes when undo record type is DEL_MARK.
607 @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
608 static
609 ulint
row_undo_mod_del_mark_sec(undo_node_t * node,que_thr_t * thr)610 row_undo_mod_del_mark_sec(
611 /*======================*/
612 undo_node_t* node, /*!< in: row undo node */
613 que_thr_t* thr) /*!< in: query thread */
614 {
615 mem_heap_t* heap;
616 dtuple_t* entry;
617 dict_index_t* index;
618 ulint err;
619
620 ut_ad(!node->undo_row);
621
622 heap = mem_heap_create(1024);
623
624 while (node->index != NULL) {
625 /* Skip all corrupted secondary index */
626 dict_table_skip_corrupt_index(node->index);
627
628 if (!node->index) {
629 break;
630 }
631
632 index = node->index;
633
634 entry = row_build_index_entry(node->row, node->ext,
635 index, heap);
636 ut_a(entry);
637 err = row_undo_mod_del_unmark_sec_and_undo_update(
638 BTR_MODIFY_LEAF, thr, index, entry);
639 if (err == DB_FAIL) {
640 err = row_undo_mod_del_unmark_sec_and_undo_update(
641 BTR_MODIFY_TREE, thr, index, entry);
642 }
643
644 if (err != DB_SUCCESS) {
645
646 mem_heap_free(heap);
647
648 return(err);
649 }
650
651 node->index = dict_table_get_next_index(node->index);
652 }
653
654 mem_heap_free(heap);
655
656 return(DB_SUCCESS);
657 }
658
659 /***********************************************************//**
660 Undoes a modify in secondary indexes when undo record type is UPD_EXIST.
661 @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
662 static
663 ulint
row_undo_mod_upd_exist_sec(undo_node_t * node,que_thr_t * thr)664 row_undo_mod_upd_exist_sec(
665 /*=======================*/
666 undo_node_t* node, /*!< in: row undo node */
667 que_thr_t* thr) /*!< in: query thread */
668 {
669 mem_heap_t* heap;
670 dtuple_t* entry;
671 dict_index_t* index;
672 ulint err;
673
674 if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
675 /* No change in secondary indexes */
676
677 return(DB_SUCCESS);
678 }
679
680 heap = mem_heap_create(1024);
681
682 while (node->index != NULL) {
683 /* Skip all corrupted secondary index */
684 dict_table_skip_corrupt_index(node->index);
685
686 if (!node->index) {
687 break;
688 }
689
690 index = node->index;
691
692 if (row_upd_changes_ord_field_binary(node->index, node->update,
693 thr,
694 node->row, node->ext)) {
695
696 /* Build the newest version of the index entry */
697 entry = row_build_index_entry(node->row, node->ext,
698 index, heap);
699 if (UNIV_UNLIKELY(!entry)) {
700 /* The server must have crashed in
701 row_upd_clust_rec_by_insert() before
702 the updated externally stored columns (BLOBs)
703 of the new clustered index entry were
704 written. */
705
706 /* The table must be in DYNAMIC or COMPRESSED
707 format. REDUNDANT and COMPACT formats
708 store a local 768-byte prefix of each
709 externally stored column. */
710 ut_a(dict_table_get_format(index->table)
711 >= DICT_TF_FORMAT_ZIP);
712
713 /* This is only legitimate when
714 rolling back an incomplete transaction
715 after crash recovery. */
716 ut_a(thr_get_trx(thr)->is_recovered);
717
718 /* The server must have crashed before
719 completing the insert of the new
720 clustered index entry and before
721 inserting to the secondary indexes.
722 Because node->row was not yet written
723 to this index, we can ignore it. But
724 we must restore node->undo_row. */
725 } else {
726 /* NOTE that if we updated the fields of a
727 delete-marked secondary index record so that
728 alphabetically they stayed the same, e.g.,
729 'abc' -> 'aBc', we cannot return to the
730 original values because we do not know them.
731 But this should not cause problems because
732 in row0sel.c, in queries we always retrieve
733 the clustered index record or an earlier
734 version of it, if the secondary index record
735 through which we do the search is
736 delete-marked. */
737
738 err = row_undo_mod_del_mark_or_remove_sec(
739 node, thr, index, entry);
740 if (err != DB_SUCCESS) {
741 mem_heap_free(heap);
742
743 return(err);
744 }
745
746 mem_heap_empty(heap);
747 }
748
749 /* We may have to update the delete mark in the
750 secondary index record of the previous version of
751 the row. We also need to update the fields of
752 the secondary index record if we updated its fields
753 but alphabetically they stayed the same, e.g.,
754 'abc' -> 'aBc'. */
755 entry = row_build_index_entry(node->undo_row,
756 node->undo_ext,
757 index, heap);
758 ut_a(entry);
759
760 err = row_undo_mod_del_unmark_sec_and_undo_update(
761 BTR_MODIFY_LEAF, thr, index, entry);
762 if (err == DB_FAIL) {
763 err = row_undo_mod_del_unmark_sec_and_undo_update(
764 BTR_MODIFY_TREE, thr, index, entry);
765 }
766
767 if (err != DB_SUCCESS) {
768 mem_heap_free(heap);
769
770 return(err);
771 }
772 }
773
774 node->index = dict_table_get_next_index(node->index);
775 }
776
777 mem_heap_free(heap);
778
779 return(DB_SUCCESS);
780 }
781
782 /***********************************************************//**
783 Parses the row reference and other info in a modify undo log record. */
784 static
785 void
row_undo_mod_parse_undo_rec(undo_node_t * node,que_thr_t * thr)786 row_undo_mod_parse_undo_rec(
787 /*========================*/
788 undo_node_t* node, /*!< in: row undo node */
789 que_thr_t* thr) /*!< in: query thread */
790 {
791 dict_index_t* clust_index;
792 byte* ptr;
793 undo_no_t undo_no;
794 table_id_t table_id;
795 trx_id_t trx_id;
796 roll_ptr_t roll_ptr;
797 ulint info_bits;
798 ulint type;
799 ulint cmpl_info;
800 ibool dummy_extern;
801 trx_t* trx;
802
803 ut_ad(node && thr);
804 trx = thr_get_trx(thr);
805 ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
806 &dummy_extern, &undo_no, &table_id);
807 node->rec_type = type;
808
809 node->table = dict_table_get_on_id(table_id, trx);
810
811 /* TODO: other fixes associated with DROP TABLE + rollback in the
812 same table by another user */
813
814 if (node->table == NULL) {
815 /* Table was dropped */
816 return;
817 }
818
819 if (node->table->ibd_file_missing) {
820 /* We skip undo operations to missing .ibd files */
821 node->table = NULL;
822
823 return;
824 }
825
826 clust_index = dict_table_get_first_index(node->table);
827
828 ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
829 &info_bits);
830
831 ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
832 node->heap);
833
834 trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
835 roll_ptr, info_bits, trx,
836 node->heap, &(node->update));
837 node->new_trx_id = trx_id;
838 node->cmpl_info = cmpl_info;
839 }
840
841 /***********************************************************//**
842 Undoes a modify operation on a row of a table.
843 @return DB_SUCCESS or error code */
844 UNIV_INTERN
845 ulint
row_undo_mod(undo_node_t * node,que_thr_t * thr)846 row_undo_mod(
847 /*=========*/
848 undo_node_t* node, /*!< in: row undo node */
849 que_thr_t* thr) /*!< in: query thread */
850 {
851 ulint err;
852
853 ut_ad(node && thr);
854 ut_ad(node->state == UNDO_NODE_MODIFY);
855
856 row_undo_mod_parse_undo_rec(node, thr);
857
858 if (!node->table || !row_undo_search_clust_to_pcur(node)) {
859 /* It is already undone, or will be undone by another query
860 thread, or table was dropped */
861
862 trx_undo_rec_release(node->trx, node->undo_no);
863 node->state = UNDO_NODE_FETCH_NEXT;
864
865 return(DB_SUCCESS);
866 }
867
868 node->index = dict_table_get_next_index(
869 dict_table_get_first_index(node->table));
870
871 /* Skip all corrupted secondary index */
872 dict_table_skip_corrupt_index(node->index);
873
874 if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
875
876 err = row_undo_mod_upd_exist_sec(node, thr);
877
878 } else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
879
880 err = row_undo_mod_del_mark_sec(node, thr);
881 } else {
882 ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
883 err = row_undo_mod_upd_del_sec(node, thr);
884 }
885
886 if (err != DB_SUCCESS) {
887
888 return(err);
889 }
890
891 err = row_undo_mod_clust(node, thr);
892
893 return(err);
894 }
895