Drizzled Public API Documentation

row0umod.cc
1 /*****************************************************************************
2 
3 Copyright (C) 1997, 2010, Innobase Oy. 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., 51 Franklin
15 St, Fifth Floor, Boston, MA 02110-1301 USA
16 
17 *****************************************************************************/
18 
19 /**************************************************/
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 /***********************************************************/
76 static
77 ibool
78 row_undo_mod_undo_also_prev_vers(
79 /*=============================*/
80  undo_node_t* node,
81  undo_no_t* undo_no)
82 {
83  trx_undo_rec_t* undo_rec;
84  trx_t* trx;
85 
86  trx = node->trx;
87 
88  if (node->new_trx_id != trx->id) {
89 
90  *undo_no = 0;
91  return(FALSE);
92  }
93 
94  undo_rec = trx_undo_get_undo_rec_low(node->new_roll_ptr, node->heap);
95 
96  *undo_no = trx_undo_rec_get_undo_no(undo_rec);
97 
98  return(trx->roll_limit <= *undo_no);
99 }
100 
101 /***********************************************************/
104 static
105 ulint
106 row_undo_mod_clust_low(
107 /*===================*/
108  undo_node_t* node,
109  que_thr_t* thr,
110  mtr_t* mtr,
112  ulint mode)
113 {
114  btr_pcur_t* pcur;
115  btr_cur_t* btr_cur;
116  ulint err;
117 #ifdef UNIV_DEBUG
118  ibool success;
119 #endif /* UNIV_DEBUG */
120 
121  pcur = &(node->pcur);
122  btr_cur = btr_pcur_get_btr_cur(pcur);
123 
124 #ifdef UNIV_DEBUG
125  success =
126 #endif /* UNIV_DEBUG */
127  btr_pcur_restore_position(mode, pcur, mtr);
128 
129  ut_ad(success);
130 
131  if (mode == BTR_MODIFY_LEAF) {
132 
133  err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG
134  | BTR_NO_UNDO_LOG_FLAG
135  | BTR_KEEP_SYS_FLAG,
136  btr_cur, node->update,
137  node->cmpl_info, thr, mtr);
138  } else {
139  mem_heap_t* heap = NULL;
140  big_rec_t* dummy_big_rec;
141 
142  ut_ad(mode == BTR_MODIFY_TREE);
143 
144  err = btr_cur_pessimistic_update(
145  BTR_NO_LOCKING_FLAG
146  | BTR_NO_UNDO_LOG_FLAG
147  | BTR_KEEP_SYS_FLAG,
148  btr_cur, &heap, &dummy_big_rec, node->update,
149  node->cmpl_info, thr, mtr);
150 
151  ut_a(!dummy_big_rec);
152  if (UNIV_LIKELY_NULL(heap)) {
153  mem_heap_free(heap);
154  }
155  }
156 
157  return(err);
158 }
159 
160 /***********************************************************/
167 static
168 ulint
169 row_undo_mod_remove_clust_low(
170 /*==========================*/
171  undo_node_t* node,
172  que_thr_t* thr,
173  mtr_t* mtr,
174  ulint mode)
175 {
176  btr_pcur_t* pcur;
177  btr_cur_t* btr_cur;
178  ulint err;
179  ibool success;
180 
181  ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
182  pcur = &(node->pcur);
183  btr_cur = btr_pcur_get_btr_cur(pcur);
184 
185  success = btr_pcur_restore_position(mode, pcur, mtr);
186 
187  if (!success) {
188 
189  return(DB_SUCCESS);
190  }
191 
192  /* Find out if we can remove the whole clustered index record */
193 
194  if (node->rec_type == TRX_UNDO_UPD_DEL_REC
196 
197  /* Ok, we can remove */
198  } else {
199  return(DB_SUCCESS);
200  }
201 
202  if (mode == BTR_MODIFY_LEAF) {
203  success = btr_cur_optimistic_delete(btr_cur, mtr);
204 
205  if (success) {
206  err = DB_SUCCESS;
207  } else {
208  err = DB_FAIL;
209  }
210  } else {
211  ut_ad(mode == BTR_MODIFY_TREE);
212 
213  /* This operation is analogous to purge, we can free also
214  inherited externally stored fields */
215 
216  btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
217  thr_is_recv(thr)
219  : RB_NONE, mtr);
220 
221  /* The delete operation may fail if we have little
222  file space left: TODO: easiest to crash the database
223  and restart with more file space */
224  }
225 
226  return(err);
227 }
228 
229 /***********************************************************/
233 static
234 ulint
235 row_undo_mod_clust(
236 /*===============*/
237  undo_node_t* node,
238  que_thr_t* thr)
239 {
240  btr_pcur_t* pcur;
241  mtr_t mtr;
242  ulint err;
243  ibool success;
244  ibool more_vers;
245  undo_no_t new_undo_no;
246 
247  ut_ad(node && thr);
248 
249  log_free_check();
250 
251  /* Check if also the previous version of the clustered index record
252  should be undone in this same rollback operation */
253 
254  more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no);
255 
256  pcur = &(node->pcur);
257 
258  mtr_start(&mtr);
259 
260  /* Try optimistic processing of the record, keeping changes within
261  the index page */
262 
263  err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);
264 
265  if (err != DB_SUCCESS) {
266  btr_pcur_commit_specify_mtr(pcur, &mtr);
267 
268  /* We may have to modify tree structure: do a pessimistic
269  descent down the index tree */
270 
271  mtr_start(&mtr);
272 
273  err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
274  }
275 
276  btr_pcur_commit_specify_mtr(pcur, &mtr);
277 
278  if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
279 
280  mtr_start(&mtr);
281 
282  err = row_undo_mod_remove_clust_low(node, thr, &mtr,
284  if (err != DB_SUCCESS) {
285  btr_pcur_commit_specify_mtr(pcur, &mtr);
286 
287  /* We may have to modify tree structure: do a
288  pessimistic descent down the index tree */
289 
290  mtr_start(&mtr);
291 
292  err = row_undo_mod_remove_clust_low(node, thr, &mtr,
294  }
295 
296  btr_pcur_commit_specify_mtr(pcur, &mtr);
297  }
298 
299  node->state = UNDO_NODE_FETCH_NEXT;
300 
301  trx_undo_rec_release(node->trx, node->undo_no);
302 
303  if (more_vers && err == DB_SUCCESS) {
304 
305  /* Reserve the undo log record to the prior version after
306  committing &mtr: this is necessary to comply with the latching
307  order, as &mtr may contain the fsp latch which is lower in
308  the latch hierarchy than trx->undo_mutex. */
309 
310  success = trx_undo_rec_reserve(node->trx, new_undo_no);
311 
312  if (success) {
313  node->state = UNDO_NODE_PREV_VERS;
314  }
315  }
316 
317  return(err);
318 }
319 
320 /***********************************************************/
323 static
324 ulint
325 row_undo_mod_del_mark_or_remove_sec_low(
326 /*====================================*/
327  undo_node_t* node,
328  que_thr_t* thr,
329  dict_index_t* index,
330  dtuple_t* entry,
331  ulint mode)
333 {
334  btr_pcur_t pcur;
335  btr_cur_t* btr_cur;
336  ibool success;
337  ibool old_has;
338  ulint err;
339  mtr_t mtr;
340  mtr_t mtr_vers;
341  enum row_search_result search_result;
342 
343  log_free_check();
344  mtr_start(&mtr);
345 
346  btr_cur = btr_pcur_get_btr_cur(&pcur);
347 
348  ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
349 
350  search_result = row_search_index_entry(index, entry, mode,
351  &pcur, &mtr);
352 
353  switch (UNIV_EXPECT(search_result, ROW_FOUND)) {
354  case ROW_NOT_FOUND:
355  /* In crash recovery, the secondary index record may
356  be missing if the UPDATE did not have time to insert
357  the secondary index records before the crash. When we
358  are undoing that UPDATE in crash recovery, the record
359  may be missing.
360 
361  In normal processing, if an update ends in a deadlock
362  before it has inserted all updated secondary index
363  records, then the undo will not find those records. */
364 
365  err = DB_SUCCESS;
366  goto func_exit;
367  case ROW_FOUND:
368  break;
369  case ROW_BUFFERED:
370  case ROW_NOT_DELETED_REF:
371  /* These are invalid outcomes, because the mode passed
372  to row_search_index_entry() did not include any of the
373  flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
374  ut_error;
375  }
376 
377  /* We should remove the index record if no prior version of the row,
378  which cannot be purged yet, requires its existence. If some requires,
379  we should delete mark the record. */
380 
381  mtr_start(&mtr_vers);
382 
383  success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
384  &mtr_vers);
385  ut_a(success);
386 
387  old_has = row_vers_old_has_index_entry(FALSE,
388  btr_pcur_get_rec(&(node->pcur)),
389  &mtr_vers, index, entry);
390  if (old_has) {
391  err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
392  btr_cur, TRUE, thr, &mtr);
393  ut_ad(err == DB_SUCCESS);
394  } else {
395  /* Remove the index record */
396 
397  if (mode == BTR_MODIFY_LEAF) {
398  success = btr_cur_optimistic_delete(btr_cur, &mtr);
399  if (success) {
400  err = DB_SUCCESS;
401  } else {
402  err = DB_FAIL;
403  }
404  } else {
405  ut_ad(mode == BTR_MODIFY_TREE);
406 
407  /* No need to distinguish RB_RECOVERY_PURGE here,
408  because we are deleting a secondary index record:
409  the distinction between RB_NORMAL and
410  RB_RECOVERY_PURGE only matters when deleting a
411  record that contains externally stored
412  columns. */
413  ut_ad(!dict_index_is_clust(index));
414  btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
415  RB_NORMAL, &mtr);
416 
417  /* The delete operation may fail if we have little
418  file space left: TODO: easiest to crash the database
419  and restart with more file space */
420  }
421  }
422 
423  btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
424 
425 func_exit:
426  btr_pcur_close(&pcur);
427  mtr_commit(&mtr);
428 
429  return(err);
430 }
431 
432 /***********************************************************/
441 static
442 ulint
443 row_undo_mod_del_mark_or_remove_sec(
444 /*================================*/
445  undo_node_t* node,
446  que_thr_t* thr,
447  dict_index_t* index,
448  dtuple_t* entry)
449 {
450  ulint err;
451 
452  err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
453  entry, BTR_MODIFY_LEAF);
454  if (err == DB_SUCCESS) {
455 
456  return(err);
457  }
458 
459  err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
460  entry, BTR_MODIFY_TREE);
461  return(err);
462 }
463 
464 /***********************************************************/
470 static
471 ulint
472 row_undo_mod_del_unmark_sec_and_undo_update(
473 /*========================================*/
474  ulint mode,
476  que_thr_t* thr,
477  dict_index_t* index,
478  const dtuple_t* entry)
479 {
480  mem_heap_t* heap;
481  btr_pcur_t pcur;
482  btr_cur_t* btr_cur;
483  upd_t* update;
484  ulint err = DB_SUCCESS;
485  big_rec_t* dummy_big_rec;
486  mtr_t mtr;
487  trx_t* trx = thr_get_trx(thr);
488  enum row_search_result search_result;
489 
490  /* Ignore indexes that are being created. */
491  if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {
492 
493  return(DB_SUCCESS);
494  }
495 
496  log_free_check();
497  mtr_start(&mtr);
498 
499  ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
500 
501  search_result = row_search_index_entry(index, entry, mode,
502  &pcur, &mtr);
503 
504  switch (search_result) {
505  case ROW_BUFFERED:
506  case ROW_NOT_DELETED_REF:
507  /* These are invalid outcomes, because the mode passed
508  to row_search_index_entry() did not include any of the
509  flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
510  ut_error;
511  case ROW_NOT_FOUND:
512  fputs("InnoDB: error in sec index entry del undo in\n"
513  "InnoDB: ", stderr);
514  dict_index_name_print(stderr, trx, index);
515  fputs("\n"
516  "InnoDB: tuple ", stderr);
517  dtuple_print(stderr, entry);
518  fputs("\n"
519  "InnoDB: record ", stderr);
520  rec_print(stderr, btr_pcur_get_rec(&pcur), index);
521  putc('\n', stderr);
522  trx_print(stderr, trx, 0);
523  fputs("\n"
524  "InnoDB: Submit a detailed bug report"
525  " to http://bugs.mysql.com\n", stderr);
526  break;
527  case ROW_FOUND:
528  btr_cur = btr_pcur_get_btr_cur(&pcur);
529  err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
530  btr_cur, FALSE, thr, &mtr);
531  ut_a(err == DB_SUCCESS);
532  heap = mem_heap_create(100);
533 
535  index, entry, btr_cur_get_rec(btr_cur), trx, heap);
536  if (upd_get_n_fields(update) == 0) {
537 
538  /* Do nothing */
539 
540  } else if (mode == BTR_MODIFY_LEAF) {
541  /* Try an optimistic updating of the record, keeping
542  changes within the page */
543 
544  err = btr_cur_optimistic_update(
545  BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
546  btr_cur, update, 0, thr, &mtr);
547  switch (err) {
548  case DB_OVERFLOW:
549  case DB_UNDERFLOW:
550  case DB_ZIP_OVERFLOW:
551  err = DB_FAIL;
552  }
553  } else {
554  ut_a(mode == BTR_MODIFY_TREE);
555  err = btr_cur_pessimistic_update(
556  BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
557  btr_cur, &heap, &dummy_big_rec,
558  update, 0, thr, &mtr);
559  ut_a(!dummy_big_rec);
560  }
561 
562  mem_heap_free(heap);
563  }
564 
565  btr_pcur_close(&pcur);
566  mtr_commit(&mtr);
567 
568  return(err);
569 }
570 
571 /***********************************************************/
574 static
575 ulint
576 row_undo_mod_upd_del_sec(
577 /*=====================*/
578  undo_node_t* node,
579  que_thr_t* thr)
580 {
581  mem_heap_t* heap;
582  dtuple_t* entry;
583  dict_index_t* index;
584  ulint err = DB_SUCCESS;
585 
586  ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
587  heap = mem_heap_create(1024);
588 
589  while (node->index != NULL) {
590  index = node->index;
591 
592  entry = row_build_index_entry(node->row, node->ext,
593  index, heap);
594  if (UNIV_UNLIKELY(!entry)) {
595  /* The database must have crashed after
596  inserting a clustered index record but before
597  writing all the externally stored columns of
598  that record. Because secondary index entries
599  are inserted after the clustered index record,
600  we may assume that the secondary index record
601  does not exist. However, this situation may
602  only occur during the rollback of incomplete
603  transactions. */
604  ut_a(thr_is_recv(thr));
605  } else {
606  err = row_undo_mod_del_mark_or_remove_sec(
607  node, thr, index, entry);
608 
609  if (err != DB_SUCCESS) {
610 
611  break;
612  }
613  }
614 
615  mem_heap_empty(heap);
616 
617  node->index = dict_table_get_next_index(node->index);
618  }
619 
620  mem_heap_free(heap);
621 
622  return(err);
623 }
624 
625 /***********************************************************/
628 static
629 ulint
630 row_undo_mod_del_mark_sec(
631 /*======================*/
632  undo_node_t* node,
633  que_thr_t* thr)
634 {
635  mem_heap_t* heap;
636  dtuple_t* entry;
637  dict_index_t* index;
638  ulint err;
639 
640  heap = mem_heap_create(1024);
641 
642  while (node->index != NULL) {
643  index = node->index;
644 
645  entry = row_build_index_entry(node->row, node->ext,
646  index, heap);
647  ut_a(entry);
648  err = row_undo_mod_del_unmark_sec_and_undo_update(
649  BTR_MODIFY_LEAF, thr, index, entry);
650  if (err == DB_FAIL) {
651  err = row_undo_mod_del_unmark_sec_and_undo_update(
652  BTR_MODIFY_TREE, thr, index, entry);
653  }
654 
655  if (err != DB_SUCCESS) {
656 
657  mem_heap_free(heap);
658 
659  return(err);
660  }
661 
662  node->index = dict_table_get_next_index(node->index);
663  }
664 
665  mem_heap_free(heap);
666 
667  return(DB_SUCCESS);
668 }
669 
670 /***********************************************************/
673 static
674 ulint
675 row_undo_mod_upd_exist_sec(
676 /*=======================*/
677  undo_node_t* node,
678  que_thr_t* thr)
679 {
680  mem_heap_t* heap;
681  dtuple_t* entry;
682  dict_index_t* index;
683  ulint err;
684 
685  if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
686  /* No change in secondary indexes */
687 
688  return(DB_SUCCESS);
689  }
690 
691  heap = mem_heap_create(1024);
692 
693  while (node->index != NULL) {
694  index = node->index;
695 
696  if (row_upd_changes_ord_field_binary(node->row, node->index,
697  node->update)) {
698 
699  /* Build the newest version of the index entry */
700  entry = row_build_index_entry(node->row, node->ext,
701  index, heap);
702  if (UNIV_UNLIKELY(!entry)) {
703  /* The server must have crashed in
704  row_upd_clust_rec_by_insert(), in
705  row_ins_index_entry_low() before
706  btr_store_big_rec_extern_fields()
707  has written the externally stored columns
708  (BLOBs) of the new clustered index entry. */
709 
710  /* The table must be in DYNAMIC or COMPRESSED
711  format. REDUNDANT and COMPACT formats
712  store a local 768-byte prefix of each
713  externally stored column. */
715  >= DICT_TF_FORMAT_ZIP);
716 
717  /* This is only legitimate when
718  rolling back an incomplete transaction
719  after crash recovery. */
720  ut_a(thr_get_trx(thr)->is_recovered);
721 
722  /* The server must have crashed before
723  completing the insert of the new
724  clustered index entry and before
725  inserting to the secondary indexes.
726  Because node->row was not yet written
727  to this index, we can ignore it. But
728  we must restore node->undo_row. */
729  } else {
730  /* NOTE that if we updated the fields of a
731  delete-marked secondary index record so that
732  alphabetically they stayed the same, e.g.,
733  'abc' -> 'aBc', we cannot return to the
734  original values because we do not know them.
735  But this should not cause problems because
736  in row0sel.c, in queries we always retrieve
737  the clustered index record or an earlier
738  version of it, if the secondary index record
739  through which we do the search is
740  delete-marked. */
741 
742  err = row_undo_mod_del_mark_or_remove_sec(
743  node, thr, index, entry);
744  if (err != DB_SUCCESS) {
745  mem_heap_free(heap);
746 
747  return(err);
748  }
749 
750  mem_heap_empty(heap);
751  }
752 
753  /* We may have to update the delete mark in the
754  secondary index record of the previous version of
755  the row. We also need to update the fields of
756  the secondary index record if we updated its fields
757  but alphabetically they stayed the same, e.g.,
758  'abc' -> 'aBc'. */
759  entry = row_build_index_entry(node->undo_row,
760  node->undo_ext,
761  index, heap);
762  ut_a(entry);
763 
764  err = row_undo_mod_del_unmark_sec_and_undo_update(
765  BTR_MODIFY_LEAF, thr, index, entry);
766  if (err == DB_FAIL) {
767  err = row_undo_mod_del_unmark_sec_and_undo_update(
768  BTR_MODIFY_TREE, thr, index, entry);
769  }
770 
771  if (err != DB_SUCCESS) {
772  mem_heap_free(heap);
773 
774  return(err);
775  }
776  }
777 
778  node->index = dict_table_get_next_index(node->index);
779  }
780 
781  mem_heap_free(heap);
782 
783  return(DB_SUCCESS);
784 }
785 
786 /***********************************************************/
788 static
789 void
790 row_undo_mod_parse_undo_rec(
791 /*========================*/
792  undo_node_t* node,
793  que_thr_t* thr)
794 {
795  dict_index_t* clust_index;
796  byte* ptr;
797  undo_no_t undo_no;
798  table_id_t table_id;
799  trx_id_t trx_id;
800  roll_ptr_t roll_ptr;
801  ulint info_bits;
802  ulint type;
803  ulint cmpl_info;
804  ibool dummy_extern;
805  trx_t* trx;
806 
807  ut_ad(node && thr);
808  trx = thr_get_trx(thr);
809  ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
810  &dummy_extern, &undo_no, &table_id);
811  node->rec_type = type;
812 
813  node->table = dict_table_get_on_id(table_id, trx);
814 
815  /* TODO: other fixes associated with DROP TABLE + rollback in the
816  same table by another user */
817 
818  if (node->table == NULL) {
819  /* Table was dropped */
820  return;
821  }
822 
823  if (node->table->ibd_file_missing) {
824  /* We skip undo operations to missing .ibd files */
825  node->table = NULL;
826 
827  return;
828  }
829 
830  clust_index = dict_table_get_first_index(node->table);
831 
832  ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
833  &info_bits);
834 
835  ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
836  node->heap);
837 
838  trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
839  roll_ptr, info_bits, trx,
840  node->heap, &(node->update));
841  node->new_roll_ptr = roll_ptr;
842  node->new_trx_id = trx_id;
843  node->cmpl_info = cmpl_info;
844 }
845 
846 /***********************************************************/
849 UNIV_INTERN
850 ulint
852 /*=========*/
853  undo_node_t* node,
854  que_thr_t* thr)
855 {
856  ulint err;
857 
858  ut_ad(node && thr);
859  ut_ad(node->state == UNDO_NODE_MODIFY);
860 
861  row_undo_mod_parse_undo_rec(node, thr);
862 
863  if (!node->table || !row_undo_search_clust_to_pcur(node)) {
864  /* It is already undone, or will be undone by another query
865  thread, or table was dropped */
866 
867  trx_undo_rec_release(node->trx, node->undo_no);
868  node->state = UNDO_NODE_FETCH_NEXT;
869 
870  return(DB_SUCCESS);
871  }
872 
873  node->index = dict_table_get_next_index(
874  dict_table_get_first_index(node->table));
875 
876  if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
877 
878  err = row_undo_mod_upd_exist_sec(node, thr);
879 
880  } else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
881 
882  err = row_undo_mod_del_mark_sec(node, thr);
883  } else {
884  ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
885  err = row_undo_mod_upd_del_sec(node, thr);
886  }
887 
888  if (err != DB_SUCCESS) {
889 
890  return(err);
891  }
892 
893  err = row_undo_mod_clust(node, thr);
894 
895  return(err);
896 }