~stewart/haildb/trunk

« back to all changes in this revision

Viewing changes to trx/trx0undo.c

  • Committer: Stewart Smith
  • Date: 2010-04-09 07:57:43 UTC
  • Revision ID: stewart@flamingspork.com-20100409075743-jfh1oml3el1uouvh
Embedded InnoDB 1.0.0 released

2009-04-21      The InnoDB Team

        Embedded InnoDB 1.0.0 released

Show diffs side-by-side

added added

removed removed

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