~drizzle-trunk/drizzle/development

1 by brian
clean slate
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
}