~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

Merge Revision revid:marko.makela@oracle.com-20100514133144-fe0l0b89tea4x4uu from MySQL InnoDB

Original revid:marko.makela@oracle.com-20100514133144-fe0l0b89tea4x4uu

Original Authors: Marko Mkel <marko.makela@oracle.com>
Original commit message:
Merge from mysql-5.1-innodb:

Post-merge fixes: Remove the MYSQL_VERSION_ID checks, because they only
apply to the InnoDB Plugin. Fix potential race condition accessing
trx->op_info and trx->detailed_error.
------------------------------------------------------------
revno: 3466
revision-id: marko.makela@oracle.com-20100514130815-ym7j7cfu88ro6km4
parent: marko.makela@oracle.com-20100514130228-n3n42nw7ht78k0wn
committer: Marko Mkel <marko.makela@oracle.com>
branch nick: mysql-5.1-innodb2
timestamp: Fri 2010-05-14 16:08:15 +0300
message:
  Make the InnoDB FOREIGN KEY parser understand multi-statements. (Bug #48024)
  Also make InnoDB thinks that /*/ only starts a comment. (Bug #53644).

  This fixes the bugs in the InnoDB Plugin.

  ha_innodb.h: Use trx_query_string() instead of trx_query() when
  available (MySQL 5.1.42 or later).

  innobase_get_stmt(): New function, to retrieve the currently running
  SQL statement.

  struct trx_struct: Remove mysql_query_str. Use innobase_get_stmt() instead.

  dict_strip_comments(): Add and observe the parameter sql_length. Treat
  /*/ as the start of a comment.

  dict_create_foreign_constraints(), row_table_add_foreign_constraints():
  Add the parameter sql_length.

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