Drizzled Public API Documentation

trx0rec.cc

00001 /*****************************************************************************
00002 
00003 Copyright (C) 1996, 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 "trx0rec.h"
00027 
00028 #ifdef UNIV_NONINL
00029 #include "trx0rec.ic"
00030 #endif
00031 
00032 #include "fsp0fsp.h"
00033 #include "mach0data.h"
00034 #include "trx0undo.h"
00035 #include "mtr0log.h"
00036 #ifndef UNIV_HOTBACKUP
00037 #include "dict0dict.h"
00038 #include "ut0mem.h"
00039 #include "row0ext.h"
00040 #include "row0upd.h"
00041 #include "que0que.h"
00042 #include "trx0purge.h"
00043 #include "trx0rseg.h"
00044 #include "row0row.h"
00045 
00046 /*=========== UNDO LOG RECORD CREATION AND DECODING ====================*/
00047 
00048 /**********************************************************************/
00051 UNIV_INLINE
00052 void
00053 trx_undof_page_add_undo_rec_log(
00054 /*============================*/
00055   page_t* undo_page,  
00056   ulint old_free, 
00057   ulint new_free, 
00058   mtr_t*  mtr)    
00059 {
00060   byte*   log_ptr;
00061   const byte* log_end;
00062   ulint   len;
00063 
00064   log_ptr = mlog_open(mtr, 11 + 13 + MLOG_BUF_MARGIN);
00065 
00066   if (log_ptr == NULL) {
00067 
00068     return;
00069   }
00070 
00071   log_end = &log_ptr[11 + 13 + MLOG_BUF_MARGIN];
00072   log_ptr = mlog_write_initial_log_record_fast(
00073     undo_page, MLOG_UNDO_INSERT, log_ptr, mtr);
00074   len = new_free - old_free - 4;
00075 
00076   mach_write_to_2(log_ptr, len);
00077   log_ptr += 2;
00078 
00079   if (log_ptr + len <= log_end) {
00080     memcpy(log_ptr, undo_page + old_free + 2, len);
00081     mlog_close(mtr, log_ptr + len);
00082   } else {
00083     mlog_close(mtr, log_ptr);
00084     mlog_catenate_string(mtr, undo_page + old_free + 2, len);
00085   }
00086 }
00087 #endif /* !UNIV_HOTBACKUP */
00088 
00089 /***********************************************************/
00092 UNIV_INTERN
00093 byte*
00094 trx_undo_parse_add_undo_rec(
00095 /*========================*/
00096   byte* ptr,  
00097   byte* end_ptr,
00098   page_t* page) 
00099 {
00100   ulint len;
00101   byte* rec;
00102   ulint first_free;
00103 
00104   if (end_ptr < ptr + 2) {
00105 
00106     return(NULL);
00107   }
00108 
00109   len = mach_read_from_2(ptr);
00110   ptr += 2;
00111 
00112   if (end_ptr < ptr + len) {
00113 
00114     return(NULL);
00115   }
00116 
00117   if (page == NULL) {
00118 
00119     return(ptr + len);
00120   }
00121 
00122   first_free = mach_read_from_2(page + TRX_UNDO_PAGE_HDR
00123               + TRX_UNDO_PAGE_FREE);
00124   rec = page + first_free;
00125 
00126   mach_write_to_2(rec, first_free + 4 + len);
00127   mach_write_to_2(rec + 2 + len, first_free);
00128 
00129   mach_write_to_2(page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
00130       first_free + 4 + len);
00131   ut_memcpy(rec + 2, ptr, len);
00132 
00133   return(ptr + len);
00134 }
00135 
00136 #ifndef UNIV_HOTBACKUP
00137 /**********************************************************************/
00140 UNIV_INLINE
00141 ulint
00142 trx_undo_left(
00143 /*==========*/
00144   const page_t* page, 
00145   const byte* ptr)  
00146 {
00147   /* The '- 10' is a safety margin, in case we have some small
00148   calculation error below */
00149 
00150   return(UNIV_PAGE_SIZE - (ptr - page) - 10 - FIL_PAGE_DATA_END);
00151 }
00152 
00153 /**********************************************************************/
00158 static
00159 ulint
00160 trx_undo_page_set_next_prev_and_add(
00161 /*================================*/
00162   page_t*   undo_page,  
00163   byte*   ptr,    
00165   mtr_t*    mtr)    
00166 {
00167   ulint   first_free; 
00168   ulint   end_of_rec; 
00169   byte*   ptr_to_first_free;
00170           /* pointer within undo_page
00171           that points to the next free
00172           offset value within undo_page.*/
00173 
00174   ut_ad(ptr > undo_page);
00175   ut_ad(ptr < undo_page + UNIV_PAGE_SIZE);
00176 
00177   if (UNIV_UNLIKELY(trx_undo_left(undo_page, ptr) < 2)) {
00178 
00179     return(0);
00180   }
00181 
00182   ptr_to_first_free = undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE;
00183 
00184   first_free = mach_read_from_2(ptr_to_first_free);
00185 
00186   /* Write offset of the previous undo log record */
00187   mach_write_to_2(ptr, first_free);
00188   ptr += 2;
00189 
00190   end_of_rec = ptr - undo_page;
00191 
00192   /* Write offset of the next undo log record */
00193   mach_write_to_2(undo_page + first_free, end_of_rec);
00194 
00195   /* Update the offset to first free undo record */
00196   mach_write_to_2(ptr_to_first_free, end_of_rec);
00197 
00198   /* Write this log entry to the UNDO log */
00199   trx_undof_page_add_undo_rec_log(undo_page, first_free,
00200           end_of_rec, mtr);
00201 
00202   return(first_free);
00203 }
00204 
00205 /**********************************************************************/
00208 static
00209 ulint
00210 trx_undo_page_report_insert(
00211 /*========================*/
00212   page_t*   undo_page,  
00213   trx_t*    trx,    
00214   dict_index_t* index,    
00215   const dtuple_t* clust_entry,  
00217   mtr_t*    mtr)    
00218 {
00219   ulint   first_free;
00220   byte*   ptr;
00221   ulint   i;
00222 
00223   ut_ad(dict_index_is_clust(index));
00224   ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
00225              + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT);
00226 
00227   first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
00228               + TRX_UNDO_PAGE_FREE);
00229   ptr = undo_page + first_free;
00230 
00231   ut_ad(first_free <= UNIV_PAGE_SIZE);
00232 
00233   if (trx_undo_left(undo_page, ptr) < 2 + 1 + 11 + 11) {
00234 
00235     /* Not enough space for writing the general parameters */
00236 
00237     return(0);
00238   }
00239 
00240   /* Reserve 2 bytes for the pointer to the next undo log record */
00241   ptr += 2;
00242 
00243   /* Store first some general parameters to the undo log */
00244   *ptr++ = TRX_UNDO_INSERT_REC;
00245   ptr += mach_ull_write_much_compressed(ptr, trx->undo_no);
00246   ptr += mach_ull_write_much_compressed(ptr, index->table->id);
00247   /*----------------------------------------*/
00248   /* Store then the fields required to uniquely determine the record
00249   to be inserted in the clustered index */
00250 
00251   for (i = 0; i < dict_index_get_n_unique(index); i++) {
00252 
00253     const dfield_t* field = dtuple_get_nth_field(clust_entry, i);
00254     ulint   flen  = dfield_get_len(field);
00255 
00256     if (trx_undo_left(undo_page, ptr) < 5) {
00257 
00258       return(0);
00259     }
00260 
00261     ptr += mach_write_compressed(ptr, flen);
00262 
00263     if (flen != UNIV_SQL_NULL) {
00264       if (trx_undo_left(undo_page, ptr) < flen) {
00265 
00266         return(0);
00267       }
00268 
00269       ut_memcpy(ptr, dfield_get_data(field), flen);
00270       ptr += flen;
00271     }
00272   }
00273 
00274   return(trx_undo_page_set_next_prev_and_add(undo_page, ptr, mtr));
00275 }
00276 
00277 /**********************************************************************/
00280 UNIV_INTERN
00281 byte*
00282 trx_undo_rec_get_pars(
00283 /*==================*/
00284   trx_undo_rec_t* undo_rec, 
00285   ulint*    type,   
00287   ulint*    cmpl_info,  
00289   ibool*    updated_extern, 
00291   undo_no_t*  undo_no,  
00292   table_id_t* table_id) 
00293 {
00294   byte*   ptr;
00295   ulint   type_cmpl;
00296 
00297   ptr = undo_rec + 2;
00298 
00299   type_cmpl = mach_read_from_1(ptr);
00300   ptr++;
00301 
00302   if (type_cmpl & TRX_UNDO_UPD_EXTERN) {
00303     *updated_extern = TRUE;
00304     type_cmpl -= TRX_UNDO_UPD_EXTERN;
00305   } else {
00306     *updated_extern = FALSE;
00307   }
00308 
00309   *type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1);
00310   *cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT;
00311 
00312   *undo_no = mach_ull_read_much_compressed(ptr);
00313   ptr += mach_ull_get_much_compressed_size(*undo_no);
00314 
00315   *table_id = mach_ull_read_much_compressed(ptr);
00316   ptr += mach_ull_get_much_compressed_size(*table_id);
00317 
00318   return(ptr);
00319 }
00320 
00321 /**********************************************************************/
00324 static
00325 byte*
00326 trx_undo_rec_get_col_val(
00327 /*=====================*/
00328   byte* ptr,  
00329   byte**  field,  
00330   ulint*  len,  
00331   ulint*  orig_len)
00333 {
00334   *len = mach_read_compressed(ptr);
00335   ptr += mach_get_compressed_size(*len);
00336 
00337   *orig_len = 0;
00338 
00339   switch (*len) {
00340   case UNIV_SQL_NULL:
00341     *field = NULL;
00342     break;
00343   case UNIV_EXTERN_STORAGE_FIELD:
00344     *orig_len = mach_read_compressed(ptr);
00345     ptr += mach_get_compressed_size(*orig_len);
00346     *len = mach_read_compressed(ptr);
00347     ptr += mach_get_compressed_size(*len);
00348     *field = ptr;
00349     ptr += *len;
00350 
00351     ut_ad(*orig_len >= BTR_EXTERN_FIELD_REF_SIZE);
00352     ut_ad(*len > *orig_len);
00353     /* @see dtuple_convert_big_rec() */
00354     ut_ad(*len >= BTR_EXTERN_FIELD_REF_SIZE * 2);
00355     /* we do not have access to index->table here
00356     ut_ad(dict_table_get_format(index->table) >= DICT_TF_FORMAT_ZIP
00357           || *len >= REC_MAX_INDEX_COL_LEN
00358           + BTR_EXTERN_FIELD_REF_SIZE);
00359     */
00360 
00361     *len += UNIV_EXTERN_STORAGE_FIELD;
00362     break;
00363   default:
00364     *field = ptr;
00365     if (*len >= UNIV_EXTERN_STORAGE_FIELD) {
00366       ptr += *len - UNIV_EXTERN_STORAGE_FIELD;
00367     } else {
00368       ptr += *len;
00369     }
00370   }
00371 
00372   return(ptr);
00373 }
00374 
00375 /*******************************************************************/
00378 UNIV_INTERN
00379 byte*
00380 trx_undo_rec_get_row_ref(
00381 /*=====================*/
00382   byte*   ptr,  
00388   dict_index_t* index,  
00389   dtuple_t**  ref,  
00390   mem_heap_t* heap) 
00392 {
00393   ulint   ref_len;
00394   ulint   i;
00395 
00396   ut_ad(index && ptr && ref && heap);
00397   ut_a(dict_index_is_clust(index));
00398 
00399   ref_len = dict_index_get_n_unique(index);
00400 
00401   *ref = dtuple_create(heap, ref_len);
00402 
00403   dict_index_copy_types(*ref, index, ref_len);
00404 
00405   for (i = 0; i < ref_len; i++) {
00406     dfield_t* dfield;
00407     byte*   field;
00408     ulint   len;
00409     ulint   orig_len;
00410 
00411     dfield = dtuple_get_nth_field(*ref, i);
00412 
00413     ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len);
00414 
00415     dfield_set_data(dfield, field, len);
00416   }
00417 
00418   return(ptr);
00419 }
00420 
00421 /*******************************************************************/
00424 UNIV_INTERN
00425 byte*
00426 trx_undo_rec_skip_row_ref(
00427 /*======================*/
00428   byte*   ptr,  
00430   dict_index_t* index)  
00431 {
00432   ulint ref_len;
00433   ulint i;
00434 
00435   ut_ad(index && ptr);
00436   ut_a(dict_index_is_clust(index));
00437 
00438   ref_len = dict_index_get_n_unique(index);
00439 
00440   for (i = 0; i < ref_len; i++) {
00441     byte* field;
00442     ulint len;
00443     ulint orig_len;
00444 
00445     ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len);
00446   }
00447 
00448   return(ptr);
00449 }
00450 
00451 /**********************************************************************/
00455 static
00456 byte*
00457 trx_undo_page_fetch_ext(
00458 /*====================*/
00459   byte*   ext_buf,  
00462   ulint   zip_size, 
00464   const byte* field,    
00465   ulint*    len)    
00467 {
00468   /* Fetch the BLOB. */
00469   ulint ext_len = btr_copy_externally_stored_field_prefix(
00470     ext_buf, REC_MAX_INDEX_COL_LEN, zip_size, field, *len);
00471   /* BLOBs should always be nonempty. */
00472   ut_a(ext_len);
00473   /* Append the BLOB pointer to the prefix. */
00474   memcpy(ext_buf + ext_len,
00475          field + *len - BTR_EXTERN_FIELD_REF_SIZE,
00476          BTR_EXTERN_FIELD_REF_SIZE);
00477   *len = ext_len + BTR_EXTERN_FIELD_REF_SIZE;
00478   return(ext_buf);
00479 }
00480 
00481 /**********************************************************************/
00484 static
00485 byte*
00486 trx_undo_page_report_modify_ext(
00487 /*============================*/
00488   byte*   ptr,    
00490   byte*   ext_buf,  
00495   ulint   zip_size, 
00497   const byte**  field,    
00499   ulint*    len)    
00500 {
00501   if (ext_buf) {
00502     /* If an ordering column is externally stored, we will
00503     have to store a longer prefix of the field.  In this
00504     case, write to the log a marker followed by the
00505     original length and the real length of the field. */
00506     ptr += mach_write_compressed(ptr, UNIV_EXTERN_STORAGE_FIELD);
00507 
00508     ptr += mach_write_compressed(ptr, *len);
00509 
00510     *field = trx_undo_page_fetch_ext(ext_buf, zip_size,
00511              *field, len);
00512 
00513     ptr += mach_write_compressed(ptr, *len);
00514   } else {
00515     ptr += mach_write_compressed(ptr, UNIV_EXTERN_STORAGE_FIELD
00516                + *len);
00517   }
00518 
00519   return(ptr);
00520 }
00521 
00522 /**********************************************************************/
00527 static
00528 ulint
00529 trx_undo_page_report_modify(
00530 /*========================*/
00531   page_t*   undo_page,  
00532   trx_t*    trx,    
00533   dict_index_t* index,    
00535   const rec_t*  rec,    
00537   const ulint*  offsets,  
00538   const upd_t*  update,   
00541   ulint   cmpl_info,  
00543   mtr_t*    mtr)    
00544 {
00545   dict_table_t* table;
00546   ulint   first_free;
00547   byte*   ptr;
00548   const byte* field;
00549   ulint   flen;
00550   ulint   col_no;
00551   ulint   type_cmpl;
00552   byte*   type_cmpl_ptr;
00553   ulint   i;
00554   trx_id_t  trx_id;
00555   ibool   ignore_prefix = FALSE;
00556   byte    ext_buf[REC_MAX_INDEX_COL_LEN
00557         + BTR_EXTERN_FIELD_REF_SIZE];
00558 
00559   ut_a(dict_index_is_clust(index));
00560   ut_ad(rec_offs_validate(rec, index, offsets));
00561   ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
00562              + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE);
00563   table = index->table;
00564 
00565   first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
00566               + TRX_UNDO_PAGE_FREE);
00567   ptr = undo_page + first_free;
00568 
00569   ut_ad(first_free <= UNIV_PAGE_SIZE);
00570 
00571   if (trx_undo_left(undo_page, ptr) < 50) {
00572 
00573     /* NOTE: the value 50 must be big enough so that the general
00574     fields written below fit on the undo log page */
00575 
00576     return(0);
00577   }
00578 
00579   /* Reserve 2 bytes for the pointer to the next undo log record */
00580   ptr += 2;
00581 
00582   /* Store first some general parameters to the undo log */
00583 
00584   if (!update) {
00585     type_cmpl = TRX_UNDO_DEL_MARK_REC;
00586   } else if (rec_get_deleted_flag(rec, dict_table_is_comp(table))) {
00587     type_cmpl = TRX_UNDO_UPD_DEL_REC;
00588     /* We are about to update a delete marked record.
00589     We don't typically need the prefix in this case unless
00590     the delete marking is done by the same transaction
00591     (which we check below). */
00592     ignore_prefix = TRUE;
00593   } else {
00594     type_cmpl = TRX_UNDO_UPD_EXIST_REC;
00595   }
00596 
00597   type_cmpl |= cmpl_info * TRX_UNDO_CMPL_INFO_MULT;
00598   type_cmpl_ptr = ptr;
00599 
00600   *ptr++ = (byte) type_cmpl;
00601   ptr += mach_ull_write_much_compressed(ptr, trx->undo_no);
00602 
00603   ptr += mach_ull_write_much_compressed(ptr, table->id);
00604 
00605   /*----------------------------------------*/
00606   /* Store the state of the info bits */
00607 
00608   *ptr++ = (byte) rec_get_info_bits(rec, dict_table_is_comp(table));
00609 
00610   /* Store the values of the system columns */
00611   field = rec_get_nth_field(rec, offsets,
00612           dict_index_get_sys_col_pos(
00613             index, DATA_TRX_ID), &flen);
00614   ut_ad(flen == DATA_TRX_ID_LEN);
00615 
00616   trx_id = trx_read_trx_id(field);
00617 
00618   /* If it is an update of a delete marked record, then we are
00619   allowed to ignore blob prefixes if the delete marking was done
00620   by some other trx as it must have committed by now for us to
00621   allow an over-write. */
00622   if (ignore_prefix) {
00623     ignore_prefix = (trx_id != trx->id);
00624   }
00625   ptr += mach_ull_write_compressed(ptr, trx_id);
00626 
00627   field = rec_get_nth_field(rec, offsets,
00628           dict_index_get_sys_col_pos(
00629             index, DATA_ROLL_PTR), &flen);
00630   ut_ad(flen == DATA_ROLL_PTR_LEN);
00631 
00632   ptr += mach_ull_write_compressed(ptr, trx_read_roll_ptr(field));
00633 
00634   /*----------------------------------------*/
00635   /* Store then the fields required to uniquely determine the
00636   record which will be modified in the clustered index */
00637 
00638   for (i = 0; i < dict_index_get_n_unique(index); i++) {
00639 
00640     field = rec_get_nth_field(rec, offsets, i, &flen);
00641 
00642     /* The ordering columns must not be stored externally. */
00643     ut_ad(!rec_offs_nth_extern(offsets, i));
00644     ut_ad(dict_index_get_nth_col(index, i)->ord_part);
00645 
00646     if (trx_undo_left(undo_page, ptr) < 5) {
00647 
00648       return(0);
00649     }
00650 
00651     ptr += mach_write_compressed(ptr, flen);
00652 
00653     if (flen != UNIV_SQL_NULL) {
00654       if (trx_undo_left(undo_page, ptr) < flen) {
00655 
00656         return(0);
00657       }
00658 
00659       ut_memcpy(ptr, field, flen);
00660       ptr += flen;
00661     }
00662   }
00663 
00664   /*----------------------------------------*/
00665   /* Save to the undo log the old values of the columns to be updated. */
00666 
00667   if (update) {
00668     if (trx_undo_left(undo_page, ptr) < 5) {
00669 
00670       return(0);
00671     }
00672 
00673     ptr += mach_write_compressed(ptr, upd_get_n_fields(update));
00674 
00675     for (i = 0; i < upd_get_n_fields(update); i++) {
00676 
00677       ulint pos = upd_get_nth_field(update, i)->field_no;
00678 
00679       /* Write field number to undo log */
00680       if (trx_undo_left(undo_page, ptr) < 5) {
00681 
00682         return(0);
00683       }
00684 
00685       ptr += mach_write_compressed(ptr, pos);
00686 
00687       /* Save the old value of field */
00688       field = rec_get_nth_field(rec, offsets, pos, &flen);
00689 
00690       if (trx_undo_left(undo_page, ptr) < 15) {
00691 
00692         return(0);
00693       }
00694 
00695       if (rec_offs_nth_extern(offsets, pos)) {
00696         ptr = trx_undo_page_report_modify_ext(
00697           ptr,
00698           dict_index_get_nth_col(index, pos)
00699           ->ord_part
00700           && !ignore_prefix
00701           && flen < REC_MAX_INDEX_COL_LEN
00702           ? ext_buf : NULL,
00703           dict_table_zip_size(table),
00704           &field, &flen);
00705 
00706         /* Notify purge that it eventually has to
00707         free the old externally stored field */
00708 
00709         trx->update_undo->del_marks = TRUE;
00710 
00711         *type_cmpl_ptr |= TRX_UNDO_UPD_EXTERN;
00712       } else {
00713         ptr += mach_write_compressed(ptr, flen);
00714       }
00715 
00716       if (flen != UNIV_SQL_NULL) {
00717         if (trx_undo_left(undo_page, ptr) < flen) {
00718 
00719           return(0);
00720         }
00721 
00722         ut_memcpy(ptr, field, flen);
00723         ptr += flen;
00724       }
00725     }
00726   }
00727 
00728   /*----------------------------------------*/
00729   /* In the case of a delete marking, and also in the case of an update
00730   where any ordering field of any index changes, store the values of all
00731   columns which occur as ordering fields in any index. This info is used
00732   in the purge of old versions where we use it to build and search the
00733   delete marked index records, to look if we can remove them from the
00734   index tree. Note that starting from 4.0.14 also externally stored
00735   fields can be ordering in some index. Starting from 5.2, we no longer
00736   store REC_MAX_INDEX_COL_LEN first bytes to the undo log record,
00737   but we can construct the column prefix fields in the index by
00738   fetching the first page of the BLOB that is pointed to by the
00739   clustered index. This works also in crash recovery, because all pages
00740   (including BLOBs) are recovered before anything is rolled back. */
00741 
00742   if (!update || !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
00743     byte* old_ptr = ptr;
00744 
00745     trx->update_undo->del_marks = TRUE;
00746 
00747     if (trx_undo_left(undo_page, ptr) < 5) {
00748 
00749       return(0);
00750     }
00751 
00752     /* Reserve 2 bytes to write the number of bytes the stored
00753     fields take in this undo record */
00754 
00755     ptr += 2;
00756 
00757     for (col_no = 0; col_no < dict_table_get_n_cols(table);
00758          col_no++) {
00759 
00760       const dict_col_t* col
00761         = dict_table_get_nth_col(table, col_no);
00762 
00763       if (col->ord_part) {
00764         ulint pos;
00765 
00766         /* Write field number to undo log */
00767         if (trx_undo_left(undo_page, ptr) < 5 + 15) {
00768 
00769           return(0);
00770         }
00771 
00772         pos = dict_index_get_nth_col_pos(index,
00773                  col_no);
00774         ptr += mach_write_compressed(ptr, pos);
00775 
00776         /* Save the old value of field */
00777         field = rec_get_nth_field(rec, offsets, pos,
00778                 &flen);
00779 
00780         if (rec_offs_nth_extern(offsets, pos)) {
00781           ptr = trx_undo_page_report_modify_ext(
00782             ptr,
00783             flen < REC_MAX_INDEX_COL_LEN
00784             && !ignore_prefix
00785             ? ext_buf : NULL,
00786             dict_table_zip_size(table),
00787             &field, &flen);
00788         } else {
00789           ptr += mach_write_compressed(
00790             ptr, flen);
00791         }
00792 
00793         if (flen != UNIV_SQL_NULL) {
00794           if (trx_undo_left(undo_page, ptr)
00795               < flen) {
00796 
00797             return(0);
00798           }
00799 
00800           ut_memcpy(ptr, field, flen);
00801           ptr += flen;
00802         }
00803       }
00804     }
00805 
00806     mach_write_to_2(old_ptr, ptr - old_ptr);
00807   }
00808 
00809   /*----------------------------------------*/
00810   /* Write pointers to the previous and the next undo log records */
00811   if (trx_undo_left(undo_page, ptr) < 2) {
00812 
00813     return(0);
00814   }
00815 
00816   mach_write_to_2(ptr, first_free);
00817   ptr += 2;
00818   mach_write_to_2(undo_page + first_free, ptr - undo_page);
00819 
00820   mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
00821       ptr - undo_page);
00822 
00823   /* Write to the REDO log about this change in the UNDO log */
00824 
00825   trx_undof_page_add_undo_rec_log(undo_page, first_free,
00826           ptr - undo_page, mtr);
00827   return(first_free);
00828 }
00829 
00830 /**********************************************************************/
00834 UNIV_INTERN
00835 byte*
00836 trx_undo_update_rec_get_sys_cols(
00837 /*=============================*/
00838   byte*   ptr,    
00841   trx_id_t* trx_id,   
00842   roll_ptr_t* roll_ptr, 
00843   ulint*    info_bits)  
00844 {
00845   /* Read the state of the info bits */
00846   *info_bits = mach_read_from_1(ptr);
00847   ptr += 1;
00848 
00849   /* Read the values of the system columns */
00850 
00851   *trx_id = mach_ull_read_compressed(ptr);
00852   ptr += mach_ull_get_compressed_size(*trx_id);
00853 
00854   *roll_ptr = mach_ull_read_compressed(ptr);
00855   ptr += mach_ull_get_compressed_size(*roll_ptr);
00856 
00857   return(ptr);
00858 }
00859 
00860 /**********************************************************************/
00863 UNIV_INLINE
00864 byte*
00865 trx_undo_update_rec_get_n_upd_fields(
00866 /*=================================*/
00867   byte* ptr,  
00868   ulint*  n)  
00869 {
00870   *n = mach_read_compressed(ptr);
00871   ptr += mach_get_compressed_size(*n);
00872 
00873   return(ptr);
00874 }
00875 
00876 /**********************************************************************/
00879 UNIV_INLINE
00880 byte*
00881 trx_undo_update_rec_get_field_no(
00882 /*=============================*/
00883   byte* ptr,  
00884   ulint*  field_no)
00885 {
00886   *field_no = mach_read_compressed(ptr);
00887   ptr += mach_get_compressed_size(*field_no);
00888 
00889   return(ptr);
00890 }
00891 
00892 /*******************************************************************/
00896 UNIV_INTERN
00897 byte*
00898 trx_undo_update_rec_get_update(
00899 /*===========================*/
00900   byte*   ptr,  
00906   dict_index_t* index,  
00907   ulint   type, 
00912   trx_id_t  trx_id, 
00913   roll_ptr_t  roll_ptr,
00914   ulint   info_bits,
00915   trx_t*    trx,  
00916   mem_heap_t* heap, 
00918   upd_t**   upd)  
00919 {
00920   upd_field_t*  upd_field;
00921   upd_t*    update;
00922   ulint   n_fields;
00923   byte*   buf;
00924   ulint   i;
00925 
00926   ut_a(dict_index_is_clust(index));
00927 
00928   if (type != TRX_UNDO_DEL_MARK_REC) {
00929     ptr = trx_undo_update_rec_get_n_upd_fields(ptr, &n_fields);
00930   } else {
00931     n_fields = 0;
00932   }
00933 
00934   update = upd_create(n_fields + 2, heap);
00935 
00936   update->info_bits = info_bits;
00937 
00938   /* Store first trx id and roll ptr to update vector */
00939 
00940   upd_field = upd_get_nth_field(update, n_fields);
00941   buf = static_cast<byte *>(mem_heap_alloc(heap, DATA_TRX_ID_LEN));
00942   trx_write_trx_id(buf, trx_id);
00943 
00944   upd_field_set_field_no(upd_field,
00945              dict_index_get_sys_col_pos(index, DATA_TRX_ID),
00946              index, trx);
00947   dfield_set_data(&(upd_field->new_val), buf, DATA_TRX_ID_LEN);
00948 
00949   upd_field = upd_get_nth_field(update, n_fields + 1);
00950   buf = static_cast<byte *>(mem_heap_alloc(heap, DATA_ROLL_PTR_LEN));
00951   trx_write_roll_ptr(buf, roll_ptr);
00952 
00953   upd_field_set_field_no(
00954     upd_field, dict_index_get_sys_col_pos(index, DATA_ROLL_PTR),
00955     index, trx);
00956   dfield_set_data(&(upd_field->new_val), buf, DATA_ROLL_PTR_LEN);
00957 
00958   /* Store then the updated ordinary columns to the update vector */
00959 
00960   for (i = 0; i < n_fields; i++) {
00961 
00962     byte* field;
00963     ulint len;
00964     ulint field_no;
00965     ulint orig_len;
00966 
00967     ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
00968 
00969     if (field_no >= dict_index_get_n_fields(index)) {
00970       fprintf(stderr,
00971         "InnoDB: Error: trying to access"
00972         " update undo rec field %lu in ",
00973         (ulong) field_no);
00974       dict_index_name_print(stderr, trx, index);
00975       fprintf(stderr, "\n"
00976         "InnoDB: but index has only %lu fields\n"
00977         "InnoDB: Submit a detailed bug report"
00978         " to http://bugs.mysql.com\n"
00979         "InnoDB: Run also CHECK TABLE ",
00980         (ulong) dict_index_get_n_fields(index));
00981       ut_print_name(stderr, trx, TRUE, index->table_name);
00982       fprintf(stderr, "\n"
00983         "InnoDB: n_fields = %lu, i = %lu, ptr %p\n",
00984         (ulong) n_fields, (ulong) i, ptr);
00985       *upd = NULL;
00986       return(NULL);
00987     }
00988 
00989     upd_field = upd_get_nth_field(update, i);
00990 
00991     upd_field_set_field_no(upd_field, field_no, index, trx);
00992 
00993     ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len);
00994 
00995     upd_field->orig_len = orig_len;
00996 
00997     if (len == UNIV_SQL_NULL) {
00998       dfield_set_null(&upd_field->new_val);
00999     } else if (len < UNIV_EXTERN_STORAGE_FIELD) {
01000       dfield_set_data(&upd_field->new_val, field, len);
01001     } else {
01002       len -= UNIV_EXTERN_STORAGE_FIELD;
01003 
01004       dfield_set_data(&upd_field->new_val, field, len);
01005       dfield_set_ext(&upd_field->new_val);
01006     }
01007   }
01008 
01009   *upd = update;
01010 
01011   return(ptr);
01012 }
01013 
01014 /*******************************************************************/
01018 UNIV_INTERN
01019 byte*
01020 trx_undo_rec_get_partial_row(
01021 /*=========================*/
01022   byte*   ptr,  
01029   dict_index_t* index,  
01030   dtuple_t**  row,  
01031   ibool   ignore_prefix, 
01034   mem_heap_t* heap) 
01036 {
01037   const byte* end_ptr;
01038   ulint   row_len;
01039 
01040   ut_ad(index);
01041   ut_ad(ptr);
01042   ut_ad(row);
01043   ut_ad(heap);
01044   ut_ad(dict_index_is_clust(index));
01045 
01046   row_len = dict_table_get_n_cols(index->table);
01047 
01048   *row = dtuple_create(heap, row_len);
01049 
01050   dict_table_copy_types(*row, index->table);
01051 
01052   end_ptr = ptr + mach_read_from_2(ptr);
01053   ptr += 2;
01054 
01055   while (ptr != end_ptr) {
01056     dfield_t*   dfield;
01057     byte*     field;
01058     ulint     field_no;
01059     const dict_col_t* col;
01060     ulint     col_no;
01061     ulint     len;
01062     ulint     orig_len;
01063 
01064     ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
01065 
01066     col = dict_index_get_nth_col(index, field_no);
01067     col_no = dict_col_get_no(col);
01068 
01069     ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len);
01070 
01071     dfield = dtuple_get_nth_field(*row, col_no);
01072 
01073     dfield_set_data(dfield, field, len);
01074 
01075     if (len != UNIV_SQL_NULL
01076         && len >= UNIV_EXTERN_STORAGE_FIELD) {
01077       dfield_set_len(dfield,
01078                len - UNIV_EXTERN_STORAGE_FIELD);
01079       dfield_set_ext(dfield);
01080       /* If the prefix of this column is indexed,
01081       ensure that enough prefix is stored in the
01082       undo log record. */
01083       if (!ignore_prefix && col->ord_part) {
01084         ut_a(dfield_get_len(dfield)
01085              >= 2 * BTR_EXTERN_FIELD_REF_SIZE);
01086         ut_a(dict_table_get_format(index->table)
01087              >= DICT_TF_FORMAT_ZIP
01088              || dfield_get_len(dfield)
01089              >= REC_MAX_INDEX_COL_LEN
01090              + BTR_EXTERN_FIELD_REF_SIZE);
01091       }
01092     }
01093   }
01094 
01095   return(ptr);
01096 }
01097 #endif /* !UNIV_HOTBACKUP */
01098 
01099 /***********************************************************************/
01101 static
01102 void
01103 trx_undo_erase_page_end(
01104 /*====================*/
01105   page_t* undo_page,  
01106   mtr_t*  mtr)    
01107 {
01108   ulint first_free;
01109 
01110   first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
01111               + TRX_UNDO_PAGE_FREE);
01112   memset(undo_page + first_free, 0xff,
01113          (UNIV_PAGE_SIZE - FIL_PAGE_DATA_END) - first_free);
01114 
01115   mlog_write_initial_log_record(undo_page, MLOG_UNDO_ERASE_END, mtr);
01116 }
01117 
01118 /***********************************************************/
01121 UNIV_INTERN
01122 byte*
01123 trx_undo_parse_erase_page_end(
01124 /*==========================*/
01125   byte* ptr,  
01126   byte* /*end_ptr*/, 
01127   page_t* page, 
01128   mtr_t*  mtr)  
01129 {
01130   ut_ad(ptr && end_ptr);
01131 
01132   if (page == NULL) {
01133 
01134     return(ptr);
01135   }
01136 
01137   trx_undo_erase_page_end(page, mtr);
01138 
01139   return(ptr);
01140 }
01141 
01142 #ifndef UNIV_HOTBACKUP
01143 /***********************************************************************/
01149 UNIV_INTERN
01150 ulint
01151 trx_undo_report_row_operation(
01152 /*==========================*/
01153   ulint   flags,    
01155   ulint   op_type,  
01157   que_thr_t*  thr,    
01158   dict_index_t* index,    
01159   const dtuple_t* clust_entry,  
01162   const upd_t*  update,   
01164   ulint   cmpl_info,  
01166   const rec_t*  rec,    
01169   roll_ptr_t* roll_ptr) 
01173 {
01174   trx_t*    trx;
01175   trx_undo_t* undo;
01176   ulint   page_no;
01177   trx_rseg_t* rseg;
01178   mtr_t   mtr;
01179   ulint   err   = DB_SUCCESS;
01180   mem_heap_t* heap    = NULL;
01181   ulint   offsets_[REC_OFFS_NORMAL_SIZE];
01182   ulint*    offsets   = offsets_;
01183   rec_offs_init(offsets_);
01184 
01185   ut_a(dict_index_is_clust(index));
01186 
01187   if (flags & BTR_NO_UNDO_LOG_FLAG) {
01188 
01189     *roll_ptr = 0;
01190 
01191     return(DB_SUCCESS);
01192   }
01193 
01194   ut_ad(thr);
01195   ut_ad((op_type != TRX_UNDO_INSERT_OP)
01196         || (clust_entry && !update && !rec));
01197 
01198   trx = thr_get_trx(thr);
01199   rseg = trx->rseg;
01200 
01201   mutex_enter(&(trx->undo_mutex));
01202 
01203   /* If the undo log is not assigned yet, assign one */
01204 
01205   if (op_type == TRX_UNDO_INSERT_OP) {
01206 
01207     if (trx->insert_undo == NULL) {
01208 
01209       err = trx_undo_assign_undo(trx, TRX_UNDO_INSERT);
01210     }
01211 
01212     undo = trx->insert_undo;
01213 
01214     if (UNIV_UNLIKELY(!undo)) {
01215       /* Did not succeed */
01216       mutex_exit(&(trx->undo_mutex));
01217 
01218       return(err);
01219     }
01220   } else {
01221     ut_ad(op_type == TRX_UNDO_MODIFY_OP);
01222 
01223     if (trx->update_undo == NULL) {
01224 
01225       err = trx_undo_assign_undo(trx, TRX_UNDO_UPDATE);
01226 
01227     }
01228 
01229     undo = trx->update_undo;
01230 
01231     if (UNIV_UNLIKELY(!undo)) {
01232       /* Did not succeed */
01233       mutex_exit(&(trx->undo_mutex));
01234       return(err);
01235     }
01236 
01237     offsets = rec_get_offsets(rec, index, offsets,
01238             ULINT_UNDEFINED, &heap);
01239   }
01240 
01241   page_no = undo->last_page_no;
01242 
01243   mtr_start(&mtr);
01244 
01245   for (;;) {
01246     buf_block_t*  undo_block;
01247     page_t*   undo_page;
01248     ulint   offset;
01249 
01250     undo_block = buf_page_get_gen(undo->space, undo->zip_size,
01251                 page_no, RW_X_LATCH,
01252                 undo->guess_block, BUF_GET,
01253                 __FILE__, __LINE__, &mtr);
01254     buf_block_dbg_add_level(undo_block, SYNC_TRX_UNDO_PAGE);
01255 
01256     undo_page = buf_block_get_frame(undo_block);
01257 
01258     if (op_type == TRX_UNDO_INSERT_OP) {
01259       offset = trx_undo_page_report_insert(
01260         undo_page, trx, index, clust_entry, &mtr);
01261     } else {
01262       offset = trx_undo_page_report_modify(
01263         undo_page, trx, index, rec, offsets, update,
01264         cmpl_info, &mtr);
01265     }
01266 
01267     if (UNIV_UNLIKELY(offset == 0)) {
01268       /* The record did not fit on the page. We erase the
01269       end segment of the undo log page and write a log
01270       record of it: this is to ensure that in the debug
01271       version the replicate page constructed using the log
01272       records stays identical to the original page */
01273 
01274       trx_undo_erase_page_end(undo_page, &mtr);
01275       mtr_commit(&mtr);
01276     } else {
01277       /* Success */
01278 
01279       mtr_commit(&mtr);
01280 
01281       undo->empty = FALSE;
01282       undo->top_page_no = page_no;
01283       undo->top_offset  = offset;
01284       undo->top_undo_no = trx->undo_no;
01285       undo->guess_block = undo_block;
01286 
01287       trx->undo_no++;
01288 
01289       mutex_exit(&trx->undo_mutex);
01290 
01291       *roll_ptr = trx_undo_build_roll_ptr(
01292         op_type == TRX_UNDO_INSERT_OP,
01293         rseg->id, page_no, offset);
01294       if (UNIV_LIKELY_NULL(heap)) {
01295         mem_heap_free(heap);
01296       }
01297       return(DB_SUCCESS);
01298     }
01299 
01300     ut_ad(page_no == undo->last_page_no);
01301 
01302     /* We have to extend the undo log by one page */
01303 
01304     mtr_start(&mtr);
01305 
01306     /* When we add a page to an undo log, this is analogous to
01307     a pessimistic insert in a B-tree, and we must reserve the
01308     counterpart of the tree latch, which is the rseg mutex. */
01309 
01310     mutex_enter(&(rseg->mutex));
01311 
01312     page_no = trx_undo_add_page(trx, undo, &mtr);
01313 
01314     mutex_exit(&(rseg->mutex));
01315 
01316     if (UNIV_UNLIKELY(page_no == FIL_NULL)) {
01317       /* Did not succeed: out of space */
01318 
01319       mutex_exit(&(trx->undo_mutex));
01320       mtr_commit(&mtr);
01321       if (UNIV_LIKELY_NULL(heap)) {
01322         mem_heap_free(heap);
01323       }
01324       return(DB_OUT_OF_FILE_SPACE);
01325     }
01326   }
01327 }
01328 
01329 /*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
01330 
01331 /******************************************************************/
01335 UNIV_INTERN
01336 trx_undo_rec_t*
01337 trx_undo_get_undo_rec_low(
01338 /*======================*/
01339   roll_ptr_t  roll_ptr, 
01340   mem_heap_t* heap)   
01341 {
01342   trx_undo_rec_t* undo_rec;
01343   ulint   rseg_id;
01344   ulint   page_no;
01345   ulint   offset;
01346   const page_t* undo_page;
01347   trx_rseg_t* rseg;
01348   ibool   is_insert;
01349   mtr_t   mtr;
01350 
01351   trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id, &page_no,
01352          &offset);
01353   rseg = trx_rseg_get_on_id(rseg_id);
01354 
01355   mtr_start(&mtr);
01356 
01357   undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
01358             page_no, &mtr);
01359 
01360   undo_rec = trx_undo_rec_copy(undo_page + offset, heap);
01361 
01362   mtr_commit(&mtr);
01363 
01364   return(undo_rec);
01365 }
01366 
01367 /******************************************************************/
01375 UNIV_INTERN
01376 ulint
01377 trx_undo_get_undo_rec(
01378 /*==================*/
01379   roll_ptr_t  roll_ptr, 
01380   trx_id_t  trx_id,   
01383   trx_undo_rec_t** undo_rec,  
01384   mem_heap_t* heap)   
01385 {
01386 #ifdef UNIV_SYNC_DEBUG
01387   ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
01388 #endif /* UNIV_SYNC_DEBUG */
01389 
01390   if (!trx_purge_update_undo_must_exist(trx_id)) {
01391 
01392     /* It may be that the necessary undo log has already been
01393     deleted */
01394 
01395     return(DB_MISSING_HISTORY);
01396   }
01397 
01398   *undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
01399 
01400   return(DB_SUCCESS);
01401 }
01402 
01403 /*******************************************************************/
01411 UNIV_INTERN
01412 ulint
01413 trx_undo_prev_version_build(
01414 /*========================*/
01415   const rec_t*  index_rec,
01417   mtr_t*    /*index_mtr*/,
01420   const rec_t*  rec,  
01421   dict_index_t* index,  
01422   ulint*    offsets,
01423   mem_heap_t* heap, 
01425   rec_t**   old_vers)
01430 {
01431   trx_undo_rec_t* undo_rec  = NULL;
01432   dtuple_t* entry;
01433   trx_id_t  rec_trx_id;
01434   ulint   type;
01435   undo_no_t undo_no;
01436   table_id_t  table_id;
01437   trx_id_t  trx_id;
01438   roll_ptr_t  roll_ptr;
01439   roll_ptr_t  old_roll_ptr;
01440   upd_t*    update= NULL;
01441   byte*   ptr;
01442   ulint   info_bits;
01443   ulint   cmpl_info;
01444   ibool   dummy_extern;
01445   byte*   buf;
01446   ulint   err;
01447 #ifdef UNIV_SYNC_DEBUG
01448   ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
01449 #endif /* UNIV_SYNC_DEBUG */
01450   ut_ad(mtr_memo_contains_page(index_mtr, index_rec, MTR_MEMO_PAGE_S_FIX)
01451         || mtr_memo_contains_page(index_mtr, index_rec,
01452           MTR_MEMO_PAGE_X_FIX));
01453   ut_ad(rec_offs_validate(rec, index, offsets));
01454 
01455   if (!dict_index_is_clust(index)) {
01456     fprintf(stderr, "InnoDB: Error: trying to access"
01457       " update undo rec for non-clustered index %s\n"
01458       "InnoDB: Submit a detailed bug report to"
01459       " http://bugs.mysql.com\n"
01460       "InnoDB: index record ", index->name);
01461     rec_print(stderr, index_rec, index);
01462     fputs("\n"
01463           "InnoDB: record version ", stderr);
01464     rec_print_new(stderr, rec, offsets);
01465     putc('\n', stderr);
01466     return(DB_ERROR);
01467   }
01468 
01469   roll_ptr = row_get_rec_roll_ptr(rec, index, offsets);
01470   old_roll_ptr = roll_ptr;
01471 
01472   *old_vers = NULL;
01473 
01474   if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
01475 
01476     /* The record rec is the first inserted version */
01477 
01478     return(DB_SUCCESS);
01479   }
01480 
01481   rec_trx_id = row_get_rec_trx_id(rec, index, offsets);
01482 
01483   err = trx_undo_get_undo_rec(roll_ptr, rec_trx_id, &undo_rec, heap);
01484 
01485   if (UNIV_UNLIKELY(err != DB_SUCCESS)) {
01486     /* The undo record may already have been purged.
01487     This should never happen in InnoDB. */
01488 
01489     return(err);
01490   }
01491 
01492   ptr = trx_undo_rec_get_pars(undo_rec, &type, &cmpl_info,
01493             &dummy_extern, &undo_no, &table_id);
01494 
01495   ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
01496                  &info_bits);
01497 
01498   /* (a) If a clustered index record version is such that the
01499   trx id stamp in it is bigger than purge_sys->view, then the
01500   BLOBs in that version are known to exist (the purge has not
01501   progressed that far);
01502 
01503   (b) if the version is the first version such that trx id in it
01504   is less than purge_sys->view, and it is not delete-marked,
01505   then the BLOBs in that version are known to exist (the purge
01506   cannot have purged the BLOBs referenced by that version
01507   yet).
01508 
01509   This function does not fetch any BLOBs.  The callers might, by
01510   possibly invoking row_ext_create() via row_build().  However,
01511   they should have all needed information in the *old_vers
01512   returned by this function.  This is because *old_vers is based
01513   on the transaction undo log records.  The function
01514   trx_undo_page_fetch_ext() will write BLOB prefixes to the
01515   transaction undo log that are at least as long as the longest
01516   possible column prefix in a secondary index.  Thus, secondary
01517   index entries for *old_vers can be constructed without
01518   dereferencing any BLOB pointers. */
01519 
01520   ptr = trx_undo_rec_skip_row_ref(ptr, index);
01521 
01522   ptr = trx_undo_update_rec_get_update(ptr, index, type, trx_id,
01523                roll_ptr, info_bits,
01524                NULL, heap, &update);
01525 
01526   if (UNIV_UNLIKELY(table_id != index->table->id)) {
01527     ptr = NULL;
01528 
01529     fprintf(stderr,
01530       "InnoDB: Error: trying to access update undo rec"
01531       " for table %s\n"
01532       "InnoDB: but the table id in the"
01533       " undo record is wrong\n"
01534       "InnoDB: Submit a detailed bug report"
01535       " to http://bugs.mysql.com\n"
01536       "InnoDB: Run also CHECK TABLE %s\n",
01537       index->table_name, index->table_name);
01538   }
01539 
01540   if (ptr == NULL) {
01541     /* The record was corrupted, return an error; these printfs
01542     should catch an elusive bug in row_vers_old_has_index_entry */
01543 
01544     fprintf(stderr,
01545       "InnoDB: table %s, index %s, n_uniq %lu\n"
01546       "InnoDB: undo rec address %p, type %lu cmpl_info %lu\n"
01547       "InnoDB: undo rec table id %llu,"
01548       " index table id %llu\n"
01549       "InnoDB: dump of 150 bytes in undo rec: ",
01550       index->table_name, index->name,
01551       (ulong) dict_index_get_n_unique(index),
01552       undo_rec, (ulong) type, (ulong) cmpl_info,
01553       (ullint) table_id,
01554       (ullint) index->table->id);
01555     ut_print_buf(stderr, undo_rec, 150);
01556     fputs("\n"
01557           "InnoDB: index record ", stderr);
01558     rec_print(stderr, index_rec, index);
01559     fputs("\n"
01560           "InnoDB: record version ", stderr);
01561     rec_print_new(stderr, rec, offsets);
01562     fprintf(stderr, "\n"
01563       "InnoDB: Record trx id " TRX_ID_FMT
01564       ", update rec trx id " TRX_ID_FMT "\n"
01565       "InnoDB: Roll ptr in rec " TRX_ID_FMT
01566       ", in update rec" TRX_ID_FMT "\n",
01567       rec_trx_id, trx_id,
01568       old_roll_ptr, roll_ptr);
01569 
01570     trx_purge_sys_print();
01571     return(DB_ERROR);
01572   }
01573 
01574   if (row_upd_changes_field_size_or_external(index, offsets, update)) {
01575     ulint n_ext;
01576 
01577     /* We have to set the appropriate extern storage bits in the
01578     old version of the record: the extern bits in rec for those
01579     fields that update does NOT update, as well as the the bits for
01580     those fields that update updates to become externally stored
01581     fields. Store the info: */
01582 
01583     entry = row_rec_to_index_entry(ROW_COPY_DATA, rec, index,
01584                  offsets, &n_ext, heap);
01585     n_ext += btr_push_update_extern_fields(entry, update, heap);
01586     /* The page containing the clustered index record
01587     corresponding to entry is latched in mtr.  Thus the
01588     following call is safe. */
01589     row_upd_index_replace_new_col_vals(entry, index, update, heap);
01590 
01591     buf = static_cast<byte *>(mem_heap_alloc(heap, rec_get_converted_size(index, entry,
01592                   n_ext)));
01593 
01594     *old_vers = rec_convert_dtuple_to_rec(buf, index,
01595                   entry, n_ext);
01596   } else {
01597     buf = static_cast<byte *>(mem_heap_alloc(heap, rec_offs_size(offsets)));
01598     *old_vers = rec_copy(buf, rec, offsets);
01599     rec_offs_make_valid(*old_vers, index, offsets);
01600     row_upd_rec_in_place(*old_vers, index, offsets, update, NULL);
01601   }
01602 
01603   return(DB_SUCCESS);
01604 }
01605 #endif /* !UNIV_HOTBACKUP */