~mysql/mysql-server/mysql-6.0

« back to all changes in this revision

Viewing changes to innobase/trx/trx0rec.c

  • Committer: monty at mysql
  • Date: 2001-02-17 12:19:19 UTC
  • mto: (554.1.1)
  • mto: This revision was merged to the branch mainline in revision 556.
  • Revision ID: sp1r-monty@donna.mysql.com-20010217121919-07904
Added Innobase to source distribution

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************
 
2
Transaction undo log record
 
3
 
 
4
(c) 1996 Innobase Oy
 
5
 
 
6
Created 3/26/1996 Heikki Tuuri
 
7
*******************************************************/
 
8
 
 
9
#include "trx0rec.h"
 
10
 
 
11
#ifdef UNIV_NONINL
 
12
#include "trx0rec.ic"
 
13
#endif
 
14
 
 
15
#include "fsp0fsp.h"
 
16
#include "mach0data.h"
 
17
#include "trx0rseg.h"
 
18
#include "trx0trx.h"
 
19
#include "trx0undo.h"
 
20
#include "dict0dict.h"
 
21
#include "ut0mem.h"
 
22
#include "row0upd.h"
 
23
#include "que0que.h"
 
24
#include "trx0purge.h"
 
25
#include "row0row.h"
 
26
 
 
27
/*=========== UNDO LOG RECORD CREATION AND DECODING ====================*/
 
28
 
 
29
/**************************************************************************
 
30
Writes the mtr log entry of the inserted undo log record on the undo log
 
31
page. */
 
32
UNIV_INLINE
 
33
void
 
34
trx_undof_page_add_undo_rec_log(
 
35
/*============================*/
 
36
        page_t* undo_page,      /* in: undo log page */
 
37
        ulint   old_free,       /* in: start offset of the inserted entry */
 
38
        ulint   new_free,       /* in: end offset of the entry */
 
39
        mtr_t*  mtr)            /* in: mtr */
 
40
{
 
41
        byte*   log_ptr;
 
42
        ulint   len;
 
43
 
 
44
#ifdef notdefined
 
45
        ulint   i;
 
46
        byte*   prev_rec_ptr;
 
47
        byte*   ptr;
 
48
        ulint   min_len;
 
49
 
 
50
        ut_ad(new_free >= old_free + 4);
 
51
 
 
52
        i = 0;
 
53
        ptr = undo_page + old_free + 2;
 
54
        
 
55
        if (old_free > mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
 
56
                                        + TRX_UNDO_PAGE_START)) {
 
57
                prev_rec_ptr = undo_page + mach_read_from_2(ptr - 4) + 2;
 
58
 
 
59
                min_len = ut_min(new_free - old_free - 4,
 
60
                                 (undo_page + old_free - 2) - prev_rec_ptr); 
 
61
                for (;;) {
 
62
                        if (i >= min_len) {
 
63
 
 
64
                                break;
 
65
                        } else if ((*ptr == *prev_rec_ptr)
 
66
                                   || ((*ptr == *prev_rec_ptr + 1)
 
67
                                       && (ptr + 1 == suffix))) {
 
68
                                i++;
 
69
                                ptr++;
 
70
                                prev_rec_ptr++;
 
71
                        } else {
 
72
                                break;
 
73
                        }
 
74
                }
 
75
        }
 
76
        
 
77
        mlog_write_initial_log_record(undo_page, MLOG_UNDO_INSERT, mtr);
 
78
 
 
79
        mlog_catenate_ulint(mtr, old_free, MLOG_2BYTES);
 
80
 
 
81
        mlog_catenate_ulint_compressed(mtr, i);
 
82
 
 
83
        mlog_catenate_string(mtr, ptr, new_free - old_free - 2 - i);
 
84
#endif
 
85
        log_ptr = mlog_open(mtr, 30 + MLOG_BUF_MARGIN);
 
86
 
 
87
        if (log_ptr == NULL) {
 
88
 
 
89
                return;
 
90
        }
 
91
 
 
92
        log_ptr = mlog_write_initial_log_record_fast(undo_page,
 
93
                                        MLOG_UNDO_INSERT, log_ptr, mtr);
 
94
        len = new_free - old_free - 4;
 
95
 
 
96
        mach_write_to_2(log_ptr, len);
 
97
        log_ptr += 2;
 
98
 
 
99
        if (len < 256) {
 
100
                ut_memcpy(log_ptr, undo_page + old_free + 2, len);
 
101
                log_ptr += len;
 
102
        }
 
103
 
 
104
        mlog_close(mtr, log_ptr);
 
105
 
 
106
        if (len >= MLOG_BUF_MARGIN) {
 
107
                mlog_catenate_string(mtr, undo_page + old_free + 2, len);
 
108
        }
 
109
}       
 
110
 
 
111
/***************************************************************
 
112
Parses a redo log record of adding an undo log record. */
 
113
 
 
114
byte*
 
115
trx_undo_parse_add_undo_rec(
 
116
/*========================*/
 
117
                        /* out: end of log record or NULL */
 
118
        byte*   ptr,    /* in: buffer */
 
119
        byte*   end_ptr,/* in: buffer end */
 
120
        page_t* page)   /* in: page or NULL */
 
121
{
 
122
        ulint   len;
 
123
        byte*   rec;
 
124
        ulint   first_free;
 
125
 
 
126
        if (end_ptr < ptr + 2) {
 
127
 
 
128
                return(NULL);
 
129
        }
 
130
 
 
131
        len = mach_read_from_2(ptr);
 
132
        ptr += 2;
 
133
 
 
134
        if (end_ptr < ptr + len) {
 
135
 
 
136
                return(NULL);
 
137
        }
 
138
        
 
139
        if (page == NULL) {
 
140
 
 
141
                return(ptr + len);
 
142
        }
 
143
        
 
144
        first_free = mach_read_from_2(page + TRX_UNDO_PAGE_HDR
 
145
                                                        + TRX_UNDO_PAGE_FREE);
 
146
        rec = page + first_free;
 
147
        
 
148
        mach_write_to_2(rec, first_free + 4 + len);
 
149
        mach_write_to_2(rec + 2 + len, first_free);
 
150
 
 
151
        mach_write_to_2(page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
 
152
                                                        first_free + 4 + len);
 
153
        ut_memcpy(rec + 2, ptr, len);
 
154
 
 
155
        return(ptr + len);
 
156
}
 
157
        
 
158
/**************************************************************************
 
159
Calculates the free space left for extending an undo log record. */
 
160
UNIV_INLINE
 
161
ulint
 
162
trx_undo_left(
 
163
/*==========*/
 
164
                        /* out: bytes left */
 
165
        page_t* page,   /* in: undo log page */
 
166
        byte*   ptr)    /* in: pointer to page */
 
167
{
 
168
        /* The '- 10' is a safety margin, in case we have some small
 
169
        calculation error below */
 
170
 
 
171
        return(UNIV_PAGE_SIZE - (ptr - page) - 10 - FIL_PAGE_DATA_END);
 
172
}
 
173
 
 
174
/**************************************************************************
 
175
Reports in the undo log of an insert of a clustered index record. */
 
176
static
 
177
ulint
 
178
trx_undo_page_report_insert(
 
179
/*========================*/
 
180
                                        /* out: offset of the inserted entry
 
181
                                        on the page if succeed, 0 if fail */
 
182
        page_t*         undo_page,      /* in: undo log page */
 
183
        trx_t*          trx,            /* in: transaction */
 
184
        dict_index_t*   index,          /* in: clustered index */
 
185
        dtuple_t*       clust_entry,    /* in: index entry which will be
 
186
                                        inserted to the clustered index */
 
187
        mtr_t*          mtr)            /* in: mtr */
 
188
{
 
189
        ulint           first_free;
 
190
        byte*           ptr;
 
191
        ulint           len;
 
192
        dfield_t*       field;
 
193
        ulint           flen;
 
194
        ulint           i;
 
195
        
 
196
        ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
 
197
                                + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT);
 
198
 
 
199
        first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
 
200
                                                        + TRX_UNDO_PAGE_FREE);
 
201
        ptr = undo_page + first_free;
 
202
        
 
203
        ut_ad(first_free <= UNIV_PAGE_SIZE);
 
204
 
 
205
        if (trx_undo_left(undo_page, ptr) < 30) {
 
206
 
 
207
                /* NOTE: the value 30 must be big enough such that the general
 
208
                fields written below fit on the undo log page */
 
209
 
 
210
                return(0);
 
211
        }
 
212
 
 
213
        /* Reserve 2 bytes for the pointer to the next undo log record */
 
214
        ptr += 2;
 
215
                
 
216
        /* Store first some general parameters to the undo log */ 
 
217
        mach_write_to_1(ptr, TRX_UNDO_INSERT_REC);
 
218
        ptr++;
 
219
 
 
220
        len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
 
221
        ptr += len;
 
222
 
 
223
        len = mach_dulint_write_much_compressed(ptr, (index->table)->id);
 
224
        ptr += len;
 
225
        /*----------------------------------------*/
 
226
        /* Store then the fields required to uniquely determine the record
 
227
        to be inserted in the clustered index */
 
228
 
 
229
        for (i = 0; i < dict_index_get_n_unique(index); i++) {
 
230
 
 
231
                field = dtuple_get_nth_field(clust_entry, i);
 
232
 
 
233
                flen = dfield_get_len(field);
 
234
 
 
235
                if (trx_undo_left(undo_page, ptr) < 5) {
 
236
 
 
237
                        return(0);
 
238
                }
 
239
 
 
240
                len = mach_write_compressed(ptr, flen); 
 
241
                ptr += len;
 
242
 
 
243
                if (flen != UNIV_SQL_NULL) {
 
244
                        if (trx_undo_left(undo_page, ptr) < flen) {
 
245
 
 
246
                                return(0);
 
247
                        }
 
248
 
 
249
                        ut_memcpy(ptr, dfield_get_data(field), flen);
 
250
                        ptr += flen;
 
251
                }
 
252
        }
 
253
 
 
254
        if (trx_undo_left(undo_page, ptr) < 2) {
 
255
 
 
256
                return(0);
 
257
        }
 
258
 
 
259
        /*----------------------------------------*/
 
260
        /* Write pointers to the previous and the next undo log records */
 
261
 
 
262
        if (trx_undo_left(undo_page, ptr) < 2) {
 
263
 
 
264
                return(0);
 
265
        }
 
266
 
 
267
        mach_write_to_2(ptr, first_free);
 
268
        ptr += 2;
 
269
 
 
270
        mach_write_to_2(undo_page + first_free, ptr - undo_page);
 
271
 
 
272
        mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
 
273
                                                        ptr - undo_page);
 
274
 
 
275
        /* Write the log entry to the REDO log of this change in the UNDO log */
 
276
 
 
277
        trx_undof_page_add_undo_rec_log(undo_page, first_free,
 
278
                                                        ptr - undo_page, mtr);
 
279
        return(first_free);     
 
280
}
 
281
 
 
282
/**************************************************************************
 
283
Reads from an undo log record the general parameters. */
 
284
 
 
285
byte*
 
286
trx_undo_rec_get_pars(
 
287
/*==================*/
 
288
                                        /* out: remaining part of undo log
 
289
                                        record after reading these values */
 
290
        trx_undo_rec_t* undo_rec,       /* in: undo log record */
 
291
        ulint*          type,           /* out: undo record type:
 
292
                                        TRX_UNDO_INSERT_REC, ... */
 
293
        ulint*          cmpl_info,      /* out: compiler info, relevant only
 
294
                                        for update type records */
 
295
        dulint*         undo_no,        /* out: undo log record number */
 
296
        dulint*         table_id)       /* out: table id */
 
297
{
 
298
        byte*           ptr;
 
299
        ulint           len;
 
300
        ulint           type_cmpl;
 
301
 
 
302
        ptr = undo_rec + 2;
 
303
 
 
304
        type_cmpl = mach_read_from_1(ptr);
 
305
        ptr++;
 
306
        
 
307
        *type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1);
 
308
        *cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT;
 
309
 
 
310
        *undo_no = mach_dulint_read_much_compressed(ptr);               
 
311
        len = mach_dulint_get_much_compressed_size(*undo_no);
 
312
        ptr += len;
 
313
 
 
314
        *table_id = mach_dulint_read_much_compressed(ptr);              
 
315
        len = mach_dulint_get_much_compressed_size(*table_id);
 
316
        ptr += len;
 
317
 
 
318
        return(ptr);
 
319
}
 
320
 
 
321
/**************************************************************************
 
322
Reads from an undo log record a stored column value. */
 
323
UNIV_INLINE
 
324
byte*
 
325
trx_undo_rec_get_col_val(
 
326
/*=====================*/
 
327
                        /* out: remaining part of undo log record after
 
328
                        reading these values */
 
329
        byte*   ptr,    /* in: pointer to remaining part of undo log record */
 
330
        byte**  field,  /* out: pointer to stored field */
 
331
        ulint*  len)    /* out: length of the field, or UNIV_SQL_NULL */
 
332
{
 
333
        *len = mach_read_compressed(ptr); 
 
334
        ptr += mach_get_compressed_size(*len);
 
335
 
 
336
        *field = ptr;
 
337
        
 
338
        if (*len != UNIV_SQL_NULL) {
 
339
                ptr += *len;
 
340
        }
 
341
 
 
342
        return(ptr);
 
343
}
 
344
 
 
345
/***********************************************************************
 
346
Builds a row reference from an undo log record. */
 
347
 
 
348
byte*
 
349
trx_undo_rec_get_row_ref(
 
350
/*=====================*/
 
351
                                /* out: pointer to remaining part of undo
 
352
                                record */
 
353
        byte*           ptr,    /* in: remaining part of a copy of an undo log
 
354
                                record, at the start of the row reference;
 
355
                                NOTE that this copy of the undo log record must
 
356
                                be preserved as long as the row reference is
 
357
                                used, as we do NOT copy the data in the
 
358
                                record! */
 
359
        dict_index_t*   index,  /* in: clustered index */
 
360
        dtuple_t**      ref,    /* out, own: row reference */
 
361
        mem_heap_t*     heap)   /* in: memory heap from which the memory
 
362
                                needed is allocated */
 
363
{
 
364
        ulint           i;
 
365
        dfield_t*       dfield;
 
366
        byte*           field;
 
367
        ulint           len;
 
368
        ulint           ref_len;
 
369
        
 
370
        ut_ad(index && ptr && ref && heap);
 
371
        
 
372
        ref_len = dict_index_get_n_unique(index);
 
373
 
 
374
        *ref = dtuple_create(heap, ref_len);
 
375
 
 
376
        dict_index_copy_types(*ref, index, ref_len);
 
377
 
 
378
        for (i = 0; i < ref_len; i++) {
 
379
                dfield = dtuple_get_nth_field(*ref, i);
 
380
 
 
381
                ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
 
382
 
 
383
                dfield_set_data(dfield, field, len);
 
384
        }
 
385
 
 
386
        return(ptr);
 
387
}       
 
388
 
 
389
/***********************************************************************
 
390
Skips a row reference from an undo log record. */
 
391
 
 
392
byte*
 
393
trx_undo_rec_skip_row_ref(
 
394
/*======================*/
 
395
                                /* out: pointer to remaining part of undo
 
396
                                record */
 
397
        byte*           ptr,    /* in: remaining part in update undo log
 
398
                                record, at the start of the row reference */
 
399
        dict_index_t*   index)  /* in: clustered index */
 
400
{
 
401
        ulint           i;
 
402
        byte*           field;
 
403
        ulint           len;
 
404
        ulint           ref_len;
 
405
        
 
406
        ut_ad(index && ptr);
 
407
        
 
408
        ref_len = dict_index_get_n_unique(index);
 
409
 
 
410
        for (i = 0; i < ref_len; i++) {
 
411
                ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
 
412
        }
 
413
 
 
414
        return(ptr);
 
415
}       
 
416
 
 
417
/**************************************************************************
 
418
Reports in the undo log of an update or delete marking of a clustered index
 
419
record. */
 
420
static
 
421
ulint
 
422
trx_undo_page_report_modify(
 
423
/*========================*/
 
424
                                        /* out: byte offset of the inserted
 
425
                                        undo log entry on the page if succeed,
 
426
                                        0 if fail */
 
427
        page_t*         undo_page,      /* in: undo log page */
 
428
        trx_t*          trx,            /* in: transaction */
 
429
        dict_index_t*   index,          /* in: clustered index where update or
 
430
                                        delete marking is done */
 
431
        rec_t*          rec,            /* in: clustered index record which
 
432
                                        has NOT yet been modified */
 
433
        upd_t*          update,         /* in: update vector which tells the
 
434
                                        columns to be updated; in the case of
 
435
                                        a delete, this should be set to NULL */
 
436
        ulint           cmpl_info,      /* in: compiler info on secondary
 
437
                                        index updates */
 
438
        mtr_t*          mtr)            /* in: mtr */
 
439
{
 
440
        dict_table_t*   table;
 
441
        upd_field_t*    upd_field;
 
442
        dict_col_t*     col;
 
443
        ulint           first_free;
 
444
        byte*           ptr;
 
445
        ulint           len;
 
446
        byte*           field;
 
447
        ulint           flen;
 
448
        ulint           pos;
 
449
        dulint          roll_ptr;
 
450
        dulint          trx_id;
 
451
        ulint           bits;
 
452
        ulint           col_no;
 
453
        byte*           old_ptr;
 
454
        ulint           type_cmpl;
 
455
        ulint           i;
 
456
        
 
457
        ut_ad(index->type & DICT_CLUSTERED);
 
458
        ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
 
459
                                + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE);
 
460
        table = index->table;
 
461
        
 
462
        first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
 
463
                                                + TRX_UNDO_PAGE_FREE);
 
464
        ptr = undo_page + first_free;
 
465
                                                
 
466
        ut_ad(first_free <= UNIV_PAGE_SIZE);
 
467
 
 
468
        if (trx_undo_left(undo_page, ptr) < 50) {
 
469
 
 
470
                /* NOTE: the value 50 must be big enough so that the general
 
471
                fields written below fit on the undo log page */
 
472
 
 
473
                return(0);
 
474
        }
 
475
 
 
476
        /* Reserve 2 bytes for the pointer to the next undo log record */
 
477
        ptr += 2;
 
478
 
 
479
        /* Store first some general parameters to the undo log */               
 
480
        if (update) {
 
481
                if (rec_get_deleted_flag(rec)) {
 
482
                        type_cmpl = TRX_UNDO_UPD_DEL_REC;
 
483
                } else {
 
484
                        type_cmpl = TRX_UNDO_UPD_EXIST_REC;
 
485
                }
 
486
        } else {
 
487
                type_cmpl = TRX_UNDO_DEL_MARK_REC;
 
488
        }
 
489
 
 
490
        type_cmpl = type_cmpl | (cmpl_info * TRX_UNDO_CMPL_INFO_MULT);
 
491
 
 
492
        mach_write_to_1(ptr, type_cmpl);
 
493
        
 
494
        ptr++;
 
495
        len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
 
496
        ptr += len;
 
497
 
 
498
        len = mach_dulint_write_much_compressed(ptr, table->id);
 
499
        ptr += len;
 
500
 
 
501
        /*----------------------------------------*/
 
502
        /* Store the state of the info bits */
 
503
 
 
504
        bits = rec_get_info_bits(rec);
 
505
        mach_write_to_1(ptr, bits);
 
506
        ptr += 1;
 
507
 
 
508
        /* Store the values of the system columns */
 
509
        trx_id = dict_index_rec_get_sys_col(index, DATA_TRX_ID, rec);
 
510
 
 
511
        roll_ptr = dict_index_rec_get_sys_col(index, DATA_ROLL_PTR, rec);       
 
512
 
 
513
        len = mach_dulint_write_compressed(ptr, trx_id);
 
514
        ptr += len;
 
515
 
 
516
        len = mach_dulint_write_compressed(ptr, roll_ptr);
 
517
        ptr += len;
 
518
 
 
519
        /*----------------------------------------*/
 
520
        /* Store then the fields required to uniquely determine the
 
521
        record which will be modified in the clustered index */
 
522
 
 
523
        for (i = 0; i < dict_index_get_n_unique(index); i++) {
 
524
 
 
525
                field = rec_get_nth_field(rec, i, &flen);
 
526
 
 
527
                if (trx_undo_left(undo_page, ptr) < 4) {
 
528
 
 
529
                        return(0);
 
530
                }
 
531
 
 
532
                len = mach_write_compressed(ptr, flen); 
 
533
                ptr += len;
 
534
 
 
535
                if (flen != UNIV_SQL_NULL) {
 
536
                        if (trx_undo_left(undo_page, ptr) < flen) {
 
537
 
 
538
                                return(0);
 
539
                        }
 
540
 
 
541
                        ut_memcpy(ptr, field, flen);
 
542
                        ptr += flen;
 
543
                }
 
544
        }
 
545
 
 
546
        /*----------------------------------------*/
 
547
        /* Save to the undo log the old values of the columns to be updated. */
 
548
 
 
549
        if (update) {
 
550
            if (trx_undo_left(undo_page, ptr) < 5) {
 
551
 
 
552
                return(0);
 
553
            }
 
554
 
 
555
            len = mach_write_compressed(ptr, upd_get_n_fields(update));
 
556
            ptr += len;
 
557
 
 
558
            for (i = 0; i < upd_get_n_fields(update); i++) {
 
559
 
 
560
                upd_field = upd_get_nth_field(update, i);
 
561
                pos = upd_field->field_no;
 
562
 
 
563
                /* Write field number to undo log */
 
564
                if (trx_undo_left(undo_page, ptr) < 5) {
 
565
 
 
566
                        return(0);
 
567
                }
 
568
 
 
569
                len = mach_write_compressed(ptr, pos);
 
570
                ptr += len;
 
571
 
 
572
                /* Save the old value of field */
 
573
                field = rec_get_nth_field(rec, pos, &flen);
 
574
 
 
575
                if (trx_undo_left(undo_page, ptr) < 5) {
 
576
 
 
577
                        return(0);
 
578
                }
 
579
 
 
580
                len = mach_write_compressed(ptr, flen);
 
581
                ptr += len;
 
582
 
 
583
                if (flen != UNIV_SQL_NULL) {
 
584
                        if (trx_undo_left(undo_page, ptr) < flen) {
 
585
 
 
586
                                return(0);
 
587
                        }
 
588
 
 
589
                        ut_memcpy(ptr, field, flen);
 
590
                        ptr += flen;
 
591
                }
 
592
            }
 
593
        }               
 
594
 
 
595
        /*----------------------------------------*/
 
596
        /* In the case of a delete marking, and also in the case of an update
 
597
        where any ordering field of any index changes, store the values of all
 
598
        columns which occur as ordering fields in any index. This info is used
 
599
        in the purge of old versions where we use it to build and search the
 
600
        delete marked index records, to look if we can remove them from the
 
601
        index tree. */
 
602
 
 
603
        if (!update || !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {     
 
604
 
 
605
            (trx->update_undo)->del_marks = TRUE;
 
606
 
 
607
            if (trx_undo_left(undo_page, ptr) < 5) {
 
608
 
 
609
                return(0);
 
610
            }
 
611
            
 
612
            old_ptr = ptr;
 
613
 
 
614
            /* Reserve 2 bytes to write the number of bytes the stored fields
 
615
            take in this undo record */
 
616
 
 
617
            ptr += 2;
 
618
 
 
619
            for (col_no = 0; col_no < dict_table_get_n_cols(table); col_no++) {
 
620
 
 
621
                col = dict_table_get_nth_col(table, col_no);
 
622
 
 
623
                if (col->ord_part > 0) {
 
624
                        
 
625
                        pos = dict_index_get_nth_col_pos(index, col_no);
 
626
 
 
627
                        /* Write field number to undo log */
 
628
                        if (trx_undo_left(undo_page, ptr) < 5) {
 
629
        
 
630
                                return(0);
 
631
                        }
 
632
 
 
633
                        len = mach_write_compressed(ptr, pos);
 
634
                        ptr += len;
 
635
        
 
636
                        /* Save the old value of field */
 
637
                        field = rec_get_nth_field(rec, pos, &flen);
 
638
        
 
639
                        if (trx_undo_left(undo_page, ptr) < 5) {
 
640
        
 
641
                                return(0);
 
642
                        }
 
643
 
 
644
                        len = mach_write_compressed(ptr, flen);
 
645
                        ptr += len;
 
646
 
 
647
                        if (flen != UNIV_SQL_NULL) {
 
648
                                if (trx_undo_left(undo_page, ptr) < flen) {
 
649
        
 
650
                                        return(0);
 
651
                                }
 
652
 
 
653
                                ut_memcpy(ptr, field, flen);
 
654
                                ptr += flen;
 
655
                        }
 
656
                }
 
657
            }
 
658
 
 
659
            mach_write_to_2(old_ptr, ptr - old_ptr);        
 
660
        }               
 
661
 
 
662
        /*----------------------------------------*/
 
663
        /* Write pointers to the previous and the next undo log records */
 
664
        if (trx_undo_left(undo_page, ptr) < 2) {
 
665
 
 
666
                return(0);
 
667
        }
 
668
        
 
669
        mach_write_to_2(ptr, first_free);
 
670
        ptr += 2;
 
671
        mach_write_to_2(undo_page + first_free, ptr - undo_page);
 
672
 
 
673
        mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE,
 
674
                                                        ptr - undo_page);
 
675
 
 
676
        /* Write to the REDO log about this change in the UNDO log */
 
677
 
 
678
        trx_undof_page_add_undo_rec_log(undo_page, first_free,
 
679
                                                        ptr - undo_page, mtr);
 
680
        return(first_free);     
 
681
}
 
682
 
 
683
/**************************************************************************
 
684
Reads from an undo log update record the system field values of the old
 
685
version. */
 
686
 
 
687
byte*
 
688
trx_undo_update_rec_get_sys_cols(
 
689
/*=============================*/
 
690
                                /* out: remaining part of undo log
 
691
                                record after reading these values */
 
692
        byte*   ptr,            /* in: remaining part of undo log
 
693
                                record after reading general
 
694
                                parameters */
 
695
        dulint* trx_id,         /* out: trx id */
 
696
        dulint* roll_ptr,       /* out: roll ptr */
 
697
        ulint*  info_bits)      /* out: info bits state */
 
698
{
 
699
        ulint   len;
 
700
 
 
701
        /* Read the state of the info bits */
 
702
        *info_bits = mach_read_from_1(ptr);
 
703
        ptr += 1;
 
704
 
 
705
        /* Read the values of the system columns */
 
706
 
 
707
        *trx_id = mach_dulint_read_compressed(ptr);             
 
708
        len = mach_dulint_get_compressed_size(*trx_id);
 
709
        ptr += len;
 
710
 
 
711
        *roll_ptr = mach_dulint_read_compressed(ptr);           
 
712
        len = mach_dulint_get_compressed_size(*roll_ptr);
 
713
        ptr += len;
 
714
 
 
715
        return(ptr);
 
716
}
 
717
 
 
718
/**************************************************************************
 
719
Reads from an update undo log record the number of updated fields. */
 
720
UNIV_INLINE
 
721
byte*
 
722
trx_undo_update_rec_get_n_upd_fields(
 
723
/*=================================*/
 
724
                        /* out: remaining part of undo log record after
 
725
                        reading this value */
 
726
        byte*   ptr,    /* in: pointer to remaining part of undo log record */
 
727
        ulint*  n)      /* out: number of fields */
 
728
{
 
729
        *n = mach_read_compressed(ptr); 
 
730
        ptr += mach_get_compressed_size(*n);
 
731
 
 
732
        return(ptr);
 
733
}
 
734
 
 
735
/**************************************************************************
 
736
Reads from an update undo log record a stored field number. */
 
737
UNIV_INLINE
 
738
byte*
 
739
trx_undo_update_rec_get_field_no(
 
740
/*=============================*/
 
741
                        /* out: remaining part of undo log record after
 
742
                        reading this value */
 
743
        byte*   ptr,    /* in: pointer to remaining part of undo log record */
 
744
        ulint*  field_no)/* out: field number */
 
745
{
 
746
        *field_no = mach_read_compressed(ptr); 
 
747
        ptr += mach_get_compressed_size(*field_no);
 
748
 
 
749
        return(ptr);
 
750
}
 
751
 
 
752
/***********************************************************************
 
753
Builds an update vector based on a remaining part of an undo log record. */
 
754
 
 
755
byte*
 
756
trx_undo_update_rec_get_update(
 
757
/*===========================*/
 
758
                                /* out: remaining part of the record */
 
759
        byte*           ptr,    /* in: remaining part in update undo log
 
760
                                record, after reading the row reference
 
761
                                NOTE that this copy of the undo log record must
 
762
                                be preserved as long as the update vector is
 
763
                                used, as we do NOT copy the data in the
 
764
                                record! */
 
765
        dict_index_t*   index,  /* in: clustered index */
 
766
        ulint           type,   /* in: TRX_UNDO_UPD_EXIST_REC,
 
767
                                TRX_UNDO_UPD_DEL_REC, or
 
768
                                TRX_UNDO_DEL_MARK_REC; in the last case,
 
769
                                only trx id and roll ptr fields are added to
 
770
                                the update vector */
 
771
        dulint          trx_id, /* in: transaction id from this undorecord */
 
772
        dulint          roll_ptr,/* in: roll pointer from this undo record */
 
773
        ulint           info_bits,/* in: info bits from this undo record */
 
774
        mem_heap_t*     heap,   /* in: memory heap from which the memory
 
775
                                needed is allocated */
 
776
        upd_t**         upd)    /* out, own: update vector */
 
777
{
 
778
        upd_field_t*    upd_field;
 
779
        upd_t*          update;
 
780
        ulint           n_fields;
 
781
        byte*           buf;
 
782
        byte*           field;
 
783
        ulint           len;
 
784
        ulint           field_no;
 
785
        ulint           i;
 
786
        
 
787
        if (type != TRX_UNDO_DEL_MARK_REC) {
 
788
                ptr = trx_undo_update_rec_get_n_upd_fields(ptr, &n_fields);
 
789
        } else {
 
790
                n_fields = 0;
 
791
        }
 
792
 
 
793
        update = upd_create(n_fields + 2, heap);
 
794
 
 
795
        update->info_bits = info_bits;
 
796
 
 
797
        /* Store first trx id and roll ptr to update vector */
 
798
 
 
799
        upd_field = upd_get_nth_field(update, n_fields);
 
800
        buf = mem_heap_alloc(heap, DATA_TRX_ID_LEN);
 
801
        trx_write_trx_id(buf, trx_id);
 
802
 
 
803
        upd_field_set_field_no(upd_field,
 
804
                        dict_index_get_sys_col_pos(index, DATA_TRX_ID),
 
805
                                                                        index);
 
806
        dfield_set_data(&(upd_field->new_val), buf, DATA_TRX_ID_LEN);
 
807
 
 
808
        upd_field = upd_get_nth_field(update, n_fields + 1);
 
809
        buf = mem_heap_alloc(heap, DATA_ROLL_PTR_LEN);
 
810
        trx_write_roll_ptr(buf, roll_ptr);
 
811
 
 
812
        upd_field_set_field_no(upd_field,
 
813
                        dict_index_get_sys_col_pos(index, DATA_ROLL_PTR),
 
814
                                                                        index);
 
815
        dfield_set_data(&(upd_field->new_val), buf, DATA_ROLL_PTR_LEN);
 
816
        
 
817
        /* Store then the updated ordinary columns to update vector */
 
818
 
 
819
        for (i = 0; i < n_fields; i++) {
 
820
 
 
821
                ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
 
822
                ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
 
823
 
 
824
                upd_field = upd_get_nth_field(update, i);
 
825
 
 
826
                upd_field_set_field_no(upd_field, field_no, index);
 
827
 
 
828
                dfield_set_data(&(upd_field->new_val), field, len);
 
829
        }
 
830
 
 
831
        *upd = update;
 
832
 
 
833
        return(ptr);
 
834
}
 
835
        
 
836
/***********************************************************************
 
837
Builds a partial row from an update undo log record. It contains the
 
838
columns which occur as ordering in any index of the table. */
 
839
 
 
840
byte*
 
841
trx_undo_rec_get_partial_row(
 
842
/*=========================*/
 
843
                                /* out: pointer to remaining part of undo
 
844
                                record */
 
845
        byte*           ptr,    /* in: remaining part in update undo log
 
846
                                record of a suitable type, at the start of
 
847
                                the stored index columns;
 
848
                                NOTE that this copy of the undo log record must
 
849
                                be preserved as long as the partial row is
 
850
                                used, as we do NOT copy the data in the
 
851
                                record! */
 
852
        dict_index_t*   index,  /* in: clustered index */
 
853
        dtuple_t**      row,    /* out, own: partial row */
 
854
        mem_heap_t*     heap)   /* in: memory heap from which the memory
 
855
                                needed is allocated */
 
856
{
 
857
        dfield_t*       dfield;
 
858
        byte*           field;
 
859
        ulint           len;
 
860
        ulint           field_no;
 
861
        ulint           col_no;
 
862
        ulint           row_len;
 
863
        ulint           total_len;
 
864
        byte*           start_ptr;
 
865
        ulint           i;
 
866
        
 
867
        ut_ad(index && ptr && row && heap);
 
868
        
 
869
        row_len = dict_table_get_n_cols(index->table);
 
870
 
 
871
        *row = dtuple_create(heap, row_len);
 
872
 
 
873
        dict_table_copy_types(*row, index->table);
 
874
 
 
875
        start_ptr = ptr;
 
876
 
 
877
        total_len = mach_read_from_2(ptr);
 
878
        ptr += 2;
 
879
        
 
880
        for (i = 0;; i++) {
 
881
 
 
882
                if (ptr == start_ptr + total_len) {
 
883
 
 
884
                        break;
 
885
                }
 
886
        
 
887
                ptr = trx_undo_update_rec_get_field_no(ptr, &field_no);
 
888
 
 
889
                col_no = dict_index_get_nth_col_no(index, field_no);
 
890
                
 
891
                ptr = trx_undo_rec_get_col_val(ptr, &field, &len);
 
892
 
 
893
                dfield = dtuple_get_nth_field(*row, col_no);
 
894
 
 
895
                dfield_set_data(dfield, field, len);
 
896
        }
 
897
 
 
898
        return(ptr);
 
899
}       
 
900
 
 
901
/***************************************************************************
 
902
Erases the unused undo log page end. */
 
903
static
 
904
void
 
905
trx_undo_erase_page_end(
 
906
/*====================*/
 
907
        page_t* undo_page,      /* in: undo page whose end to erase */
 
908
        mtr_t*  mtr)            /* in: mtr */
 
909
{
 
910
        ulint   first_free;
 
911
        ulint   i;
 
912
        
 
913
        first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
 
914
                                                        + TRX_UNDO_PAGE_FREE);
 
915
        for (i = first_free; i < UNIV_PAGE_SIZE - FIL_PAGE_DATA_END; i++) {
 
916
                undo_page[i] = 0xFF;
 
917
        }
 
918
 
 
919
        mlog_write_initial_log_record(undo_page, MLOG_UNDO_ERASE_END, mtr);
 
920
}
 
921
        
 
922
/***************************************************************
 
923
Parses a redo log record of erasing of an undo page end. */
 
924
 
 
925
byte*
 
926
trx_undo_parse_erase_page_end(
 
927
/*==========================*/
 
928
                        /* out: end of log record or NULL */
 
929
        byte*   ptr,    /* in: buffer */
 
930
        byte*   end_ptr,/* in: buffer end */
 
931
        page_t* page,   /* in: page or NULL */
 
932
        mtr_t*  mtr)    /* in: mtr or NULL */
 
933
{
 
934
        ut_ad(ptr && end_ptr);
 
935
 
 
936
        if (page == NULL) {
 
937
 
 
938
                return(ptr);
 
939
        }
 
940
 
 
941
        trx_undo_erase_page_end(page, mtr);
 
942
 
 
943
        return(ptr);
 
944
}
 
945
 
 
946
/***************************************************************************
 
947
Writes information to an undo log about an insert, update, or a delete marking
 
948
of a clustered index record. This information is used in a rollback of the
 
949
transaction and in consistent reads that must look to the history of this
 
950
transaction. */
 
951
 
 
952
ulint
 
953
trx_undo_report_row_operation(
 
954
/*==========================*/
 
955
                                        /* out: DB_SUCCESS or error code */
 
956
        ulint           flags,          /* in: if BTR_NO_UNDO_LOG_FLAG bit is
 
957
                                        set, does nothing */
 
958
        ulint           op_type,        /* in: TRX_UNDO_INSERT_OP or
 
959
                                        TRX_UNDO_MODIFY_OP */
 
960
        que_thr_t*      thr,            /* in: query thread */
 
961
        dict_index_t*   index,          /* in: clustered index */
 
962
        dtuple_t*       clust_entry,    /* in: in the case of an insert,
 
963
                                        index entry to insert into the
 
964
                                        clustered index, otherwise NULL */
 
965
        upd_t*          update,         /* in: in the case of an update,
 
966
                                        the update vector, otherwise NULL */
 
967
        ulint           cmpl_info,      /* in: compiler info on secondary
 
968
                                        index updates */
 
969
        rec_t*          rec,            /* in: case of an update or delete
 
970
                                        marking, the record in the clustered
 
971
                                        index, otherwise NULL */
 
972
        dulint*         roll_ptr)       /* out: rollback pointer to the
 
973
                                        inserted undo log record,
 
974
                                        ut_dulint_zero if BTR_NO_UNDO_LOG
 
975
                                        flag was specified */
 
976
{
 
977
        trx_t*          trx;
 
978
        trx_undo_t*     undo;
 
979
        page_t*         undo_page;
 
980
        ulint           offset;
 
981
        mtr_t           mtr;
 
982
        ulint           page_no;
 
983
        ibool           is_insert;
 
984
        trx_rseg_t*     rseg;
 
985
        
 
986
        if (flags & BTR_NO_UNDO_LOG_FLAG) {
 
987
 
 
988
                *roll_ptr = ut_dulint_zero;
 
989
 
 
990
                return(DB_SUCCESS);
 
991
        }
 
992
                
 
993
        ut_ad(thr);
 
994
        ut_ad(index->type & DICT_CLUSTERED);
 
995
        ut_ad((op_type != TRX_UNDO_INSERT_OP)
 
996
              || (clust_entry && !update && !rec));
 
997
        
 
998
        trx = thr_get_trx(thr);
 
999
        rseg = trx->rseg;
 
1000
        
 
1001
        mutex_enter(&(trx->undo_mutex));
 
1002
 
 
1003
        /* If the undo log is not assigned yet, assign one */
 
1004
 
 
1005
        if (op_type == TRX_UNDO_INSERT_OP) {
 
1006
 
 
1007
                if (trx->insert_undo == NULL) {
 
1008
 
 
1009
                        trx_undo_assign_undo(trx, TRX_UNDO_INSERT);
 
1010
                }
 
1011
 
 
1012
                undo = trx->insert_undo;
 
1013
                is_insert = TRUE;
 
1014
        } else {
 
1015
                ut_ad(op_type == TRX_UNDO_MODIFY_OP);
 
1016
 
 
1017
                if (trx->update_undo == NULL) {
 
1018
 
 
1019
                        trx_undo_assign_undo(trx, TRX_UNDO_UPDATE);
 
1020
 
 
1021
                }
 
1022
 
 
1023
                undo = trx->update_undo;
 
1024
                is_insert = FALSE;
 
1025
        }
 
1026
 
 
1027
        if (undo == NULL) {
 
1028
                /* Did not succeed: out of space */
 
1029
                mutex_exit(&(trx->undo_mutex));
 
1030
 
 
1031
                return(DB_OUT_OF_FILE_SPACE);
 
1032
        }
 
1033
 
 
1034
        page_no = undo->last_page_no;
 
1035
        
 
1036
        mtr_start(&mtr);
 
1037
 
 
1038
        for (;;) {
 
1039
                undo_page = buf_page_get_gen(undo->space, page_no,
 
1040
                                                RW_X_LATCH, undo->guess_page,
 
1041
                                                BUF_GET,
 
1042
                                                #ifdef UNIV_SYNC_DEBUG
 
1043
                                                __FILE__, __LINE__,
 
1044
                                                #endif
 
1045
                                                &mtr);
 
1046
 
 
1047
                buf_page_dbg_add_level(undo_page, SYNC_TRX_UNDO_PAGE);
 
1048
 
 
1049
                if (op_type == TRX_UNDO_INSERT_OP) {
 
1050
                        offset = trx_undo_page_report_insert(undo_page, trx,
 
1051
                                                        index, clust_entry,
 
1052
                                                        &mtr);
 
1053
                } else {
 
1054
                        offset = trx_undo_page_report_modify(undo_page, trx,
 
1055
                                                        index, rec, update,
 
1056
                                                        cmpl_info, &mtr);
 
1057
                }
 
1058
 
 
1059
                if (offset == 0) {
 
1060
                        /* The record did not fit on the page. We erase the
 
1061
                        end segment of the undo log page and write a log
 
1062
                        record of it: this is to ensure that in the debug
 
1063
                        version the replicate page constructed using the log
 
1064
                        records stays identical to the original page */
 
1065
 
 
1066
                        trx_undo_erase_page_end(undo_page, &mtr);
 
1067
                }
 
1068
                
 
1069
                mtr_commit(&mtr);
 
1070
 
 
1071
                if (offset != 0) {
 
1072
                        /* Success */
 
1073
 
 
1074
                        break;
 
1075
                }
 
1076
 
 
1077
                ut_ad(page_no == undo->last_page_no);
 
1078
                
 
1079
                /* We have to extend the undo log by one page */
 
1080
 
 
1081
                mtr_start(&mtr);
 
1082
 
 
1083
                /* When we add a page to an undo log, this is analogous to
 
1084
                a pessimistic insert in a B-tree, and we must reserve the
 
1085
                counterpart of the tree latch, which is the rseg mutex. */
 
1086
 
 
1087
                mutex_enter(&(rseg->mutex));
 
1088
                
 
1089
                page_no = trx_undo_add_page(trx, undo, &mtr);
 
1090
 
 
1091
                mutex_exit(&(rseg->mutex));
 
1092
                
 
1093
                if (page_no == FIL_NULL) {
 
1094
                        /* Did not succeed: out of space */
 
1095
 
 
1096
                        mutex_exit(&(trx->undo_mutex));
 
1097
                        mtr_commit(&mtr);
 
1098
 
 
1099
                        return(DB_OUT_OF_FILE_SPACE);
 
1100
                }
 
1101
        }
 
1102
 
 
1103
        undo->empty = FALSE;
 
1104
        undo->top_page_no = page_no;
 
1105
        undo->top_offset  = offset;
 
1106
        undo->top_undo_no = trx->undo_no;
 
1107
        undo->guess_page = undo_page;
 
1108
 
 
1109
        UT_DULINT_INC(trx->undo_no);
 
1110
        
 
1111
        mutex_exit(&(trx->undo_mutex));
 
1112
 
 
1113
        *roll_ptr = trx_undo_build_roll_ptr(is_insert, rseg->id, page_no,
 
1114
                                                                offset);
 
1115
        return(DB_SUCCESS);
 
1116
}
 
1117
 
 
1118
/*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
 
1119
 
 
1120
/**********************************************************************
 
1121
Copies an undo record to heap. This function can be called if we know that
 
1122
the undo log record exists. */
 
1123
 
 
1124
trx_undo_rec_t*
 
1125
trx_undo_get_undo_rec_low(
 
1126
/*======================*/
 
1127
                                        /* out, own: copy of the record */
 
1128
        dulint          roll_ptr,       /* in: roll pointer to record */
 
1129
        mem_heap_t*     heap)           /* in: memory heap where copied */
 
1130
{
 
1131
        ulint           rseg_id;
 
1132
        ulint           page_no;
 
1133
        ulint           offset;
 
1134
        page_t*         undo_page;
 
1135
        trx_rseg_t*     rseg;
 
1136
        ibool           is_insert;
 
1137
        mtr_t           mtr;
 
1138
        trx_undo_rec_t* undo_rec;
 
1139
        
 
1140
        trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id, &page_no,
 
1141
                                                                &offset);
 
1142
        rseg = trx_rseg_get_on_id(rseg_id);
 
1143
 
 
1144
        mtr_start(&mtr);
 
1145
        
 
1146
        undo_page = trx_undo_page_get_s_latched(rseg->space, page_no, &mtr);
 
1147
        
 
1148
        undo_rec = trx_undo_rec_copy(undo_page + offset, heap);
 
1149
 
 
1150
        mtr_commit(&mtr);
 
1151
 
 
1152
        return(undo_rec);
 
1153
}
 
1154
 
 
1155
/**********************************************************************
 
1156
Copies an undo record to heap. */
 
1157
 
 
1158
ulint
 
1159
trx_undo_get_undo_rec(
 
1160
/*==================*/
 
1161
                                        /* out: DB_SUCCESS, or
 
1162
                                        DB_MISSING_HISTORY if the undo log
 
1163
                                        has been truncated and we cannot
 
1164
                                        fetch the old version; NOTE: the
 
1165
                                        caller must have latches on the
 
1166
                                        clustered index page and purge_view */
 
1167
        dulint          roll_ptr,       /* in: roll pointer to record */
 
1168
        dulint          trx_id,         /* in: id of the trx that generated
 
1169
                                        the roll pointer: it points to an
 
1170
                                        undo log of this transaction */
 
1171
        trx_undo_rec_t** undo_rec,      /* out, own: copy of the record */
 
1172
        mem_heap_t*     heap)           /* in: memory heap where copied */
 
1173
{
 
1174
        ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
 
1175
 
 
1176
        if (!trx_purge_update_undo_must_exist(trx_id)) {
 
1177
 
 
1178
                /* It may be that the necessary undo log has already been
 
1179
                deleted */
 
1180
 
 
1181
                return(DB_MISSING_HISTORY);
 
1182
        }
 
1183
 
 
1184
        *undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
 
1185
        
 
1186
        return(DB_SUCCESS);
 
1187
}
 
1188
 
 
1189
/***********************************************************************
 
1190
Build a previous version of a clustered index record. This function checks
 
1191
that the caller has a latch on the index page of the clustered index record
 
1192
and an s-latch on the purge_view. This guarantees that the stack of versions
 
1193
is locked. */
 
1194
 
 
1195
ulint
 
1196
trx_undo_prev_version_build(
 
1197
/*========================*/
 
1198
                                /* out: DB_SUCCESS, or DB_MISSING_HISTORY if
 
1199
                                the previous version is not >= purge_view,
 
1200
                                which means that it may have been removed */
 
1201
        rec_t*          index_rec,/* in: clustered index record in the
 
1202
                                index tree */
 
1203
        mtr_t*          index_mtr,/* in: mtr which contains the latch to
 
1204
                                index_rec page and purge_view */
 
1205
        rec_t*          rec,    /* in: version of a clustered index record */
 
1206
        dict_index_t*   index,  /* in: clustered index */
 
1207
        mem_heap_t*     heap,   /* in: memory heap from which the memory
 
1208
                                needed is allocated */
 
1209
        rec_t**         old_vers)/* out, own: previous version, or NULL if
 
1210
                                rec is the first inserted version, or if
 
1211
                                history data has been deleted */
 
1212
{
 
1213
        trx_undo_rec_t* undo_rec;
 
1214
        dtuple_t*       entry;
 
1215
        dulint          rec_trx_id;
 
1216
        ulint           type;
 
1217
        dulint          undo_no;
 
1218
        dulint          table_id;
 
1219
        dulint          trx_id;
 
1220
        dulint          roll_ptr;
 
1221
        upd_t*          update;
 
1222
        byte*           ptr;
 
1223
        ulint           info_bits;
 
1224
        ulint           cmpl_info;
 
1225
        byte*           buf;
 
1226
        ulint           err;
 
1227
 
 
1228
        ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
 
1229
        ut_ad(mtr_memo_contains(index_mtr, buf_block_align(index_rec), 
 
1230
                                                MTR_MEMO_PAGE_S_FIX) ||
 
1231
              mtr_memo_contains(index_mtr, buf_block_align(index_rec), 
 
1232
                                                MTR_MEMO_PAGE_X_FIX));
 
1233
 
 
1234
        roll_ptr = row_get_rec_roll_ptr(rec, index);
 
1235
 
 
1236
        if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
 
1237
 
 
1238
                /* The record rec is the first inserted version */
 
1239
                *old_vers = NULL;
 
1240
 
 
1241
                return(DB_SUCCESS);
 
1242
        }
 
1243
 
 
1244
        rec_trx_id = row_get_rec_trx_id(rec, index);
 
1245
        
 
1246
        err = trx_undo_get_undo_rec(roll_ptr, rec_trx_id, &undo_rec, heap);
 
1247
 
 
1248
        if (err != DB_SUCCESS) {
 
1249
 
 
1250
                *old_vers = NULL;
 
1251
 
 
1252
                return(err);
 
1253
        }
 
1254
 
 
1255
        ptr = trx_undo_rec_get_pars(undo_rec, &type, &cmpl_info, &undo_no,
 
1256
                                                                &table_id);
 
1257
        ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
 
1258
                                                                &info_bits);
 
1259
        ptr = trx_undo_rec_skip_row_ref(ptr, index);
 
1260
 
 
1261
        trx_undo_update_rec_get_update(ptr, index, type, trx_id, roll_ptr,
 
1262
                                                info_bits, heap, &update);
 
1263
 
 
1264
        if (row_upd_changes_field_size(rec, index, update)) {
 
1265
 
 
1266
                entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap);
 
1267
 
 
1268
                row_upd_clust_index_replace_new_col_vals(entry, update);
 
1269
 
 
1270
                buf = mem_heap_alloc(heap, rec_get_converted_size(entry));
 
1271
 
 
1272
                *old_vers = rec_convert_dtuple_to_rec(buf, entry);
 
1273
        } else {
 
1274
                buf = mem_heap_alloc(heap, rec_get_size(rec));
 
1275
 
 
1276
                *old_vers = rec_copy(buf, rec);
 
1277
 
 
1278
                row_upd_rec_in_place(*old_vers, update);
 
1279
        }
 
1280
 
 
1281
        return(DB_SUCCESS);
 
1282
}