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
/**************************************************//**
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 0 used in purge */
172
mem_heap_t* heap) /*!< in: memory heap from which
175
read_view_t* old_view;
176
read_view_t* view_copy;
177
ibool needs_insert = TRUE;
178
ulint insert_done = 0;
182
ut_ad(mutex_own(&kernel_mutex));
184
old_view = UT_LIST_GET_LAST(trx_sys->view_list);
186
if (old_view == NULL) {
188
return(read_view_open_now(cr_trx_id, heap));
191
n = old_view->n_trx_ids;
193
if (old_view->creator_trx_id) {
196
needs_insert = FALSE;
199
view_copy = read_view_create_low(n, heap);
201
/* Insert the id of the creator in the right place of the descending
202
array of ids, if needs_insert is TRUE: */
207
&& (i >= old_view->n_trx_ids
208
|| old_view->creator_trx_id
209
> read_view_get_nth_trx_id(old_view, i))) {
211
read_view_set_nth_trx_id(view_copy, i,
212
old_view->creator_trx_id);
213
needs_insert = FALSE;
216
read_view_set_nth_trx_id(view_copy, i,
217
read_view_get_nth_trx_id(
225
view_copy->creator_trx_id = cr_trx_id;
227
view_copy->low_limit_no = old_view->low_limit_no;
228
view_copy->low_limit_id = old_view->low_limit_id;
232
/* The last active transaction has the smallest id: */
233
view_copy->up_limit_id = read_view_get_nth_trx_id(
236
view_copy->up_limit_id = old_view->up_limit_id;
239
UT_LIST_ADD_LAST(view_list, trx_sys->view_list, view_copy);
244
/*********************************************************************//**
245
Opens a read view where exactly the transactions serialized before this
246
point in time are seen in the view.
247
@return own: read view struct */
252
trx_id_t cr_trx_id, /*!< in: trx_id of creating
253
transaction, or 0 used in purge */
254
mem_heap_t* heap) /*!< in: memory heap from which
261
ut_ad(mutex_own(&kernel_mutex));
263
view = read_view_create_low(UT_LIST_GET_LEN(trx_sys->trx_list), heap);
265
view->creator_trx_id = cr_trx_id;
266
view->type = VIEW_NORMAL;
269
/* No future transactions should be visible in the view */
271
view->low_limit_no = trx_sys->max_trx_id;
272
view->low_limit_id = view->low_limit_no;
275
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
277
/* No active transaction should be visible, except cr_trx */
280
if (trx->id != cr_trx_id
281
&& (trx->conc_state == TRX_ACTIVE
282
|| trx->conc_state == TRX_PREPARED)) {
284
read_view_set_nth_trx_id(view, n, trx->id);
288
/* NOTE that a transaction whose trx number is <
289
trx_sys->max_trx_id can still be active, if it is
290
in the middle of its commit! Note that when a
291
transaction starts, we initialize trx->no to
294
if (view->low_limit_no > trx->no) {
296
view->low_limit_no = trx->no;
300
trx = UT_LIST_GET_NEXT(trx_list, trx);
306
/* The last active transaction has the smallest id: */
307
view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
309
view->up_limit_id = view->low_limit_id;
313
UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
318
/*********************************************************************//**
319
Closes a read view. */
324
read_view_t* view) /*!< in: read view */
326
ut_ad(mutex_own(&kernel_mutex));
328
UT_LIST_REMOVE(view_list, trx_sys->view_list, view);
331
/*********************************************************************//**
332
Closes a consistent read view for MySQL. This function is called at an SQL
333
statement end if the trx isolation level is <= TRX_ISO_READ_COMMITTED. */
336
read_view_close_for_mysql(
337
/*======================*/
338
trx_t* trx) /*!< in: trx which has a read view */
340
ut_a(trx->global_read_view);
342
mutex_enter(&kernel_mutex);
344
read_view_close(trx->global_read_view);
346
mem_heap_empty(trx->global_read_view_heap);
348
trx->read_view = NULL;
349
trx->global_read_view = NULL;
351
mutex_exit(&kernel_mutex);
354
/*********************************************************************//**
355
Prints a read view to stderr. */
360
const read_view_t* view) /*!< in: read view */
365
if (view->type == VIEW_HIGH_GRANULARITY) {
367
"High-granularity read view undo_n:o %llu\n",
368
(ullint) view->undo_no);
370
fprintf(stderr, "Normal read view\n");
373
fprintf(stderr, "Read view low limit trx n:o " TRX_ID_FMT "\n",
376
fprintf(stderr, "Read view up limit trx id " TRX_ID_FMT "\n",
379
fprintf(stderr, "Read view low limit trx id " TRX_ID_FMT "\n",
382
fprintf(stderr, "Read view individually stored trx ids:\n");
384
n_ids = view->n_trx_ids;
386
for (i = 0; i < n_ids; i++) {
387
fprintf(stderr, "Read view trx id " TRX_ID_FMT "\n",
388
read_view_get_nth_trx_id(view, i));
392
/*********************************************************************//**
393
Create a high-granularity consistent cursor view for mysql to be used
394
in cursors. In this consistent read view modifications done by the
395
creating transaction after the cursor is created or future transactions
399
read_cursor_view_create_for_mysql(
400
/*==============================*/
401
trx_t* cr_trx) /*!< in: trx where cursor view is created */
403
cursor_view_t* curview;
411
/* Use larger heap than in trx_create when creating a read_view
412
because cursors are quite long. */
414
heap = mem_heap_create(512);
416
curview = (cursor_view_t*) mem_heap_alloc(heap, sizeof(cursor_view_t));
417
curview->heap = heap;
419
mutex_enter(&kernel_mutex);
421
curview->read_view = read_view_create_low(
422
UT_LIST_GET_LEN(trx_sys->trx_list), curview->heap);
424
view = curview->read_view;
425
view->creator_trx_id = cr_trx->id;
426
view->type = VIEW_HIGH_GRANULARITY;
427
view->undo_no = cr_trx->undo_no;
429
/* No future transactions should be visible in the view */
431
view->low_limit_no = trx_sys->max_trx_id;
432
view->low_limit_id = view->low_limit_no;
435
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
437
/* No active transaction should be visible */
441
if (trx->conc_state == TRX_ACTIVE
442
|| trx->conc_state == TRX_PREPARED) {
444
read_view_set_nth_trx_id(view, n, trx->id);
448
/* NOTE that a transaction whose trx number is <
449
trx_sys->max_trx_id can still be active, if it is
450
in the middle of its commit! Note that when a
451
transaction starts, we initialize trx->no to
454
if (view->low_limit_no > trx->no) {
456
view->low_limit_no = trx->no;
460
trx = UT_LIST_GET_NEXT(trx_list, trx);
466
/* The last active transaction has the smallest id: */
467
view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
469
view->up_limit_id = view->low_limit_id;
472
UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
474
mutex_exit(&kernel_mutex);
479
/*********************************************************************//**
480
Close a given consistent cursor view for mysql and restore global read view
481
back to a transaction read view. */
484
read_cursor_view_close_for_mysql(
485
/*=============================*/
486
trx_t* trx, /*!< in: trx */
487
cursor_view_t* curview)/*!< in: cursor view to be closed */
490
ut_a(curview->read_view);
493
/* Add cursor's tables to the global count of active tables that
494
belong to this transaction */
496
mutex_enter(&kernel_mutex);
498
read_view_close(curview->read_view);
499
trx->read_view = trx->global_read_view;
501
mutex_exit(&kernel_mutex);
503
mem_heap_free(curview->heap);
506
/*********************************************************************//**
507
This function sets a given consistent cursor view to a transaction
508
read view if given consistent cursor view is not NULL. Otherwise, function
509
restores a global read view to a transaction read view. */
512
read_cursor_set_for_mysql(
513
/*======================*/
514
trx_t* trx, /*!< in: transaction where cursor is set */
515
cursor_view_t* curview)/*!< in: consistent cursor view to be set */
519
mutex_enter(&kernel_mutex);
521
if (UNIV_LIKELY(curview != NULL)) {
522
trx->read_view = curview->read_view;
524
trx->read_view = trx->global_read_view;
527
mutex_exit(&kernel_mutex);