Drizzled Public API Documentation

trx0undo.cc

00001 /*****************************************************************************
00002 
00003 Copyright (C) 1996, 2009, 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 "trx0undo.h"
00027 
00028 #ifdef UNIV_NONINL
00029 #include "trx0undo.ic"
00030 #endif
00031 
00032 #include "fsp0fsp.h"
00033 #ifndef UNIV_HOTBACKUP
00034 #include "mach0data.h"
00035 #include "mtr0log.h"
00036 #include "trx0rseg.h"
00037 #include "trx0trx.h"
00038 #include "srv0srv.h"
00039 #include "trx0rec.h"
00040 #include "trx0purge.h"
00041 
00042 /* How should the old versions in the history list be managed?
00043    ----------------------------------------------------------
00044 If each transaction is given a whole page for its update undo log, file
00045 space consumption can be 10 times higher than necessary. Therefore,
00046 partly filled update undo log pages should be reusable. But then there
00047 is no way individual pages can be ordered so that the ordering agrees
00048 with the serialization numbers of the transactions on the pages. Thus,
00049 the history list must be formed of undo logs, not their header pages as
00050 it was in the old implementation.
00051   However, on a single header page the transactions are placed in
00052 the order of their serialization numbers. As old versions are purged, we
00053 may free the page when the last transaction on the page has been purged.
00054   A problem is that the purge has to go through the transactions
00055 in the serialization order. This means that we have to look through all
00056 rollback segments for the one that has the smallest transaction number
00057 in its history list.
00058   When should we do a purge? A purge is necessary when space is
00059 running out in any of the rollback segments. Then we may have to purge
00060 also old version which might be needed by some consistent read. How do
00061 we trigger the start of a purge? When a transaction writes to an undo log,
00062 it may notice that the space is running out. When a read view is closed,
00063 it may make some history superfluous. The server can have an utility which
00064 periodically checks if it can purge some history.
00065   In a parallellized purge we have the problem that a query thread
00066 can remove a delete marked clustered index record before another query
00067 thread has processed an earlier version of the record, which cannot then
00068 be done because the row cannot be constructed from the clustered index
00069 record. To avoid this problem, we will store in the update and delete mark
00070 undo record also the columns necessary to construct the secondary index
00071 entries which are modified.
00072   We can latch the stack of versions of a single clustered index record
00073 by taking a latch on the clustered index page. As long as the latch is held,
00074 no new versions can be added and no versions removed by undo. But, a purge
00075 can still remove old versions from the bottom of the stack. */
00076 
00077 /* How to protect rollback segments, undo logs, and history lists with
00078    -------------------------------------------------------------------
00079 latches?
00080 -------
00081 The contention of the kernel mutex should be minimized. When a transaction
00082 does its first insert or modify in an index, an undo log is assigned for it.
00083 Then we must have an x-latch to the rollback segment header.
00084   When the transaction does more modifys or rolls back, the undo log is
00085 protected with undo_mutex in the transaction.
00086   When the transaction commits, its insert undo log is either reset and
00087 cached for a fast reuse, or freed. In these cases we must have an x-latch on
00088 the rollback segment page. The update undo log is put to the history list. If
00089 it is not suitable for reuse, its slot in the rollback segment is reset. In
00090 both cases, an x-latch must be acquired on the rollback segment.
00091   The purge operation steps through the history list without modifying
00092 it until a truncate operation occurs, which can remove undo logs from the end
00093 of the list and release undo log segments. In stepping through the list,
00094 s-latches on the undo log pages are enough, but in a truncate, x-latches must
00095 be obtained on the rollback segment and individual pages. */
00096 #endif /* !UNIV_HOTBACKUP */
00097 
00098 /********************************************************************/
00100 static
00101 void
00102 trx_undo_page_init(
00103 /*===============*/
00104   page_t* undo_page,  
00105   ulint type,   
00106   mtr_t*  mtr);   
00108 #ifndef UNIV_HOTBACKUP
00109 /********************************************************************/
00112 static
00113 trx_undo_t*
00114 trx_undo_mem_create(
00115 /*================*/
00116   trx_rseg_t* rseg, 
00117   ulint   id, 
00118   ulint   type, 
00120   trx_id_t  trx_id, 
00122   const XID*  xid,  
00123   ulint   page_no,
00124   ulint   offset);
00125 #endif /* !UNIV_HOTBACKUP */
00126 /***************************************************************/
00131 static
00132 ulint
00133 trx_undo_insert_header_reuse(
00134 /*=========================*/
00135   page_t*   undo_page,  
00137   trx_id_t  trx_id,   
00138   mtr_t*    mtr);   
00139 /**********************************************************************/
00142 static
00143 void
00144 trx_undo_discard_latest_update_undo(
00145 /*================================*/
00146   page_t* undo_page,  
00147   mtr_t*  mtr);   
00149 #ifndef UNIV_HOTBACKUP
00150 /***********************************************************************/
00153 static
00154 trx_undo_rec_t*
00155 trx_undo_get_prev_rec_from_prev_page(
00156 /*=================================*/
00157   trx_undo_rec_t* rec,  
00158   ulint   page_no,
00159   ulint   offset, 
00160   mtr_t*    mtr)  
00161 {
00162   ulint space;
00163   ulint zip_size;
00164   ulint prev_page_no;
00165   page_t* prev_page;
00166   page_t* undo_page;
00167 
00168   undo_page = page_align(rec);
00169 
00170   prev_page_no = flst_get_prev_addr(undo_page + TRX_UNDO_PAGE_HDR
00171             + TRX_UNDO_PAGE_NODE, mtr)
00172     .page;
00173 
00174   if (prev_page_no == FIL_NULL) {
00175 
00176     return(NULL);
00177   }
00178 
00179   space = page_get_space_id(undo_page);
00180   zip_size = fil_space_get_zip_size(space);
00181 
00182   prev_page = trx_undo_page_get_s_latched(space, zip_size,
00183             prev_page_no, mtr);
00184 
00185   return(trx_undo_page_get_last_rec(prev_page, page_no, offset));
00186 }
00187 
00188 /***********************************************************************/
00191 UNIV_INTERN
00192 trx_undo_rec_t*
00193 trx_undo_get_prev_rec(
00194 /*==================*/
00195   trx_undo_rec_t* rec,  
00196   ulint   page_no,
00197   ulint   offset, 
00198   mtr_t*    mtr)  
00199 {
00200   trx_undo_rec_t* prev_rec;
00201 
00202   prev_rec = trx_undo_page_get_prev_rec(rec, page_no, offset);
00203 
00204   if (prev_rec) {
00205 
00206     return(prev_rec);
00207   }
00208 
00209   /* We have to go to the previous undo log page to look for the
00210   previous record */
00211 
00212   return(trx_undo_get_prev_rec_from_prev_page(rec, page_no, offset,
00213                 mtr));
00214 }
00215 
00216 /***********************************************************************/
00219 static
00220 trx_undo_rec_t*
00221 trx_undo_get_next_rec_from_next_page(
00222 /*=================================*/
00223   ulint space,  
00224   ulint zip_size,
00226   page_t* undo_page, 
00227   ulint page_no,
00228   ulint offset, 
00229   ulint mode, 
00230   mtr_t*  mtr)  
00231 {
00232   trx_ulogf_t*  log_hdr;
00233   ulint   next_page_no;
00234   page_t*   next_page;
00235   ulint   next;
00236 
00237   if (page_no == page_get_page_no(undo_page)) {
00238 
00239     log_hdr = undo_page + offset;
00240     next = mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG);
00241 
00242     if (next != 0) {
00243 
00244       return(NULL);
00245     }
00246   }
00247 
00248   next_page_no = flst_get_next_addr(undo_page + TRX_UNDO_PAGE_HDR
00249             + TRX_UNDO_PAGE_NODE, mtr)
00250     .page;
00251   if (next_page_no == FIL_NULL) {
00252 
00253     return(NULL);
00254   }
00255 
00256   if (mode == RW_S_LATCH) {
00257     next_page = trx_undo_page_get_s_latched(space, zip_size,
00258               next_page_no, mtr);
00259   } else {
00260     ut_ad(mode == RW_X_LATCH);
00261     next_page = trx_undo_page_get(space, zip_size,
00262                 next_page_no, mtr);
00263   }
00264 
00265   return(trx_undo_page_get_first_rec(next_page, page_no, offset));
00266 }
00267 
00268 /***********************************************************************/
00271 UNIV_INTERN
00272 trx_undo_rec_t*
00273 trx_undo_get_next_rec(
00274 /*==================*/
00275   trx_undo_rec_t* rec,  
00276   ulint   page_no,
00277   ulint   offset, 
00278   mtr_t*    mtr)  
00279 {
00280   ulint   space;
00281   ulint   zip_size;
00282   trx_undo_rec_t* next_rec;
00283 
00284   next_rec = trx_undo_page_get_next_rec(rec, page_no, offset);
00285 
00286   if (next_rec) {
00287     return(next_rec);
00288   }
00289 
00290   space = page_get_space_id(page_align(rec));
00291   zip_size = fil_space_get_zip_size(space);
00292 
00293   return(trx_undo_get_next_rec_from_next_page(space, zip_size,
00294                 page_align(rec),
00295                 page_no, offset,
00296                 RW_S_LATCH, mtr));
00297 }
00298 
00299 /***********************************************************************/
00302 UNIV_INTERN
00303 trx_undo_rec_t*
00304 trx_undo_get_first_rec(
00305 /*===================*/
00306   ulint space,  
00307   ulint zip_size,
00309   ulint page_no,
00310   ulint offset, 
00311   ulint mode, 
00312   mtr_t*  mtr)  
00313 {
00314   page_t*   undo_page;
00315   trx_undo_rec_t* rec;
00316 
00317   if (mode == RW_S_LATCH) {
00318     undo_page = trx_undo_page_get_s_latched(space, zip_size,
00319               page_no, mtr);
00320   } else {
00321     undo_page = trx_undo_page_get(space, zip_size, page_no, mtr);
00322   }
00323 
00324   rec = trx_undo_page_get_first_rec(undo_page, page_no, offset);
00325 
00326   if (rec) {
00327     return(rec);
00328   }
00329 
00330   return(trx_undo_get_next_rec_from_next_page(space, zip_size,
00331                 undo_page, page_no, offset,
00332                 mode, mtr));
00333 }
00334 
00335 /*============== UNDO LOG FILE COPY CREATION AND FREEING ==================*/
00336 
00337 /**********************************************************************/
00339 UNIV_INLINE
00340 void
00341 trx_undo_page_init_log(
00342 /*===================*/
00343   page_t* undo_page,  
00344   ulint type,   
00345   mtr_t*  mtr)    
00346 {
00347   mlog_write_initial_log_record(undo_page, MLOG_UNDO_INIT, mtr);
00348 
00349   mlog_catenate_ulint_compressed(mtr, type);
00350 }
00351 #else /* !UNIV_HOTBACKUP */
00352 # define trx_undo_page_init_log(undo_page,type,mtr) ((void) 0)
00353 #endif /* !UNIV_HOTBACKUP */
00354 
00355 /***********************************************************/
00358 UNIV_INTERN
00359 byte*
00360 trx_undo_parse_page_init(
00361 /*=====================*/
00362   byte* ptr,  
00363   byte* end_ptr,
00364   page_t* page, 
00365   mtr_t*  mtr)  
00366 {
00367   ulint type;
00368 
00369   ptr = mach_parse_compressed(ptr, end_ptr, &type);
00370 
00371   if (ptr == NULL) {
00372 
00373     return(NULL);
00374   }
00375 
00376   if (page) {
00377     trx_undo_page_init(page, type, mtr);
00378   }
00379 
00380   return(ptr);
00381 }
00382 
00383 /********************************************************************/
00385 static
00386 void
00387 trx_undo_page_init(
00388 /*===============*/
00389   page_t* undo_page,  
00390   ulint type,   
00391   mtr_t*  mtr)    
00392 {
00393   trx_upagef_t* page_hdr;
00394 
00395   page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
00396 
00397   mach_write_to_2(page_hdr + TRX_UNDO_PAGE_TYPE, type);
00398 
00399   mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START,
00400       TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE);
00401   mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE,
00402       TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE);
00403 
00404   fil_page_set_type(undo_page, FIL_PAGE_UNDO_LOG);
00405 
00406   trx_undo_page_init_log(undo_page, type, mtr);
00407 }
00408 
00409 #ifndef UNIV_HOTBACKUP
00410 /***************************************************************/
00414 static
00415 ulint
00416 trx_undo_seg_create(
00417 /*================*/
00418   trx_rseg_t* /*rseg*/,
00419   trx_rsegf_t*  rseg_hdr,
00421   ulint   type, 
00423   ulint*    id, 
00424   page_t**  undo_page,
00427   mtr_t*    mtr)  
00428 {
00429   ulint   slot_no;
00430   ulint   space;
00431   buf_block_t*  block;
00432   trx_upagef_t* page_hdr;
00433   trx_usegf_t*  seg_hdr;
00434   ulint   n_reserved;
00435   ibool   success;
00436   ulint   err = DB_SUCCESS;
00437 
00438   ut_ad(mtr && id && rseg_hdr);
00439   ut_ad(mutex_own(&(rseg->mutex)));
00440 
00441   /*  fputs(type == TRX_UNDO_INSERT
00442   ? "Creating insert undo log segment\n"
00443   : "Creating update undo log segment\n", stderr); */
00444   slot_no = trx_rsegf_undo_find_free(rseg_hdr, mtr);
00445 
00446   if (slot_no == ULINT_UNDEFINED) {
00447     ut_print_timestamp(stderr);
00448     fprintf(stderr,
00449       "  InnoDB: Warning: cannot find a free slot for"
00450       " an undo log. Do you have too\n"
00451       "InnoDB: many active transactions"
00452       " running concurrently?\n");
00453 
00454     return(DB_TOO_MANY_CONCURRENT_TRXS);
00455   }
00456 
00457   space = page_get_space_id(page_align(rseg_hdr));
00458 
00459   success = fsp_reserve_free_extents(&n_reserved, space, 2, FSP_UNDO,
00460              mtr);
00461   if (!success) {
00462 
00463     return(DB_OUT_OF_FILE_SPACE);
00464   }
00465 
00466   /* Allocate a new file segment for the undo log */
00467   block = fseg_create_general(space, 0,
00468             TRX_UNDO_SEG_HDR
00469             + TRX_UNDO_FSEG_HEADER, TRUE, mtr);
00470 
00471   fil_space_release_free_extents(space, n_reserved);
00472 
00473   if (block == NULL) {
00474     /* No space left */
00475 
00476     return(DB_OUT_OF_FILE_SPACE);
00477   }
00478 
00479   buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE);
00480 
00481   *undo_page = buf_block_get_frame(block);
00482 
00483   page_hdr = *undo_page + TRX_UNDO_PAGE_HDR;
00484   seg_hdr = *undo_page + TRX_UNDO_SEG_HDR;
00485 
00486   trx_undo_page_init(*undo_page, type, mtr);
00487 
00488   mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE,
00489        TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE,
00490        MLOG_2BYTES, mtr);
00491 
00492   mlog_write_ulint(seg_hdr + TRX_UNDO_LAST_LOG, 0, MLOG_2BYTES, mtr);
00493 
00494   flst_init(seg_hdr + TRX_UNDO_PAGE_LIST, mtr);
00495 
00496   flst_add_last(seg_hdr + TRX_UNDO_PAGE_LIST,
00497           page_hdr + TRX_UNDO_PAGE_NODE, mtr);
00498 
00499   trx_rsegf_set_nth_undo(rseg_hdr, slot_no,
00500              page_get_page_no(*undo_page), mtr);
00501   *id = slot_no;
00502 
00503   return(err);
00504 }
00505 
00506 /**********************************************************************/
00508 UNIV_INLINE
00509 void
00510 trx_undo_header_create_log(
00511 /*=======================*/
00512   const page_t* undo_page,  
00513   trx_id_t  trx_id,   
00514   mtr_t*    mtr)    
00515 {
00516   mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_CREATE, mtr);
00517 
00518   mlog_catenate_ull_compressed(mtr, trx_id);
00519 }
00520 #else /* !UNIV_HOTBACKUP */
00521 # define trx_undo_header_create_log(undo_page,trx_id,mtr) ((void) 0)
00522 #endif /* !UNIV_HOTBACKUP */
00523 
00524 /***************************************************************/
00529 static
00530 ulint
00531 trx_undo_header_create(
00532 /*===================*/
00533   page_t*   undo_page,  
00538   trx_id_t  trx_id,   
00539   mtr_t*    mtr)    
00540 {
00541   trx_upagef_t* page_hdr;
00542   trx_usegf_t*  seg_hdr;
00543   trx_ulogf_t*  log_hdr;
00544   trx_ulogf_t*  prev_log_hdr;
00545   ulint   prev_log;
00546   ulint   free;
00547   ulint   new_free;
00548 
00549   ut_ad(mtr && undo_page);
00550 
00551   page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
00552   seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
00553 
00554   free = mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE);
00555 
00556   log_hdr = undo_page + free;
00557 
00558   new_free = free + TRX_UNDO_LOG_OLD_HDR_SIZE;
00559 
00560   ut_a(free + TRX_UNDO_LOG_XA_HDR_SIZE < UNIV_PAGE_SIZE - 100);
00561 
00562   mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, new_free);
00563 
00564   mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, new_free);
00565 
00566   mach_write_to_2(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_ACTIVE);
00567 
00568   prev_log = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG);
00569 
00570   if (prev_log != 0) {
00571     prev_log_hdr = undo_page + prev_log;
00572 
00573     mach_write_to_2(prev_log_hdr + TRX_UNDO_NEXT_LOG, free);
00574   }
00575 
00576   mach_write_to_2(seg_hdr + TRX_UNDO_LAST_LOG, free);
00577 
00578   log_hdr = undo_page + free;
00579 
00580   mach_write_to_2(log_hdr + TRX_UNDO_DEL_MARKS, TRUE);
00581 
00582   mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id);
00583   mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free);
00584 
00585   mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE);
00586   mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE);
00587 
00588   mach_write_to_2(log_hdr + TRX_UNDO_NEXT_LOG, 0);
00589   mach_write_to_2(log_hdr + TRX_UNDO_PREV_LOG, prev_log);
00590 
00591   /* Write the log record about the header creation */
00592   trx_undo_header_create_log(undo_page, trx_id, mtr);
00593 
00594   return(free);
00595 }
00596 
00597 #ifndef UNIV_HOTBACKUP
00598 /********************************************************************/
00600 static
00601 void
00602 trx_undo_write_xid(
00603 /*===============*/
00604   trx_ulogf_t*  log_hdr,
00605   const XID*  xid,  
00606   mtr_t*    mtr)  
00607 {
00608   mlog_write_ulint(log_hdr + TRX_UNDO_XA_FORMAT,
00609        (ulint)xid->formatID, MLOG_4BYTES, mtr);
00610 
00611   mlog_write_ulint(log_hdr + TRX_UNDO_XA_TRID_LEN,
00612        (ulint)xid->gtrid_length, MLOG_4BYTES, mtr);
00613 
00614   mlog_write_ulint(log_hdr + TRX_UNDO_XA_BQUAL_LEN,
00615        (ulint)xid->bqual_length, MLOG_4BYTES, mtr);
00616 
00617   mlog_write_string(log_hdr + TRX_UNDO_XA_XID, (const byte*) xid->data,
00618         XIDDATASIZE, mtr);
00619 }
00620 
00621 /********************************************************************/
00623 static
00624 void
00625 trx_undo_read_xid(
00626 /*==============*/
00627   trx_ulogf_t*  log_hdr,
00628   XID*    xid)  
00629 {
00630   xid->formatID = (long)mach_read_from_4(log_hdr + TRX_UNDO_XA_FORMAT);
00631 
00632   xid->gtrid_length
00633     = (long) mach_read_from_4(log_hdr + TRX_UNDO_XA_TRID_LEN);
00634   xid->bqual_length
00635     = (long) mach_read_from_4(log_hdr + TRX_UNDO_XA_BQUAL_LEN);
00636 
00637   memcpy(xid->data, log_hdr + TRX_UNDO_XA_XID, XIDDATASIZE);
00638 }
00639 
00640 /***************************************************************/
00642 static
00643 void
00644 trx_undo_header_add_space_for_xid(
00645 /*==============================*/
00646   page_t*   undo_page,
00647   trx_ulogf_t*  log_hdr,
00648   mtr_t*    mtr)  
00649 {
00650   trx_upagef_t* page_hdr;
00651   ulint   free;
00652   ulint   new_free;
00653 
00654   page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
00655 
00656   free = mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE);
00657 
00658   /* free is now the end offset of the old style undo log header */
00659 
00660   ut_a(free == (ulint)(log_hdr - undo_page) + TRX_UNDO_LOG_OLD_HDR_SIZE);
00661 
00662   new_free = free + (TRX_UNDO_LOG_XA_HDR_SIZE
00663          - TRX_UNDO_LOG_OLD_HDR_SIZE);
00664 
00665   /* Add space for a XID after the header, update the free offset
00666   fields on the undo log page and in the undo log header */
00667 
00668   mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_START, new_free,
00669        MLOG_2BYTES, mtr);
00670 
00671   mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE, new_free,
00672        MLOG_2BYTES, mtr);
00673 
00674   mlog_write_ulint(log_hdr + TRX_UNDO_LOG_START, new_free,
00675        MLOG_2BYTES, mtr);
00676 }
00677 
00678 /**********************************************************************/
00680 UNIV_INLINE
00681 void
00682 trx_undo_insert_header_reuse_log(
00683 /*=============================*/
00684   const page_t* undo_page,  
00685   trx_id_t  trx_id,   
00686   mtr_t*    mtr)    
00687 {
00688   mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_REUSE, mtr);
00689 
00690   mlog_catenate_ull_compressed(mtr, trx_id);
00691 }
00692 #else /* !UNIV_HOTBACKUP */
00693 # define trx_undo_insert_header_reuse_log(undo_page,trx_id,mtr) ((void) 0)
00694 #endif /* !UNIV_HOTBACKUP */
00695 
00696 /***********************************************************/
00699 UNIV_INTERN
00700 byte*
00701 trx_undo_parse_page_header(
00702 /*=======================*/
00703   ulint type, 
00704   byte* ptr,  
00705   byte* end_ptr,
00706   page_t* page, 
00707   mtr_t*  mtr)  
00708 {
00709   trx_id_t  trx_id;
00710   /* Silence a GCC warning about possibly uninitialized variable
00711   when mach_ull_parse_compressed() is not inlined. */
00712   ut_d(trx_id = 0);
00713   /* Declare the variable uninitialized in Valgrind, so that the
00714   above initialization will not mask any bugs. */
00715   UNIV_MEM_INVALID(&trx_id, sizeof trx_id);
00716 
00717   ptr = mach_ull_parse_compressed(ptr, end_ptr, &trx_id);
00718 
00719   if (ptr == NULL) {
00720 
00721     return(NULL);
00722   }
00723 
00724   if (page) {
00725     if (type == MLOG_UNDO_HDR_CREATE) {
00726       trx_undo_header_create(page, trx_id, mtr);
00727     } else {
00728       ut_ad(type == MLOG_UNDO_HDR_REUSE);
00729       trx_undo_insert_header_reuse(page, trx_id, mtr);
00730     }
00731   }
00732 
00733   return(ptr);
00734 }
00735 
00736 /***************************************************************/
00741 static
00742 ulint
00743 trx_undo_insert_header_reuse(
00744 /*=========================*/
00745   page_t*   undo_page,  
00747   trx_id_t  trx_id,   
00748   mtr_t*    mtr)    
00749 {
00750   trx_upagef_t* page_hdr;
00751   trx_usegf_t*  seg_hdr;
00752   trx_ulogf_t*  log_hdr;
00753   ulint   free;
00754   ulint   new_free;
00755 
00756   ut_ad(mtr && undo_page);
00757 
00758   page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
00759   seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
00760 
00761   free = TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE;
00762 
00763   ut_a(free + TRX_UNDO_LOG_XA_HDR_SIZE < UNIV_PAGE_SIZE - 100);
00764 
00765   log_hdr = undo_page + free;
00766 
00767   new_free = free + TRX_UNDO_LOG_OLD_HDR_SIZE;
00768 
00769   /* Insert undo data is not needed after commit: we may free all
00770   the space on the page */
00771 
00772   ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
00773             + TRX_UNDO_PAGE_TYPE)
00774        == TRX_UNDO_INSERT);
00775 
00776   mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, new_free);
00777 
00778   mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, new_free);
00779 
00780   mach_write_to_2(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_ACTIVE);
00781 
00782   log_hdr = undo_page + free;
00783 
00784   mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id);
00785   mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free);
00786 
00787   mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE);
00788   mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE);
00789 
00790   /* Write the log record MLOG_UNDO_HDR_REUSE */
00791   trx_undo_insert_header_reuse_log(undo_page, trx_id, mtr);
00792 
00793   return(free);
00794 }
00795 
00796 #ifndef UNIV_HOTBACKUP
00797 /**********************************************************************/
00799 UNIV_INLINE
00800 void
00801 trx_undo_discard_latest_log(
00802 /*========================*/
00803   page_t* undo_page,  
00804   mtr_t*  mtr)    
00805 {
00806   mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_DISCARD, mtr);
00807 }
00808 #else /* !UNIV_HOTBACKUP */
00809 # define trx_undo_discard_latest_log(undo_page, mtr) ((void) 0)
00810 #endif /* !UNIV_HOTBACKUP */
00811 
00812 /***********************************************************/
00815 UNIV_INTERN
00816 byte*
00817 trx_undo_parse_discard_latest(
00818 /*==========================*/
00819   byte* ptr,  
00820   byte* /*end_ptr*/, 
00821   page_t* page, 
00822   mtr_t*  mtr)  
00823 {
00824   ut_ad(end_ptr);
00825 
00826   if (page) {
00827     trx_undo_discard_latest_update_undo(page, mtr);
00828   }
00829 
00830   return(ptr);
00831 }
00832 
00833 /**********************************************************************/
00836 static
00837 void
00838 trx_undo_discard_latest_update_undo(
00839 /*================================*/
00840   page_t* undo_page,  
00841   mtr_t*  mtr)    
00842 {
00843   trx_usegf_t*  seg_hdr;
00844   trx_upagef_t* page_hdr;
00845   trx_ulogf_t*  log_hdr;
00846   trx_ulogf_t*  prev_log_hdr;
00847   ulint   free;
00848   ulint   prev_hdr_offset;
00849 
00850   seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
00851   page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
00852 
00853   free = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG);
00854   log_hdr = undo_page + free;
00855 
00856   prev_hdr_offset = mach_read_from_2(log_hdr + TRX_UNDO_PREV_LOG);
00857 
00858   if (prev_hdr_offset != 0) {
00859     prev_log_hdr = undo_page + prev_hdr_offset;
00860 
00861     mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START,
00862         mach_read_from_2(prev_log_hdr
00863              + TRX_UNDO_LOG_START));
00864     mach_write_to_2(prev_log_hdr + TRX_UNDO_NEXT_LOG, 0);
00865   }
00866 
00867   mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, free);
00868 
00869   mach_write_to_2(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_CACHED);
00870   mach_write_to_2(seg_hdr + TRX_UNDO_LAST_LOG, prev_hdr_offset);
00871 
00872   trx_undo_discard_latest_log(undo_page, mtr);
00873 }
00874 
00875 #ifndef UNIV_HOTBACKUP
00876 /********************************************************************/
00879 UNIV_INTERN
00880 ulint
00881 trx_undo_add_page(
00882 /*==============*/
00883   trx_t*    trx,  
00884   trx_undo_t* undo, 
00885   mtr_t*    mtr)  
00888 {
00889   page_t*   header_page;
00890   page_t*   new_page;
00891   trx_rseg_t* rseg;
00892   ulint   page_no;
00893   ulint   n_reserved;
00894   ibool   success;
00895 
00896   ut_ad(mutex_own(&(trx->undo_mutex)));
00897   ut_ad(!mutex_own(&kernel_mutex));
00898   ut_ad(mutex_own(&(trx->rseg->mutex)));
00899 
00900   rseg = trx->rseg;
00901 
00902   if (rseg->curr_size == rseg->max_size) {
00903 
00904     return(FIL_NULL);
00905   }
00906 
00907   header_page = trx_undo_page_get(undo->space, undo->zip_size,
00908           undo->hdr_page_no, mtr);
00909 
00910   success = fsp_reserve_free_extents(&n_reserved, undo->space, 1,
00911              FSP_UNDO, mtr);
00912   if (!success) {
00913 
00914     return(FIL_NULL);
00915   }
00916 
00917   page_no = fseg_alloc_free_page_general(header_page + TRX_UNDO_SEG_HDR
00918                  + TRX_UNDO_FSEG_HEADER,
00919                  undo->top_page_no + 1, FSP_UP,
00920                  TRUE, mtr);
00921 
00922   fil_space_release_free_extents(undo->space, n_reserved);
00923 
00924   if (page_no == FIL_NULL) {
00925 
00926     /* No space left */
00927 
00928     return(FIL_NULL);
00929   }
00930 
00931   undo->last_page_no = page_no;
00932 
00933   new_page = trx_undo_page_get(undo->space, undo->zip_size,
00934              page_no, mtr);
00935 
00936   trx_undo_page_init(new_page, undo->type, mtr);
00937 
00938   flst_add_last(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST,
00939           new_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr);
00940   undo->size++;
00941   rseg->curr_size++;
00942 
00943   return(page_no);
00944 }
00945 
00946 /********************************************************************/
00949 static
00950 ulint
00951 trx_undo_free_page(
00952 /*===============*/
00953   trx_rseg_t* rseg, 
00954   ibool in_history, 
00956   ulint space,    
00957   ulint hdr_page_no,  
00958   ulint page_no,  
00960   mtr_t*  mtr)    
00963 {
00964   page_t*   header_page;
00965   page_t*   undo_page;
00966   fil_addr_t  last_addr;
00967   trx_rsegf_t*  rseg_header;
00968   ulint   hist_size;
00969   ulint   zip_size;
00970 
00971   ut_a(hdr_page_no != page_no);
00972   ut_ad(!mutex_own(&kernel_mutex));
00973   ut_ad(mutex_own(&(rseg->mutex)));
00974 
00975   zip_size = rseg->zip_size;
00976 
00977   undo_page = trx_undo_page_get(space, zip_size, page_no, mtr);
00978 
00979   header_page = trx_undo_page_get(space, zip_size, hdr_page_no, mtr);
00980 
00981   flst_remove(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST,
00982         undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr);
00983 
00984   fseg_free_page(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER,
00985            space, page_no, mtr);
00986 
00987   last_addr = flst_get_last(header_page + TRX_UNDO_SEG_HDR
00988           + TRX_UNDO_PAGE_LIST, mtr);
00989   rseg->curr_size--;
00990 
00991   if (in_history) {
00992     rseg_header = trx_rsegf_get(space, zip_size,
00993               rseg->page_no, mtr);
00994 
00995     hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
00996              MLOG_4BYTES, mtr);
00997     ut_ad(hist_size > 0);
00998     mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
00999          hist_size - 1, MLOG_4BYTES, mtr);
01000   }
01001 
01002   return(last_addr.page);
01003 }
01004 
01005 /********************************************************************/
01008 static
01009 void
01010 trx_undo_free_page_in_rollback(
01011 /*===========================*/
01012   trx_t*    /*trx*/, 
01013   trx_undo_t* undo, 
01014   ulint   page_no,
01016   mtr_t*    mtr)  
01019 {
01020   ulint last_page_no;
01021 
01022   ut_ad(undo->hdr_page_no != page_no);
01023   ut_ad(mutex_own(&(trx->undo_mutex)));
01024 
01025   last_page_no = trx_undo_free_page(undo->rseg, FALSE, undo->space,
01026             undo->hdr_page_no, page_no, mtr);
01027 
01028   undo->last_page_no = last_page_no;
01029   undo->size--;
01030 }
01031 
01032 /********************************************************************/
01035 static
01036 void
01037 trx_undo_empty_header_page(
01038 /*=======================*/
01039   ulint space,    
01040   ulint zip_size, 
01042   ulint hdr_page_no,  
01043   ulint hdr_offset, 
01044   mtr_t*  mtr)    
01045 {
01046   page_t*   header_page;
01047   trx_ulogf_t*  log_hdr;
01048   ulint   end;
01049 
01050   header_page = trx_undo_page_get(space, zip_size, hdr_page_no, mtr);
01051 
01052   log_hdr = header_page + hdr_offset;
01053 
01054   end = trx_undo_page_get_end(header_page, hdr_page_no, hdr_offset);
01055 
01056   mlog_write_ulint(log_hdr + TRX_UNDO_LOG_START, end, MLOG_2BYTES, mtr);
01057 }
01058 
01059 /***********************************************************************/
01062 UNIV_INTERN
01063 void
01064 trx_undo_truncate_end(
01065 /*==================*/
01066   trx_t*    trx,  
01067   trx_undo_t* undo, 
01068   undo_no_t limit)  
01070 {
01071   page_t*   undo_page;
01072   ulint   last_page_no;
01073   trx_undo_rec_t* rec;
01074   trx_undo_rec_t* trunc_here;
01075   mtr_t   mtr;
01076 
01077   ut_ad(mutex_own(&(trx->undo_mutex)));
01078   ut_ad(mutex_own(&(trx->rseg->mutex)));
01079 
01080   for (;;) {
01081     mtr_start(&mtr);
01082 
01083     trunc_here = NULL;
01084 
01085     last_page_no = undo->last_page_no;
01086 
01087     undo_page = trx_undo_page_get(undo->space, undo->zip_size,
01088                 last_page_no, &mtr);
01089 
01090     rec = trx_undo_page_get_last_rec(undo_page, undo->hdr_page_no,
01091              undo->hdr_offset);
01092     for (;;) {
01093       if (rec == NULL) {
01094         if (last_page_no == undo->hdr_page_no) {
01095 
01096           goto function_exit;
01097         }
01098 
01099         trx_undo_free_page_in_rollback(
01100           trx, undo, last_page_no, &mtr);
01101         break;
01102       }
01103 
01104       if (trx_undo_rec_get_undo_no(rec) >= limit) {
01105         /* Truncate at least this record off, maybe
01106         more */
01107         trunc_here = rec;
01108       } else {
01109         goto function_exit;
01110       }
01111 
01112       rec = trx_undo_page_get_prev_rec(rec,
01113                undo->hdr_page_no,
01114                undo->hdr_offset);
01115     }
01116 
01117     mtr_commit(&mtr);
01118   }
01119 
01120 function_exit:
01121   if (trunc_here) {
01122     mlog_write_ulint(undo_page + TRX_UNDO_PAGE_HDR
01123          + TRX_UNDO_PAGE_FREE,
01124          trunc_here - undo_page, MLOG_2BYTES, &mtr);
01125   }
01126 
01127   mtr_commit(&mtr);
01128 }
01129 
01130 /***********************************************************************/
01133 UNIV_INTERN
01134 void
01135 trx_undo_truncate_start(
01136 /*====================*/
01137   trx_rseg_t* rseg,   
01138   ulint   space,    
01139   ulint   hdr_page_no,  
01140   ulint   hdr_offset, 
01141   undo_no_t limit)    
01148 {
01149   page_t*   undo_page;
01150   trx_undo_rec_t* rec;
01151   trx_undo_rec_t* last_rec;
01152   ulint   page_no;
01153   mtr_t   mtr;
01154 
01155   ut_ad(mutex_own(&(rseg->mutex)));
01156 
01157   if (!limit) {
01158 
01159     return;
01160   }
01161 loop:
01162   mtr_start(&mtr);
01163 
01164   rec = trx_undo_get_first_rec(space, rseg->zip_size,
01165              hdr_page_no, hdr_offset,
01166              RW_X_LATCH, &mtr);
01167   if (rec == NULL) {
01168     /* Already empty */
01169 
01170     mtr_commit(&mtr);
01171 
01172     return;
01173   }
01174 
01175   undo_page = page_align(rec);
01176 
01177   last_rec = trx_undo_page_get_last_rec(undo_page, hdr_page_no,
01178                 hdr_offset);
01179   if (trx_undo_rec_get_undo_no(last_rec) >= limit) {
01180 
01181     mtr_commit(&mtr);
01182 
01183     return;
01184   }
01185 
01186   page_no = page_get_page_no(undo_page);
01187 
01188   if (page_no == hdr_page_no) {
01189     trx_undo_empty_header_page(space, rseg->zip_size,
01190              hdr_page_no, hdr_offset,
01191              &mtr);
01192   } else {
01193     trx_undo_free_page(rseg, TRUE, space, hdr_page_no,
01194            page_no, &mtr);
01195   }
01196 
01197   mtr_commit(&mtr);
01198 
01199   goto loop;
01200 }
01201 
01202 /**********************************************************************/
01204 static
01205 void
01206 trx_undo_seg_free(
01207 /*==============*/
01208   trx_undo_t* undo) 
01209 {
01210   trx_rseg_t* rseg;
01211   fseg_header_t*  file_seg;
01212   trx_rsegf_t*  rseg_header;
01213   trx_usegf_t*  seg_header;
01214   ibool   finished;
01215   mtr_t   mtr;
01216 
01217   rseg = undo->rseg;
01218 
01219   do {
01220 
01221     mtr_start(&mtr);
01222 
01223     ut_ad(!mutex_own(&kernel_mutex));
01224 
01225     mutex_enter(&(rseg->mutex));
01226 
01227     seg_header = trx_undo_page_get(undo->space, undo->zip_size,
01228                  undo->hdr_page_no,
01229                  &mtr) + TRX_UNDO_SEG_HDR;
01230 
01231     file_seg = seg_header + TRX_UNDO_FSEG_HEADER;
01232 
01233     finished = fseg_free_step(file_seg, &mtr);
01234 
01235     if (finished) {
01236       /* Update the rseg header */
01237       rseg_header = trx_rsegf_get(
01238         rseg->space, rseg->zip_size, rseg->page_no,
01239         &mtr);
01240       trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL,
01241                  &mtr);
01242     }
01243 
01244     mutex_exit(&(rseg->mutex));
01245     mtr_commit(&mtr);
01246   } while (!finished);
01247 }
01248 
01249 /*========== UNDO LOG MEMORY COPY INITIALIZATION =====================*/
01250 
01251 /********************************************************************/
01256 static
01257 trx_undo_t*
01258 trx_undo_mem_create_at_db_start(
01259 /*============================*/
01260   trx_rseg_t* rseg, 
01261   ulint   id, 
01262   ulint   page_no,
01263   mtr_t*    mtr)  
01264 {
01265   page_t*   undo_page;
01266   trx_upagef_t* page_header;
01267   trx_usegf_t*  seg_header;
01268   trx_ulogf_t*  undo_header;
01269   trx_undo_t* undo;
01270   ulint   type;
01271   ulint   state;
01272   trx_id_t  trx_id;
01273   ulint   offset;
01274   fil_addr_t  last_addr;
01275   page_t*   last_page;
01276   trx_undo_rec_t* rec;
01277   XID   xid;
01278   ibool   xid_exists = FALSE;
01279 
01280   if (id >= TRX_RSEG_N_SLOTS) {
01281     fprintf(stderr,
01282       "InnoDB: Error: undo->id is %lu\n", (ulong) id);
01283     ut_error;
01284   }
01285 
01286   undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
01287               page_no, mtr);
01288 
01289   page_header = undo_page + TRX_UNDO_PAGE_HDR;
01290 
01291   type = mtr_read_ulint(page_header + TRX_UNDO_PAGE_TYPE, MLOG_2BYTES,
01292             mtr);
01293   seg_header = undo_page + TRX_UNDO_SEG_HDR;
01294 
01295   state = mach_read_from_2(seg_header + TRX_UNDO_STATE);
01296 
01297   offset = mach_read_from_2(seg_header + TRX_UNDO_LAST_LOG);
01298 
01299   undo_header = undo_page + offset;
01300 
01301   trx_id = mach_read_from_8(undo_header + TRX_UNDO_TRX_ID);
01302 
01303   xid_exists = mtr_read_ulint(undo_header + TRX_UNDO_XID_EXISTS,
01304             MLOG_1BYTE, mtr);
01305 
01306   /* Read X/Open XA transaction identification if it exists, or
01307   set it to NULL. */
01308 
01309   memset(&xid, 0, sizeof(xid));
01310   xid.formatID = -1;
01311 
01312   if (xid_exists == TRUE) {
01313     trx_undo_read_xid(undo_header, &xid);
01314   }
01315 
01316   mutex_enter(&(rseg->mutex));
01317 
01318   undo = trx_undo_mem_create(rseg, id, type, trx_id, &xid,
01319            page_no, offset);
01320   mutex_exit(&(rseg->mutex));
01321 
01322   undo->dict_operation =  mtr_read_ulint(
01323     undo_header + TRX_UNDO_DICT_TRANS, MLOG_1BYTE, mtr);
01324 
01325   undo->table_id = mach_read_from_8(undo_header + TRX_UNDO_TABLE_ID);
01326   undo->state = state;
01327   undo->size = flst_get_len(seg_header + TRX_UNDO_PAGE_LIST, mtr);
01328 
01329   /* If the log segment is being freed, the page list is inconsistent! */
01330   if (state == TRX_UNDO_TO_FREE) {
01331 
01332     goto add_to_list;
01333   }
01334 
01335   last_addr = flst_get_last(seg_header + TRX_UNDO_PAGE_LIST, mtr);
01336 
01337   undo->last_page_no = last_addr.page;
01338   undo->top_page_no = last_addr.page;
01339 
01340   last_page = trx_undo_page_get(rseg->space, rseg->zip_size,
01341               undo->last_page_no, mtr);
01342 
01343   rec = trx_undo_page_get_last_rec(last_page, page_no, offset);
01344 
01345   if (rec == NULL) {
01346     undo->empty = TRUE;
01347   } else {
01348     undo->empty = FALSE;
01349     undo->top_offset = rec - last_page;
01350     undo->top_undo_no = trx_undo_rec_get_undo_no(rec);
01351   }
01352 add_to_list:
01353   if (type == TRX_UNDO_INSERT) {
01354     if (state != TRX_UNDO_CACHED) {
01355       UT_LIST_ADD_LAST(undo_list, rseg->insert_undo_list,
01356            undo);
01357     } else {
01358       UT_LIST_ADD_LAST(undo_list, rseg->insert_undo_cached,
01359            undo);
01360     }
01361   } else {
01362     ut_ad(type == TRX_UNDO_UPDATE);
01363     if (state != TRX_UNDO_CACHED) {
01364       UT_LIST_ADD_LAST(undo_list, rseg->update_undo_list,
01365            undo);
01366     } else {
01367       UT_LIST_ADD_LAST(undo_list, rseg->update_undo_cached,
01368            undo);
01369     }
01370   }
01371 
01372   return(undo);
01373 }
01374 
01375 /********************************************************************/
01380 UNIV_INTERN
01381 ulint
01382 trx_undo_lists_init(
01383 /*================*/
01384   trx_rseg_t* rseg) 
01385 {
01386   ulint   page_no;
01387   trx_undo_t* undo;
01388   ulint   size  = 0;
01389   trx_rsegf_t*  rseg_header;
01390   ulint   i;
01391   mtr_t   mtr;
01392 
01393   UT_LIST_INIT(rseg->update_undo_list);
01394   UT_LIST_INIT(rseg->update_undo_cached);
01395   UT_LIST_INIT(rseg->insert_undo_list);
01396   UT_LIST_INIT(rseg->insert_undo_cached);
01397 
01398   mtr_start(&mtr);
01399 
01400   rseg_header = trx_rsegf_get_new(rseg->space, rseg->zip_size,
01401           rseg->page_no, &mtr);
01402 
01403   for (i = 0; i < TRX_RSEG_N_SLOTS; i++) {
01404     page_no = trx_rsegf_get_nth_undo(rseg_header, i, &mtr);
01405 
01406     /* In forced recovery: try to avoid operations which look
01407     at database pages; undo logs are rapidly changing data, and
01408     the probability that they are in an inconsistent state is
01409     high */
01410 
01411     if (page_no != FIL_NULL
01412         && srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) {
01413 
01414       undo = trx_undo_mem_create_at_db_start(rseg, i,
01415                      page_no, &mtr);
01416       size += undo->size;
01417 
01418       mtr_commit(&mtr);
01419 
01420       mtr_start(&mtr);
01421 
01422       rseg_header = trx_rsegf_get(
01423         rseg->space, rseg->zip_size, rseg->page_no,
01424         &mtr);
01425     }
01426   }
01427 
01428   mtr_commit(&mtr);
01429 
01430   return(size);
01431 }
01432 
01433 /********************************************************************/
01436 static
01437 trx_undo_t*
01438 trx_undo_mem_create(
01439 /*================*/
01440   trx_rseg_t* rseg, 
01441   ulint   id, 
01442   ulint   type, 
01444   trx_id_t  trx_id, 
01446   const XID*  xid,  
01447   ulint   page_no,
01448   ulint   offset) 
01449 {
01450   trx_undo_t* undo;
01451 
01452   ut_ad(mutex_own(&(rseg->mutex)));
01453 
01454   if (id >= TRX_RSEG_N_SLOTS) {
01455     fprintf(stderr,
01456       "InnoDB: Error: undo->id is %lu\n", (ulong) id);
01457     ut_error;
01458   }
01459 
01460   undo = static_cast<trx_undo_t *>(mem_alloc(sizeof(trx_undo_t)));
01461 
01462   if (undo == NULL) {
01463 
01464     return NULL;
01465   }
01466 
01467   undo->id = id;
01468   undo->type = type;
01469   undo->state = TRX_UNDO_ACTIVE;
01470   undo->del_marks = FALSE;
01471   undo->trx_id = trx_id;
01472   undo->xid = *xid;
01473 
01474   undo->dict_operation = FALSE;
01475 
01476   undo->rseg = rseg;
01477 
01478   undo->space = rseg->space;
01479   undo->zip_size = rseg->zip_size;
01480   undo->hdr_page_no = page_no;
01481   undo->hdr_offset = offset;
01482   undo->last_page_no = page_no;
01483   undo->size = 1;
01484 
01485   undo->empty = TRUE;
01486   undo->top_page_no = page_no;
01487   undo->guess_block = NULL;
01488 
01489   return(undo);
01490 }
01491 
01492 /********************************************************************/
01494 static
01495 void
01496 trx_undo_mem_init_for_reuse(
01497 /*========================*/
01498   trx_undo_t* undo, 
01499   trx_id_t  trx_id, 
01501   const XID*  xid,  
01502   ulint   offset) 
01503 {
01504   ut_ad(mutex_own(&((undo->rseg)->mutex)));
01505 
01506   if (UNIV_UNLIKELY(undo->id >= TRX_RSEG_N_SLOTS)) {
01507     fprintf(stderr, "InnoDB: Error: undo->id is %lu\n",
01508       (ulong) undo->id);
01509 
01510     mem_analyze_corruption(undo);
01511     ut_error;
01512   }
01513 
01514   undo->state = TRX_UNDO_ACTIVE;
01515   undo->del_marks = FALSE;
01516   undo->trx_id = trx_id;
01517   undo->xid = *xid;
01518 
01519   undo->dict_operation = FALSE;
01520 
01521   undo->hdr_offset = offset;
01522   undo->empty = TRUE;
01523 }
01524 
01525 /********************************************************************/
01527 UNIV_INTERN
01528 void
01529 trx_undo_mem_free(
01530 /*==============*/
01531   trx_undo_t* undo) 
01532 {
01533   if (undo->id >= TRX_RSEG_N_SLOTS) {
01534     fprintf(stderr,
01535       "InnoDB: Error: undo->id is %lu\n", (ulong) undo->id);
01536     ut_error;
01537   }
01538 
01539   mem_free(undo);
01540 }
01541 
01542 /**********************************************************************/
01547 static
01548 ulint
01549 trx_undo_create(
01550 /*============*/
01551   trx_t*    trx,  
01552   trx_rseg_t* rseg, 
01553   ulint   type, 
01555   trx_id_t  trx_id, 
01557   const XID*  xid,  
01558   trx_undo_t**  undo, 
01560   mtr_t*    mtr)  
01561 {
01562   trx_rsegf_t*  rseg_header;
01563   ulint   page_no;
01564   ulint   offset;
01565   ulint   id;
01566   page_t*   undo_page;
01567   ulint   err;
01568 
01569   ut_ad(mutex_own(&(rseg->mutex)));
01570 
01571   if (rseg->curr_size == rseg->max_size) {
01572 
01573     return(DB_OUT_OF_FILE_SPACE);
01574   }
01575 
01576   rseg->curr_size++;
01577 
01578   rseg_header = trx_rsegf_get(rseg->space, rseg->zip_size, rseg->page_no,
01579             mtr);
01580 
01581   err = trx_undo_seg_create(rseg, rseg_header, type, &id,
01582           &undo_page, mtr);
01583 
01584   if (err != DB_SUCCESS) {
01585     /* Did not succeed */
01586 
01587     rseg->curr_size--;
01588 
01589     return(err);
01590   }
01591 
01592   page_no = page_get_page_no(undo_page);
01593 
01594   offset = trx_undo_header_create(undo_page, trx_id, mtr);
01595 
01596   if (trx->support_xa) {
01597     trx_undo_header_add_space_for_xid(undo_page,
01598               undo_page + offset, mtr);
01599   }
01600 
01601   *undo = trx_undo_mem_create(rseg, id, type, trx_id, xid,
01602            page_no, offset);
01603   if (*undo == NULL) {
01604 
01605     err = DB_OUT_OF_MEMORY;
01606   }
01607 
01608   return(err);
01609 }
01610 
01611 /*================ UNDO LOG ASSIGNMENT AND CLEANUP =====================*/
01612 
01613 /********************************************************************/
01616 static
01617 trx_undo_t*
01618 trx_undo_reuse_cached(
01619 /*==================*/
01620   trx_t*    trx,  
01621   trx_rseg_t* rseg, 
01622   ulint   type, 
01624   trx_id_t  trx_id, 
01626   const XID*  xid,  
01627   mtr_t*    mtr)  
01628 {
01629   trx_undo_t* undo;
01630   page_t*   undo_page;
01631   ulint   offset;
01632 
01633   ut_ad(mutex_own(&(rseg->mutex)));
01634 
01635   if (type == TRX_UNDO_INSERT) {
01636 
01637     undo = UT_LIST_GET_FIRST(rseg->insert_undo_cached);
01638     if (undo == NULL) {
01639 
01640       return(NULL);
01641     }
01642 
01643     UT_LIST_REMOVE(undo_list, rseg->insert_undo_cached, undo);
01644   } else {
01645     ut_ad(type == TRX_UNDO_UPDATE);
01646 
01647     undo = UT_LIST_GET_FIRST(rseg->update_undo_cached);
01648     if (undo == NULL) {
01649 
01650       return(NULL);
01651     }
01652 
01653     UT_LIST_REMOVE(undo_list, rseg->update_undo_cached, undo);
01654   }
01655 
01656   ut_ad(undo->size == 1);
01657 
01658   if (undo->id >= TRX_RSEG_N_SLOTS) {
01659     fprintf(stderr, "InnoDB: Error: undo->id is %lu\n",
01660       (ulong) undo->id);
01661     mem_analyze_corruption(undo);
01662     ut_error;
01663   }
01664 
01665   undo_page = trx_undo_page_get(undo->space, undo->zip_size,
01666               undo->hdr_page_no, mtr);
01667 
01668   if (type == TRX_UNDO_INSERT) {
01669     offset = trx_undo_insert_header_reuse(undo_page, trx_id, mtr);
01670 
01671     if (trx->support_xa) {
01672       trx_undo_header_add_space_for_xid(
01673         undo_page, undo_page + offset, mtr);
01674     }
01675   } else {
01676     ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
01677               + TRX_UNDO_PAGE_TYPE)
01678          == TRX_UNDO_UPDATE);
01679 
01680     offset = trx_undo_header_create(undo_page, trx_id, mtr);
01681 
01682     if (trx->support_xa) {
01683       trx_undo_header_add_space_for_xid(
01684         undo_page, undo_page + offset, mtr);
01685     }
01686   }
01687 
01688   trx_undo_mem_init_for_reuse(undo, trx_id, xid, offset);
01689 
01690   return(undo);
01691 }
01692 
01693 /**********************************************************************/
01696 static
01697 void
01698 trx_undo_mark_as_dict_operation(
01699 /*============================*/
01700   trx_t*    trx,  
01701   trx_undo_t* undo, 
01702   mtr_t*    mtr)  
01703 {
01704   page_t* hdr_page;
01705 
01706   hdr_page = trx_undo_page_get(undo->space, undo->zip_size,
01707              undo->hdr_page_no, mtr);
01708 
01709   switch (trx_get_dict_operation(trx)) {
01710   case TRX_DICT_OP_NONE:
01711     ut_error;
01712   case TRX_DICT_OP_INDEX:
01713     /* Do not discard the table on recovery. */
01714     undo->table_id = 0;
01715     break;
01716   case TRX_DICT_OP_TABLE:
01717     undo->table_id = trx->table_id;
01718     break;
01719   }
01720 
01721   mlog_write_ulint(hdr_page + undo->hdr_offset
01722        + TRX_UNDO_DICT_TRANS,
01723        TRUE, MLOG_1BYTE, mtr);
01724 
01725   mlog_write_ull(hdr_page + undo->hdr_offset + TRX_UNDO_TABLE_ID,
01726            undo->table_id, mtr);
01727 
01728   undo->dict_operation = TRUE;
01729 }
01730 
01731 /**********************************************************************/
01737 UNIV_INTERN
01738 ulint
01739 trx_undo_assign_undo(
01740 /*=================*/
01741   trx_t*    trx,  
01742   ulint   type) 
01743 {
01744   trx_rseg_t* rseg;
01745   trx_undo_t* undo;
01746   mtr_t   mtr;
01747   ulint   err = DB_SUCCESS;
01748 
01749   ut_ad(trx);
01750   ut_ad(trx->rseg);
01751 
01752   rseg = trx->rseg;
01753 
01754   ut_ad(mutex_own(&(trx->undo_mutex)));
01755 
01756   mtr_start(&mtr);
01757 
01758   ut_ad(!mutex_own(&kernel_mutex));
01759 
01760   mutex_enter(&(rseg->mutex));
01761 
01762   undo = trx_undo_reuse_cached(trx, rseg, type, trx->id, &trx->xid,
01763              &mtr);
01764   if (undo == NULL) {
01765     err = trx_undo_create(trx, rseg, type, trx->id, &trx->xid,
01766                 &undo, &mtr);
01767     if (err != DB_SUCCESS) {
01768 
01769       goto func_exit;
01770     }
01771   }
01772 
01773   if (type == TRX_UNDO_INSERT) {
01774     UT_LIST_ADD_FIRST(undo_list, rseg->insert_undo_list, undo);
01775     ut_ad(trx->insert_undo == NULL);
01776     trx->insert_undo = undo;
01777   } else {
01778     UT_LIST_ADD_FIRST(undo_list, rseg->update_undo_list, undo);
01779     ut_ad(trx->update_undo == NULL);
01780     trx->update_undo = undo;
01781   }
01782 
01783   if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
01784     trx_undo_mark_as_dict_operation(trx, undo, &mtr);
01785   }
01786 
01787 func_exit:
01788   mutex_exit(&(rseg->mutex));
01789   mtr_commit(&mtr);
01790 
01791   return err;
01792 }
01793 
01794 /******************************************************************/
01797 UNIV_INTERN
01798 page_t*
01799 trx_undo_set_state_at_finish(
01800 /*=========================*/
01801   trx_undo_t* undo, 
01802   mtr_t*    mtr)  
01803 {
01804   trx_usegf_t*  seg_hdr;
01805   trx_upagef_t* page_hdr;
01806   page_t*   undo_page;
01807   ulint   state;
01808 
01809   ut_ad(undo);
01810   ut_ad(mtr);
01811 
01812   if (undo->id >= TRX_RSEG_N_SLOTS) {
01813     fprintf(stderr, "InnoDB: Error: undo->id is %lu\n",
01814       (ulong) undo->id);
01815     mem_analyze_corruption(undo);
01816     ut_error;
01817   }
01818 
01819   undo_page = trx_undo_page_get(undo->space, undo->zip_size,
01820               undo->hdr_page_no, mtr);
01821 
01822   seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
01823   page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
01824 
01825   if (undo->size == 1
01826       && mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE)
01827          < TRX_UNDO_PAGE_REUSE_LIMIT) {
01828 
01829     state = TRX_UNDO_CACHED;
01830 
01831   } else if (undo->type == TRX_UNDO_INSERT) {
01832 
01833     state = TRX_UNDO_TO_FREE;
01834   } else {
01835     state = TRX_UNDO_TO_PURGE;
01836   }
01837 
01838   undo->state = state;
01839 
01840   mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, state, MLOG_2BYTES, mtr);
01841 
01842   return(undo_page);
01843 }
01844 
01845 /******************************************************************/
01848 UNIV_INTERN
01849 page_t*
01850 trx_undo_set_state_at_prepare(
01851 /*==========================*/
01852   trx_t*    trx,  
01853   trx_undo_t* undo, 
01854   mtr_t*    mtr)  
01855 {
01856   trx_usegf_t*  seg_hdr;
01857   trx_ulogf_t*  undo_header;
01858   page_t*   undo_page;
01859   ulint   offset;
01860 
01861   ut_ad(trx && undo && mtr);
01862 
01863   if (undo->id >= TRX_RSEG_N_SLOTS) {
01864     fprintf(stderr, "InnoDB: Error: undo->id is %lu\n",
01865       (ulong) undo->id);
01866     mem_analyze_corruption(undo);
01867     ut_error;
01868   }
01869 
01870   undo_page = trx_undo_page_get(undo->space, undo->zip_size,
01871               undo->hdr_page_no, mtr);
01872 
01873   seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
01874 
01875   /*------------------------------*/
01876   undo->state = TRX_UNDO_PREPARED;
01877   undo->xid   = trx->xid;
01878   /*------------------------------*/
01879 
01880   mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, undo->state,
01881        MLOG_2BYTES, mtr);
01882 
01883   offset = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG);
01884   undo_header = undo_page + offset;
01885 
01886   mlog_write_ulint(undo_header + TRX_UNDO_XID_EXISTS,
01887        TRUE, MLOG_1BYTE, mtr);
01888 
01889   trx_undo_write_xid(undo_header, &undo->xid, mtr);
01890 
01891   return(undo_page);
01892 }
01893 
01894 /**********************************************************************/
01898 UNIV_INTERN
01899 void
01900 trx_undo_update_cleanup(
01901 /*====================*/
01902   trx_t*  trx,    
01903   page_t* undo_page,  
01905   mtr_t*  mtr)    
01906 {
01907   trx_rseg_t* rseg;
01908   trx_undo_t* undo;
01909 
01910   undo = trx->update_undo;
01911   rseg = trx->rseg;
01912 
01913   ut_ad(mutex_own(&(rseg->mutex)));
01914 
01915   trx_purge_add_update_undo_to_history(trx, undo_page, mtr);
01916 
01917   UT_LIST_REMOVE(undo_list, rseg->update_undo_list, undo);
01918 
01919   trx->update_undo = NULL;
01920 
01921   if (undo->state == TRX_UNDO_CACHED) {
01922 
01923     UT_LIST_ADD_FIRST(undo_list, rseg->update_undo_cached, undo);
01924   } else {
01925     ut_ad(undo->state == TRX_UNDO_TO_PURGE
01926           || undo->state == TRX_UNDO_TO_FREE);
01927 
01928     trx_undo_mem_free(undo);
01929   }
01930 }
01931 
01932 /******************************************************************/
01936 UNIV_INTERN
01937 void
01938 trx_undo_insert_cleanup(
01939 /*====================*/
01940   trx_t*  trx)  
01941 {
01942   trx_undo_t* undo;
01943   trx_rseg_t* rseg;
01944 
01945   undo = trx->insert_undo;
01946   ut_ad(undo);
01947 
01948   rseg = trx->rseg;
01949 
01950   mutex_enter(&(rseg->mutex));
01951 
01952   UT_LIST_REMOVE(undo_list, rseg->insert_undo_list, undo);
01953   trx->insert_undo = NULL;
01954 
01955   if (undo->state == TRX_UNDO_CACHED) {
01956 
01957     UT_LIST_ADD_FIRST(undo_list, rseg->insert_undo_cached, undo);
01958   } else {
01959     ut_ad(undo->state == TRX_UNDO_TO_FREE);
01960 
01961     /* Delete first the undo log segment in the file */
01962 
01963     mutex_exit(&(rseg->mutex));
01964 
01965     trx_undo_seg_free(undo);
01966 
01967     mutex_enter(&(rseg->mutex));
01968 
01969     ut_ad(rseg->curr_size > undo->size);
01970 
01971     rseg->curr_size -= undo->size;
01972 
01973     trx_undo_mem_free(undo);
01974   }
01975 
01976   mutex_exit(&(rseg->mutex));
01977 }
01978 #endif /* !UNIV_HOTBACKUP */