~drizzle-trunk/drizzle/development

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