~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/innobase/read/read0read.c

  • Committer: Padraig O'Sullivan
  • Date: 2009-09-13 01:03:01 UTC
  • mto: (1126.9.2 captain-20090915-01)
  • mto: This revision was merged to the branch mainline in revision 1133.
  • Revision ID: osullivan.padraig@gmail.com-20090913010301-tcvvezipx1124acy
Added calls to the dtrace delete begin/end probes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
14
 
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15
 
Place, Suite 330, Boston, MA 02111-1307 USA
16
 
 
17
 
*****************************************************************************/
18
 
 
19
 
/**************************************************//**
20
 
@file read/read0read.c
21
 
Cursor read
22
 
 
23
 
Created 2/16/1997 Heikki Tuuri
24
 
*******************************************************/
25
 
 
26
 
#include "read0read.h"
27
 
 
28
 
#ifdef UNIV_NONINL
29
 
#include "read0read.ic"
30
 
#endif
31
 
 
32
 
#include "srv0srv.h"
33
 
#include "trx0sys.h"
34
 
 
35
 
/*
36
 
-------------------------------------------------------------------------------
37
 
FACT A: Cursor read view on a secondary index sees only committed versions
38
 
-------
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.
42
 
 
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:
46
 
 
47
 
create table t1(a int not null, b int, primary key(a), index(b));
48
 
insert into t1 values (1,1),(2,2);
49
 
commit;
50
 
 
51
 
Now consider that we have a cursor for a query
52
 
 
53
 
select b from t1 where b >= 1;
54
 
 
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:
57
 
 
58
 
update t1 set b = 5 where b = 2;
59
 
 
60
 
Now second fetch of the cursor should not see record (2,5) instead it should
61
 
see record (2,2).
62
 
 
63
 
We also should show that if we have delete t1 where b = 5; we still
64
 
can see record (2,2).
65
 
 
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.
82
 
 
83
 
-------------------------------------------------------------------------------
84
 
FACT B: Cursor read view on a clustered index sees only committed versions
85
 
-------
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.
89
 
 
90
 
PROOF:  Consider e.g.following table and SQL-clauses:
91
 
 
92
 
create table t1(a int not null, b int, primary key(a));
93
 
insert into t1 values (1),(2);
94
 
commit;
95
 
 
96
 
Now consider that we have a cursor for a query
97
 
 
98
 
select a from t1 where a >= 1;
99
 
 
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:
102
 
 
103
 
update t1 set a = 5 where a = 2;
104
 
 
105
 
Now second fetch of the cursor should not see record (5) instead it should
106
 
see record (2).
107
 
 
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).
110
 
 
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
133
 
-------
134
 
to cursor view.
135
 
 
136
 
TODO: proof this
137
 
 
138
 
*/
139
 
 
140
 
/*********************************************************************//**
141
 
Creates a read view object.
142
 
@return own: read view struct */
143
 
UNIV_INLINE
144
 
read_view_t*
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 */
149
 
{
150
 
        read_view_t*    view;
151
 
 
152
 
        view = mem_heap_alloc(heap, sizeof(read_view_t));
153
 
 
154
 
        view->n_trx_ids = n;
155
 
        view->trx_ids = mem_heap_alloc(heap, n * sizeof *view->trx_ids);
156
 
 
157
 
        return(view);
158
 
}
159
 
 
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 */
166
 
UNIV_INTERN
167
 
read_view_t*
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
172
 
                                        used in purge */
173
 
        mem_heap_t*     heap)           /*!< in: memory heap from which
174
 
                                        allocated */
175
 
{
176
 
        read_view_t*    old_view;
177
 
        read_view_t*    view_copy;
178
 
        ibool           needs_insert    = TRUE;
179
 
        ulint           insert_done     = 0;
180
 
        ulint           n;
181
 
        ulint           i;
182
 
 
183
 
        ut_ad(mutex_own(&kernel_mutex));
184
 
 
185
 
        old_view = UT_LIST_GET_LAST(trx_sys->view_list);
186
 
 
187
 
        if (old_view == NULL) {
188
 
 
189
 
                return(read_view_open_now(cr_trx_id, heap));
190
 
        }
191
 
 
192
 
        n = old_view->n_trx_ids;
193
 
 
194
 
        if (!ut_dulint_is_zero(old_view->creator_trx_id)) {
195
 
                n++;
196
 
        } else {
197
 
                needs_insert = FALSE;
198
 
        }
199
 
 
200
 
        view_copy = read_view_create_low(n, heap);
201
 
 
202
 
        /* Insert the id of the creator in the right place of the descending
203
 
        array of ids, if needs_insert is TRUE: */
204
 
 
205
 
        i = 0;
206
 
        while (i < n) {
207
 
                if (needs_insert
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))
211
 
                        > 0)) {
212
 
 
213
 
                        read_view_set_nth_trx_id(view_copy, i,
214
 
                                                 old_view->creator_trx_id);
215
 
                        needs_insert = FALSE;
216
 
                        insert_done = 1;
217
 
                } else {
218
 
                        read_view_set_nth_trx_id(view_copy, i,
219
 
                                                 read_view_get_nth_trx_id(
220
 
                                                         old_view,
221
 
                                                         i - insert_done));
222
 
                }
223
 
 
224
 
                i++;
225
 
        }
226
 
 
227
 
        view_copy->creator_trx_id = cr_trx_id;
228
 
 
229
 
        view_copy->low_limit_no = old_view->low_limit_no;
230
 
        view_copy->low_limit_id = old_view->low_limit_id;
231
 
 
232
 
 
233
 
        if (n > 0) {
234
 
                /* The last active transaction has the smallest id: */
235
 
                view_copy->up_limit_id = read_view_get_nth_trx_id(
236
 
                        view_copy, n - 1);
237
 
        } else {
238
 
                view_copy->up_limit_id = old_view->up_limit_id;
239
 
        }
240
 
 
241
 
        UT_LIST_ADD_LAST(view_list, trx_sys->view_list, view_copy);
242
 
 
243
 
        return(view_copy);
244
 
}
245
 
 
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 */
250
 
UNIV_INTERN
251
 
read_view_t*
252
 
read_view_open_now(
253
 
/*===============*/
254
 
        trx_id_t        cr_trx_id,      /*!< in: trx_id of creating
255
 
                                        transaction, or ut_dulint_zero
256
 
                                        used in purge */
257
 
        mem_heap_t*     heap)           /*!< in: memory heap from which
258
 
                                        allocated */
259
 
{
260
 
        read_view_t*    view;
261
 
        trx_t*          trx;
262
 
        ulint           n;
263
 
 
264
 
        ut_ad(mutex_own(&kernel_mutex));
265
 
 
266
 
        view = read_view_create_low(UT_LIST_GET_LEN(trx_sys->trx_list), heap);
267
 
 
268
 
        view->creator_trx_id = cr_trx_id;
269
 
        view->type = VIEW_NORMAL;
270
 
        view->undo_no = ut_dulint_zero;
271
 
 
272
 
        /* No future transactions should be visible in the view */
273
 
 
274
 
        view->low_limit_no = trx_sys->max_trx_id;
275
 
        view->low_limit_id = view->low_limit_no;
276
 
 
277
 
        n = 0;
278
 
        trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
279
 
 
280
 
        /* No active transaction should be visible, except cr_trx */
281
 
 
282
 
        while (trx) {
283
 
                if (ut_dulint_cmp(trx->id, cr_trx_id) != 0
284
 
                    && (trx->conc_state == TRX_ACTIVE
285
 
                        || trx->conc_state == TRX_PREPARED)) {
286
 
 
287
 
                        read_view_set_nth_trx_id(view, n, trx->id);
288
 
 
289
 
                        n++;
290
 
 
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
295
 
                        ut_dulint_max. */
296
 
 
297
 
                        if (ut_dulint_cmp(view->low_limit_no, trx->no) > 0) {
298
 
 
299
 
                                view->low_limit_no = trx->no;
300
 
                        }
301
 
                }
302
 
 
303
 
                trx = UT_LIST_GET_NEXT(trx_list, trx);
304
 
        }
305
 
 
306
 
        view->n_trx_ids = n;
307
 
 
308
 
        if (n > 0) {
309
 
                /* The last active transaction has the smallest id: */
310
 
                view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
311
 
        } else {
312
 
                view->up_limit_id = view->low_limit_id;
313
 
        }
314
 
 
315
 
 
316
 
        UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
317
 
 
318
 
        return(view);
319
 
}
320
 
 
321
 
/*********************************************************************//**
322
 
Closes a read view. */
323
 
UNIV_INTERN
324
 
void
325
 
read_view_close(
326
 
/*============*/
327
 
        read_view_t*    view)   /*!< in: read view */
328
 
{
329
 
        ut_ad(mutex_own(&kernel_mutex));
330
 
 
331
 
        UT_LIST_REMOVE(view_list, trx_sys->view_list, view);
332
 
}
333
 
 
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. */
337
 
UNIV_INTERN
338
 
void
339
 
read_view_close_for_mysql(
340
 
/*======================*/
341
 
        trx_t*  trx)    /*!< in: trx which has a read view */
342
 
{
343
 
        ut_a(trx->global_read_view);
344
 
 
345
 
        mutex_enter(&kernel_mutex);
346
 
 
347
 
        read_view_close(trx->global_read_view);
348
 
 
349
 
        mem_heap_empty(trx->global_read_view_heap);
350
 
 
351
 
        trx->read_view = NULL;
352
 
        trx->global_read_view = NULL;
353
 
 
354
 
        mutex_exit(&kernel_mutex);
355
 
}
356
 
 
357
 
/*********************************************************************//**
358
 
Prints a read view to stderr. */
359
 
UNIV_INTERN
360
 
void
361
 
read_view_print(
362
 
/*============*/
363
 
        const read_view_t*      view)   /*!< in: read view */
364
 
{
365
 
        ulint   n_ids;
366
 
        ulint   i;
367
 
 
368
 
        if (view->type == VIEW_HIGH_GRANULARITY) {
369
 
                fprintf(stderr,
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));
373
 
        } else {
374
 
                fprintf(stderr, "Normal read view\n");
375
 
        }
376
 
 
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));
380
 
 
381
 
        fprintf(stderr, "Read view up limit trx id " TRX_ID_FMT "\n",
382
 
                TRX_ID_PREP_PRINTF(view->up_limit_id));
383
 
 
384
 
        fprintf(stderr, "Read view low limit trx id " TRX_ID_FMT "\n",
385
 
                TRX_ID_PREP_PRINTF(view->low_limit_id));
386
 
 
387
 
        fprintf(stderr, "Read view individually stored trx ids:\n");
388
 
 
389
 
        n_ids = view->n_trx_ids;
390
 
 
391
 
        for (i = 0; i < n_ids; i++) {
392
 
                fprintf(stderr, "Read view trx id " TRX_ID_FMT "\n",
393
 
                        TRX_ID_PREP_PRINTF(
394
 
                                read_view_get_nth_trx_id(view, i)));
395
 
        }
396
 
}
397
 
 
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
402
 
are not visible. */
403
 
UNIV_INTERN
404
 
cursor_view_t*
405
 
read_cursor_view_create_for_mysql(
406
 
/*==============================*/
407
 
        trx_t*  cr_trx) /*!< in: trx where cursor view is created */
408
 
{
409
 
        cursor_view_t*  curview;
410
 
        read_view_t*    view;
411
 
        mem_heap_t*     heap;
412
 
        trx_t*          trx;
413
 
        ulint           n;
414
 
 
415
 
        ut_a(cr_trx);
416
 
 
417
 
        /* Use larger heap than in trx_create when creating a read_view
418
 
        because cursors are quite long. */
419
 
 
420
 
        heap = mem_heap_create(512);
421
 
 
422
 
        curview = (cursor_view_t*) mem_heap_alloc(heap, sizeof(cursor_view_t));
423
 
        curview->heap = heap;
424
 
 
425
 
        mutex_enter(&kernel_mutex);
426
 
 
427
 
        curview->read_view = read_view_create_low(
428
 
                UT_LIST_GET_LEN(trx_sys->trx_list), curview->heap);
429
 
 
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;
434
 
 
435
 
        /* No future transactions should be visible in the view */
436
 
 
437
 
        view->low_limit_no = trx_sys->max_trx_id;
438
 
        view->low_limit_id = view->low_limit_no;
439
 
 
440
 
        n = 0;
441
 
        trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
442
 
 
443
 
        /* No active transaction should be visible */
444
 
 
445
 
        while (trx) {
446
 
 
447
 
                if (trx->conc_state == TRX_ACTIVE
448
 
                    || trx->conc_state == TRX_PREPARED) {
449
 
 
450
 
                        read_view_set_nth_trx_id(view, n, trx->id);
451
 
 
452
 
                        n++;
453
 
 
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
458
 
                        ut_dulint_max. */
459
 
 
460
 
                        if (ut_dulint_cmp(view->low_limit_no, trx->no) > 0) {
461
 
 
462
 
                                view->low_limit_no = trx->no;
463
 
                        }
464
 
                }
465
 
 
466
 
                trx = UT_LIST_GET_NEXT(trx_list, trx);
467
 
        }
468
 
 
469
 
        view->n_trx_ids = n;
470
 
 
471
 
        if (n > 0) {
472
 
                /* The last active transaction has the smallest id: */
473
 
                view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
474
 
        } else {
475
 
                view->up_limit_id = view->low_limit_id;
476
 
        }
477
 
 
478
 
        UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
479
 
 
480
 
        mutex_exit(&kernel_mutex);
481
 
 
482
 
        return(curview);
483
 
}
484
 
 
485
 
/*********************************************************************//**
486
 
Close a given consistent cursor view for mysql and restore global read view
487
 
back to a transaction read view. */
488
 
UNIV_INTERN
489
 
void
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 */
494
 
{
495
 
        ut_a(curview);
496
 
        ut_a(curview->read_view);
497
 
        ut_a(curview->heap);
498
 
 
499
 
        /* Add cursor's tables to the global count of active tables that
500
 
        belong to this transaction */
501
 
 
502
 
        mutex_enter(&kernel_mutex);
503
 
 
504
 
        read_view_close(curview->read_view);
505
 
        trx->read_view = trx->global_read_view;
506
 
 
507
 
        mutex_exit(&kernel_mutex);
508
 
 
509
 
        mem_heap_free(curview->heap);
510
 
}
511
 
 
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. */
516
 
UNIV_INTERN
517
 
void
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 */
522
 
{
523
 
        ut_a(trx);
524
 
 
525
 
        mutex_enter(&kernel_mutex);
526
 
 
527
 
        if (UNIV_LIKELY(curview != NULL)) {
528
 
                trx->read_view = curview->read_view;
529
 
        } else {
530
 
                trx->read_view = trx->global_read_view;
531
 
        }
532
 
 
533
 
        mutex_exit(&kernel_mutex);
534
 
}