~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to storage/innobase/row/row0vers.c

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

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