1
/*****************************************************************************
3
Copyright (C) 1997, 2009, Innobase Oy. All Rights Reserved.
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.
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.
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., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 USA
17
*****************************************************************************/
19
/**************************************************//**
1
/******************************************************
23
6
Created 2/6/1997 Heikki Tuuri
24
7
*******************************************************/
46
29
#include "read0read.h"
47
30
#include "lock0lock.h"
49
/*****************************************************************//**
32
/*********************************************************************
50
33
Finds out if an active transaction has inserted or modified a secondary
51
34
index record. NOTE: the kernel mutex is temporarily released in this
53
@return NULL if committed, else the active transaction */
56
38
row_vers_impl_x_locked_off_kernel(
57
39
/*==============================*/
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) */
40
/* out: NULL if committed, else the active
41
transaction; NOTE that the kernel mutex is
42
temporarily released! */
43
const 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) */
62
47
dict_index_t* clust_index;
64
49
ulint* clust_offsets;
70
57
dtuple_t* entry = NULL; /* assignment to eliminate compiler
76
#endif /* UNIV_DEBUG */
173
156
heap = mem_heap_create(1024);
176
#endif /* UNIV_DEBUG */
177
trx_undo_prev_version_build(clust_rec, &mtr, version,
178
clust_index, clust_offsets,
179
heap, &prev_version);
157
err = trx_undo_prev_version_build(clust_rec, &mtr, version,
158
clust_index, clust_offsets,
159
heap, &prev_version);
180
160
mem_heap_free(heap2); /* free version and clust_offsets */
182
if (prev_version == NULL) {
183
mutex_enter(&kernel_mutex);
185
if (!trx_is_active(trx_id)) {
186
/* Transaction no longer active: no
165
clust_offsets = rec_get_offsets(
166
prev_version, clust_index, NULL,
167
ULINT_UNDEFINED, &heap);
169
vers_del = rec_get_deleted_flag(prev_version,
171
prev_trx_id = row_get_rec_trx_id(prev_version,
175
/* If the trx_id and prev_trx_id are
176
different and if the prev_version is marked
177
deleted then the prev_trx_id must have
178
already committed for the trx_id to be able to
179
modify the row. Therefore, prev_trx_id cannot
180
hold any implicit lock. */
181
if (0 != ut_dulint_cmp(trx_id, prev_trx_id)
184
mutex_enter(&kernel_mutex);
192
/* If the transaction is still active,
193
clust_rec must be a fresh insert, because no
194
previous version was found. */
195
ut_ad(err == DB_SUCCESS);
188
/* The stack of versions is locked by mtr.
189
Thus, it is safe to fetch the prefixes for
190
externally stored columns. */
191
row = row_build(ROW_COPY_POINTERS, clust_index,
192
prev_version, clust_offsets,
194
entry = row_build_index_entry(row, ext, index, heap);
195
/* entry may be NULL if a record was inserted
196
in place of a deleted record, and the BLOB
197
pointers of the new record were not
198
initialized yet. But in that case,
199
prev_version should be NULL. */
203
mutex_enter(&kernel_mutex);
205
if (!trx_is_active(trx_id)) {
206
/* Transaction no longer active: no implicit x-lock */
211
/* If the transaction is still active, the previous version
212
of clust_rec must be accessible if not a fresh insert; we
213
may assert the following: */
215
ut_ad(err == DB_SUCCESS);
217
if (prev_version == NULL) {
197
218
/* It was a freshly inserted version: there is an
198
219
implicit x-lock on rec */
205
clust_offsets = rec_get_offsets(prev_version, clust_index,
206
NULL, ULINT_UNDEFINED, &heap);
208
vers_del = rec_get_deleted_flag(prev_version, comp);
209
prev_trx_id = row_get_rec_trx_id(prev_version, clust_index,
212
/* If the trx_id and prev_trx_id are different and if
213
the prev_version is marked deleted then the
214
prev_trx_id must have already committed for the trx_id
215
to be able to modify the row. Therefore, prev_trx_id
216
cannot hold any implicit lock. */
217
if (vers_del && trx_id != prev_trx_id) {
219
mutex_enter(&kernel_mutex);
223
/* The stack of versions is locked by mtr. Thus, it
224
is safe to fetch the prefixes for externally stored
226
row = row_build(ROW_COPY_POINTERS, clust_index, prev_version,
227
clust_offsets, NULL, &ext, heap);
228
entry = row_build_index_entry(row, ext, index, heap);
229
/* entry may be NULL if a record was inserted in place
230
of a deleted record, and the BLOB pointers of the new
231
record were not initialized yet. But in that case,
232
prev_version should be NULL. */
235
mutex_enter(&kernel_mutex);
237
if (!trx_is_active(trx_id)) {
238
/* Transaction no longer active: no implicit x-lock */
243
226
/* If we get here, we know that the trx_id transaction is
244
227
still active and it has modified prev_version. Let us check
245
228
if prev_version would require rec to be in a different
248
/* The previous version of clust_rec must be
249
accessible, because the transaction is still active
250
and clust_rec was not a fresh insert. */
251
ut_ad(err == DB_SUCCESS);
253
231
/* We check if entry and rec are identified in the alphabetical
255
233
if (0 == cmp_dtuple_rec(entry, rec, offsets)) {
305
/*****************************************************************//**
283
/*********************************************************************
306
284
Finds out if we must preserve a delete marked earlier version of a clustered
307
index record, because it is >= the purge view.
308
@return TRUE if earlier version should be preserved */
285
index record, because it is >= the purge view. */
311
288
row_vers_must_preserve_del_marked(
312
289
/*==============================*/
313
trx_id_t trx_id, /*!< in: transaction id in the version */
314
mtr_t* mtr) /*!< in: mtr holding the latch on the
315
clustered index record; it will also
316
hold the latch on purge_view */
290
/* out: TRUE if earlier version should be preserved */
291
dulint trx_id, /* in: transaction id in the version */
292
mtr_t* mtr) /* in: mtr holding the latch on the clustered index
293
record; it will also hold the latch on purge_view */
318
295
#ifdef UNIV_SYNC_DEBUG
319
296
ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
335
/*****************************************************************//**
312
/*********************************************************************
336
313
Finds out if a version of the record, where the version >= the current
337
314
purge view, should have ientry as its secondary index entry. We check
338
315
if there is any not delete marked version of the record where the trx
339
316
id >= purge view, and the secondary index entry and ientry are identified in
340
the alphabetical ordering; exactly in this case we return TRUE.
341
@return TRUE if earlier version should have */
317
the alphabetical ordering; exactly in this case we return TRUE. */
344
320
row_vers_old_has_index_entry(
345
321
/*=========================*/
346
ibool also_curr,/*!< in: TRUE if also rec is included in the
322
/* out: TRUE if earlier version should have */
323
ibool also_curr,/* in: TRUE if also rec is included in the
347
324
versions to search; otherwise only versions
348
325
prior to it are searched */
349
const rec_t* rec, /*!< in: record in the clustered index; the
326
const rec_t* rec, /* in: record in the clustered index; the
350
327
caller must have a latch on the page */
351
mtr_t* mtr, /*!< in: mtr holding the latch on rec; it will
328
mtr_t* mtr, /* in: mtr holding the latch on rec; it will
352
329
also hold the latch on purge_view */
353
dict_index_t* index, /*!< in: the secondary index */
354
const dtuple_t* ientry) /*!< in: the secondary index entry */
330
dict_index_t* index, /* in: the secondary index */
331
const dtuple_t* ientry) /* in: the secondary index entry */
356
333
const rec_t* version;
357
334
rec_t* prev_version;
477
/*****************************************************************//**
454
/*********************************************************************
478
455
Constructs the version of a clustered index record which a consistent
479
456
read should see. We assume that the trx id stored in rec is such that
480
the consistent read should not see rec in its present version.
481
@return DB_SUCCESS or DB_MISSING_HISTORY */
457
the consistent read should not see rec in its present version. */
484
460
row_vers_build_for_consistent_read(
485
461
/*===============================*/
486
const rec_t* rec, /*!< in: record in a clustered index; the
462
/* out: DB_SUCCESS or DB_MISSING_HISTORY */
463
const rec_t* rec, /* in: record in a clustered index; the
487
464
caller must have a latch on the page; this
488
465
latch locks the top of the stack of versions
489
466
of this records */
490
mtr_t* mtr, /*!< in: mtr holding the latch on rec */
491
dict_index_t* index, /*!< in: the clustered index */
492
ulint** offsets,/*!< in/out: offsets returned by
467
mtr_t* mtr, /* in: mtr holding the latch on rec */
468
dict_index_t* index, /* in: the clustered index */
469
ulint** offsets,/* in/out: offsets returned by
493
470
rec_get_offsets(rec, index) */
494
read_view_t* view, /*!< in: the consistent read view */
495
mem_heap_t** offset_heap,/*!< in/out: memory heap from which
471
read_view_t* view, /* in: the consistent read view */
472
mem_heap_t** offset_heap,/* in/out: memory heap from which
496
473
the offsets are allocated */
497
mem_heap_t* in_heap,/*!< in: memory heap from which the memory for
474
mem_heap_t* in_heap,/* in: memory heap from which the memory for
498
475
*old_vers is allocated; memory for possible
499
476
intermediate versions is allocated and freed
500
477
locally within the function */
501
rec_t** old_vers)/*!< out, own: old version, or NULL if the
478
rec_t** old_vers)/* out, own: old version, or NULL if the
502
479
record does not exist in the view, that is,
503
480
it was freshly inserted afterwards */
505
482
const rec_t* version;
506
483
rec_t* prev_version;
508
485
mem_heap_t* heap = NULL;
546
523
undo_no = trx_undo_rec_get_undo_no(undo_rec);
547
524
mem_heap_empty(heap);
549
if (view->undo_no > undo_no) {
526
if (ut_dulint_cmp(view->undo_no, undo_no) > 0) {
550
527
/* The view already sees this version: we can
551
528
copy it to in_heap and return */
553
buf = static_cast<byte *>(mem_heap_alloc(in_heap,
554
rec_offs_size(*offsets)));
530
buf = mem_heap_alloc(in_heap,
531
rec_offs_size(*offsets));
555
532
*old_vers = rec_copy(buf, version, *offsets);
556
533
rec_offs_make_valid(*old_vers, index,
590
567
/* The view already sees this version: we can copy
591
568
it to in_heap and return */
593
buf = static_cast<byte *>(mem_heap_alloc(in_heap, rec_offs_size(*offsets)));
570
buf = mem_heap_alloc(in_heap, rec_offs_size(*offsets));
594
571
*old_vers = rec_copy(buf, prev_version, *offsets);
595
572
rec_offs_make_valid(*old_vers, index, *offsets);
596
573
err = DB_SUCCESS;
610
/*****************************************************************//**
587
/*********************************************************************
611
588
Constructs the last committed version of a clustered index record,
612
which should be seen by a semi-consistent read.
613
@return DB_SUCCESS or DB_MISSING_HISTORY */
589
which should be seen by a semi-consistent read. */
616
592
row_vers_build_for_semi_consistent_read(
617
593
/*====================================*/
618
const rec_t* rec, /*!< in: record in a clustered index; the
594
/* out: DB_SUCCESS or DB_MISSING_HISTORY */
595
const rec_t* rec, /* in: record in a clustered index; the
619
596
caller must have a latch on the page; this
620
597
latch locks the top of the stack of versions
621
598
of this records */
622
mtr_t* mtr, /*!< in: mtr holding the latch on rec */
623
dict_index_t* index, /*!< in: the clustered index */
624
ulint** offsets,/*!< in/out: offsets returned by
599
mtr_t* mtr, /* in: mtr holding the latch on rec */
600
dict_index_t* index, /* in: the clustered index */
601
ulint** offsets,/* in/out: offsets returned by
625
602
rec_get_offsets(rec, index) */
626
mem_heap_t** offset_heap,/*!< in/out: memory heap from which
603
mem_heap_t** offset_heap,/* in/out: memory heap from which
627
604
the offsets are allocated */
628
mem_heap_t* in_heap,/*!< in: memory heap from which the memory for
605
mem_heap_t* in_heap,/* in: memory heap from which the memory for
629
606
*old_vers is allocated; memory for possible
630
607
intermediate versions is allocated and freed
631
608
locally within the function */
632
const rec_t** old_vers)/*!< out: rec, old version, or NULL if the
609
const rec_t** old_vers)/* out: rec, old version, or NULL if the
633
610
record does not exist in the view, that is,
634
611
it was freshly inserted afterwards */