~drizzle-trunk/drizzle/development

641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
1
/*****************************************************************************
2
3
Copyright (c) 1997, 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
1802.10.2 by Monty Taylor
Update all of the copyright headers to include the correct address.
14
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 USA
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
16
17
*****************************************************************************/
18
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
19
/**************************************************//**
20
@file row/row0vers.c
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
21
Row versions
22
23
Created 2/6/1997 Heikki Tuuri
24
*******************************************************/
25
26
#include "row0vers.h"
27
28
#ifdef UNIV_NONINL
29
#include "row0vers.ic"
30
#endif
31
32
#include "dict0dict.h"
33
#include "dict0boot.h"
34
#include "btr0btr.h"
35
#include "mach0data.h"
36
#include "trx0rseg.h"
37
#include "trx0trx.h"
38
#include "trx0roll.h"
39
#include "trx0undo.h"
40
#include "trx0purge.h"
41
#include "trx0rec.h"
42
#include "que0que.h"
43
#include "row0row.h"
44
#include "row0upd.h"
45
#include "rem0cmp.h"
46
#include "read0read.h"
47
#include "lock0lock.h"
48
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
49
/*****************************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
50
Finds out if an active transaction has inserted or modified a secondary
51
index record. NOTE: the kernel mutex is temporarily released in this
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
52
function!
53
@return NULL if committed, else the active transaction */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
54
UNIV_INTERN
55
trx_t*
56
row_vers_impl_x_locked_off_kernel(
57
/*==============================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
58
	const rec_t*	rec,	/*!< in: record in a secondary index */
59
	dict_index_t*	index,	/*!< in: the secondary index */
60
	const ulint*	offsets)/*!< in: rec_get_offsets(rec, index) */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
61
{
62
	dict_index_t*	clust_index;
63
	rec_t*		clust_rec;
64
	ulint*		clust_offsets;
65
	rec_t*		version;
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
66
	trx_id_t	trx_id;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
67
	mem_heap_t*	heap;
68
	mem_heap_t*	heap2;
69
	dtuple_t*	row;
70
	dtuple_t*	entry	= NULL; /* assignment to eliminate compiler
71
					warning */
72
	trx_t*		trx;
73
	ulint		rec_del;
74
	ulint		err;
75
	mtr_t		mtr;
76
	ulint		comp;
77
78
	ut_ad(mutex_own(&kernel_mutex));
79
#ifdef UNIV_SYNC_DEBUG
80
	ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
81
#endif /* UNIV_SYNC_DEBUG */
82
83
	mutex_exit(&kernel_mutex);
84
85
	mtr_start(&mtr);
86
87
	/* Search for the clustered index record: this is a time-consuming
88
	operation: therefore we release the kernel mutex; also, the release
89
	is required by the latching order convention. The latch on the
90
	clustered index locks the top of the stack of versions. We also
91
	reserve purge_latch to lock the bottom of the version stack. */
92
93
	clust_rec = row_get_clust_rec(BTR_SEARCH_LEAF, rec, index,
94
				      &clust_index, &mtr);
95
	if (!clust_rec) {
96
		/* In a rare case it is possible that no clust rec is found
97
		for a secondary index record: if in row0umod.c
98
		row_undo_mod_remove_clust_low() we have already removed the
99
		clust rec, while purge is still cleaning and removing
100
		secondary index records associated with earlier versions of
101
		the clustered index record. In that case there cannot be
102
		any implicit lock on the secondary index record, because
103
		an active transaction which has modified the secondary index
104
		record has also modified the clustered index record. And in
105
		a rollback we always undo the modifications to secondary index
106
		records before the clustered index record. */
107
108
		mutex_enter(&kernel_mutex);
109
		mtr_commit(&mtr);
110
111
		return(NULL);
112
	}
113
114
	heap = mem_heap_create(1024);
115
	clust_offsets = rec_get_offsets(clust_rec, clust_index, NULL,
116
					ULINT_UNDEFINED, &heap);
117
	trx_id = row_get_rec_trx_id(clust_rec, clust_index, clust_offsets);
118
119
	mtr_s_lock(&(purge_sys->latch), &mtr);
120
121
	mutex_enter(&kernel_mutex);
122
123
	trx = NULL;
124
	if (!trx_is_active(trx_id)) {
125
		/* The transaction that modified or inserted clust_rec is no
126
		longer active: no implicit lock on rec */
127
		goto exit_func;
128
	}
129
130
	if (!lock_check_trx_id_sanity(trx_id, clust_rec, clust_index,
131
				      clust_offsets, TRUE)) {
132
		/* Corruption noticed: try to avoid a crash by returning */
133
		goto exit_func;
134
	}
135
136
	comp = page_rec_is_comp(rec);
137
	ut_ad(index->table == clust_index->table);
138
	ut_ad(!!comp == dict_table_is_comp(index->table));
139
	ut_ad(!comp == !page_rec_is_comp(clust_rec));
140
141
	/* We look up if some earlier version, which was modified by the trx_id
142
	transaction, of the clustered index record would require rec to be in
143
	a different state (delete marked or unmarked, or have different field
144
	values, or not existing). If there is such a version, then rec was
145
	modified by the trx_id transaction, and it has an implicit x-lock on
146
	rec. Note that if clust_rec itself would require rec to be in a
147
	different state, then the trx_id transaction has not yet had time to
148
	modify rec, and does not necessarily have an implicit x-lock on rec. */
149
150
	rec_del = rec_get_deleted_flag(rec, comp);
151
	trx = NULL;
152
153
	version = clust_rec;
154
155
	for (;;) {
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
156
		rec_t*		prev_version;
157
		ulint		vers_del;
158
		row_ext_t*	ext;
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
159
		trx_id_t	prev_trx_id;
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
160
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
161
		mutex_exit(&kernel_mutex);
162
163
		/* While we retrieve an earlier version of clust_rec, we
164
		release the kernel mutex, because it may take time to access
165
		the disk. After the release, we have to check if the trx_id
166
		transaction is still active. We keep the semaphore in mtr on
167
		the clust_rec page, so that no other transaction can update
168
		it and get an implicit x-lock on rec. */
169
170
		heap2 = heap;
171
		heap = mem_heap_create(1024);
172
		err = trx_undo_prev_version_build(clust_rec, &mtr, version,
173
						  clust_index, clust_offsets,
174
						  heap, &prev_version);
175
		mem_heap_free(heap2); /* free version and clust_offsets */
176
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
177
		if (prev_version == NULL) {
178
			mutex_enter(&kernel_mutex);
179
180
			if (!trx_is_active(trx_id)) {
181
				/* Transaction no longer active: no
182
				implicit x-lock */
183
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
184
				break;
185
			}
186
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
187
			/* If the transaction is still active,
188
			clust_rec must be a fresh insert, because no
189
			previous version was found. */
190
			ut_ad(err == DB_SUCCESS);
191
192
			/* It was a freshly inserted version: there is an
193
			implicit x-lock on rec */
194
195
			trx = trx_get_on_id(trx_id);
196
197
			break;
198
		}
199
200
		clust_offsets = rec_get_offsets(prev_version, clust_index,
201
						NULL, ULINT_UNDEFINED, &heap);
202
203
		vers_del = rec_get_deleted_flag(prev_version, comp);
204
		prev_trx_id = row_get_rec_trx_id(prev_version, clust_index,
205
						 clust_offsets);
206
207
		/* If the trx_id and prev_trx_id are different and if
208
		the prev_version is marked deleted then the
209
		prev_trx_id must have already committed for the trx_id
210
		to be able to modify the row. Therefore, prev_trx_id
211
		cannot hold any implicit lock. */
1819.9.31 by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB
212
		if (vers_del && trx_id != prev_trx_id) {
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
213
214
			mutex_enter(&kernel_mutex);
215
			break;
216
		}
217
218
		/* The stack of versions is locked by mtr.  Thus, it
219
		is safe to fetch the prefixes for externally stored
220
		columns. */
221
		row = row_build(ROW_COPY_POINTERS, clust_index, prev_version,
222
				clust_offsets, NULL, &ext, heap);
223
		entry = row_build_index_entry(row, ext, index, heap);
224
		/* entry may be NULL if a record was inserted in place
225
		of a deleted record, and the BLOB pointers of the new
226
		record were not initialized yet.  But in that case,
227
		prev_version should be NULL. */
228
		ut_a(entry);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
229
230
		mutex_enter(&kernel_mutex);
231
232
		if (!trx_is_active(trx_id)) {
233
			/* Transaction no longer active: no implicit x-lock */
234
235
			break;
236
		}
237
238
		/* If we get here, we know that the trx_id transaction is
239
		still active and it has modified prev_version. Let us check
240
		if prev_version would require rec to be in a different
241
		state. */
242
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
243
		/* The previous version of clust_rec must be
244
		accessible, because the transaction is still active
245
		and clust_rec was not a fresh insert. */
246
		ut_ad(err == DB_SUCCESS);
247
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
248
		/* We check if entry and rec are identified in the alphabetical
249
		ordering */
250
		if (0 == cmp_dtuple_rec(entry, rec, offsets)) {
251
			/* The delete marks of rec and prev_version should be
252
			equal for rec to be in the state required by
253
			prev_version */
254
255
			if (rec_del != vers_del) {
256
				trx = trx_get_on_id(trx_id);
257
258
				break;
259
			}
260
261
			/* It is possible that the row was updated so that the
262
			secondary index record remained the same in
263
			alphabetical ordering, but the field values changed
264
			still. For example, 'abc' -> 'ABC'. Check also that. */
265
266
			dtuple_set_types_binary(entry,
267
						dtuple_get_n_fields(entry));
268
			if (0 != cmp_dtuple_rec(entry, rec, offsets)) {
269
270
				trx = trx_get_on_id(trx_id);
271
272
				break;
273
			}
274
		} else if (!rec_del) {
275
			/* The delete mark should be set in rec for it to be
276
			in the state required by prev_version */
277
278
			trx = trx_get_on_id(trx_id);
279
280
			break;
281
		}
282
1819.9.31 by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB
283
		if (trx_id != prev_trx_id) {
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
284
			/* The versions modified by the trx_id transaction end
285
			to prev_version: no implicit x-lock */
286
287
			break;
288
		}
289
290
		version = prev_version;
291
	}/* for (;;) */
292
293
exit_func:
294
	mtr_commit(&mtr);
295
	mem_heap_free(heap);
296
297
	return(trx);
298
}
299
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
300
/*****************************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
301
Finds out if we must preserve a delete marked earlier version of a clustered
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
302
index record, because it is >= the purge view.
303
@return	TRUE if earlier version should be preserved */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
304
UNIV_INTERN
305
ibool
306
row_vers_must_preserve_del_marked(
307
/*==============================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
308
	trx_id_t	trx_id,	/*!< in: transaction id in the version */
309
	mtr_t*		mtr)	/*!< in: mtr holding the latch on the
310
				clustered index record; it will also
311
				hold the latch on purge_view */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
312
{
313
#ifdef UNIV_SYNC_DEBUG
314
	ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
315
#endif /* UNIV_SYNC_DEBUG */
316
317
	mtr_s_lock(&(purge_sys->latch), mtr);
318
319
	if (trx_purge_update_undo_must_exist(trx_id)) {
320
321
		/* A purge operation is not yet allowed to remove this
322
		delete marked record */
323
324
		return(TRUE);
325
	}
326
327
	return(FALSE);
328
}
329
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
330
/*****************************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
331
Finds out if a version of the record, where the version >= the current
332
purge view, should have ientry as its secondary index entry. We check
333
if there is any not delete marked version of the record where the trx
334
id >= purge view, and the secondary index entry and ientry are identified in
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
335
the alphabetical ordering; exactly in this case we return TRUE.
336
@return	TRUE if earlier version should have */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
337
UNIV_INTERN
338
ibool
339
row_vers_old_has_index_entry(
340
/*=========================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
341
	ibool		also_curr,/*!< in: TRUE if also rec is included in the
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
342
				versions to search; otherwise only versions
343
				prior to it are searched */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
344
	const rec_t*	rec,	/*!< in: record in the clustered index; the
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
345
				caller must have a latch on the page */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
346
	mtr_t*		mtr,	/*!< in: mtr holding the latch on rec; it will
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
347
				also hold the latch on purge_view */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
348
	dict_index_t*	index,	/*!< in: the secondary index */
349
	const dtuple_t*	ientry)	/*!< in: the secondary index entry */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
350
{
351
	const rec_t*	version;
352
	rec_t*		prev_version;
353
	dict_index_t*	clust_index;
354
	ulint*		clust_offsets;
355
	mem_heap_t*	heap;
356
	mem_heap_t*	heap2;
357
	const dtuple_t*	row;
358
	const dtuple_t*	entry;
359
	ulint		err;
360
	ulint		comp;
361
362
	ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX)
363
	      || mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX));
364
#ifdef UNIV_SYNC_DEBUG
365
	ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
366
#endif /* UNIV_SYNC_DEBUG */
367
	mtr_s_lock(&(purge_sys->latch), mtr);
368
369
	clust_index = dict_table_get_first_index(index->table);
370
371
	comp = page_rec_is_comp(rec);
372
	ut_ad(!dict_table_is_comp(index->table) == !comp);
373
	heap = mem_heap_create(1024);
374
	clust_offsets = rec_get_offsets(rec, clust_index, NULL,
375
					ULINT_UNDEFINED, &heap);
376
377
	if (also_curr && !rec_get_deleted_flag(rec, comp)) {
378
		row_ext_t*	ext;
379
380
		/* The stack of versions is locked by mtr.
381
		Thus, it is safe to fetch the prefixes for
382
		externally stored columns. */
383
		row = row_build(ROW_COPY_POINTERS, clust_index,
384
				rec, clust_offsets, NULL, &ext, heap);
385
		entry = row_build_index_entry(row, ext, index, heap);
386
387
		/* If entry == NULL, the record contains unset BLOB
388
		pointers.  This must be a freshly inserted record.  If
389
		this is called from
390
		row_purge_remove_sec_if_poss_low(), the thread will
391
		hold latches on the clustered index and the secondary
392
		index.  Because the insert works in three steps:
393
394
			(1) insert the record to clustered index
395
			(2) store the BLOBs and update BLOB pointers
396
			(3) insert records to secondary indexes
397
398
		the purge thread can safely ignore freshly inserted
399
		records and delete the secondary index record.  The
400
		thread that inserted the new record will be inserting
401
		the secondary index records. */
402
403
		/* NOTE that we cannot do the comparison as binary
404
		fields because the row is maybe being modified so that
405
		the clustered index record has already been updated to
406
		a different binary value in a char field, but the
407
		collation identifies the old and new value anyway! */
408
		if (entry && !dtuple_coll_cmp(ientry, entry)) {
409
410
			mem_heap_free(heap);
411
412
			return(TRUE);
413
		}
414
	}
415
416
	version = rec;
417
418
	for (;;) {
419
		heap2 = heap;
420
		heap = mem_heap_create(1024);
421
		err = trx_undo_prev_version_build(rec, mtr, version,
422
						  clust_index, clust_offsets,
423
						  heap, &prev_version);
424
		mem_heap_free(heap2); /* free version and clust_offsets */
425
426
		if (err != DB_SUCCESS || !prev_version) {
427
			/* Versions end here */
428
429
			mem_heap_free(heap);
430
431
			return(FALSE);
432
		}
433
434
		clust_offsets = rec_get_offsets(prev_version, clust_index,
435
						NULL, ULINT_UNDEFINED, &heap);
436
437
		if (!rec_get_deleted_flag(prev_version, comp)) {
438
			row_ext_t*	ext;
439
440
			/* The stack of versions is locked by mtr.
441
			Thus, it is safe to fetch the prefixes for
442
			externally stored columns. */
443
			row = row_build(ROW_COPY_POINTERS, clust_index,
444
					prev_version, clust_offsets,
445
					NULL, &ext, heap);
446
			entry = row_build_index_entry(row, ext, index, heap);
447
448
			/* If entry == NULL, the record contains unset
449
			BLOB pointers.  This must be a freshly
450
			inserted record that we can safely ignore.
451
			For the justification, see the comments after
452
			the previous row_build_index_entry() call. */
453
454
			/* NOTE that we cannot do the comparison as binary
455
			fields because maybe the secondary index record has
456
			already been updated to a different binary value in
457
			a char field, but the collation identifies the old
458
			and new value anyway! */
459
460
			if (entry && !dtuple_coll_cmp(ientry, entry)) {
461
462
				mem_heap_free(heap);
463
464
				return(TRUE);
465
			}
466
		}
467
468
		version = prev_version;
469
	}
470
}
471
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
472
/*****************************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
473
Constructs the version of a clustered index record which a consistent
474
read should see. We assume that the trx id stored in rec is such that
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
475
the consistent read should not see rec in its present version.
476
@return	DB_SUCCESS or DB_MISSING_HISTORY */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
477
UNIV_INTERN
478
ulint
479
row_vers_build_for_consistent_read(
480
/*===============================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
481
	const rec_t*	rec,	/*!< in: record in a clustered index; the
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
482
				caller must have a latch on the page; this
483
				latch locks the top of the stack of versions
484
				of this records */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
485
	mtr_t*		mtr,	/*!< in: mtr holding the latch on rec */
486
	dict_index_t*	index,	/*!< in: the clustered index */
487
	ulint**		offsets,/*!< in/out: offsets returned by
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
488
				rec_get_offsets(rec, index) */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
489
	read_view_t*	view,	/*!< in: the consistent read view */
490
	mem_heap_t**	offset_heap,/*!< in/out: memory heap from which
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
491
				the offsets are allocated */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
492
	mem_heap_t*	in_heap,/*!< in: memory heap from which the memory for
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
493
				*old_vers is allocated; memory for possible
494
				intermediate versions is allocated and freed
495
				locally within the function */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
496
	rec_t**		old_vers)/*!< out, own: old version, or NULL if the
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
497
				record does not exist in the view, that is,
498
				it was freshly inserted afterwards */
499
{
500
	const rec_t*	version;
501
	rec_t*		prev_version;
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
502
	trx_id_t	trx_id;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
503
	mem_heap_t*	heap		= NULL;
504
	byte*		buf;
505
	ulint		err;
506
507
	ut_ad(dict_index_is_clust(index));
508
	ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX)
509
	      || mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX));
510
#ifdef UNIV_SYNC_DEBUG
511
	ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
512
#endif /* UNIV_SYNC_DEBUG */
513
514
	ut_ad(rec_offs_validate(rec, index, *offsets));
515
516
	trx_id = row_get_rec_trx_id(rec, index, *offsets);
517
518
	ut_ad(!read_view_sees_trx_id(view, trx_id));
519
520
	rw_lock_s_lock(&(purge_sys->latch));
521
	version = rec;
522
523
	for (;;) {
524
		mem_heap_t*	heap2	= heap;
525
		trx_undo_rec_t* undo_rec;
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
526
		roll_ptr_t	roll_ptr;
527
		undo_no_t	undo_no;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
528
		heap = mem_heap_create(1024);
529
530
		/* If we have high-granularity consistent read view and
531
		creating transaction of the view is the same as trx_id in
532
		the record we see this record only in the case when
533
		undo_no of the record is < undo_no in the view. */
534
535
		if (view->type == VIEW_HIGH_GRANULARITY
1819.9.31 by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB
536
		    && view->creator_trx_id == trx_id) {
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
537
538
			roll_ptr = row_get_rec_roll_ptr(version, index,
539
							*offsets);
540
			undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
541
			undo_no = trx_undo_rec_get_undo_no(undo_rec);
542
			mem_heap_empty(heap);
543
1819.9.31 by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB
544
			if (view->undo_no > undo_no) {
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
545
				/* The view already sees this version: we can
546
				copy it to in_heap and return */
547
548
				buf = mem_heap_alloc(in_heap,
549
						     rec_offs_size(*offsets));
550
				*old_vers = rec_copy(buf, version, *offsets);
551
				rec_offs_make_valid(*old_vers, index,
552
						    *offsets);
553
				err = DB_SUCCESS;
554
555
				break;
556
			}
557
		}
558
559
		err = trx_undo_prev_version_build(rec, mtr, version, index,
560
						  *offsets, heap,
561
						  &prev_version);
562
		if (heap2) {
563
			mem_heap_free(heap2); /* free version */
564
		}
565
566
		if (err != DB_SUCCESS) {
567
			break;
568
		}
569
570
		if (prev_version == NULL) {
571
			/* It was a freshly inserted version */
572
			*old_vers = NULL;
573
			err = DB_SUCCESS;
574
575
			break;
576
		}
577
578
		*offsets = rec_get_offsets(prev_version, index, *offsets,
579
					   ULINT_UNDEFINED, offset_heap);
580
581
		trx_id = row_get_rec_trx_id(prev_version, index, *offsets);
582
583
		if (read_view_sees_trx_id(view, trx_id)) {
584
585
			/* The view already sees this version: we can copy
586
			it to in_heap and return */
587
588
			buf = mem_heap_alloc(in_heap, rec_offs_size(*offsets));
589
			*old_vers = rec_copy(buf, prev_version, *offsets);
590
			rec_offs_make_valid(*old_vers, index, *offsets);
591
			err = DB_SUCCESS;
592
593
			break;
594
		}
595
596
		version = prev_version;
597
	}/* for (;;) */
598
599
	mem_heap_free(heap);
600
	rw_lock_s_unlock(&(purge_sys->latch));
601
602
	return(err);
603
}
604
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
605
/*****************************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
606
Constructs the last committed version of a clustered index record,
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
607
which should be seen by a semi-consistent read.
608
@return	DB_SUCCESS or DB_MISSING_HISTORY */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
609
UNIV_INTERN
610
ulint
611
row_vers_build_for_semi_consistent_read(
612
/*====================================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
613
	const rec_t*	rec,	/*!< in: record in a clustered index; the
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
614
				caller must have a latch on the page; this
615
				latch locks the top of the stack of versions
616
				of this records */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
617
	mtr_t*		mtr,	/*!< in: mtr holding the latch on rec */
618
	dict_index_t*	index,	/*!< in: the clustered index */
619
	ulint**		offsets,/*!< in/out: offsets returned by
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
620
				rec_get_offsets(rec, index) */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
621
	mem_heap_t**	offset_heap,/*!< in/out: memory heap from which
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
622
				the offsets are allocated */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
623
	mem_heap_t*	in_heap,/*!< in: memory heap from which the memory for
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
624
				*old_vers is allocated; memory for possible
625
				intermediate versions is allocated and freed
626
				locally within the function */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
627
	const rec_t**	old_vers)/*!< out: rec, old version, or NULL if the
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
628
				record does not exist in the view, that is,
629
				it was freshly inserted afterwards */
630
{
631
	const rec_t*	version;
632
	mem_heap_t*	heap		= NULL;
633
	byte*		buf;
634
	ulint		err;
1819.9.31 by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB
635
	trx_id_t	rec_trx_id	= 0;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
636
637
	ut_ad(dict_index_is_clust(index));
638
	ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX)
639
	      || mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX));
640
#ifdef UNIV_SYNC_DEBUG
641
	ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
642
#endif /* UNIV_SYNC_DEBUG */
643
644
	ut_ad(rec_offs_validate(rec, index, *offsets));
645
646
	rw_lock_s_lock(&(purge_sys->latch));
647
	/* The S-latch on purge_sys prevents the purge view from
648
	changing.  Thus, if we have an uncommitted transaction at
649
	this point, then purge cannot remove its undo log even if
650
	the transaction could commit now. */
651
652
	version = rec;
653
654
	for (;;) {
655
		trx_t*		version_trx;
656
		mem_heap_t*	heap2;
657
		rec_t*		prev_version;
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
658
		trx_id_t	version_trx_id;
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
659
660
		version_trx_id = row_get_rec_trx_id(version, index, *offsets);
661
		if (rec == version) {
662
			rec_trx_id = version_trx_id;
663
		}
664
665
		mutex_enter(&kernel_mutex);
666
		version_trx = trx_get_on_id(version_trx_id);
667
		mutex_exit(&kernel_mutex);
668
669
		if (!version_trx
670
		    || version_trx->conc_state == TRX_NOT_STARTED
671
		    || version_trx->conc_state == TRX_COMMITTED_IN_MEMORY) {
672
673
			/* We found a version that belongs to a
674
			committed transaction: return it. */
675
676
			if (rec == version) {
677
				*old_vers = rec;
678
				err = DB_SUCCESS;
679
				break;
680
			}
681
682
			/* We assume that a rolled-back transaction stays in
683
			TRX_ACTIVE state until all the changes have been
684
			rolled back and the transaction is removed from
685
			the global list of transactions. */
686
1819.9.31 by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB
687
			if (rec_trx_id == version_trx_id) {
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
688
				/* The transaction was committed while
689
				we searched for earlier versions.
690
				Return the current version as a
691
				semi-consistent read. */
692
693
				version = rec;
694
				*offsets = rec_get_offsets(version,
695
							   index, *offsets,
696
							   ULINT_UNDEFINED,
697
							   offset_heap);
698
			}
699
700
			buf = mem_heap_alloc(in_heap, rec_offs_size(*offsets));
701
			*old_vers = rec_copy(buf, version, *offsets);
702
			rec_offs_make_valid(*old_vers, index, *offsets);
703
			err = DB_SUCCESS;
704
705
			break;
706
		}
707
708
		heap2 = heap;
709
		heap = mem_heap_create(1024);
710
711
		err = trx_undo_prev_version_build(rec, mtr, version, index,
712
						  *offsets, heap,
713
						  &prev_version);
714
		if (heap2) {
715
			mem_heap_free(heap2); /* free version */
716
		}
717
718
		if (UNIV_UNLIKELY(err != DB_SUCCESS)) {
719
			break;
720
		}
721
722
		if (prev_version == NULL) {
723
			/* It was a freshly inserted version */
724
			*old_vers = NULL;
725
			err = DB_SUCCESS;
726
727
			break;
728
		}
729
730
		version = prev_version;
731
		*offsets = rec_get_offsets(version, index, *offsets,
732
					   ULINT_UNDEFINED, offset_heap);
733
	}/* for (;;) */
734
735
	if (heap) {
736
		mem_heap_free(heap);
737
	}
738
	rw_lock_s_unlock(&(purge_sys->latch));
739
740
	return(err);
741
}