~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

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., 51 Franklin
15
 
St, Fifth Floor, Boston, MA 02110-1301 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 0 used in purge */
172
 
        mem_heap_t*     heap)           /*!< in: memory heap from which
173
 
                                        allocated */
174
 
{
175
 
        read_view_t*    old_view;
176
 
        read_view_t*    view_copy;
177
 
        ibool           needs_insert    = TRUE;
178
 
        ulint           insert_done     = 0;
179
 
        ulint           n;
180
 
        ulint           i;
181
 
 
182
 
        ut_ad(mutex_own(&kernel_mutex));
183
 
 
184
 
        old_view = UT_LIST_GET_LAST(trx_sys->view_list);
185
 
 
186
 
        if (old_view == NULL) {
187
 
 
188
 
                return(read_view_open_now(cr_trx_id, heap));
189
 
        }
190
 
 
191
 
        n = old_view->n_trx_ids;
192
 
 
193
 
        if (old_view->creator_trx_id) {
194
 
                n++;
195
 
        } else {
196
 
                needs_insert = FALSE;
197
 
        }
198
 
 
199
 
        view_copy = read_view_create_low(n, heap);
200
 
 
201
 
        /* Insert the id of the creator in the right place of the descending
202
 
        array of ids, if needs_insert is TRUE: */
203
 
 
204
 
        i = 0;
205
 
        while (i < n) {
206
 
                if (needs_insert
207
 
                    && (i >= old_view->n_trx_ids
208
 
                        || old_view->creator_trx_id
209
 
                        > read_view_get_nth_trx_id(old_view, i))) {
210
 
 
211
 
                        read_view_set_nth_trx_id(view_copy, i,
212
 
                                                 old_view->creator_trx_id);
213
 
                        needs_insert = FALSE;
214
 
                        insert_done = 1;
215
 
                } else {
216
 
                        read_view_set_nth_trx_id(view_copy, i,
217
 
                                                 read_view_get_nth_trx_id(
218
 
                                                         old_view,
219
 
                                                         i - insert_done));
220
 
                }
221
 
 
222
 
                i++;
223
 
        }
224
 
 
225
 
        view_copy->creator_trx_id = cr_trx_id;
226
 
 
227
 
        view_copy->low_limit_no = old_view->low_limit_no;
228
 
        view_copy->low_limit_id = old_view->low_limit_id;
229
 
 
230
 
 
231
 
        if (n > 0) {
232
 
                /* The last active transaction has the smallest id: */
233
 
                view_copy->up_limit_id = read_view_get_nth_trx_id(
234
 
                        view_copy, n - 1);
235
 
        } else {
236
 
                view_copy->up_limit_id = old_view->up_limit_id;
237
 
        }
238
 
 
239
 
        UT_LIST_ADD_LAST(view_list, trx_sys->view_list, view_copy);
240
 
 
241
 
        return(view_copy);
242
 
}
243
 
 
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 */
248
 
UNIV_INTERN
249
 
read_view_t*
250
 
read_view_open_now(
251
 
/*===============*/
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
255
 
                                        allocated */
256
 
{
257
 
        read_view_t*    view;
258
 
        trx_t*          trx;
259
 
        ulint           n;
260
 
 
261
 
        ut_ad(mutex_own(&kernel_mutex));
262
 
 
263
 
        view = read_view_create_low(UT_LIST_GET_LEN(trx_sys->trx_list), heap);
264
 
 
265
 
        view->creator_trx_id = cr_trx_id;
266
 
        view->type = VIEW_NORMAL;
267
 
        view->undo_no = 0;
268
 
 
269
 
        /* No future transactions should be visible in the view */
270
 
 
271
 
        view->low_limit_no = trx_sys->max_trx_id;
272
 
        view->low_limit_id = view->low_limit_no;
273
 
 
274
 
        n = 0;
275
 
        trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
276
 
 
277
 
        /* No active transaction should be visible, except cr_trx */
278
 
 
279
 
        while (trx) {
280
 
                if (trx->id != cr_trx_id
281
 
                    && (trx->conc_state == TRX_ACTIVE
282
 
                        || trx->conc_state == TRX_PREPARED)) {
283
 
 
284
 
                        read_view_set_nth_trx_id(view, n, trx->id);
285
 
 
286
 
                        n++;
287
 
 
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
292
 
                        IB_ULONGLONG_MAX. */
293
 
 
294
 
                        if (view->low_limit_no > trx->no) {
295
 
 
296
 
                                view->low_limit_no = trx->no;
297
 
                        }
298
 
                }
299
 
 
300
 
                trx = UT_LIST_GET_NEXT(trx_list, trx);
301
 
        }
302
 
 
303
 
        view->n_trx_ids = n;
304
 
 
305
 
        if (n > 0) {
306
 
                /* The last active transaction has the smallest id: */
307
 
                view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
308
 
        } else {
309
 
                view->up_limit_id = view->low_limit_id;
310
 
        }
311
 
 
312
 
 
313
 
        UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
314
 
 
315
 
        return(view);
316
 
}
317
 
 
318
 
/*********************************************************************//**
319
 
Closes a read view. */
320
 
UNIV_INTERN
321
 
void
322
 
read_view_close(
323
 
/*============*/
324
 
        read_view_t*    view)   /*!< in: read view */
325
 
{
326
 
        ut_ad(mutex_own(&kernel_mutex));
327
 
 
328
 
        UT_LIST_REMOVE(view_list, trx_sys->view_list, view);
329
 
}
330
 
 
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. */
334
 
UNIV_INTERN
335
 
void
336
 
read_view_close_for_mysql(
337
 
/*======================*/
338
 
        trx_t*  trx)    /*!< in: trx which has a read view */
339
 
{
340
 
        ut_a(trx->global_read_view);
341
 
 
342
 
        mutex_enter(&kernel_mutex);
343
 
 
344
 
        read_view_close(trx->global_read_view);
345
 
 
346
 
        mem_heap_empty(trx->global_read_view_heap);
347
 
 
348
 
        trx->read_view = NULL;
349
 
        trx->global_read_view = NULL;
350
 
 
351
 
        mutex_exit(&kernel_mutex);
352
 
}
353
 
 
354
 
/*********************************************************************//**
355
 
Prints a read view to stderr. */
356
 
UNIV_INTERN
357
 
void
358
 
read_view_print(
359
 
/*============*/
360
 
        const read_view_t*      view)   /*!< in: read view */
361
 
{
362
 
        ulint   n_ids;
363
 
        ulint   i;
364
 
 
365
 
        if (view->type == VIEW_HIGH_GRANULARITY) {
366
 
                fprintf(stderr,
367
 
                        "High-granularity read view undo_n:o %llu\n",
368
 
                        (ullint) view->undo_no);
369
 
        } else {
370
 
                fprintf(stderr, "Normal read view\n");
371
 
        }
372
 
 
373
 
        fprintf(stderr, "Read view low limit trx n:o " TRX_ID_FMT "\n",
374
 
                view->low_limit_no);
375
 
 
376
 
        fprintf(stderr, "Read view up limit trx id " TRX_ID_FMT "\n",
377
 
                view->up_limit_id);
378
 
 
379
 
        fprintf(stderr, "Read view low limit trx id " TRX_ID_FMT "\n",
380
 
                view->low_limit_id);
381
 
 
382
 
        fprintf(stderr, "Read view individually stored trx ids:\n");
383
 
 
384
 
        n_ids = view->n_trx_ids;
385
 
 
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));
389
 
        }
390
 
}
391
 
 
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
396
 
are not visible. */
397
 
UNIV_INTERN
398
 
cursor_view_t*
399
 
read_cursor_view_create_for_mysql(
400
 
/*==============================*/
401
 
        trx_t*  cr_trx) /*!< in: trx where cursor view is created */
402
 
{
403
 
        cursor_view_t*  curview;
404
 
        read_view_t*    view;
405
 
        mem_heap_t*     heap;
406
 
        trx_t*          trx;
407
 
        ulint           n;
408
 
 
409
 
        ut_a(cr_trx);
410
 
 
411
 
        /* Use larger heap than in trx_create when creating a read_view
412
 
        because cursors are quite long. */
413
 
 
414
 
        heap = mem_heap_create(512);
415
 
 
416
 
        curview = (cursor_view_t*) mem_heap_alloc(heap, sizeof(cursor_view_t));
417
 
        curview->heap = heap;
418
 
 
419
 
        mutex_enter(&kernel_mutex);
420
 
 
421
 
        curview->read_view = read_view_create_low(
422
 
                UT_LIST_GET_LEN(trx_sys->trx_list), curview->heap);
423
 
 
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;
428
 
 
429
 
        /* No future transactions should be visible in the view */
430
 
 
431
 
        view->low_limit_no = trx_sys->max_trx_id;
432
 
        view->low_limit_id = view->low_limit_no;
433
 
 
434
 
        n = 0;
435
 
        trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
436
 
 
437
 
        /* No active transaction should be visible */
438
 
 
439
 
        while (trx) {
440
 
 
441
 
                if (trx->conc_state == TRX_ACTIVE
442
 
                    || trx->conc_state == TRX_PREPARED) {
443
 
 
444
 
                        read_view_set_nth_trx_id(view, n, trx->id);
445
 
 
446
 
                        n++;
447
 
 
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
452
 
                        IB_ULONGLONG_MAX. */
453
 
 
454
 
                        if (view->low_limit_no > trx->no) {
455
 
 
456
 
                                view->low_limit_no = trx->no;
457
 
                        }
458
 
                }
459
 
 
460
 
                trx = UT_LIST_GET_NEXT(trx_list, trx);
461
 
        }
462
 
 
463
 
        view->n_trx_ids = n;
464
 
 
465
 
        if (n > 0) {
466
 
                /* The last active transaction has the smallest id: */
467
 
                view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
468
 
        } else {
469
 
                view->up_limit_id = view->low_limit_id;
470
 
        }
471
 
 
472
 
        UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
473
 
 
474
 
        mutex_exit(&kernel_mutex);
475
 
 
476
 
        return(curview);
477
 
}
478
 
 
479
 
/*********************************************************************//**
480
 
Close a given consistent cursor view for mysql and restore global read view
481
 
back to a transaction read view. */
482
 
UNIV_INTERN
483
 
void
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 */
488
 
{
489
 
        ut_a(curview);
490
 
        ut_a(curview->read_view);
491
 
        ut_a(curview->heap);
492
 
 
493
 
        /* Add cursor's tables to the global count of active tables that
494
 
        belong to this transaction */
495
 
 
496
 
        mutex_enter(&kernel_mutex);
497
 
 
498
 
        read_view_close(curview->read_view);
499
 
        trx->read_view = trx->global_read_view;
500
 
 
501
 
        mutex_exit(&kernel_mutex);
502
 
 
503
 
        mem_heap_free(curview->heap);
504
 
}
505
 
 
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. */
510
 
UNIV_INTERN
511
 
void
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 */
516
 
{
517
 
        ut_a(trx);
518
 
 
519
 
        mutex_enter(&kernel_mutex);
520
 
 
521
 
        if (UNIV_LIKELY(curview != NULL)) {
522
 
                trx->read_view = curview->read_view;
523
 
        } else {
524
 
                trx->read_view = trx->global_read_view;
525
 
        }
526
 
 
527
 
        mutex_exit(&kernel_mutex);
528
 
}