Drizzled Public API Documentation

read0read.cc
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 /**************************************************/
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 /*********************************************************************/
143 UNIV_INLINE
145 read_view_create_low(
146 /*=================*/
147  ulint n,
148  mem_heap_t* heap)
149 {
150  read_view_t* view;
151 
152  view = static_cast<read_view_t *>(mem_heap_alloc(heap, sizeof(read_view_t)));
153 
154  view->n_trx_ids = n;
155  view->trx_ids = static_cast<trx_id_t *>(mem_heap_alloc(heap, n * sizeof *view->trx_ids));
156 
157  return(view);
158 }
159 
160 /*********************************************************************/
166 UNIV_INTERN
169 /*==============================*/
170  trx_id_t cr_trx_id,
172  mem_heap_t* heap)
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 /*********************************************************************/
248 UNIV_INTERN
251 /*===============*/
252  trx_id_t cr_trx_id,
254  mem_heap_t* heap)
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 
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 /*********************************************************************/
320 UNIV_INTERN
321 void
323 /*============*/
324  read_view_t* view)
325 {
326  ut_ad(mutex_own(&kernel_mutex));
327 
328  UT_LIST_REMOVE(view_list, trx_sys->view_list, view);
329 }
330 
331 /*********************************************************************/
334 UNIV_INTERN
335 void
337 /*======================*/
338  trx_t* trx)
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 /*********************************************************************/
356 UNIV_INTERN
357 void
359 /*============*/
360  const read_view_t* 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 /*********************************************************************/
397 UNIV_INTERN
400 /*==============================*/
401  trx_t* cr_trx)
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 
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 /*********************************************************************/
482 UNIV_INTERN
483 void
485 /*=============================*/
486  trx_t* trx,
487  cursor_view_t* curview)
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 /*********************************************************************/
510 UNIV_INTERN
511 void
513 /*======================*/
514  trx_t* trx,
515  cursor_view_t* curview)
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 }
#define UT_LIST_GET_LEN(BASE)
Definition: ut0lst.h:217
trx_sys_t * trx_sys
Definition: trx0sys.cc:61
#define UT_LIST_GET_NEXT(NAME, N)
Definition: ut0lst.h:201
trx_id_t max_trx_id
Definition: trx0sys.h:579
trx_id_t id
Definition: trx0trx.h:548
mem_heap_t * heap
Definition: read0read.h:187
UNIV_INTERN void read_view_close(read_view_t *view)
Definition: read0read.cc:322
#define TRX_ID_FMT
Definition: trx0types.h:33
undo_no_t undo_no
Definition: trx0trx.h:681
trx_id_t up_limit_id
Definition: read0read.h:141
undo_no_t undo_no
Definition: read0read.h:127
ulint conc_state
Definition: trx0trx.h:480
#define mem_heap_free(heap)
Definition: mem0mem.h:117
UNIV_INTERN void read_cursor_view_close_for_mysql(trx_t *trx, cursor_view_t *curview)
Definition: read0read.cc:484
read_view_t * read_view
Definition: read0read.h:189
trx_id_t low_limit_id
Definition: read0read.h:137
UNIV_INTERN read_view_t * read_view_open_now(trx_id_t cr_trx_id, mem_heap_t *heap)
Definition: read0read.cc:250
#define UT_LIST_REMOVE(NAME, BASE, N)
Definition: ut0lst.h:178
UNIV_INTERN read_view_t * read_view_oldest_copy_or_open_new(trx_id_t cr_trx_id, mem_heap_t *heap)
Definition: read0read.cc:168
trx_id_t low_limit_no
Definition: read0read.h:131
#define VIEW_HIGH_GRANULARITY
Definition: read0read.h:171
#define ut_a(EXPR)
Definition: ut0dbg.h:105
UNIV_INLINE void * mem_heap_alloc(mem_heap_t *heap, ulint n)
#define mem_heap_create(N)
Definition: mem0mem.h:97
#define UT_LIST_ADD_LAST(NAME, BASE, N)
Definition: ut0lst.h:119
#define UT_LIST_GET_FIRST(BASE)
Definition: ut0lst.h:224
UNIV_INLINE void mem_heap_empty(mem_heap_t *heap)
#define ut_ad(EXPR)
Definition: ut0dbg.h:127
trx_id_t no
Definition: trx0trx.h:552
ib_id_t trx_id_t
Definition: trx0types.h:85
#define VIEW_NORMAL
Definition: read0read.h:164
read_view_t * read_view
Definition: trx0trx.h:664
#define UT_LIST_ADD_FIRST(NAME, BASE, N)
Definition: ut0lst.h:97
UNIV_INTERN void read_view_print(const read_view_t *view)
Definition: read0read.cc:358
UNIV_INTERN void read_view_close_for_mysql(trx_t *trx)
Definition: read0read.cc:336
trx_id_t creator_trx_id
Definition: read0read.h:156
trx_id_t * trx_ids
Definition: read0read.h:148
UNIV_INTERN void read_cursor_set_for_mysql(trx_t *trx, cursor_view_t *curview)
Definition: read0read.cc:512
#define UT_LIST_GET_LAST(BASE)
Definition: ut0lst.h:235
UNIV_INTERN cursor_view_t * read_cursor_view_create_for_mysql(trx_t *cr_trx)
Definition: read0read.cc:399