Drizzled Public API Documentation

row0umod.cc
00001 /*****************************************************************************
00002 
00003 Copyright (C) 1997, 2010, Innobase Oy. All Rights Reserved.
00004 
00005 This program is free software; you can redistribute it and/or modify it under
00006 the terms of the GNU General Public License as published by the Free Software
00007 Foundation; version 2 of the License.
00008 
00009 This program is distributed in the hope that it will be useful, but WITHOUT
00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00011 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00012 
00013 You should have received a copy of the GNU General Public License along with
00014 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
00015 St, Fifth Floor, Boston, MA 02110-1301 USA
00016 
00017 *****************************************************************************/
00018 
00019 /**************************************************/
00026 #include "row0umod.h"
00027 
00028 #ifdef UNIV_NONINL
00029 #include "row0umod.ic"
00030 #endif
00031 
00032 #include "dict0dict.h"
00033 #include "dict0boot.h"
00034 #include "trx0undo.h"
00035 #include "trx0roll.h"
00036 #include "btr0btr.h"
00037 #include "mach0data.h"
00038 #include "row0undo.h"
00039 #include "row0vers.h"
00040 #include "trx0trx.h"
00041 #include "trx0rec.h"
00042 #include "row0row.h"
00043 #include "row0upd.h"
00044 #include "que0que.h"
00045 #include "log0log.h"
00046 
00047 /* Considerations on undoing a modify operation.
00048 (1) Undoing a delete marking: all index records should be found. Some of
00049 them may have delete mark already FALSE, if the delete mark operation was
00050 stopped underway, or if the undo operation ended prematurely because of a
00051 system crash.
00052 (2) Undoing an update of a delete unmarked record: the newer version of
00053 an updated secondary index entry should be removed if no prior version
00054 of the clustered index record requires its existence. Otherwise, it should
00055 be delete marked.
00056 (3) Undoing an update of a delete marked record. In this kind of update a
00057 delete marked clustered index record was delete unmarked and possibly also
00058 some of its fields were changed. Now, it is possible that the delete marked
00059 version has become obsolete at the time the undo is started. */
00060 
00061 /*************************************************************************
00062 IMPORTANT NOTE: Any operation that generates redo MUST check that there
00063 is enough space in the redo log before for that operation. This is
00064 done by calling log_free_check(). The reason for checking the
00065 availability of the redo log space before the start of the operation is
00066 that we MUST not hold any synchonization objects when performing the
00067 check.
00068 If you make a change in this module make sure that no codepath is
00069 introduced where a call to log_free_check() is bypassed. */
00070 
00071 /***********************************************************/
00076 static
00077 ibool
00078 row_undo_mod_undo_also_prev_vers(
00079 /*=============================*/
00080   undo_node_t*  node, 
00081   undo_no_t*  undo_no)
00082 {
00083   trx_undo_rec_t* undo_rec;
00084   trx_t*    trx;
00085 
00086   trx = node->trx;
00087 
00088   if (node->new_trx_id != trx->id) {
00089 
00090     *undo_no = 0;
00091     return(FALSE);
00092   }
00093 
00094   undo_rec = trx_undo_get_undo_rec_low(node->new_roll_ptr, node->heap);
00095 
00096   *undo_no = trx_undo_rec_get_undo_no(undo_rec);
00097 
00098   return(trx->roll_limit <= *undo_no);
00099 }
00100 
00101 /***********************************************************/
00104 static
00105 ulint
00106 row_undo_mod_clust_low(
00107 /*===================*/
00108   undo_node_t*  node, 
00109   que_thr_t*  thr,  
00110   mtr_t*    mtr,  
00112   ulint   mode) 
00113 {
00114   btr_pcur_t* pcur;
00115   btr_cur_t*  btr_cur;
00116   ulint   err;
00117 #ifdef UNIV_DEBUG
00118   ibool   success;
00119 #endif /* UNIV_DEBUG */
00120 
00121   pcur = &(node->pcur);
00122   btr_cur = btr_pcur_get_btr_cur(pcur);
00123 
00124 #ifdef UNIV_DEBUG
00125   success =
00126 #endif /* UNIV_DEBUG */
00127   btr_pcur_restore_position(mode, pcur, mtr);
00128 
00129   ut_ad(success);
00130 
00131   if (mode == BTR_MODIFY_LEAF) {
00132 
00133     err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG
00134             | BTR_NO_UNDO_LOG_FLAG
00135             | BTR_KEEP_SYS_FLAG,
00136             btr_cur, node->update,
00137             node->cmpl_info, thr, mtr);
00138   } else {
00139     mem_heap_t* heap    = NULL;
00140     big_rec_t*  dummy_big_rec;
00141 
00142     ut_ad(mode == BTR_MODIFY_TREE);
00143 
00144     err = btr_cur_pessimistic_update(
00145       BTR_NO_LOCKING_FLAG
00146       | BTR_NO_UNDO_LOG_FLAG
00147       | BTR_KEEP_SYS_FLAG,
00148       btr_cur, &heap, &dummy_big_rec, node->update,
00149       node->cmpl_info, thr, mtr);
00150 
00151     ut_a(!dummy_big_rec);
00152     if (UNIV_LIKELY_NULL(heap)) {
00153       mem_heap_free(heap);
00154     }
00155   }
00156 
00157   return(err);
00158 }
00159 
00160 /***********************************************************/
00167 static
00168 ulint
00169 row_undo_mod_remove_clust_low(
00170 /*==========================*/
00171   undo_node_t*  node, 
00172   que_thr_t*  thr,  
00173   mtr_t*    mtr,  
00174   ulint   mode) 
00175 {
00176   btr_pcur_t* pcur;
00177   btr_cur_t*  btr_cur;
00178   ulint   err;
00179   ibool   success;
00180 
00181   ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
00182   pcur = &(node->pcur);
00183   btr_cur = btr_pcur_get_btr_cur(pcur);
00184 
00185   success = btr_pcur_restore_position(mode, pcur, mtr);
00186 
00187   if (!success) {
00188 
00189     return(DB_SUCCESS);
00190   }
00191 
00192   /* Find out if we can remove the whole clustered index record */
00193 
00194   if (node->rec_type == TRX_UNDO_UPD_DEL_REC
00195       && !row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
00196 
00197     /* Ok, we can remove */
00198   } else {
00199     return(DB_SUCCESS);
00200   }
00201 
00202   if (mode == BTR_MODIFY_LEAF) {
00203     success = btr_cur_optimistic_delete(btr_cur, mtr);
00204 
00205     if (success) {
00206       err = DB_SUCCESS;
00207     } else {
00208       err = DB_FAIL;
00209     }
00210   } else {
00211     ut_ad(mode == BTR_MODIFY_TREE);
00212 
00213     /* This operation is analogous to purge, we can free also
00214     inherited externally stored fields */
00215 
00216     btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
00217              thr_is_recv(thr)
00218              ? RB_RECOVERY_PURGE_REC
00219              : RB_NONE, mtr);
00220 
00221     /* The delete operation may fail if we have little
00222     file space left: TODO: easiest to crash the database
00223     and restart with more file space */
00224   }
00225 
00226   return(err);
00227 }
00228 
00229 /***********************************************************/
00233 static
00234 ulint
00235 row_undo_mod_clust(
00236 /*===============*/
00237   undo_node_t*  node, 
00238   que_thr_t*  thr)  
00239 {
00240   btr_pcur_t* pcur;
00241   mtr_t   mtr;
00242   ulint   err;
00243   ibool   success;
00244   ibool   more_vers;
00245   undo_no_t new_undo_no;
00246 
00247   ut_ad(node && thr);
00248 
00249   log_free_check();
00250 
00251   /* Check if also the previous version of the clustered index record
00252   should be undone in this same rollback operation */
00253 
00254   more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no);
00255 
00256   pcur = &(node->pcur);
00257 
00258   mtr_start(&mtr);
00259 
00260   /* Try optimistic processing of the record, keeping changes within
00261   the index page */
00262 
00263   err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);
00264 
00265   if (err != DB_SUCCESS) {
00266     btr_pcur_commit_specify_mtr(pcur, &mtr);
00267 
00268     /* We may have to modify tree structure: do a pessimistic
00269     descent down the index tree */
00270 
00271     mtr_start(&mtr);
00272 
00273     err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
00274   }
00275 
00276   btr_pcur_commit_specify_mtr(pcur, &mtr);
00277 
00278   if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
00279 
00280     mtr_start(&mtr);
00281 
00282     err = row_undo_mod_remove_clust_low(node, thr, &mtr,
00283                 BTR_MODIFY_LEAF);
00284     if (err != DB_SUCCESS) {
00285       btr_pcur_commit_specify_mtr(pcur, &mtr);
00286 
00287       /* We may have to modify tree structure: do a
00288       pessimistic descent down the index tree */
00289 
00290       mtr_start(&mtr);
00291 
00292       err = row_undo_mod_remove_clust_low(node, thr, &mtr,
00293                   BTR_MODIFY_TREE);
00294     }
00295 
00296     btr_pcur_commit_specify_mtr(pcur, &mtr);
00297   }
00298 
00299   node->state = UNDO_NODE_FETCH_NEXT;
00300 
00301   trx_undo_rec_release(node->trx, node->undo_no);
00302 
00303   if (more_vers && err == DB_SUCCESS) {
00304 
00305     /* Reserve the undo log record to the prior version after
00306     committing &mtr: this is necessary to comply with the latching
00307     order, as &mtr may contain the fsp latch which is lower in
00308     the latch hierarchy than trx->undo_mutex. */
00309 
00310     success = trx_undo_rec_reserve(node->trx, new_undo_no);
00311 
00312     if (success) {
00313       node->state = UNDO_NODE_PREV_VERS;
00314     }
00315   }
00316 
00317   return(err);
00318 }
00319 
00320 /***********************************************************/
00323 static
00324 ulint
00325 row_undo_mod_del_mark_or_remove_sec_low(
00326 /*====================================*/
00327   undo_node_t*  node, 
00328   que_thr_t*  thr,  
00329   dict_index_t* index,  
00330   dtuple_t* entry,  
00331   ulint   mode) 
00333 {
00334   btr_pcur_t    pcur;
00335   btr_cur_t*    btr_cur;
00336   ibool     success;
00337   ibool     old_has;
00338   ulint     err;
00339   mtr_t     mtr;
00340   mtr_t     mtr_vers;
00341   enum row_search_result  search_result;
00342 
00343   log_free_check();
00344   mtr_start(&mtr);
00345 
00346   btr_cur = btr_pcur_get_btr_cur(&pcur);
00347 
00348   ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
00349 
00350   search_result = row_search_index_entry(index, entry, mode,
00351                  &pcur, &mtr);
00352 
00353   switch (UNIV_EXPECT(search_result, ROW_FOUND)) {
00354   case ROW_NOT_FOUND:
00355     /* In crash recovery, the secondary index record may
00356     be missing if the UPDATE did not have time to insert
00357     the secondary index records before the crash.  When we
00358     are undoing that UPDATE in crash recovery, the record
00359     may be missing.
00360 
00361     In normal processing, if an update ends in a deadlock
00362     before it has inserted all updated secondary index
00363     records, then the undo will not find those records. */
00364 
00365     err = DB_SUCCESS;
00366     goto func_exit;
00367   case ROW_FOUND:
00368     break;
00369   case ROW_BUFFERED:
00370   case ROW_NOT_DELETED_REF:
00371     /* These are invalid outcomes, because the mode passed
00372     to row_search_index_entry() did not include any of the
00373     flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
00374     ut_error;
00375   }
00376 
00377   /* We should remove the index record if no prior version of the row,
00378   which cannot be purged yet, requires its existence. If some requires,
00379   we should delete mark the record. */
00380 
00381   mtr_start(&mtr_vers);
00382 
00383   success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
00384               &mtr_vers);
00385   ut_a(success);
00386 
00387   old_has = row_vers_old_has_index_entry(FALSE,
00388                  btr_pcur_get_rec(&(node->pcur)),
00389                  &mtr_vers, index, entry);
00390   if (old_has) {
00391     err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
00392                btr_cur, TRUE, thr, &mtr);
00393     ut_ad(err == DB_SUCCESS);
00394   } else {
00395     /* Remove the index record */
00396 
00397     if (mode == BTR_MODIFY_LEAF) {
00398       success = btr_cur_optimistic_delete(btr_cur, &mtr);
00399       if (success) {
00400         err = DB_SUCCESS;
00401       } else {
00402         err = DB_FAIL;
00403       }
00404     } else {
00405       ut_ad(mode == BTR_MODIFY_TREE);
00406 
00407       /* No need to distinguish RB_RECOVERY_PURGE here,
00408       because we are deleting a secondary index record:
00409       the distinction between RB_NORMAL and
00410       RB_RECOVERY_PURGE only matters when deleting a
00411       record that contains externally stored
00412       columns. */
00413       ut_ad(!dict_index_is_clust(index));
00414       btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
00415                RB_NORMAL, &mtr);
00416 
00417       /* The delete operation may fail if we have little
00418       file space left: TODO: easiest to crash the database
00419       and restart with more file space */
00420     }
00421   }
00422 
00423   btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
00424 
00425 func_exit:
00426   btr_pcur_close(&pcur);
00427   mtr_commit(&mtr);
00428 
00429   return(err);
00430 }
00431 
00432 /***********************************************************/
00441 static
00442 ulint
00443 row_undo_mod_del_mark_or_remove_sec(
00444 /*================================*/
00445   undo_node_t*  node, 
00446   que_thr_t*  thr,  
00447   dict_index_t* index,  
00448   dtuple_t* entry)  
00449 {
00450   ulint err;
00451 
00452   err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
00453                   entry, BTR_MODIFY_LEAF);
00454   if (err == DB_SUCCESS) {
00455 
00456     return(err);
00457   }
00458 
00459   err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
00460                   entry, BTR_MODIFY_TREE);
00461   return(err);
00462 }
00463 
00464 /***********************************************************/
00470 static
00471 ulint
00472 row_undo_mod_del_unmark_sec_and_undo_update(
00473 /*========================================*/
00474   ulint   mode, 
00476   que_thr_t*  thr,  
00477   dict_index_t* index,  
00478   const dtuple_t* entry)  
00479 {
00480   mem_heap_t*   heap;
00481   btr_pcur_t    pcur;
00482   btr_cur_t*    btr_cur;
00483   upd_t*      update;
00484   ulint     err   = DB_SUCCESS;
00485   big_rec_t*    dummy_big_rec;
00486   mtr_t     mtr;
00487   trx_t*      trx   = thr_get_trx(thr);
00488   enum row_search_result  search_result;
00489 
00490   /* Ignore indexes that are being created. */
00491   if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {
00492 
00493     return(DB_SUCCESS);
00494   }
00495 
00496   log_free_check();
00497   mtr_start(&mtr);
00498 
00499   ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
00500 
00501   search_result = row_search_index_entry(index, entry, mode,
00502                  &pcur, &mtr);
00503 
00504   switch (search_result) {
00505   case ROW_BUFFERED:
00506   case ROW_NOT_DELETED_REF:
00507     /* These are invalid outcomes, because the mode passed
00508     to row_search_index_entry() did not include any of the
00509     flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
00510     ut_error;
00511   case ROW_NOT_FOUND:
00512     fputs("InnoDB: error in sec index entry del undo in\n"
00513           "InnoDB: ", stderr);
00514     dict_index_name_print(stderr, trx, index);
00515     fputs("\n"
00516           "InnoDB: tuple ", stderr);
00517     dtuple_print(stderr, entry);
00518     fputs("\n"
00519           "InnoDB: record ", stderr);
00520     rec_print(stderr, btr_pcur_get_rec(&pcur), index);
00521     putc('\n', stderr);
00522     trx_print(stderr, trx, 0);
00523     fputs("\n"
00524           "InnoDB: Submit a detailed bug report"
00525           " to http://bugs.mysql.com\n", stderr);
00526     break;
00527   case ROW_FOUND:
00528     btr_cur = btr_pcur_get_btr_cur(&pcur);
00529     err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
00530                btr_cur, FALSE, thr, &mtr);
00531     ut_a(err == DB_SUCCESS);
00532     heap = mem_heap_create(100);
00533 
00534     update = row_upd_build_sec_rec_difference_binary(
00535       index, entry, btr_cur_get_rec(btr_cur), trx, heap);
00536     if (upd_get_n_fields(update) == 0) {
00537 
00538       /* Do nothing */
00539 
00540     } else if (mode == BTR_MODIFY_LEAF) {
00541       /* Try an optimistic updating of the record, keeping
00542       changes within the page */
00543 
00544       err = btr_cur_optimistic_update(
00545         BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
00546         btr_cur, update, 0, thr, &mtr);
00547       switch (err) {
00548       case DB_OVERFLOW:
00549       case DB_UNDERFLOW:
00550       case DB_ZIP_OVERFLOW:
00551         err = DB_FAIL;
00552       }
00553     } else {
00554       ut_a(mode == BTR_MODIFY_TREE);
00555       err = btr_cur_pessimistic_update(
00556         BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
00557         btr_cur, &heap, &dummy_big_rec,
00558         update, 0, thr, &mtr);
00559       ut_a(!dummy_big_rec);
00560     }
00561 
00562     mem_heap_free(heap);
00563   }
00564 
00565   btr_pcur_close(&pcur);
00566   mtr_commit(&mtr);
00567 
00568   return(err);
00569 }
00570 
00571 /***********************************************************/
00574 static
00575 ulint
00576 row_undo_mod_upd_del_sec(
00577 /*=====================*/
00578   undo_node_t*  node, 
00579   que_thr_t*  thr)  
00580 {
00581   mem_heap_t* heap;
00582   dtuple_t* entry;
00583   dict_index_t* index;
00584   ulint   err = DB_SUCCESS;
00585 
00586   ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
00587   heap = mem_heap_create(1024);
00588 
00589   while (node->index != NULL) {
00590     index = node->index;
00591 
00592     entry = row_build_index_entry(node->row, node->ext,
00593                 index, heap);
00594     if (UNIV_UNLIKELY(!entry)) {
00595       /* The database must have crashed after
00596       inserting a clustered index record but before
00597       writing all the externally stored columns of
00598       that record.  Because secondary index entries
00599       are inserted after the clustered index record,
00600       we may assume that the secondary index record
00601       does not exist.  However, this situation may
00602       only occur during the rollback of incomplete
00603       transactions. */
00604       ut_a(thr_is_recv(thr));
00605     } else {
00606       err = row_undo_mod_del_mark_or_remove_sec(
00607         node, thr, index, entry);
00608 
00609       if (err != DB_SUCCESS) {
00610 
00611         break;
00612       }
00613     }
00614 
00615     mem_heap_empty(heap);
00616 
00617     node->index = dict_table_get_next_index(node->index);
00618   }
00619 
00620   mem_heap_free(heap);
00621 
00622   return(err);
00623 }
00624 
00625 /***********************************************************/
00628 static
00629 ulint
00630 row_undo_mod_del_mark_sec(
00631 /*======================*/
00632   undo_node_t*  node, 
00633   que_thr_t*  thr)  
00634 {
00635   mem_heap_t* heap;
00636   dtuple_t* entry;
00637   dict_index_t* index;
00638   ulint   err;
00639 
00640   heap = mem_heap_create(1024);
00641 
00642   while (node->index != NULL) {
00643     index = node->index;
00644 
00645     entry = row_build_index_entry(node->row, node->ext,
00646                 index, heap);
00647     ut_a(entry);
00648     err = row_undo_mod_del_unmark_sec_and_undo_update(
00649       BTR_MODIFY_LEAF, thr, index, entry);
00650     if (err == DB_FAIL) {
00651       err = row_undo_mod_del_unmark_sec_and_undo_update(
00652         BTR_MODIFY_TREE, thr, index, entry);
00653     }
00654 
00655     if (err != DB_SUCCESS) {
00656 
00657       mem_heap_free(heap);
00658 
00659       return(err);
00660     }
00661 
00662     node->index = dict_table_get_next_index(node->index);
00663   }
00664 
00665   mem_heap_free(heap);
00666 
00667   return(DB_SUCCESS);
00668 }
00669 
00670 /***********************************************************/
00673 static
00674 ulint
00675 row_undo_mod_upd_exist_sec(
00676 /*=======================*/
00677   undo_node_t*  node, 
00678   que_thr_t*  thr)  
00679 {
00680   mem_heap_t* heap;
00681   dtuple_t* entry;
00682   dict_index_t* index;
00683   ulint   err;
00684 
00685   if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
00686     /* No change in secondary indexes */
00687 
00688     return(DB_SUCCESS);
00689   }
00690 
00691   heap = mem_heap_create(1024);
00692 
00693   while (node->index != NULL) {
00694     index = node->index;
00695 
00696     if (row_upd_changes_ord_field_binary(node->row, node->index,
00697                  node->update)) {
00698 
00699       /* Build the newest version of the index entry */
00700       entry = row_build_index_entry(node->row, node->ext,
00701                   index, heap);
00702       if (UNIV_UNLIKELY(!entry)) {
00703         /* The server must have crashed in
00704         row_upd_clust_rec_by_insert(), in
00705         row_ins_index_entry_low() before
00706         btr_store_big_rec_extern_fields()
00707         has written the externally stored columns
00708         (BLOBs) of the new clustered index entry. */
00709 
00710         /* The table must be in DYNAMIC or COMPRESSED
00711         format.  REDUNDANT and COMPACT formats
00712         store a local 768-byte prefix of each
00713         externally stored column. */
00714         ut_a(dict_table_get_format(index->table)
00715              >= DICT_TF_FORMAT_ZIP);
00716 
00717         /* This is only legitimate when
00718         rolling back an incomplete transaction
00719         after crash recovery. */
00720         ut_a(thr_get_trx(thr)->is_recovered);
00721 
00722         /* The server must have crashed before
00723         completing the insert of the new
00724         clustered index entry and before
00725         inserting to the secondary indexes.
00726         Because node->row was not yet written
00727         to this index, we can ignore it.  But
00728         we must restore node->undo_row. */
00729       } else {
00730         /* NOTE that if we updated the fields of a
00731         delete-marked secondary index record so that
00732         alphabetically they stayed the same, e.g.,
00733         'abc' -> 'aBc', we cannot return to the
00734         original values because we do not know them.
00735         But this should not cause problems because
00736         in row0sel.c, in queries we always retrieve
00737         the clustered index record or an earlier
00738         version of it, if the secondary index record
00739         through which we do the search is
00740         delete-marked. */
00741 
00742         err = row_undo_mod_del_mark_or_remove_sec(
00743           node, thr, index, entry);
00744         if (err != DB_SUCCESS) {
00745           mem_heap_free(heap);
00746 
00747           return(err);
00748         }
00749 
00750         mem_heap_empty(heap);
00751       }
00752 
00753       /* We may have to update the delete mark in the
00754       secondary index record of the previous version of
00755       the row. We also need to update the fields of
00756       the secondary index record if we updated its fields
00757       but alphabetically they stayed the same, e.g.,
00758       'abc' -> 'aBc'. */
00759       entry = row_build_index_entry(node->undo_row,
00760                   node->undo_ext,
00761                   index, heap);
00762       ut_a(entry);
00763 
00764       err = row_undo_mod_del_unmark_sec_and_undo_update(
00765         BTR_MODIFY_LEAF, thr, index, entry);
00766       if (err == DB_FAIL) {
00767         err = row_undo_mod_del_unmark_sec_and_undo_update(
00768           BTR_MODIFY_TREE, thr, index, entry);
00769       }
00770 
00771       if (err != DB_SUCCESS) {
00772         mem_heap_free(heap);
00773 
00774         return(err);
00775       }
00776     }
00777 
00778     node->index = dict_table_get_next_index(node->index);
00779   }
00780 
00781   mem_heap_free(heap);
00782 
00783   return(DB_SUCCESS);
00784 }
00785 
00786 /***********************************************************/
00788 static
00789 void
00790 row_undo_mod_parse_undo_rec(
00791 /*========================*/
00792   undo_node_t*  node, 
00793   que_thr_t*  thr)  
00794 {
00795   dict_index_t* clust_index;
00796   byte*   ptr;
00797   undo_no_t undo_no;
00798   table_id_t  table_id;
00799   trx_id_t  trx_id;
00800   roll_ptr_t  roll_ptr;
00801   ulint   info_bits;
00802   ulint   type;
00803   ulint   cmpl_info;
00804   ibool   dummy_extern;
00805   trx_t*    trx;
00806 
00807   ut_ad(node && thr);
00808   trx = thr_get_trx(thr);
00809   ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
00810             &dummy_extern, &undo_no, &table_id);
00811   node->rec_type = type;
00812 
00813   node->table = dict_table_get_on_id(table_id, trx);
00814 
00815   /* TODO: other fixes associated with DROP TABLE + rollback in the
00816   same table by another user */
00817 
00818   if (node->table == NULL) {
00819     /* Table was dropped */
00820     return;
00821   }
00822 
00823   if (node->table->ibd_file_missing) {
00824     /* We skip undo operations to missing .ibd files */
00825     node->table = NULL;
00826 
00827     return;
00828   }
00829 
00830   clust_index = dict_table_get_first_index(node->table);
00831 
00832   ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
00833                  &info_bits);
00834 
00835   ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
00836                node->heap);
00837 
00838   trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
00839                roll_ptr, info_bits, trx,
00840                node->heap, &(node->update));
00841   node->new_roll_ptr = roll_ptr;
00842   node->new_trx_id = trx_id;
00843   node->cmpl_info = cmpl_info;
00844 }
00845 
00846 /***********************************************************/
00849 UNIV_INTERN
00850 ulint
00851 row_undo_mod(
00852 /*=========*/
00853   undo_node_t*  node, 
00854   que_thr_t*  thr)  
00855 {
00856   ulint err;
00857 
00858   ut_ad(node && thr);
00859   ut_ad(node->state == UNDO_NODE_MODIFY);
00860 
00861   row_undo_mod_parse_undo_rec(node, thr);
00862 
00863   if (!node->table || !row_undo_search_clust_to_pcur(node)) {
00864     /* It is already undone, or will be undone by another query
00865     thread, or table was dropped */
00866 
00867     trx_undo_rec_release(node->trx, node->undo_no);
00868     node->state = UNDO_NODE_FETCH_NEXT;
00869 
00870     return(DB_SUCCESS);
00871   }
00872 
00873   node->index = dict_table_get_next_index(
00874     dict_table_get_first_index(node->table));
00875 
00876   if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
00877 
00878     err = row_undo_mod_upd_exist_sec(node, thr);
00879 
00880   } else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
00881 
00882     err = row_undo_mod_del_mark_sec(node, thr);
00883   } else {
00884     ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
00885     err = row_undo_mod_upd_del_sec(node, thr);
00886   }
00887 
00888   if (err != DB_SUCCESS) {
00889 
00890     return(err);
00891   }
00892 
00893   err = row_undo_mod_clust(node, thr);
00894 
00895   return(err);
00896 }