~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

  • Committer: Mark Atwood
  • Date: 2012-01-03 02:42:00 UTC
  • mfrom: (2482.1.2 drizzle-build)
  • Revision ID: me@mark.atwood.name-20120103024200-2kjnoze027o3dnwy
mergeĀ lp:~brianaker/drizzle/icc-warnings

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/******************************************************
2
 
Transaction undo log
3
 
 
4
 
(c) 1996 Innobase Oy
5
 
 
6
 
Created 3/26/1996 Heikki Tuuri
7
 
*******************************************************/
8
 
 
9
 
#include "trx0undo.h"
10
 
 
11
 
#ifdef UNIV_NONINL
12
 
#include "trx0undo.ic"
13
 
#endif
14
 
 
15
 
#include "fsp0fsp.h"
16
 
#include "mach0data.h"
17
 
#include "trx0rseg.h"
18
 
#include "trx0trx.h"
19
 
#include "srv0srv.h"
20
 
#include "trx0rec.h"
21
 
#include "trx0purge.h"
22
 
#include "trx0xa.h"
23
 
 
24
 
/* How should the old versions in the history list be managed?
25
 
   ----------------------------------------------------------
26
 
If each transaction is given a whole page for its update undo log, file
27
 
space consumption can be 10 times higher than necessary. Therefore,
28
 
partly filled update undo log pages should be reusable. But then there
29
 
is no way individual pages can be ordered so that the ordering agrees
30
 
with the serialization numbers of the transactions on the pages. Thus,
31
 
the history list must be formed of undo logs, not their header pages as
32
 
it was in the old implementation.
33
 
        However, on a single header page the transactions are placed in
34
 
the order of their serialization numbers. As old versions are purged, we
35
 
may free the page when the last transaction on the page has been purged.
36
 
        A problem is that the purge has to go through the transactions
37
 
in the serialization order. This means that we have to look through all
38
 
rollback segments for the one that has the smallest transaction number
39
 
in its history list.
40
 
        When should we do a purge? A purge is necessary when space is
41
 
running out in any of the rollback segments. Then we may have to purge
42
 
also old version which might be needed by some consistent read. How do
43
 
we trigger the start of a purge? When a transaction writes to an undo log,
44
 
it may notice that the space is running out. When a read view is closed,
45
 
it may make some history superfluous. The server can have an utility which
46
 
periodically checks if it can purge some history.
47
 
        In a parallellized purge we have the problem that a query thread
48
 
can remove a delete marked clustered index record before another query
49
 
thread has processed an earlier version of the record, which cannot then
50
 
be done because the row cannot be constructed from the clustered index
51
 
record. To avoid this problem, we will store in the update and delete mark
52
 
undo record also the columns necessary to construct the secondary index
53
 
entries which are modified.
54
 
        We can latch the stack of versions of a single clustered index record
55
 
by taking a latch on the clustered index page. As long as the latch is held,
56
 
no new versions can be added and no versions removed by undo. But, a purge
57
 
can still remove old versions from the bottom of the stack. */
58
 
 
59
 
/* How to protect rollback segments, undo logs, and history lists with
60
 
   -------------------------------------------------------------------
61
 
latches?
62
 
-------
63
 
The contention of the kernel mutex should be minimized. When a transaction
64
 
does its first insert or modify in an index, an undo log is assigned for it.
65
 
Then we must have an x-latch to the rollback segment header.
66
 
        When the transaction does more modifys or rolls back, the undo log is
67
 
protected with undo_mutex in the transaction.
68
 
        When the transaction commits, its insert undo log is either reset and
69
 
cached for a fast reuse, or freed. In these cases we must have an x-latch on
70
 
the rollback segment page. The update undo log is put to the history list. If
71
 
it is not suitable for reuse, its slot in the rollback segment is reset. In
72
 
both cases, an x-latch must be acquired on the rollback segment.
73
 
        The purge operation steps through the history list without modifying
74
 
it until a truncate operation occurs, which can remove undo logs from the end
75
 
of the list and release undo log segments. In stepping through the list,
76
 
s-latches on the undo log pages are enough, but in a truncate, x-latches must
77
 
be obtained on the rollback segment and individual pages. */
78
 
 
79
 
/************************************************************************
80
 
Initializes the fields in an undo log segment page. */
81
 
static
82
 
void
83
 
trx_undo_page_init(
84
 
/*===============*/
85
 
        page_t* undo_page,      /* in: undo log segment page */
86
 
        ulint   type,           /* in: undo log segment type */
87
 
        mtr_t*  mtr);           /* in: mtr */
88
 
/************************************************************************
89
 
Creates and initializes an undo log memory object. */
90
 
static
91
 
trx_undo_t*
92
 
trx_undo_mem_create(
93
 
/*================*/
94
 
                                /* out, own: the undo log memory object */
95
 
        trx_rseg_t*     rseg,   /* in: rollback segment memory object */
96
 
        ulint           id,     /* in: slot index within rseg */
97
 
        ulint           type,   /* in: type of the log: TRX_UNDO_INSERT or
98
 
                                TRX_UNDO_UPDATE */
99
 
        dulint          trx_id, /* in: id of the trx for which the undo log
100
 
                                is created */
101
 
        XID*            xid,    /* in: X/Open XA transaction identification*/
102
 
        ulint           page_no,/* in: undo log header page number */
103
 
        ulint           offset);/* in: undo log header byte offset on page */
104
 
/*******************************************************************
105
 
Initializes a cached insert undo log header page for new use. NOTE that this
106
 
function has its own log record type MLOG_UNDO_HDR_REUSE. You must NOT change
107
 
the operation of this function! */
108
 
static
109
 
ulint
110
 
trx_undo_insert_header_reuse(
111
 
/*=========================*/
112
 
                                /* out: undo log header byte offset on page */
113
 
        page_t* undo_page,      /* in: insert undo log segment header page,
114
 
                                x-latched */
115
 
        dulint  trx_id,         /* in: transaction id */
116
 
        mtr_t*  mtr);           /* in: mtr */
117
 
/**************************************************************************
118
 
If an update undo log can be discarded immediately, this function frees the
119
 
space, resetting the page to the proper state for caching. */
120
 
static
121
 
void
122
 
trx_undo_discard_latest_update_undo(
123
 
/*================================*/
124
 
        page_t* undo_page,      /* in: header page of an undo log of size 1 */
125
 
        mtr_t*  mtr);           /* in: mtr */
126
 
 
127
 
 
128
 
/***************************************************************************
129
 
Gets the previous record in an undo log from the previous page. */
130
 
static
131
 
trx_undo_rec_t*
132
 
trx_undo_get_prev_rec_from_prev_page(
133
 
/*=================================*/
134
 
                                /* out: undo log record, the page s-latched,
135
 
                                NULL if none */
136
 
        trx_undo_rec_t* rec,    /* in: undo record */
137
 
        ulint           page_no,/* in: undo log header page number */
138
 
        ulint           offset, /* in: undo log header offset on page */
139
 
        mtr_t*          mtr)    /* in: mtr */
140
 
{
141
 
        ulint   prev_page_no;
142
 
        page_t* prev_page;
143
 
        page_t* undo_page;
144
 
 
145
 
        undo_page = buf_frame_align(rec);
146
 
 
147
 
        prev_page_no = flst_get_prev_addr(undo_page + TRX_UNDO_PAGE_HDR
148
 
                                          + TRX_UNDO_PAGE_NODE, mtr)
149
 
                .page;
150
 
 
151
 
        if (prev_page_no == FIL_NULL) {
152
 
 
153
 
                return(NULL);
154
 
        }
155
 
 
156
 
        prev_page = trx_undo_page_get_s_latched(
157
 
                buf_frame_get_space_id(undo_page), prev_page_no, mtr);
158
 
 
159
 
        return(trx_undo_page_get_last_rec(prev_page, page_no, offset));
160
 
}
161
 
 
162
 
/***************************************************************************
163
 
Gets the previous record in an undo log. */
164
 
 
165
 
trx_undo_rec_t*
166
 
trx_undo_get_prev_rec(
167
 
/*==================*/
168
 
                                /* out: undo log record, the page s-latched,
169
 
                                NULL if none */
170
 
        trx_undo_rec_t* rec,    /* in: undo record */
171
 
        ulint           page_no,/* in: undo log header page number */
172
 
        ulint           offset, /* in: undo log header offset on page */
173
 
        mtr_t*          mtr)    /* in: mtr */
174
 
{
175
 
        trx_undo_rec_t* prev_rec;
176
 
 
177
 
        prev_rec = trx_undo_page_get_prev_rec(rec, page_no, offset);
178
 
 
179
 
        if (prev_rec) {
180
 
 
181
 
                return(prev_rec);
182
 
        }
183
 
 
184
 
        /* We have to go to the previous undo log page to look for the
185
 
        previous record */
186
 
 
187
 
        return(trx_undo_get_prev_rec_from_prev_page(rec, page_no, offset,
188
 
                                                    mtr));
189
 
}
190
 
 
191
 
/***************************************************************************
192
 
Gets the next record in an undo log from the next page. */
193
 
static
194
 
trx_undo_rec_t*
195
 
trx_undo_get_next_rec_from_next_page(
196
 
/*=================================*/
197
 
                        /* out: undo log record, the page latched, NULL if
198
 
                        none */
199
 
        page_t* undo_page, /* in: undo log page */
200
 
        ulint   page_no,/* in: undo log header page number */
201
 
        ulint   offset, /* in: undo log header offset on page */
202
 
        ulint   mode,   /* in: latch mode: RW_S_LATCH or RW_X_LATCH */
203
 
        mtr_t*  mtr)    /* in: mtr */
204
 
{
205
 
        trx_ulogf_t*    log_hdr;
206
 
        ulint           next_page_no;
207
 
        page_t*         next_page;
208
 
        ulint           space;
209
 
        ulint           next;
210
 
 
211
 
        if (page_no == buf_frame_get_page_no(undo_page)) {
212
 
 
213
 
                log_hdr = undo_page + offset;
214
 
                next = mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG);
215
 
 
216
 
                if (next != 0) {
217
 
 
218
 
                        return(NULL);
219
 
                }
220
 
        }
221
 
 
222
 
        space = buf_frame_get_space_id(undo_page);
223
 
 
224
 
        next_page_no = flst_get_next_addr(undo_page + TRX_UNDO_PAGE_HDR
225
 
                                          + TRX_UNDO_PAGE_NODE, mtr)
226
 
                .page;
227
 
        if (next_page_no == FIL_NULL) {
228
 
 
229
 
                return(NULL);
230
 
        }
231
 
 
232
 
        if (mode == RW_S_LATCH) {
233
 
                next_page = trx_undo_page_get_s_latched(space, next_page_no,
234
 
                                                        mtr);
235
 
        } else {
236
 
                ut_ad(mode == RW_X_LATCH);
237
 
                next_page = trx_undo_page_get(space, next_page_no, mtr);
238
 
        }
239
 
 
240
 
        return(trx_undo_page_get_first_rec(next_page, page_no, offset));
241
 
}
242
 
 
243
 
/***************************************************************************
244
 
Gets the next record in an undo log. */
245
 
 
246
 
trx_undo_rec_t*
247
 
trx_undo_get_next_rec(
248
 
/*==================*/
249
 
                                /* out: undo log record, the page s-latched,
250
 
                                NULL if none */
251
 
        trx_undo_rec_t* rec,    /* in: undo record */
252
 
        ulint           page_no,/* in: undo log header page number */
253
 
        ulint           offset, /* in: undo log header offset on page */
254
 
        mtr_t*          mtr)    /* in: mtr */
255
 
{
256
 
        trx_undo_rec_t* next_rec;
257
 
 
258
 
        next_rec = trx_undo_page_get_next_rec(rec, page_no, offset);
259
 
 
260
 
        if (next_rec) {
261
 
                return(next_rec);
262
 
        }
263
 
 
264
 
        return(trx_undo_get_next_rec_from_next_page(buf_frame_align(rec),
265
 
                                                    page_no, offset,
266
 
                                                    RW_S_LATCH, mtr));
267
 
}
268
 
 
269
 
/***************************************************************************
270
 
Gets the first record in an undo log. */
271
 
 
272
 
trx_undo_rec_t*
273
 
trx_undo_get_first_rec(
274
 
/*===================*/
275
 
                        /* out: undo log record, the page latched, NULL if
276
 
                        none */
277
 
        ulint   space,  /* in: undo log header space */
278
 
        ulint   page_no,/* in: undo log header page number */
279
 
        ulint   offset, /* in: undo log header offset on page */
280
 
        ulint   mode,   /* in: latching mode: RW_S_LATCH or RW_X_LATCH */
281
 
        mtr_t*  mtr)    /* in: mtr */
282
 
{
283
 
        page_t*         undo_page;
284
 
        trx_undo_rec_t* rec;
285
 
 
286
 
        if (mode == RW_S_LATCH) {
287
 
                undo_page = trx_undo_page_get_s_latched(space, page_no, mtr);
288
 
        } else {
289
 
                undo_page = trx_undo_page_get(space, page_no, mtr);
290
 
        }
291
 
 
292
 
        rec = trx_undo_page_get_first_rec(undo_page, page_no, offset);
293
 
 
294
 
        if (rec) {
295
 
                return(rec);
296
 
        }
297
 
 
298
 
        return(trx_undo_get_next_rec_from_next_page(undo_page, page_no, offset,
299
 
                                                    mode, mtr));
300
 
}
301
 
 
302
 
/*============== UNDO LOG FILE COPY CREATION AND FREEING ==================*/
303
 
 
304
 
/**************************************************************************
305
 
Writes the mtr log entry of an undo log page initialization. */
306
 
UNIV_INLINE
307
 
void
308
 
trx_undo_page_init_log(
309
 
/*===================*/
310
 
        page_t* undo_page,      /* in: undo log page */
311
 
        ulint   type,           /* in: undo log type */
312
 
        mtr_t*  mtr)            /* in: mtr */
313
 
{
314
 
        mlog_write_initial_log_record(undo_page, MLOG_UNDO_INIT, mtr);
315
 
 
316
 
        mlog_catenate_ulint_compressed(mtr, type);
317
 
}
318
 
 
319
 
/***************************************************************
320
 
Parses the redo log entry of an undo log page initialization. */
321
 
 
322
 
byte*
323
 
trx_undo_parse_page_init(
324
 
/*=====================*/
325
 
                        /* out: end of log record or NULL */
326
 
        byte*   ptr,    /* in: buffer */
327
 
        byte*   end_ptr,/* in: buffer end */
328
 
        page_t* page,   /* in: page or NULL */
329
 
        mtr_t*  mtr)    /* in: mtr or NULL */
330
 
{
331
 
        ulint   type;
332
 
 
333
 
        ptr = mach_parse_compressed(ptr, end_ptr, &type);
334
 
 
335
 
        if (ptr == NULL) {
336
 
 
337
 
                return(NULL);
338
 
        }
339
 
 
340
 
        if (page) {
341
 
                trx_undo_page_init(page, type, mtr);
342
 
        }
343
 
 
344
 
        return(ptr);
345
 
}
346
 
 
347
 
/************************************************************************
348
 
Initializes the fields in an undo log segment page. */
349
 
static
350
 
void
351
 
trx_undo_page_init(
352
 
/*===============*/
353
 
        page_t* undo_page,      /* in: undo log segment page */
354
 
        ulint   type,           /* in: undo log segment type */
355
 
        mtr_t*  mtr)            /* in: mtr */
356
 
{
357
 
        trx_upagef_t*   page_hdr;
358
 
 
359
 
        page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
360
 
 
361
 
        mach_write_to_2(page_hdr + TRX_UNDO_PAGE_TYPE, type);
362
 
 
363
 
        mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START,
364
 
                        TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE);
365
 
        mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE,
366
 
                        TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE);
367
 
 
368
 
        fil_page_set_type(undo_page, FIL_PAGE_UNDO_LOG);
369
 
 
370
 
        trx_undo_page_init_log(undo_page, type, mtr);
371
 
}
372
 
 
373
 
/*******************************************************************
374
 
Creates a new undo log segment in file. */
375
 
static
376
 
ulint
377
 
trx_undo_seg_create(
378
 
/*================*/
379
 
                                /* out: DB_SUCCESS if page creation OK
380
 
                                possible error codes are:
381
 
                                DB_TOO_MANY_CONCURRENT_TRXS
382
 
                                DB_OUT_OF_FILE_SPACE */
383
 
        trx_rseg_t*     rseg __attribute__((unused)),/* in: rollback segment */
384
 
        trx_rsegf_t*    rseg_hdr,/* in: rollback segment header, page
385
 
                                x-latched */
386
 
        ulint           type,   /* in: type of the segment: TRX_UNDO_INSERT or
387
 
                                TRX_UNDO_UPDATE */
388
 
        ulint*          id,     /* out: slot index within rseg header */
389
 
        page_t**        undo_page,
390
 
                                /* out: segment header page x-latched, NULL
391
 
                                if there was an error */
392
 
        mtr_t*          mtr)    /* in: mtr */
393
 
{
394
 
        ulint           slot_no;
395
 
        ulint           space;
396
 
        trx_upagef_t*   page_hdr;
397
 
        trx_usegf_t*    seg_hdr;
398
 
        ulint           n_reserved;
399
 
        ibool           success;
400
 
        ulint           err = DB_SUCCESS;
401
 
 
402
 
        ut_ad(mtr && id && rseg_hdr);
403
 
        ut_ad(mutex_own(&(rseg->mutex)));
404
 
 
405
 
        /*      fputs(type == TRX_UNDO_INSERT
406
 
        ? "Creating insert undo log segment\n"
407
 
        : "Creating update undo log segment\n", stderr); */
408
 
        slot_no = trx_rsegf_undo_find_free(rseg_hdr, mtr);
409
 
 
410
 
        if (slot_no == ULINT_UNDEFINED) {
411
 
                ut_print_timestamp(stderr);
412
 
                fprintf(stderr,
413
 
                        "InnoDB: Warning: cannot find a free slot for"
414
 
                        " an undo log. Do you have too\n"
415
 
                        "InnoDB: many active transactions"
416
 
                        " running concurrently?\n");
417
 
 
418
 
                return(DB_TOO_MANY_CONCURRENT_TRXS);
419
 
        }
420
 
 
421
 
        space = buf_frame_get_space_id(rseg_hdr);
422
 
 
423
 
        success = fsp_reserve_free_extents(&n_reserved, space, 2, FSP_UNDO,
424
 
                                           mtr);
425
 
        if (!success) {
426
 
 
427
 
                return(DB_OUT_OF_FILE_SPACE);
428
 
        }
429
 
 
430
 
        /* Allocate a new file segment for the undo log */
431
 
        *undo_page = fseg_create_general(space, 0,
432
 
                                        TRX_UNDO_SEG_HDR
433
 
                                        + TRX_UNDO_FSEG_HEADER, TRUE, mtr);
434
 
 
435
 
        fil_space_release_free_extents(space, n_reserved);
436
 
 
437
 
        if (*undo_page == NULL) {
438
 
                /* No space left */
439
 
 
440
 
                return(DB_OUT_OF_FILE_SPACE);
441
 
        }
442
 
 
443
 
#ifdef UNIV_SYNC_DEBUG
444
 
        buf_page_dbg_add_level(*undo_page, SYNC_TRX_UNDO_PAGE);
445
 
#endif /* UNIV_SYNC_DEBUG */
446
 
 
447
 
        page_hdr = *undo_page + TRX_UNDO_PAGE_HDR;
448
 
        seg_hdr = *undo_page + TRX_UNDO_SEG_HDR;
449
 
 
450
 
        trx_undo_page_init(*undo_page, type, mtr);
451
 
 
452
 
        mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE,
453
 
                         TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE,
454
 
                         MLOG_2BYTES, mtr);
455
 
 
456
 
        mlog_write_ulint(seg_hdr + TRX_UNDO_LAST_LOG, 0, MLOG_2BYTES, mtr);
457
 
 
458
 
        flst_init(seg_hdr + TRX_UNDO_PAGE_LIST, mtr);
459
 
 
460
 
        flst_add_last(seg_hdr + TRX_UNDO_PAGE_LIST,
461
 
                      page_hdr + TRX_UNDO_PAGE_NODE, mtr);
462
 
 
463
 
        trx_rsegf_set_nth_undo(rseg_hdr, slot_no,
464
 
                                buf_frame_get_page_no(*undo_page), mtr);
465
 
 
466
 
        *id = slot_no;
467
 
 
468
 
        return(err);
469
 
}
470
 
 
471
 
/**************************************************************************
472
 
Writes the mtr log entry of an undo log header initialization. */
473
 
UNIV_INLINE
474
 
void
475
 
trx_undo_header_create_log(
476
 
/*=======================*/
477
 
        page_t* undo_page,      /* in: undo log header page */
478
 
        dulint  trx_id,         /* in: transaction id */
479
 
        mtr_t*  mtr)            /* in: mtr */
480
 
{
481
 
        mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_CREATE, mtr);
482
 
 
483
 
        mlog_catenate_dulint_compressed(mtr, trx_id);
484
 
}
485
 
 
486
 
/*******************************************************************
487
 
Creates a new undo log header in file. NOTE that this function has its own
488
 
log record type MLOG_UNDO_HDR_CREATE. You must NOT change the operation of
489
 
this function! */
490
 
static
491
 
ulint
492
 
trx_undo_header_create(
493
 
/*===================*/
494
 
                                /* out: header byte offset on page */
495
 
        page_t* undo_page,      /* in: undo log segment header page,
496
 
                                x-latched; it is assumed that there are
497
 
                                TRX_UNDO_LOG_XA_HDR_SIZE bytes free space
498
 
                                on it */
499
 
        dulint  trx_id,         /* in: transaction id */
500
 
        mtr_t*  mtr)            /* in: mtr */
501
 
{
502
 
        trx_upagef_t*   page_hdr;
503
 
        trx_usegf_t*    seg_hdr;
504
 
        trx_ulogf_t*    log_hdr;
505
 
        trx_ulogf_t*    prev_log_hdr;
506
 
        ulint           prev_log;
507
 
        ulint           free;
508
 
        ulint           new_free;
509
 
 
510
 
        ut_ad(mtr && undo_page);
511
 
 
512
 
        page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
513
 
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
514
 
 
515
 
        free = mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE);
516
 
 
517
 
        log_hdr = undo_page + free;
518
 
 
519
 
        new_free = free + TRX_UNDO_LOG_OLD_HDR_SIZE;
520
 
 
521
 
        ut_a(free + TRX_UNDO_LOG_XA_HDR_SIZE < UNIV_PAGE_SIZE - 100);
522
 
 
523
 
        mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, new_free);
524
 
 
525
 
        mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, new_free);
526
 
 
527
 
        mach_write_to_2(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_ACTIVE);
528
 
 
529
 
        prev_log = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG);
530
 
 
531
 
        if (prev_log != 0) {
532
 
                prev_log_hdr = undo_page + prev_log;
533
 
 
534
 
                mach_write_to_2(prev_log_hdr + TRX_UNDO_NEXT_LOG, free);
535
 
        }
536
 
 
537
 
        mach_write_to_2(seg_hdr + TRX_UNDO_LAST_LOG, free);
538
 
 
539
 
        log_hdr = undo_page + free;
540
 
 
541
 
        mach_write_to_2(log_hdr + TRX_UNDO_DEL_MARKS, TRUE);
542
 
 
543
 
        mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id);
544
 
        mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free);
545
 
 
546
 
        mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE);
547
 
        mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE);
548
 
 
549
 
        mach_write_to_2(log_hdr + TRX_UNDO_NEXT_LOG, 0);
550
 
        mach_write_to_2(log_hdr + TRX_UNDO_PREV_LOG, prev_log);
551
 
 
552
 
        /* Write the log record about the header creation */
553
 
        trx_undo_header_create_log(undo_page, trx_id, mtr);
554
 
 
555
 
        return(free);
556
 
}
557
 
 
558
 
/************************************************************************
559
 
Write X/Open XA Transaction Identification (XID) to undo log header */
560
 
static
561
 
void
562
 
trx_undo_write_xid(
563
 
/*===============*/
564
 
        trx_ulogf_t*    log_hdr,/* in: undo log header */
565
 
        const XID*      xid,    /* in: X/Open XA Transaction Identification */
566
 
        mtr_t*          mtr)    /* in: mtr */
567
 
{
568
 
        mlog_write_ulint(log_hdr + TRX_UNDO_XA_FORMAT,
569
 
                         (ulint)xid->formatID, MLOG_4BYTES, mtr);
570
 
 
571
 
        mlog_write_ulint(log_hdr + TRX_UNDO_XA_TRID_LEN,
572
 
                         (ulint)xid->gtrid_length, MLOG_4BYTES, mtr);
573
 
 
574
 
        mlog_write_ulint(log_hdr + TRX_UNDO_XA_BQUAL_LEN,
575
 
                         (ulint)xid->bqual_length, MLOG_4BYTES, mtr);
576
 
 
577
 
        mlog_write_string(log_hdr + TRX_UNDO_XA_XID, (const byte*) xid->data,
578
 
                          XIDDATASIZE, mtr);
579
 
}
580
 
 
581
 
/************************************************************************
582
 
Read X/Open XA Transaction Identification (XID) from undo log header */
583
 
static
584
 
void
585
 
trx_undo_read_xid(
586
 
/*==============*/
587
 
        trx_ulogf_t*    log_hdr,/* in: undo log header */
588
 
        XID*            xid)    /* out: X/Open XA Transaction Identification */
589
 
{
590
 
        xid->formatID = (long)mach_read_from_4(log_hdr + TRX_UNDO_XA_FORMAT);
591
 
 
592
 
        xid->gtrid_length
593
 
                = (long) mach_read_from_4(log_hdr + TRX_UNDO_XA_TRID_LEN);
594
 
        xid->bqual_length
595
 
                = (long) mach_read_from_4(log_hdr + TRX_UNDO_XA_BQUAL_LEN);
596
 
 
597
 
        memcpy(xid->data, log_hdr + TRX_UNDO_XA_XID, XIDDATASIZE);
598
 
}
599
 
 
600
 
/*******************************************************************
601
 
Adds space for the XA XID after an undo log old-style header. */
602
 
static
603
 
void
604
 
trx_undo_header_add_space_for_xid(
605
 
/*==============================*/
606
 
        page_t*         undo_page,/* in: undo log segment header page */
607
 
        trx_ulogf_t*    log_hdr,/* in: undo log header */
608
 
        mtr_t*          mtr)    /* in: mtr */
609
 
{
610
 
        trx_upagef_t*   page_hdr;
611
 
        ulint           free;
612
 
        ulint           new_free;
613
 
 
614
 
        page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
615
 
 
616
 
        free = mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE);
617
 
 
618
 
        /* free is now the end offset of the old style undo log header */
619
 
 
620
 
        ut_a(free == (ulint)(log_hdr - undo_page) + TRX_UNDO_LOG_OLD_HDR_SIZE);
621
 
 
622
 
        new_free = free + (TRX_UNDO_LOG_XA_HDR_SIZE
623
 
                           - TRX_UNDO_LOG_OLD_HDR_SIZE);
624
 
 
625
 
        /* Add space for a XID after the header, update the free offset
626
 
        fields on the undo log page and in the undo log header */
627
 
 
628
 
        mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_START, new_free,
629
 
                         MLOG_2BYTES, mtr);
630
 
 
631
 
        mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE, new_free,
632
 
                         MLOG_2BYTES, mtr);
633
 
 
634
 
        mlog_write_ulint(log_hdr + TRX_UNDO_LOG_START, new_free,
635
 
                         MLOG_2BYTES, mtr);
636
 
}
637
 
 
638
 
/**************************************************************************
639
 
Writes the mtr log entry of an undo log header reuse. */
640
 
UNIV_INLINE
641
 
void
642
 
trx_undo_insert_header_reuse_log(
643
 
/*=============================*/
644
 
        page_t* undo_page,      /* in: undo log header page */
645
 
        dulint  trx_id,         /* in: transaction id */
646
 
        mtr_t*  mtr)            /* in: mtr */
647
 
{
648
 
        mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_REUSE, mtr);
649
 
 
650
 
        mlog_catenate_dulint_compressed(mtr, trx_id);
651
 
}
652
 
 
653
 
/***************************************************************
654
 
Parses the redo log entry of an undo log page header create or reuse. */
655
 
 
656
 
byte*
657
 
trx_undo_parse_page_header(
658
 
/*=======================*/
659
 
                        /* out: end of log record or NULL */
660
 
        ulint   type,   /* in: MLOG_UNDO_HDR_CREATE or MLOG_UNDO_HDR_REUSE */
661
 
        byte*   ptr,    /* in: buffer */
662
 
        byte*   end_ptr,/* in: buffer end */
663
 
        page_t* page,   /* in: page or NULL */
664
 
        mtr_t*  mtr)    /* in: mtr or NULL */
665
 
{
666
 
        dulint  trx_id;
667
 
 
668
 
        ptr = mach_dulint_parse_compressed(ptr, end_ptr, &trx_id);
669
 
 
670
 
        if (ptr == NULL) {
671
 
 
672
 
                return(NULL);
673
 
        }
674
 
 
675
 
        if (page) {
676
 
                if (type == MLOG_UNDO_HDR_CREATE) {
677
 
                        trx_undo_header_create(page, trx_id, mtr);
678
 
                } else {
679
 
                        ut_ad(type == MLOG_UNDO_HDR_REUSE);
680
 
                        trx_undo_insert_header_reuse(page, trx_id, mtr);
681
 
                }
682
 
        }
683
 
 
684
 
        return(ptr);
685
 
}
686
 
 
687
 
/*******************************************************************
688
 
Initializes a cached insert undo log header page for new use. NOTE that this
689
 
function has its own log record type MLOG_UNDO_HDR_REUSE. You must NOT change
690
 
the operation of this function! */
691
 
static
692
 
ulint
693
 
trx_undo_insert_header_reuse(
694
 
/*=========================*/
695
 
                                /* out: undo log header byte offset on page */
696
 
        page_t* undo_page,      /* in: insert undo log segment header page,
697
 
                                x-latched */
698
 
        dulint  trx_id,         /* in: transaction id */
699
 
        mtr_t*  mtr)            /* in: mtr */
700
 
{
701
 
        trx_upagef_t*   page_hdr;
702
 
        trx_usegf_t*    seg_hdr;
703
 
        trx_ulogf_t*    log_hdr;
704
 
        ulint           free;
705
 
        ulint           new_free;
706
 
 
707
 
        ut_ad(mtr && undo_page);
708
 
 
709
 
        page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
710
 
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
711
 
 
712
 
        free = TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE;
713
 
 
714
 
        ut_a(free + TRX_UNDO_LOG_XA_HDR_SIZE < UNIV_PAGE_SIZE - 100);
715
 
 
716
 
        log_hdr = undo_page + free;
717
 
 
718
 
        new_free = free + TRX_UNDO_LOG_OLD_HDR_SIZE;
719
 
 
720
 
        /* Insert undo data is not needed after commit: we may free all
721
 
        the space on the page */
722
 
 
723
 
        ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
724
 
                              + TRX_UNDO_PAGE_TYPE)
725
 
             == TRX_UNDO_INSERT);
726
 
 
727
 
        mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, new_free);
728
 
 
729
 
        mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, new_free);
730
 
 
731
 
        mach_write_to_2(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_ACTIVE);
732
 
 
733
 
        log_hdr = undo_page + free;
734
 
 
735
 
        mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id);
736
 
        mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free);
737
 
 
738
 
        mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE);
739
 
        mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE);
740
 
 
741
 
        /* Write the log record MLOG_UNDO_HDR_REUSE */
742
 
        trx_undo_insert_header_reuse_log(undo_page, trx_id, mtr);
743
 
 
744
 
        return(free);
745
 
}
746
 
 
747
 
/**************************************************************************
748
 
Writes the redo log entry of an update undo log header discard. */
749
 
UNIV_INLINE
750
 
void
751
 
trx_undo_discard_latest_log(
752
 
/*========================*/
753
 
        page_t* undo_page,      /* in: undo log header page */
754
 
        mtr_t*  mtr)            /* in: mtr */
755
 
{
756
 
        mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_DISCARD, mtr);
757
 
}
758
 
 
759
 
/***************************************************************
760
 
Parses the redo log entry of an undo log page header discard. */
761
 
 
762
 
byte*
763
 
trx_undo_parse_discard_latest(
764
 
/*==========================*/
765
 
                        /* out: end of log record or NULL */
766
 
        byte*   ptr,    /* in: buffer */
767
 
        byte*   end_ptr __attribute__((unused)), /* in: buffer end */
768
 
        page_t* page,   /* in: page or NULL */
769
 
        mtr_t*  mtr)    /* in: mtr or NULL */
770
 
{
771
 
        ut_ad(end_ptr);
772
 
 
773
 
        if (page) {
774
 
                trx_undo_discard_latest_update_undo(page, mtr);
775
 
        }
776
 
 
777
 
        return(ptr);
778
 
}
779
 
 
780
 
/**************************************************************************
781
 
If an update undo log can be discarded immediately, this function frees the
782
 
space, resetting the page to the proper state for caching. */
783
 
static
784
 
void
785
 
trx_undo_discard_latest_update_undo(
786
 
/*================================*/
787
 
        page_t* undo_page,      /* in: header page of an undo log of size 1 */
788
 
        mtr_t*  mtr)            /* in: mtr */
789
 
{
790
 
        trx_usegf_t*    seg_hdr;
791
 
        trx_upagef_t*   page_hdr;
792
 
        trx_ulogf_t*    log_hdr;
793
 
        trx_ulogf_t*    prev_log_hdr;
794
 
        ulint           free;
795
 
        ulint           prev_hdr_offset;
796
 
 
797
 
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
798
 
        page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
799
 
 
800
 
        free = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG);
801
 
        log_hdr = undo_page + free;
802
 
 
803
 
        prev_hdr_offset = mach_read_from_2(log_hdr + TRX_UNDO_PREV_LOG);
804
 
 
805
 
        if (prev_hdr_offset != 0) {
806
 
                prev_log_hdr = undo_page + prev_hdr_offset;
807
 
 
808
 
                mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START,
809
 
                                mach_read_from_2(prev_log_hdr
810
 
                                                 + TRX_UNDO_LOG_START));
811
 
                mach_write_to_2(prev_log_hdr + TRX_UNDO_NEXT_LOG, 0);
812
 
        }
813
 
 
814
 
        mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, free);
815
 
 
816
 
        mach_write_to_2(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_CACHED);
817
 
        mach_write_to_2(seg_hdr + TRX_UNDO_LAST_LOG, prev_hdr_offset);
818
 
 
819
 
        trx_undo_discard_latest_log(undo_page, mtr);
820
 
}
821
 
 
822
 
/************************************************************************
823
 
Tries to add a page to the undo log segment where the undo log is placed. */
824
 
 
825
 
ulint
826
 
trx_undo_add_page(
827
 
/*==============*/
828
 
                                /* out: page number if success, else
829
 
                                FIL_NULL */
830
 
        trx_t*          trx,    /* in: transaction */
831
 
        trx_undo_t*     undo,   /* in: undo log memory object */
832
 
        mtr_t*          mtr)    /* in: mtr which does not have a latch to any
833
 
                                undo log page; the caller must have reserved
834
 
                                the rollback segment mutex */
835
 
{
836
 
        page_t*         header_page;
837
 
        page_t*         new_page;
838
 
        trx_rseg_t*     rseg;
839
 
        ulint           page_no;
840
 
        ulint           n_reserved;
841
 
        ibool           success;
842
 
 
843
 
        ut_ad(mutex_own(&(trx->undo_mutex)));
844
 
        ut_ad(!mutex_own(&kernel_mutex));
845
 
        ut_ad(mutex_own(&(trx->rseg->mutex)));
846
 
 
847
 
        rseg = trx->rseg;
848
 
 
849
 
        if (rseg->curr_size == rseg->max_size) {
850
 
 
851
 
                return(FIL_NULL);
852
 
        }
853
 
 
854
 
        header_page = trx_undo_page_get(undo->space, undo->hdr_page_no, mtr);
855
 
 
856
 
        success = fsp_reserve_free_extents(&n_reserved, undo->space, 1,
857
 
                                           FSP_UNDO, mtr);
858
 
        if (!success) {
859
 
 
860
 
                return(FIL_NULL);
861
 
        }
862
 
 
863
 
        page_no = fseg_alloc_free_page_general(header_page + TRX_UNDO_SEG_HDR
864
 
                                               + TRX_UNDO_FSEG_HEADER,
865
 
                                               undo->top_page_no + 1, FSP_UP,
866
 
                                               TRUE, mtr);
867
 
 
868
 
        fil_space_release_free_extents(undo->space, n_reserved);
869
 
 
870
 
        if (page_no == FIL_NULL) {
871
 
 
872
 
                /* No space left */
873
 
 
874
 
                return(FIL_NULL);
875
 
        }
876
 
 
877
 
        undo->last_page_no = page_no;
878
 
 
879
 
        new_page = trx_undo_page_get(undo->space, page_no, mtr);
880
 
 
881
 
        trx_undo_page_init(new_page, undo->type, mtr);
882
 
 
883
 
        flst_add_last(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST,
884
 
                      new_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr);
885
 
        undo->size++;
886
 
        rseg->curr_size++;
887
 
 
888
 
        return(page_no);
889
 
}
890
 
 
891
 
/************************************************************************
892
 
Frees an undo log page that is not the header page. */
893
 
static
894
 
ulint
895
 
trx_undo_free_page(
896
 
/*===============*/
897
 
                                /* out: last page number in remaining log */
898
 
        trx_rseg_t* rseg,       /* in: rollback segment */
899
 
        ibool   in_history,     /* in: TRUE if the undo log is in the history
900
 
                                list */
901
 
        ulint   space,          /* in: space */
902
 
        ulint   hdr_page_no,    /* in: header page number */
903
 
        ulint   page_no,        /* in: page number to free: must not be the
904
 
                                header page */
905
 
        mtr_t*  mtr)            /* in: mtr which does not have a latch to any
906
 
                                undo log page; the caller must have reserved
907
 
                                the rollback segment mutex */
908
 
{
909
 
        page_t*         header_page;
910
 
        page_t*         undo_page;
911
 
        fil_addr_t      last_addr;
912
 
        trx_rsegf_t*    rseg_header;
913
 
        ulint           hist_size;
914
 
 
915
 
        ut_a(hdr_page_no != page_no);
916
 
        ut_ad(!mutex_own(&kernel_mutex));
917
 
        ut_ad(mutex_own(&(rseg->mutex)));
918
 
 
919
 
        undo_page = trx_undo_page_get(space, page_no, mtr);
920
 
 
921
 
        header_page = trx_undo_page_get(space, hdr_page_no, mtr);
922
 
 
923
 
        flst_remove(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST,
924
 
                    undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr);
925
 
 
926
 
        fseg_free_page(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER,
927
 
                       space, page_no, mtr);
928
 
 
929
 
        last_addr = flst_get_last(header_page + TRX_UNDO_SEG_HDR
930
 
                                  + TRX_UNDO_PAGE_LIST, mtr);
931
 
        rseg->curr_size--;
932
 
 
933
 
        if (in_history) {
934
 
                rseg_header = trx_rsegf_get(space, rseg->page_no, mtr);
935
 
 
936
 
                hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
937
 
                                           MLOG_4BYTES, mtr);
938
 
                ut_ad(hist_size > 0);
939
 
                mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
940
 
                                 hist_size - 1, MLOG_4BYTES, mtr);
941
 
        }
942
 
 
943
 
        return(last_addr.page);
944
 
}
945
 
 
946
 
/************************************************************************
947
 
Frees an undo log page when there is also the memory object for the undo
948
 
log. */
949
 
static
950
 
void
951
 
trx_undo_free_page_in_rollback(
952
 
/*===========================*/
953
 
        trx_t*          trx __attribute__((unused)), /* in: transaction */
954
 
        trx_undo_t*     undo,   /* in: undo log memory copy */
955
 
        ulint           page_no,/* in: page number to free: must not be the
956
 
                                header page */
957
 
        mtr_t*          mtr)    /* in: mtr which does not have a latch to any
958
 
                                undo log page; the caller must have reserved
959
 
                                the rollback segment mutex */
960
 
{
961
 
        ulint   last_page_no;
962
 
 
963
 
        ut_ad(undo->hdr_page_no != page_no);
964
 
        ut_ad(mutex_own(&(trx->undo_mutex)));
965
 
 
966
 
        last_page_no = trx_undo_free_page(undo->rseg, FALSE, undo->space,
967
 
                                          undo->hdr_page_no, page_no, mtr);
968
 
 
969
 
        undo->last_page_no = last_page_no;
970
 
        undo->size--;
971
 
}
972
 
 
973
 
/************************************************************************
974
 
Empties an undo log header page of undo records for that undo log. Other
975
 
undo logs may still have records on that page, if it is an update undo log. */
976
 
static
977
 
void
978
 
trx_undo_empty_header_page(
979
 
/*=======================*/
980
 
        ulint   space,          /* in: space */
981
 
        ulint   hdr_page_no,    /* in: header page number */
982
 
        ulint   hdr_offset,     /* in: header offset */
983
 
        mtr_t*  mtr)            /* in: mtr */
984
 
{
985
 
        page_t*         header_page;
986
 
        trx_ulogf_t*    log_hdr;
987
 
        ulint           end;
988
 
 
989
 
        header_page = trx_undo_page_get(space, hdr_page_no, mtr);
990
 
 
991
 
        log_hdr = header_page + hdr_offset;
992
 
 
993
 
        end = trx_undo_page_get_end(header_page, hdr_page_no, hdr_offset);
994
 
 
995
 
        mlog_write_ulint(log_hdr + TRX_UNDO_LOG_START, end, MLOG_2BYTES, mtr);
996
 
}
997
 
 
998
 
/***************************************************************************
999
 
Truncates an undo log from the end. This function is used during a rollback
1000
 
to free space from an undo log. */
1001
 
 
1002
 
void
1003
 
trx_undo_truncate_end(
1004
 
/*==================*/
1005
 
        trx_t*          trx,    /* in: transaction whose undo log it is */
1006
 
        trx_undo_t*     undo,   /* in: undo log */
1007
 
        dulint          limit)  /* in: all undo records with undo number
1008
 
                                >= this value should be truncated */
1009
 
{
1010
 
        page_t*         undo_page;
1011
 
        ulint           last_page_no;
1012
 
        trx_undo_rec_t* rec;
1013
 
        trx_undo_rec_t* trunc_here;
1014
 
        trx_rseg_t*     rseg;
1015
 
        mtr_t           mtr;
1016
 
 
1017
 
        ut_ad(mutex_own(&(trx->undo_mutex)));
1018
 
        ut_ad(mutex_own(&(trx->rseg->mutex)));
1019
 
 
1020
 
        rseg = trx->rseg;
1021
 
 
1022
 
        for (;;) {
1023
 
                mtr_start(&mtr);
1024
 
 
1025
 
                trunc_here = NULL;
1026
 
 
1027
 
                last_page_no = undo->last_page_no;
1028
 
 
1029
 
                undo_page = trx_undo_page_get(undo->space, last_page_no, &mtr);
1030
 
 
1031
 
                rec = trx_undo_page_get_last_rec(undo_page, undo->hdr_page_no,
1032
 
                                                 undo->hdr_offset);
1033
 
                for (;;) {
1034
 
                        if (rec == NULL) {
1035
 
                                if (last_page_no == undo->hdr_page_no) {
1036
 
 
1037
 
                                        goto function_exit;
1038
 
                                }
1039
 
 
1040
 
                                trx_undo_free_page_in_rollback(
1041
 
                                        trx, undo, last_page_no, &mtr);
1042
 
                                break;
1043
 
                        }
1044
 
 
1045
 
                        if (ut_dulint_cmp(trx_undo_rec_get_undo_no(rec), limit)
1046
 
                            >= 0) {
1047
 
                                /* Truncate at least this record off, maybe
1048
 
                                more */
1049
 
                                trunc_here = rec;
1050
 
                        } else {
1051
 
                                goto function_exit;
1052
 
                        }
1053
 
 
1054
 
                        rec = trx_undo_page_get_prev_rec(rec,
1055
 
                                                         undo->hdr_page_no,
1056
 
                                                         undo->hdr_offset);
1057
 
                }
1058
 
 
1059
 
                mtr_commit(&mtr);
1060
 
        }
1061
 
 
1062
 
function_exit:
1063
 
        if (trunc_here) {
1064
 
                mlog_write_ulint(undo_page + TRX_UNDO_PAGE_HDR
1065
 
                                 + TRX_UNDO_PAGE_FREE,
1066
 
                                 trunc_here - undo_page, MLOG_2BYTES, &mtr);
1067
 
        }
1068
 
 
1069
 
        mtr_commit(&mtr);
1070
 
}
1071
 
 
1072
 
/***************************************************************************
1073
 
Truncates an undo log from the start. This function is used during a purge
1074
 
operation. */
1075
 
 
1076
 
void
1077
 
trx_undo_truncate_start(
1078
 
/*====================*/
1079
 
        trx_rseg_t* rseg,       /* in: rollback segment */
1080
 
        ulint   space,          /* in: space id of the log */
1081
 
        ulint   hdr_page_no,    /* in: header page number */
1082
 
        ulint   hdr_offset,     /* in: header offset on the page */
1083
 
        dulint  limit)          /* in: all undo pages with undo numbers <
1084
 
                                this value should be truncated; NOTE that
1085
 
                                the function only frees whole pages; the
1086
 
                                header page is not freed, but emptied, if
1087
 
                                all the records there are < limit */
1088
 
{
1089
 
        page_t*         undo_page;
1090
 
        trx_undo_rec_t* rec;
1091
 
        trx_undo_rec_t* last_rec;
1092
 
        ulint           page_no;
1093
 
        mtr_t           mtr;
1094
 
 
1095
 
        ut_ad(mutex_own(&(rseg->mutex)));
1096
 
 
1097
 
        if (0 == ut_dulint_cmp(limit, ut_dulint_zero)) {
1098
 
 
1099
 
                return;
1100
 
        }
1101
 
loop:
1102
 
        mtr_start(&mtr);
1103
 
 
1104
 
        rec = trx_undo_get_first_rec(space, hdr_page_no, hdr_offset,
1105
 
                                     RW_X_LATCH, &mtr);
1106
 
        if (rec == NULL) {
1107
 
                /* Already empty */
1108
 
 
1109
 
                mtr_commit(&mtr);
1110
 
 
1111
 
                return;
1112
 
        }
1113
 
 
1114
 
        undo_page = buf_frame_align(rec);
1115
 
 
1116
 
        last_rec = trx_undo_page_get_last_rec(undo_page, hdr_page_no,
1117
 
                                              hdr_offset);
1118
 
        if (ut_dulint_cmp(trx_undo_rec_get_undo_no(last_rec), limit) >= 0) {
1119
 
 
1120
 
                mtr_commit(&mtr);
1121
 
 
1122
 
                return;
1123
 
        }
1124
 
 
1125
 
        page_no = buf_frame_get_page_no(undo_page);
1126
 
 
1127
 
        if (page_no == hdr_page_no) {
1128
 
                trx_undo_empty_header_page(space, hdr_page_no, hdr_offset,
1129
 
                                           &mtr);
1130
 
        } else {
1131
 
                trx_undo_free_page(rseg, TRUE, space, hdr_page_no,
1132
 
                                   page_no, &mtr);
1133
 
        }
1134
 
 
1135
 
        mtr_commit(&mtr);
1136
 
 
1137
 
        goto loop;
1138
 
}
1139
 
 
1140
 
/**************************************************************************
1141
 
Frees an undo log segment which is not in the history list. */
1142
 
static
1143
 
void
1144
 
trx_undo_seg_free(
1145
 
/*==============*/
1146
 
        trx_undo_t*     undo)   /* in: undo log */
1147
 
{
1148
 
        trx_rseg_t*     rseg;
1149
 
        fseg_header_t*  file_seg;
1150
 
        trx_rsegf_t*    rseg_header;
1151
 
        trx_usegf_t*    seg_header;
1152
 
        ibool           finished;
1153
 
        mtr_t           mtr;
1154
 
 
1155
 
        finished = FALSE;
1156
 
        rseg = undo->rseg;
1157
 
 
1158
 
        while (!finished) {
1159
 
 
1160
 
                mtr_start(&mtr);
1161
 
 
1162
 
                ut_ad(!mutex_own(&kernel_mutex));
1163
 
 
1164
 
                mutex_enter(&(rseg->mutex));
1165
 
 
1166
 
                seg_header = trx_undo_page_get(undo->space, undo->hdr_page_no,
1167
 
                                               &mtr) + TRX_UNDO_SEG_HDR;
1168
 
 
1169
 
                file_seg = seg_header + TRX_UNDO_FSEG_HEADER;
1170
 
 
1171
 
                finished = fseg_free_step(file_seg, &mtr);
1172
 
 
1173
 
                if (finished) {
1174
 
                        /* Update the rseg header */
1175
 
                        rseg_header = trx_rsegf_get(rseg->space, rseg->page_no,
1176
 
                                                    &mtr);
1177
 
                        trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL,
1178
 
                                               &mtr);
1179
 
                }
1180
 
 
1181
 
                mutex_exit(&(rseg->mutex));
1182
 
                mtr_commit(&mtr);
1183
 
        }
1184
 
}
1185
 
 
1186
 
/*========== UNDO LOG MEMORY COPY INITIALIZATION =====================*/
1187
 
 
1188
 
/************************************************************************
1189
 
Creates and initializes an undo log memory object according to the values
1190
 
in the header in file, when the database is started. The memory object is
1191
 
inserted in the appropriate list of rseg. */
1192
 
static
1193
 
trx_undo_t*
1194
 
trx_undo_mem_create_at_db_start(
1195
 
/*============================*/
1196
 
                                /* out, own: the undo log memory object */
1197
 
        trx_rseg_t*     rseg,   /* in: rollback segment memory object */
1198
 
        ulint           id,     /* in: slot index within rseg */
1199
 
        ulint           page_no,/* in: undo log segment page number */
1200
 
        mtr_t*          mtr)    /* in: mtr */
1201
 
{
1202
 
        page_t*         undo_page;
1203
 
        trx_upagef_t*   page_header;
1204
 
        trx_usegf_t*    seg_header;
1205
 
        trx_ulogf_t*    undo_header;
1206
 
        trx_undo_t*     undo;
1207
 
        ulint           type;
1208
 
        ulint           state;
1209
 
        dulint          trx_id;
1210
 
        ulint           offset;
1211
 
        fil_addr_t      last_addr;
1212
 
        page_t*         last_page;
1213
 
        trx_undo_rec_t* rec;
1214
 
        XID             xid;
1215
 
        ibool           xid_exists = FALSE;
1216
 
 
1217
 
        if (id >= TRX_RSEG_N_SLOTS) {
1218
 
                fprintf(stderr,
1219
 
                        "InnoDB: Error: undo->id is %lu\n", (ulong) id);
1220
 
                ut_error;
1221
 
        }
1222
 
 
1223
 
        undo_page = trx_undo_page_get(rseg->space, page_no, mtr);
1224
 
 
1225
 
        page_header = undo_page + TRX_UNDO_PAGE_HDR;
1226
 
 
1227
 
        type = mtr_read_ulint(page_header + TRX_UNDO_PAGE_TYPE, MLOG_2BYTES,
1228
 
                              mtr);
1229
 
        seg_header = undo_page + TRX_UNDO_SEG_HDR;
1230
 
 
1231
 
        state = mach_read_from_2(seg_header + TRX_UNDO_STATE);
1232
 
 
1233
 
        offset = mach_read_from_2(seg_header + TRX_UNDO_LAST_LOG);
1234
 
 
1235
 
        undo_header = undo_page + offset;
1236
 
 
1237
 
        trx_id = mtr_read_dulint(undo_header + TRX_UNDO_TRX_ID, mtr);
1238
 
 
1239
 
        xid_exists = mtr_read_ulint(undo_header + TRX_UNDO_XID_EXISTS,
1240
 
                                    MLOG_1BYTE, mtr);
1241
 
 
1242
 
        /* Read X/Open XA transaction identification if it exists, or
1243
 
        set it to NULL. */
1244
 
 
1245
 
        memset(&xid, 0, sizeof(xid));
1246
 
        xid.formatID = -1;
1247
 
 
1248
 
        if (xid_exists == TRUE) {
1249
 
                trx_undo_read_xid(undo_header, &xid);
1250
 
        }
1251
 
 
1252
 
        mutex_enter(&(rseg->mutex));
1253
 
 
1254
 
        undo = trx_undo_mem_create(rseg, id, type, trx_id, &xid,
1255
 
                                   page_no, offset);
1256
 
        mutex_exit(&(rseg->mutex));
1257
 
 
1258
 
        undo->dict_operation =  mtr_read_ulint(
1259
 
                undo_header + TRX_UNDO_DICT_TRANS, MLOG_1BYTE, mtr);
1260
 
 
1261
 
        undo->table_id = mtr_read_dulint(undo_header + TRX_UNDO_TABLE_ID, mtr);
1262
 
        undo->state = state;
1263
 
        undo->size = flst_get_len(seg_header + TRX_UNDO_PAGE_LIST, mtr);
1264
 
 
1265
 
        /* If the log segment is being freed, the page list is inconsistent! */
1266
 
        if (state == TRX_UNDO_TO_FREE) {
1267
 
 
1268
 
                goto add_to_list;
1269
 
        }
1270
 
 
1271
 
        last_addr = flst_get_last(seg_header + TRX_UNDO_PAGE_LIST, mtr);
1272
 
 
1273
 
        undo->last_page_no = last_addr.page;
1274
 
        undo->top_page_no = last_addr.page;
1275
 
 
1276
 
        last_page = trx_undo_page_get(rseg->space, undo->last_page_no, mtr);
1277
 
 
1278
 
        rec = trx_undo_page_get_last_rec(last_page, page_no, offset);
1279
 
 
1280
 
        if (rec == NULL) {
1281
 
                undo->empty = TRUE;
1282
 
        } else {
1283
 
                undo->empty = FALSE;
1284
 
                undo->top_offset = rec - last_page;
1285
 
                undo->top_undo_no = trx_undo_rec_get_undo_no(rec);
1286
 
        }
1287
 
add_to_list:
1288
 
        if (type == TRX_UNDO_INSERT) {
1289
 
                if (state != TRX_UNDO_CACHED) {
1290
 
                        UT_LIST_ADD_LAST(undo_list, rseg->insert_undo_list,
1291
 
                                         undo);
1292
 
                } else {
1293
 
                        UT_LIST_ADD_LAST(undo_list, rseg->insert_undo_cached,
1294
 
                                         undo);
1295
 
                }
1296
 
        } else {
1297
 
                ut_ad(type == TRX_UNDO_UPDATE);
1298
 
                if (state != TRX_UNDO_CACHED) {
1299
 
                        UT_LIST_ADD_LAST(undo_list, rseg->update_undo_list,
1300
 
                                         undo);
1301
 
                } else {
1302
 
                        UT_LIST_ADD_LAST(undo_list, rseg->update_undo_cached,
1303
 
                                         undo);
1304
 
                }
1305
 
        }
1306
 
 
1307
 
        return(undo);
1308
 
}
1309
 
 
1310
 
/************************************************************************
1311
 
Initializes the undo log lists for a rollback segment memory copy. This
1312
 
function is only called when the database is started or a new rollback
1313
 
segment is created. */
1314
 
 
1315
 
ulint
1316
 
trx_undo_lists_init(
1317
 
/*================*/
1318
 
                                /* out: the combined size of undo log segments
1319
 
                                in pages */
1320
 
        trx_rseg_t*     rseg)   /* in: rollback segment memory object */
1321
 
{
1322
 
        ulint           page_no;
1323
 
        trx_undo_t*     undo;
1324
 
        ulint           size    = 0;
1325
 
        trx_rsegf_t*    rseg_header;
1326
 
        ulint           i;
1327
 
        mtr_t           mtr;
1328
 
 
1329
 
        UT_LIST_INIT(rseg->update_undo_list);
1330
 
        UT_LIST_INIT(rseg->update_undo_cached);
1331
 
        UT_LIST_INIT(rseg->insert_undo_list);
1332
 
        UT_LIST_INIT(rseg->insert_undo_cached);
1333
 
 
1334
 
        mtr_start(&mtr);
1335
 
 
1336
 
        rseg_header = trx_rsegf_get_new(rseg->space, rseg->page_no, &mtr);
1337
 
 
1338
 
        for (i = 0; i < TRX_RSEG_N_SLOTS; i++) {
1339
 
                page_no = trx_rsegf_get_nth_undo(rseg_header, i, &mtr);
1340
 
 
1341
 
                /* In forced recovery: try to avoid operations which look
1342
 
                at database pages; undo logs are rapidly changing data, and
1343
 
                the probability that they are in an inconsistent state is
1344
 
                high */
1345
 
 
1346
 
                if (page_no != FIL_NULL
1347
 
                    && srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) {
1348
 
 
1349
 
                        undo = trx_undo_mem_create_at_db_start(rseg, i,
1350
 
                                                               page_no, &mtr);
1351
 
                        size += undo->size;
1352
 
 
1353
 
                        mtr_commit(&mtr);
1354
 
 
1355
 
                        mtr_start(&mtr);
1356
 
 
1357
 
                        rseg_header = trx_rsegf_get(rseg->space,
1358
 
                                                    rseg->page_no, &mtr);
1359
 
                }
1360
 
        }
1361
 
 
1362
 
        mtr_commit(&mtr);
1363
 
 
1364
 
        return(size);
1365
 
}
1366
 
 
1367
 
/************************************************************************
1368
 
Creates and initializes an undo log memory object. */
1369
 
static
1370
 
trx_undo_t*
1371
 
trx_undo_mem_create(
1372
 
/*================*/
1373
 
                                /* out, own: the undo log memory object */
1374
 
        trx_rseg_t*     rseg,   /* in: rollback segment memory object */
1375
 
        ulint           id,     /* in: slot index within rseg */
1376
 
        ulint           type,   /* in: type of the log: TRX_UNDO_INSERT or
1377
 
                                TRX_UNDO_UPDATE */
1378
 
        dulint          trx_id, /* in: id of the trx for which the undo log
1379
 
                                is created */
1380
 
        XID*            xid,    /* in: X/Open transaction identification */
1381
 
        ulint           page_no,/* in: undo log header page number */
1382
 
        ulint           offset) /* in: undo log header byte offset on page */
1383
 
{
1384
 
        trx_undo_t*     undo;
1385
 
 
1386
 
        ut_ad(mutex_own(&(rseg->mutex)));
1387
 
 
1388
 
        if (id >= TRX_RSEG_N_SLOTS) {
1389
 
                fprintf(stderr,
1390
 
                        "InnoDB: Error: undo->id is %lu\n", (ulong) id);
1391
 
                ut_error;
1392
 
        }
1393
 
 
1394
 
        undo = mem_alloc(sizeof(trx_undo_t));
1395
 
 
1396
 
        if (undo == NULL) {
1397
 
 
1398
 
                return NULL;
1399
 
        }
1400
 
 
1401
 
        undo->id = id;
1402
 
        undo->type = type;
1403
 
        undo->state = TRX_UNDO_ACTIVE;
1404
 
        undo->del_marks = FALSE;
1405
 
        undo->trx_id = trx_id;
1406
 
        undo->xid = *xid;
1407
 
 
1408
 
        undo->dict_operation = FALSE;
1409
 
 
1410
 
        undo->rseg = rseg;
1411
 
 
1412
 
        undo->space = rseg->space;
1413
 
        undo->hdr_page_no = page_no;
1414
 
        undo->hdr_offset = offset;
1415
 
        undo->last_page_no = page_no;
1416
 
        undo->size = 1;
1417
 
 
1418
 
        undo->empty = TRUE;
1419
 
        undo->top_page_no = page_no;
1420
 
        undo->guess_page = NULL;
1421
 
 
1422
 
        return(undo);
1423
 
}
1424
 
 
1425
 
/************************************************************************
1426
 
Initializes a cached undo log object for new use. */
1427
 
static
1428
 
void
1429
 
trx_undo_mem_init_for_reuse(
1430
 
/*========================*/
1431
 
        trx_undo_t*     undo,   /* in: undo log to init */
1432
 
        dulint          trx_id, /* in: id of the trx for which the undo log
1433
 
                                is created */
1434
 
        XID*            xid,    /* in: X/Open XA transaction identification*/
1435
 
        ulint           offset) /* in: undo log header byte offset on page */
1436
 
{
1437
 
        ut_ad(mutex_own(&((undo->rseg)->mutex)));
1438
 
 
1439
 
        if (UNIV_UNLIKELY(undo->id >= TRX_RSEG_N_SLOTS)) {
1440
 
                fprintf(stderr, "InnoDB: Error: undo->id is %lu\n",
1441
 
                        (ulong) undo->id);
1442
 
 
1443
 
                mem_analyze_corruption(undo);
1444
 
                ut_error;
1445
 
        }
1446
 
 
1447
 
        undo->state = TRX_UNDO_ACTIVE;
1448
 
        undo->del_marks = FALSE;
1449
 
        undo->trx_id = trx_id;
1450
 
        undo->xid = *xid;
1451
 
 
1452
 
        undo->dict_operation = FALSE;
1453
 
 
1454
 
        undo->hdr_offset = offset;
1455
 
        undo->empty = TRUE;
1456
 
}
1457
 
 
1458
 
/************************************************************************
1459
 
Frees an undo log memory copy. */
1460
 
static
1461
 
void
1462
 
trx_undo_mem_free(
1463
 
/*==============*/
1464
 
        trx_undo_t*     undo)   /* in: the undo object to be freed */
1465
 
{
1466
 
        if (undo->id >= TRX_RSEG_N_SLOTS) {
1467
 
                fprintf(stderr,
1468
 
                        "InnoDB: Error: undo->id is %lu\n", (ulong) undo->id);
1469
 
                ut_error;
1470
 
        }
1471
 
 
1472
 
        mem_free(undo);
1473
 
}
1474
 
 
1475
 
/**************************************************************************
1476
 
Creates a new undo log. */
1477
 
static
1478
 
ulint
1479
 
trx_undo_create(
1480
 
/*============*/
1481
 
                                /* out: DB_SUCCESS if successful in creating
1482
 
                                the new undo lob object, possible error
1483
 
                                codes are: 
1484
 
                                DB_TOO_MANY_CONCURRENT_TRXS
1485
 
                                DB_OUT_OF_FILE_SPACE 
1486
 
                                DB_OUT_OF_MEMORY*/
1487
 
        trx_t*          trx,    /* in: transaction */
1488
 
        trx_rseg_t*     rseg,   /* in: rollback segment memory copy */
1489
 
        ulint           type,   /* in: type of the log: TRX_UNDO_INSERT or
1490
 
                                TRX_UNDO_UPDATE */
1491
 
        dulint          trx_id, /* in: id of the trx for which the undo log
1492
 
                                is created */
1493
 
        XID*            xid,    /* in: X/Open transaction identification*/
1494
 
        trx_undo_t**    undo,   /* out: the new undo log object, undefined
1495
 
                                 * if did not succeed */
1496
 
        mtr_t*          mtr)    /* in: mtr */
1497
 
{
1498
 
        trx_rsegf_t*    rseg_header;
1499
 
        ulint           page_no;
1500
 
        ulint           offset;
1501
 
        ulint           id;
1502
 
        page_t*         undo_page;
1503
 
        ulint           err;
1504
 
 
1505
 
        ut_ad(mutex_own(&(rseg->mutex)));
1506
 
 
1507
 
        if (rseg->curr_size == rseg->max_size) {
1508
 
 
1509
 
                return(DB_OUT_OF_FILE_SPACE);
1510
 
        }
1511
 
 
1512
 
        rseg->curr_size++;
1513
 
 
1514
 
        rseg_header = trx_rsegf_get(rseg->space, rseg->page_no, mtr);
1515
 
 
1516
 
        err = trx_undo_seg_create(rseg, rseg_header, type, &id,
1517
 
                                                        &undo_page, mtr);
1518
 
 
1519
 
        if (err != DB_SUCCESS) {
1520
 
                /* Did not succeed */
1521
 
 
1522
 
                rseg->curr_size--;
1523
 
 
1524
 
                return(err);
1525
 
        }
1526
 
 
1527
 
        page_no = buf_frame_get_page_no(undo_page);
1528
 
 
1529
 
        offset = trx_undo_header_create(undo_page, trx_id, mtr);
1530
 
 
1531
 
        if (trx->support_xa) {
1532
 
                trx_undo_header_add_space_for_xid(undo_page,
1533
 
                                                  undo_page + offset, mtr);
1534
 
        }
1535
 
 
1536
 
        *undo = trx_undo_mem_create(rseg, id, type, trx_id, xid,
1537
 
                                   page_no, offset);
1538
 
        if (*undo == NULL) {
1539
 
 
1540
 
                err = DB_OUT_OF_MEMORY;
1541
 
        }
1542
 
 
1543
 
        return(err);
1544
 
}
1545
 
 
1546
 
/*================ UNDO LOG ASSIGNMENT AND CLEANUP =====================*/
1547
 
 
1548
 
/************************************************************************
1549
 
Reuses a cached undo log. */
1550
 
static
1551
 
trx_undo_t*
1552
 
trx_undo_reuse_cached(
1553
 
/*==================*/
1554
 
                                /* out: the undo log memory object, NULL if
1555
 
                                none cached */
1556
 
        trx_t*          trx,    /* in: transaction */
1557
 
        trx_rseg_t*     rseg,   /* in: rollback segment memory object */
1558
 
        ulint           type,   /* in: type of the log: TRX_UNDO_INSERT or
1559
 
                                TRX_UNDO_UPDATE */
1560
 
        dulint          trx_id, /* in: id of the trx for which the undo log
1561
 
                                is used */
1562
 
        XID*            xid,    /* in: X/Open XA transaction identification */
1563
 
        mtr_t*          mtr)    /* in: mtr */
1564
 
{
1565
 
        trx_undo_t*     undo;
1566
 
        page_t*         undo_page;
1567
 
        ulint           offset;
1568
 
 
1569
 
        ut_ad(mutex_own(&(rseg->mutex)));
1570
 
 
1571
 
        if (type == TRX_UNDO_INSERT) {
1572
 
 
1573
 
                undo = UT_LIST_GET_FIRST(rseg->insert_undo_cached);
1574
 
                if (undo == NULL) {
1575
 
 
1576
 
                        return(NULL);
1577
 
                }
1578
 
 
1579
 
                UT_LIST_REMOVE(undo_list, rseg->insert_undo_cached, undo);
1580
 
        } else {
1581
 
                ut_ad(type == TRX_UNDO_UPDATE);
1582
 
 
1583
 
                undo = UT_LIST_GET_FIRST(rseg->update_undo_cached);
1584
 
                if (undo == NULL) {
1585
 
 
1586
 
                        return(NULL);
1587
 
                }
1588
 
 
1589
 
                UT_LIST_REMOVE(undo_list, rseg->update_undo_cached, undo);
1590
 
        }
1591
 
 
1592
 
        ut_ad(undo->size == 1);
1593
 
 
1594
 
        if (undo->id >= TRX_RSEG_N_SLOTS) {
1595
 
                fprintf(stderr, "InnoDB: Error: undo->id is %lu\n",
1596
 
                        (ulong) undo->id);
1597
 
                mem_analyze_corruption(undo);
1598
 
                ut_error;
1599
 
        }
1600
 
 
1601
 
        undo_page = trx_undo_page_get(undo->space, undo->hdr_page_no, mtr);
1602
 
 
1603
 
        if (type == TRX_UNDO_INSERT) {
1604
 
                offset = trx_undo_insert_header_reuse(undo_page, trx_id, mtr);
1605
 
 
1606
 
                if (trx->support_xa) {
1607
 
                        trx_undo_header_add_space_for_xid(
1608
 
                                undo_page, undo_page + offset, mtr);
1609
 
                }
1610
 
        } else {
1611
 
                ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
1612
 
                                      + TRX_UNDO_PAGE_TYPE)
1613
 
                     == TRX_UNDO_UPDATE);
1614
 
 
1615
 
                offset = trx_undo_header_create(undo_page, trx_id, mtr);
1616
 
 
1617
 
                if (trx->support_xa) {
1618
 
                        trx_undo_header_add_space_for_xid(
1619
 
                                undo_page, undo_page + offset, mtr);
1620
 
                }
1621
 
        }
1622
 
 
1623
 
        trx_undo_mem_init_for_reuse(undo, trx_id, xid, offset);
1624
 
 
1625
 
        return(undo);
1626
 
}
1627
 
 
1628
 
/**************************************************************************
1629
 
Marks an undo log header as a header of a data dictionary operation
1630
 
transaction. */
1631
 
static
1632
 
void
1633
 
trx_undo_mark_as_dict_operation(
1634
 
/*============================*/
1635
 
        trx_t*          trx,    /* in: dict op transaction */
1636
 
        trx_undo_t*     undo,   /* in: assigned undo log */
1637
 
        mtr_t*          mtr)    /* in: mtr */
1638
 
{
1639
 
        page_t* hdr_page;
1640
 
 
1641
 
        ut_a(trx->dict_operation);
1642
 
 
1643
 
        hdr_page = trx_undo_page_get(undo->space, undo->hdr_page_no, mtr);
1644
 
 
1645
 
        mlog_write_ulint(hdr_page + undo->hdr_offset
1646
 
                         + TRX_UNDO_DICT_TRANS,
1647
 
                         trx->dict_operation, MLOG_1BYTE, mtr);
1648
 
 
1649
 
        mlog_write_dulint(hdr_page + undo->hdr_offset + TRX_UNDO_TABLE_ID,
1650
 
                          trx->table_id, mtr);
1651
 
 
1652
 
        undo->dict_operation = trx->dict_operation;
1653
 
        undo->table_id = trx->table_id;
1654
 
}
1655
 
 
1656
 
/**************************************************************************
1657
 
Assigns an undo log for a transaction. A new undo log is created or a cached
1658
 
undo log reused. */
1659
 
 
1660
 
ulint
1661
 
trx_undo_assign_undo(
1662
 
/*=================*/
1663
 
                                /* out: DB_SUCCESS if undo log assign
1664
 
                                successful, possible error codes are:
1665
 
                                DD_TOO_MANY_CONCURRENT_TRXS
1666
 
                                DB_OUT_OF_FILE_SPACE DB_OUT_OF_MEMORY*/
1667
 
        trx_t*          trx,    /* in: transaction */
1668
 
        ulint           type)   /* in: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */
1669
 
{
1670
 
        trx_rseg_t*     rseg;
1671
 
        trx_undo_t*     undo;
1672
 
        mtr_t           mtr;
1673
 
        ulint           err = DB_SUCCESS;
1674
 
 
1675
 
        ut_ad(trx);
1676
 
        ut_ad(trx->rseg);
1677
 
 
1678
 
        rseg = trx->rseg;
1679
 
 
1680
 
        ut_ad(mutex_own(&(trx->undo_mutex)));
1681
 
 
1682
 
        mtr_start(&mtr);
1683
 
 
1684
 
        ut_ad(!mutex_own(&kernel_mutex));
1685
 
 
1686
 
        mutex_enter(&(rseg->mutex));
1687
 
 
1688
 
        undo = trx_undo_reuse_cached(trx, rseg, type, trx->id, &trx->xid,
1689
 
                                     &mtr);
1690
 
        if (undo == NULL) {
1691
 
                err = trx_undo_create(trx, rseg, type, trx->id, &trx->xid,
1692
 
                                                                &undo, &mtr);
1693
 
                if (err != DB_SUCCESS) {
1694
 
 
1695
 
                        goto func_exit;
1696
 
                }
1697
 
        }
1698
 
 
1699
 
        if (type == TRX_UNDO_INSERT) {
1700
 
                UT_LIST_ADD_FIRST(undo_list, rseg->insert_undo_list, undo);
1701
 
                ut_ad(trx->insert_undo == NULL);
1702
 
                trx->insert_undo = undo;
1703
 
        } else {
1704
 
                UT_LIST_ADD_FIRST(undo_list, rseg->update_undo_list, undo);
1705
 
                ut_ad(trx->update_undo == NULL);
1706
 
                trx->update_undo = undo;
1707
 
        }
1708
 
 
1709
 
        if (trx->dict_operation) {
1710
 
                trx_undo_mark_as_dict_operation(trx, undo, &mtr);
1711
 
        }
1712
 
 
1713
 
func_exit:
1714
 
        mutex_exit(&(rseg->mutex));
1715
 
        mtr_commit(&mtr);
1716
 
 
1717
 
        return err;
1718
 
}
1719
 
 
1720
 
/**********************************************************************
1721
 
Sets the state of the undo log segment at a transaction finish. */
1722
 
 
1723
 
page_t*
1724
 
trx_undo_set_state_at_finish(
1725
 
/*=========================*/
1726
 
                                /* out: undo log segment header page,
1727
 
                                x-latched */
1728
 
        trx_rseg_t*     rseg,   /* in: rollback segment memory object */
1729
 
        trx_t*          trx __attribute__((unused)), /* in: transaction */
1730
 
        trx_undo_t*     undo,   /* in: undo log memory copy */
1731
 
        mtr_t*          mtr)    /* in: mtr */
1732
 
{
1733
 
        trx_usegf_t*    seg_hdr;
1734
 
        trx_upagef_t*   page_hdr;
1735
 
        page_t*         undo_page;
1736
 
        ulint           state;
1737
 
 
1738
 
        ut_ad(trx);
1739
 
        ut_ad(undo);
1740
 
        ut_ad(mtr);
1741
 
        ut_ad(mutex_own(&rseg->mutex));
1742
 
 
1743
 
        if (undo->id >= TRX_RSEG_N_SLOTS) {
1744
 
                fprintf(stderr, "InnoDB: Error: undo->id is %lu\n",
1745
 
                        (ulong) undo->id);
1746
 
                mem_analyze_corruption(undo);
1747
 
                ut_error;
1748
 
        }
1749
 
 
1750
 
        undo_page = trx_undo_page_get(undo->space, undo->hdr_page_no, mtr);
1751
 
 
1752
 
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
1753
 
        page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
1754
 
 
1755
 
        if (undo->size == 1
1756
 
            && mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE)
1757
 
               < TRX_UNDO_PAGE_REUSE_LIMIT) {
1758
 
 
1759
 
                /* This is a heuristic to avoid the problem of all UNDO
1760
 
                slots ending up in one of the UNDO lists. Previously if
1761
 
                the server crashed with all the slots in one of the lists,
1762
 
                transactions that required the slots of a different type
1763
 
                would fail for lack of slots. */
1764
 
 
1765
 
                if (UT_LIST_GET_LEN(rseg->update_undo_list) < 500
1766
 
                    && UT_LIST_GET_LEN(rseg->insert_undo_list) < 500) {
1767
 
 
1768
 
                        state = TRX_UNDO_CACHED;
1769
 
                } else {
1770
 
                        state = TRX_UNDO_TO_FREE;
1771
 
                }
1772
 
 
1773
 
        } else if (undo->type == TRX_UNDO_INSERT) {
1774
 
 
1775
 
                state = TRX_UNDO_TO_FREE;
1776
 
        } else {
1777
 
                state = TRX_UNDO_TO_PURGE;
1778
 
        }
1779
 
 
1780
 
        undo->state = state;
1781
 
 
1782
 
        mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, state, MLOG_2BYTES, mtr);
1783
 
 
1784
 
        return(undo_page);
1785
 
}
1786
 
 
1787
 
/**********************************************************************
1788
 
Sets the state of the undo log segment at a transaction prepare. */
1789
 
 
1790
 
page_t*
1791
 
trx_undo_set_state_at_prepare(
1792
 
/*==========================*/
1793
 
                                /* out: undo log segment header page,
1794
 
                                x-latched */
1795
 
        trx_t*          trx,    /* in: transaction */
1796
 
        trx_undo_t*     undo,   /* in: undo log memory copy */
1797
 
        mtr_t*          mtr)    /* in: mtr */
1798
 
{
1799
 
        trx_usegf_t*    seg_hdr;
1800
 
        trx_upagef_t*   page_hdr;
1801
 
        trx_ulogf_t*    undo_header;
1802
 
        page_t*         undo_page;
1803
 
        ulint           offset;
1804
 
 
1805
 
        ut_ad(trx && undo && mtr);
1806
 
 
1807
 
        if (undo->id >= TRX_RSEG_N_SLOTS) {
1808
 
                fprintf(stderr, "InnoDB: Error: undo->id is %lu\n",
1809
 
                        (ulong) undo->id);
1810
 
                mem_analyze_corruption(undo);
1811
 
                ut_error;
1812
 
        }
1813
 
 
1814
 
        undo_page = trx_undo_page_get(undo->space, undo->hdr_page_no, mtr);
1815
 
 
1816
 
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
1817
 
        page_hdr = undo_page + TRX_UNDO_PAGE_HDR;
1818
 
 
1819
 
        /*------------------------------*/
1820
 
        undo->state = TRX_UNDO_PREPARED;
1821
 
        undo->xid   = trx->xid;
1822
 
        /*------------------------------*/
1823
 
 
1824
 
        mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, undo->state,
1825
 
                         MLOG_2BYTES, mtr);
1826
 
 
1827
 
        offset = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG);
1828
 
        undo_header = undo_page + offset;
1829
 
 
1830
 
        mlog_write_ulint(undo_header + TRX_UNDO_XID_EXISTS,
1831
 
                         TRUE, MLOG_1BYTE, mtr);
1832
 
 
1833
 
        trx_undo_write_xid(undo_header, &undo->xid, mtr);
1834
 
 
1835
 
        return(undo_page);
1836
 
}
1837
 
 
1838
 
/**************************************************************************
1839
 
Adds the update undo log header as the first in the history list, and
1840
 
frees the memory object, or puts it to the list of cached update undo log
1841
 
segments. */
1842
 
 
1843
 
void
1844
 
trx_undo_update_cleanup(
1845
 
/*====================*/
1846
 
        trx_t*  trx,            /* in: trx owning the update undo log */
1847
 
        page_t* undo_page,      /* in: update undo log header page,
1848
 
                                x-latched */
1849
 
        mtr_t*  mtr)            /* in: mtr */
1850
 
{
1851
 
        trx_rseg_t*     rseg;
1852
 
        trx_undo_t*     undo;
1853
 
 
1854
 
        undo = trx->update_undo;
1855
 
        rseg = trx->rseg;
1856
 
 
1857
 
        ut_ad(mutex_own(&(rseg->mutex)));
1858
 
 
1859
 
        trx_purge_add_update_undo_to_history(trx, undo_page, mtr);
1860
 
 
1861
 
        UT_LIST_REMOVE(undo_list, rseg->update_undo_list, undo);
1862
 
 
1863
 
        trx->update_undo = NULL;
1864
 
 
1865
 
        if (undo->state == TRX_UNDO_CACHED) {
1866
 
 
1867
 
                UT_LIST_ADD_FIRST(undo_list, rseg->update_undo_cached, undo);
1868
 
        } else {
1869
 
                ut_ad(undo->state == TRX_UNDO_TO_PURGE);
1870
 
 
1871
 
                trx_undo_mem_free(undo);
1872
 
        }
1873
 
}
1874
 
 
1875
 
/**********************************************************************
1876
 
Frees or caches an insert undo log after a transaction commit or rollback.
1877
 
Knowledge of inserts is not needed after a commit or rollback, therefore
1878
 
the data can be discarded. */
1879
 
 
1880
 
void
1881
 
trx_undo_insert_cleanup(
1882
 
/*====================*/
1883
 
        trx_t*  trx)    /* in: transaction handle */
1884
 
{
1885
 
        trx_undo_t*     undo;
1886
 
        trx_rseg_t*     rseg;
1887
 
 
1888
 
        undo = trx->insert_undo;
1889
 
        ut_ad(undo);
1890
 
 
1891
 
        rseg = trx->rseg;
1892
 
 
1893
 
        mutex_enter(&(rseg->mutex));
1894
 
 
1895
 
        UT_LIST_REMOVE(undo_list, rseg->insert_undo_list, undo);
1896
 
        trx->insert_undo = NULL;
1897
 
 
1898
 
        if (undo->state == TRX_UNDO_CACHED) {
1899
 
 
1900
 
                UT_LIST_ADD_FIRST(undo_list, rseg->insert_undo_cached, undo);
1901
 
        } else {
1902
 
                ut_ad(undo->state == TRX_UNDO_TO_FREE);
1903
 
 
1904
 
                /* Delete first the undo log segment in the file */
1905
 
 
1906
 
                mutex_exit(&(rseg->mutex));
1907
 
 
1908
 
                trx_undo_seg_free(undo);
1909
 
 
1910
 
                mutex_enter(&(rseg->mutex));
1911
 
 
1912
 
                ut_ad(rseg->curr_size > undo->size);
1913
 
 
1914
 
                rseg->curr_size -= undo->size;
1915
 
 
1916
 
                trx_undo_mem_free(undo);
1917
 
        }
1918
 
 
1919
 
        mutex_exit(&(rseg->mutex));
1920
 
}