~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/innobase/trx/trx0rec.cc

  • Committer: Daniel Nichter
  • Date: 2011-10-23 16:01:37 UTC
  • mto: This revision was merged to the branch mainline in revision 2448.
  • Revision ID: daniel@percona.com-20111023160137-7ac3blgz8z4tf8za
Add Administration Getting Started and Logging.  Capitalize SQL clause keywords.

Show diffs side-by-side

added added

removed removed

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