~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************
 
2
Transaction undo log
 
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
}