~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/innobase/trx/trx0rec.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

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