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., 59 Temple
15
Place, Suite 330, Boston, MA 02111-1307 USA
17
*****************************************************************************/
19
/**************************************************//**
20
@file read/read0read.c
23
Created 2/16/1997 Heikki Tuuri
24
*******************************************************/
26
#include "read0read.h"
29
#include "read0read.ic"
36
-------------------------------------------------------------------------------
37
FACT A: Cursor read view on a secondary index sees only committed versions
39
of the records in the secondary index or those versions of rows created
40
by transaction which created a cursor before cursor was created even
41
if transaction which created the cursor has changed that clustered index page.
43
PROOF: We must show that read goes always to the clustered index record
44
to see that record is visible in the cursor read view. Consider e.g.
45
following table and SQL-clauses:
47
create table t1(a int not null, b int, primary key(a), index(b));
48
insert into t1 values (1,1),(2,2);
51
Now consider that we have a cursor for a query
53
select b from t1 where b >= 1;
55
This query will use secondary key on the table t1. Now after the first fetch
56
on this cursor if we do a update:
58
update t1 set b = 5 where b = 2;
60
Now second fetch of the cursor should not see record (2,5) instead it should
63
We also should show that if we have delete t1 where b = 5; we still
66
When we access a secondary key record maximum transaction id is fetched
67
from this record and this trx_id is compared to up_limit_id in the view.
68
If trx_id in the record is greater or equal than up_limit_id in the view
69
cluster record is accessed. Because trx_id of the creating
70
transaction is stored when this view was created to the list of
71
trx_ids not seen by this read view previous version of the
72
record is requested to be built. This is build using clustered record.
73
If the secondary key record is delete marked it's corresponding
74
clustered record can be already be purged only if records
75
trx_id < low_limit_no. Purge can't remove any record deleted by a
76
transaction which was active when cursor was created. But, we still
77
may have a deleted secondary key record but no clustered record. But,
78
this is not a problem because this case is handled in
79
row_sel_get_clust_rec() function which is called
80
whenever we note that this read view does not see trx_id in the
81
record. Thus, we see correct version. Q. E. D.
83
-------------------------------------------------------------------------------
84
FACT B: Cursor read view on a clustered index sees only committed versions
86
of the records in the clustered index or those versions of rows created
87
by transaction which created a cursor before cursor was created even
88
if transaction which created the cursor has changed that clustered index page.
90
PROOF: Consider e.g.following table and SQL-clauses:
92
create table t1(a int not null, b int, primary key(a));
93
insert into t1 values (1),(2);
96
Now consider that we have a cursor for a query
98
select a from t1 where a >= 1;
100
This query will use clustered key on the table t1. Now after the first fetch
101
on this cursor if we do a update:
103
update t1 set a = 5 where a = 2;
105
Now second fetch of the cursor should not see record (5) instead it should
108
We also should show that if we have execute delete t1 where a = 5; after
109
the cursor is opened we still can see record (2).
111
When accessing clustered record we always check if this read view sees
112
trx_id stored to clustered record. By default we don't see any changes
113
if record trx_id >= low_limit_id i.e. change was made transaction
114
which started after transaction which created the cursor. If row
115
was changed by the future transaction a previous version of the
116
clustered record is created. Thus we see only committed version in
117
this case. We see all changes made by committed transactions i.e.
118
record trx_id < up_limit_id. In this case we don't need to do anything,
119
we already see correct version of the record. We don't see any changes
120
made by active transaction except creating transaction. We have stored
121
trx_id of creating transaction to list of trx_ids when this view was
122
created. Thus we can easily see if this record was changed by the
123
creating transaction. Because we already have clustered record we can
124
access roll_ptr. Using this roll_ptr we can fetch undo record.
125
We can now check that undo_no of the undo record is less than undo_no of the
126
trancaction which created a view when cursor was created. We see this
127
clustered record only in case when record undo_no is less than undo_no
128
in the view. If this is not true we build based on undo_rec previous
129
version of the record. This record is found because purge can't remove
130
records accessed by active transaction. Thus we see correct version. Q. E. D.
131
-------------------------------------------------------------------------------
132
FACT C: Purge does not remove any delete marked row that is visible
140
/*********************************************************************//**
141
Creates a read view object.
142
@return own: read view struct */
145
read_view_create_low(
146
/*=================*/
147
ulint n, /*!< in: number of cells in the trx_ids array */
148
mem_heap_t* heap) /*!< in: memory heap from which allocated */
152
view = mem_heap_alloc(heap, sizeof(read_view_t));
155
view->trx_ids = mem_heap_alloc(heap, n * sizeof *view->trx_ids);
160
/*********************************************************************//**
161
Makes a copy of the oldest existing read view, with the exception that also
162
the creating trx of the oldest view is set as not visible in the 'copied'
163
view. Opens a new view if no views currently exist. The view must be closed
164
with ..._close. This is used in purge.
165
@return own: read view struct */
168
read_view_oldest_copy_or_open_new(
169
/*==============================*/
170
trx_id_t cr_trx_id, /*!< in: trx_id of creating
171
transaction, or ut_dulint_zero
173
mem_heap_t* heap) /*!< in: memory heap from which
176
read_view_t* old_view;
177
read_view_t* view_copy;
178
ibool needs_insert = TRUE;
179
ulint insert_done = 0;
183
ut_ad(mutex_own(&kernel_mutex));
185
old_view = UT_LIST_GET_LAST(trx_sys->view_list);
187
if (old_view == NULL) {
189
return(read_view_open_now(cr_trx_id, heap));
192
n = old_view->n_trx_ids;
194
if (!ut_dulint_is_zero(old_view->creator_trx_id)) {
197
needs_insert = FALSE;
200
view_copy = read_view_create_low(n, heap);
202
/* Insert the id of the creator in the right place of the descending
203
array of ids, if needs_insert is TRUE: */
208
&& (i >= old_view->n_trx_ids
209
|| ut_dulint_cmp(old_view->creator_trx_id,
210
read_view_get_nth_trx_id(old_view, i))
213
read_view_set_nth_trx_id(view_copy, i,
214
old_view->creator_trx_id);
215
needs_insert = FALSE;
218
read_view_set_nth_trx_id(view_copy, i,
219
read_view_get_nth_trx_id(
227
view_copy->creator_trx_id = cr_trx_id;
229
view_copy->low_limit_no = old_view->low_limit_no;
230
view_copy->low_limit_id = old_view->low_limit_id;
234
/* The last active transaction has the smallest id: */
235
view_copy->up_limit_id = read_view_get_nth_trx_id(
238
view_copy->up_limit_id = old_view->up_limit_id;
241
UT_LIST_ADD_LAST(view_list, trx_sys->view_list, view_copy);
246
/*********************************************************************//**
247
Opens a read view where exactly the transactions serialized before this
248
point in time are seen in the view.
249
@return own: read view struct */
254
trx_id_t cr_trx_id, /*!< in: trx_id of creating
255
transaction, or ut_dulint_zero
257
mem_heap_t* heap) /*!< in: memory heap from which
264
ut_ad(mutex_own(&kernel_mutex));
266
view = read_view_create_low(UT_LIST_GET_LEN(trx_sys->trx_list), heap);
268
view->creator_trx_id = cr_trx_id;
269
view->type = VIEW_NORMAL;
270
view->undo_no = ut_dulint_zero;
272
/* No future transactions should be visible in the view */
274
view->low_limit_no = trx_sys->max_trx_id;
275
view->low_limit_id = view->low_limit_no;
278
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
280
/* No active transaction should be visible, except cr_trx */
283
if (ut_dulint_cmp(trx->id, cr_trx_id) != 0
284
&& (trx->conc_state == TRX_ACTIVE
285
|| trx->conc_state == TRX_PREPARED)) {
287
read_view_set_nth_trx_id(view, n, trx->id);
291
/* NOTE that a transaction whose trx number is <
292
trx_sys->max_trx_id can still be active, if it is
293
in the middle of its commit! Note that when a
294
transaction starts, we initialize trx->no to
297
if (ut_dulint_cmp(view->low_limit_no, trx->no) > 0) {
299
view->low_limit_no = trx->no;
303
trx = UT_LIST_GET_NEXT(trx_list, trx);
309
/* The last active transaction has the smallest id: */
310
view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
312
view->up_limit_id = view->low_limit_id;
316
UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
321
/*********************************************************************//**
322
Closes a read view. */
327
read_view_t* view) /*!< in: read view */
329
ut_ad(mutex_own(&kernel_mutex));
331
UT_LIST_REMOVE(view_list, trx_sys->view_list, view);
334
/*********************************************************************//**
335
Closes a consistent read view for MySQL. This function is called at an SQL
336
statement end if the trx isolation level is <= TRX_ISO_READ_COMMITTED. */
339
read_view_close_for_mysql(
340
/*======================*/
341
trx_t* trx) /*!< in: trx which has a read view */
343
ut_a(trx->global_read_view);
345
mutex_enter(&kernel_mutex);
347
read_view_close(trx->global_read_view);
349
mem_heap_empty(trx->global_read_view_heap);
351
trx->read_view = NULL;
352
trx->global_read_view = NULL;
354
mutex_exit(&kernel_mutex);
357
/*********************************************************************//**
358
Prints a read view to stderr. */
363
const read_view_t* view) /*!< in: read view */
368
if (view->type == VIEW_HIGH_GRANULARITY) {
370
"High-granularity read view undo_n:o %lu %lu\n",
371
(ulong) ut_dulint_get_high(view->undo_no),
372
(ulong) ut_dulint_get_low(view->undo_no));
374
fprintf(stderr, "Normal read view\n");
377
fprintf(stderr, "Read view low limit trx n:o %lu %lu\n",
378
(ulong) ut_dulint_get_high(view->low_limit_no),
379
(ulong) ut_dulint_get_low(view->low_limit_no));
381
fprintf(stderr, "Read view up limit trx id " TRX_ID_FMT "\n",
382
TRX_ID_PREP_PRINTF(view->up_limit_id));
384
fprintf(stderr, "Read view low limit trx id " TRX_ID_FMT "\n",
385
TRX_ID_PREP_PRINTF(view->low_limit_id));
387
fprintf(stderr, "Read view individually stored trx ids:\n");
389
n_ids = view->n_trx_ids;
391
for (i = 0; i < n_ids; i++) {
392
fprintf(stderr, "Read view trx id " TRX_ID_FMT "\n",
394
read_view_get_nth_trx_id(view, i)));
398
/*********************************************************************//**
399
Create a high-granularity consistent cursor view for mysql to be used
400
in cursors. In this consistent read view modifications done by the
401
creating transaction after the cursor is created or future transactions
405
read_cursor_view_create_for_mysql(
406
/*==============================*/
407
trx_t* cr_trx) /*!< in: trx where cursor view is created */
409
cursor_view_t* curview;
417
/* Use larger heap than in trx_create when creating a read_view
418
because cursors are quite long. */
420
heap = mem_heap_create(512);
422
curview = (cursor_view_t*) mem_heap_alloc(heap, sizeof(cursor_view_t));
423
curview->heap = heap;
425
mutex_enter(&kernel_mutex);
427
curview->read_view = read_view_create_low(
428
UT_LIST_GET_LEN(trx_sys->trx_list), curview->heap);
430
view = curview->read_view;
431
view->creator_trx_id = cr_trx->id;
432
view->type = VIEW_HIGH_GRANULARITY;
433
view->undo_no = cr_trx->undo_no;
435
/* No future transactions should be visible in the view */
437
view->low_limit_no = trx_sys->max_trx_id;
438
view->low_limit_id = view->low_limit_no;
441
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
443
/* No active transaction should be visible */
447
if (trx->conc_state == TRX_ACTIVE
448
|| trx->conc_state == TRX_PREPARED) {
450
read_view_set_nth_trx_id(view, n, trx->id);
454
/* NOTE that a transaction whose trx number is <
455
trx_sys->max_trx_id can still be active, if it is
456
in the middle of its commit! Note that when a
457
transaction starts, we initialize trx->no to
460
if (ut_dulint_cmp(view->low_limit_no, trx->no) > 0) {
462
view->low_limit_no = trx->no;
466
trx = UT_LIST_GET_NEXT(trx_list, trx);
472
/* The last active transaction has the smallest id: */
473
view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
475
view->up_limit_id = view->low_limit_id;
478
UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
480
mutex_exit(&kernel_mutex);
485
/*********************************************************************//**
486
Close a given consistent cursor view for mysql and restore global read view
487
back to a transaction read view. */
490
read_cursor_view_close_for_mysql(
491
/*=============================*/
492
trx_t* trx, /*!< in: trx */
493
cursor_view_t* curview)/*!< in: cursor view to be closed */
496
ut_a(curview->read_view);
499
/* Add cursor's tables to the global count of active tables that
500
belong to this transaction */
502
mutex_enter(&kernel_mutex);
504
read_view_close(curview->read_view);
505
trx->read_view = trx->global_read_view;
507
mutex_exit(&kernel_mutex);
509
mem_heap_free(curview->heap);
512
/*********************************************************************//**
513
This function sets a given consistent cursor view to a transaction
514
read view if given consistent cursor view is not NULL. Otherwise, function
515
restores a global read view to a transaction read view. */
518
read_cursor_set_for_mysql(
519
/*======================*/
520
trx_t* trx, /*!< in: transaction where cursor is set */
521
cursor_view_t* curview)/*!< in: consistent cursor view to be set */
525
mutex_enter(&kernel_mutex);
527
if (UNIV_LIKELY(curview != NULL)) {
528
trx->read_view = curview->read_view;
530
trx->read_view = trx->global_read_view;
533
mutex_exit(&kernel_mutex);