MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
read0read.cc
Go to the documentation of this file.
1 /*****************************************************************************
2 
3 Copyright (c) 1996, 2013, Oracle and/or its affiliates. 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.,
15 51 Franklin Street, Suite 500, Boston, MA 02110-1335 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, its 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 in any cursor read view.
135 
136 PROOF: We know that:
137  1: Currently active read views in trx_sys_t::view_list are ordered by
138  read_view_t::low_limit_no in descending order, that is,
139  newest read view first.
140 
141  2: Purge clones the oldest read view and uses that to determine whether there
142  are any active transactions that can see the to be purged records.
143 
144 Therefore any joining or active transaction will not have a view older
145 than the purge view, according to 1.
146 
147 When purge needs to remove a delete-marked row from a secondary index,
148 it will first check that the DB_TRX_ID value of the corresponding
149 record in the clustered index is older than the purge view. It will
150 also check if there is a newer version of the row (clustered index
151 record) that is not delete-marked in the secondary index. If such a
152 row exists and is collation-equal to the delete-marked secondary index
153 record then purge will not remove the secondary index record.
154 
155 Delete-marked clustered index records will be removed by
156 row_purge_remove_clust_if_poss(), unless the clustered index record
157 (and its DB_ROLL_PTR) has been updated. Every new version of the
158 clustered index record will update DB_ROLL_PTR, pointing to a new UNDO
159 log entry that allows the old version to be reconstructed. The
160 DB_ROLL_PTR in the oldest remaining version in the old-version chain
161 may be pointing to garbage (an undo log record discarded by purge),
162 but it will never be dereferenced, because the purge view is older
163 than any active transaction.
164 
165 For details see: row_vers_old_has_index_entry() and row_purge_poss_sec()
166 
167 Some additional issues:
168 
169 What if trx_sys->view_list == NULL and some transaction T1 and Purge both
170 try to open read_view at same time. Only one can acquire trx_sys->mutex.
171 In which order will the views be opened? Should it matter? If no, why?
172 
173 The order does not matter. No new transactions can be created and no running
174 transaction can commit or rollback (or free views).
175 */
176 
177 /*********************************************************************/
180 UNIV_INLINE
183 /*=================*/
184  ulint n,
185  mem_heap_t* heap)
186 {
187  read_view_t* view;
188 
189  view = static_cast<read_view_t*>(
191  heap, sizeof(*view) + n * sizeof(*view->trx_ids)));
192 
193  view->n_trx_ids = n;
194  view->trx_ids = (trx_id_t*) &view[1];
195 
196  return(view);
197 }
198 
199 /*********************************************************************/
205 UNIV_INLINE
208 /*============*/
209  const read_view_t* view,
210  mem_heap_t* heap)
212 {
213  ulint sz;
214  read_view_t* clone;
215  read_view_t* new_view;
216 
217  ut_ad(mutex_own(&trx_sys->mutex));
218 
219  /* Allocate space for two views. */
220 
221  sz = sizeof(*view) + view->n_trx_ids * sizeof(*view->trx_ids);
222 
223  /* Add an extra trx_id_t slot for the new view. */
224 
225  clone = static_cast<read_view_t*>(
226  mem_heap_alloc(heap, (sz * 2) + sizeof(trx_id_t)));
227 
228  /* Only the contents of the old view are important, the new view
229  will be created from this and so we don't copy that across. */
230 
231  memcpy(clone, view, sz);
232 
233  clone->trx_ids = (trx_id_t*) &clone[1];
234 
235  new_view = (read_view_t*) &clone->trx_ids[clone->n_trx_ids];
236  new_view->trx_ids = (trx_id_t*) &new_view[1];
237  new_view->n_trx_ids = clone->n_trx_ids + 1;
238 
239  ut_a(new_view->n_trx_ids == view->n_trx_ids + 1);
240 
241  return(clone);
242 }
243 
244 /*********************************************************************/
247 static
248 void
249 read_view_add(
250 /*==========*/
251  read_view_t* view)
252 {
253  read_view_t* elem;
254  read_view_t* prev_elem;
255 
256  ut_ad(mutex_own(&trx_sys->mutex));
257  ut_ad(read_view_validate(view));
258 
259  /* Find the correct slot for insertion. */
260  for (elem = UT_LIST_GET_FIRST(trx_sys->view_list), prev_elem = NULL;
261  elem != NULL && view->low_limit_no < elem->low_limit_no;
262  prev_elem = elem, elem = UT_LIST_GET_NEXT(view_list, elem)) {
263  /* No op */
264  }
265 
266  if (prev_elem == NULL) {
267  UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
268  } else {
270  view_list, trx_sys->view_list, prev_elem, view);
271  }
272 
273  ut_ad(read_view_list_validate());
274 }
275 
277 struct CreateView {
278 
279  CreateView(read_view_t* view)
280  : m_view(view)
281  {
282  m_n_trx = m_view->n_trx_ids;
283  m_view->n_trx_ids = 0;
284  }
285 
286  void operator()(const trx_t* trx)
287  {
288  ut_ad(mutex_own(&trx_sys->mutex));
289  ut_ad(trx->in_rw_trx_list);
290 
291  /* trx->state cannot change from or to NOT_STARTED
292  while we are holding the trx_sys->mutex. It may change
293  from ACTIVE to PREPARED or COMMITTED. */
294 
295  if (trx->id != m_view->creator_trx_id
296  && !trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)) {
297 
298  ut_ad(m_n_trx > m_view->n_trx_ids);
299 
300  m_view->trx_ids[m_view->n_trx_ids++] = trx->id;
301 
302  /* NOTE that a transaction whose trx number is <
303  trx_sys->max_trx_id can still be active, if it is
304  in the middle of its commit! Note that when a
305  transaction starts, we initialize trx->no to
306  TRX_ID_MAX. */
307 
308  /* trx->no is protected by trx_sys->mutex, which
309  we are holding. It is assigned by trx_commit()
310  before lock_trx_release_locks() assigns
311  trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */
312 
313  if (m_view->low_limit_no > trx->no) {
314  m_view->low_limit_no = trx->no;
315  }
316  }
317  }
318 
319  read_view_t* m_view;
320  ulint m_n_trx;
321 };
322 
323 /*********************************************************************/
327 static
329 read_view_open_now_low(
330 /*===================*/
331  trx_id_t cr_trx_id,
333  mem_heap_t* heap)
335 {
336  read_view_t* view;
337  ulint n_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list);
338 
339  ut_ad(mutex_own(&trx_sys->mutex));
340 
341  view = read_view_create_low(n_trx, heap);
342 
343  view->undo_no = 0;
344  view->type = VIEW_NORMAL;
345  view->creator_trx_id = cr_trx_id;
346 
347  /* No future transactions should be visible in the view */
348 
350  view->low_limit_id = view->low_limit_no;
351 
352  /* No active transaction should be visible, except cr_trx */
353 
355 
356  if (view->n_trx_ids > 0) {
357  /* The last active transaction has the smallest id: */
358  view->up_limit_id = view->trx_ids[view->n_trx_ids - 1];
359  } else {
360  view->up_limit_id = view->low_limit_id;
361  }
362 
363  /* Purge views are not added to the view list. */
364  if (cr_trx_id > 0) {
365  read_view_add(view);
366  }
367 
368  return(view);
369 }
370 
371 /*********************************************************************/
375 UNIV_INTERN
378 /*===============*/
379  trx_id_t cr_trx_id,
381  mem_heap_t* heap)
383 {
384  read_view_t* view;
385 
386  mutex_enter(&trx_sys->mutex);
387 
388  view = read_view_open_now_low(cr_trx_id, heap);
389 
390  mutex_exit(&trx_sys->mutex);
391 
392  return(view);
393 }
394 
395 /*********************************************************************/
401 UNIV_INTERN
404 /*=================*/
405  mem_heap_t* heap)
407 {
408  ulint i;
409  read_view_t* view;
410  read_view_t* oldest_view;
411  trx_id_t creator_trx_id;
412  ulint insert_done = 0;
413 
414  mutex_enter(&trx_sys->mutex);
415 
416  oldest_view = UT_LIST_GET_LAST(trx_sys->view_list);
417 
418  if (oldest_view == NULL) {
419 
420  view = read_view_open_now_low(0, heap);
421 
422  mutex_exit(&trx_sys->mutex);
423 
424  return(view);
425  }
426 
427  /* Allocate space for both views, the oldest and the new purge view. */
428 
429  oldest_view = read_view_clone(oldest_view, heap);
430 
431  ut_ad(read_view_validate(oldest_view));
432 
433  mutex_exit(&trx_sys->mutex);
434 
435  ut_a(oldest_view->creator_trx_id > 0);
436  creator_trx_id = oldest_view->creator_trx_id;
437 
438  view = (read_view_t*) &oldest_view->trx_ids[oldest_view->n_trx_ids];
439 
440  /* Add the creator transaction id in the trx_ids array in the
441  correct slot. */
442 
443  for (i = 0; i < oldest_view->n_trx_ids; ++i) {
444  trx_id_t id;
445 
446  id = oldest_view->trx_ids[i - insert_done];
447 
448  if (insert_done == 0 && creator_trx_id > id) {
449  id = creator_trx_id;
450  insert_done = 1;
451  }
452 
453  view->trx_ids[i] = id;
454  }
455 
456  if (insert_done == 0) {
457  view->trx_ids[i] = creator_trx_id;
458  } else {
459  ut_a(i > 0);
460  view->trx_ids[i] = oldest_view->trx_ids[i - 1];
461  }
462 
463  view->creator_trx_id = 0;
464 
465  view->low_limit_no = oldest_view->low_limit_no;
466  view->low_limit_id = oldest_view->low_limit_id;
467 
468  if (view->n_trx_ids > 0) {
469  /* The last active transaction has the smallest id: */
470 
471  view->up_limit_id = view->trx_ids[view->n_trx_ids - 1];
472  } else {
473  view->up_limit_id = oldest_view->up_limit_id;
474  }
475 
476  return(view);
477 }
478 
479 /*********************************************************************/
482 UNIV_INTERN
483 void
485 /*======================*/
486  trx_t* trx)
487 {
488  ut_a(trx->global_read_view);
489 
490  read_view_remove(trx->global_read_view, false);
491 
493 
494  trx->read_view = NULL;
495  trx->global_read_view = NULL;
496 }
497 
498 /*********************************************************************/
500 UNIV_INTERN
501 void
503 /*============*/
504  const read_view_t* view)
505 {
506  ulint n_ids;
507  ulint i;
508 
509  if (view->type == VIEW_HIGH_GRANULARITY) {
510  fprintf(stderr,
511  "High-granularity read view undo_n:o " TRX_ID_FMT "\n",
512  view->undo_no);
513  } else {
514  fprintf(stderr, "Normal read view\n");
515  }
516 
517  fprintf(stderr, "Read view low limit trx n:o " TRX_ID_FMT "\n",
518  view->low_limit_no);
519 
520  fprintf(stderr, "Read view up limit trx id " TRX_ID_FMT "\n",
521  view->up_limit_id);
522 
523  fprintf(stderr, "Read view low limit trx id " TRX_ID_FMT "\n",
524  view->low_limit_id);
525 
526  fprintf(stderr, "Read view individually stored trx ids:\n");
527 
528  n_ids = view->n_trx_ids;
529 
530  for (i = 0; i < n_ids; i++) {
531  fprintf(stderr, "Read view trx id " TRX_ID_FMT "\n",
532  view->trx_ids[i]);
533  }
534 }
535 
536 /*********************************************************************/
541 UNIV_INTERN
544 /*==============================*/
545  trx_t* cr_trx)
546 {
547  read_view_t* view;
548  mem_heap_t* heap;
549  ulint n_trx;
550  cursor_view_t* curview;
551 
552  /* Use larger heap than in trx_create when creating a read_view
553  because cursors are quite long. */
554 
555  heap = mem_heap_create(512);
556 
557  curview = (cursor_view_t*) mem_heap_alloc(heap, sizeof(*curview));
558 
559  curview->heap = heap;
560 
561  /* Drop cursor tables from consideration when evaluating the
562  need of auto-commit */
563 
564  curview->n_mysql_tables_in_use = cr_trx->n_mysql_tables_in_use;
565 
566  cr_trx->n_mysql_tables_in_use = 0;
567 
568  mutex_enter(&trx_sys->mutex);
569 
571 
572  curview->read_view = read_view_create_low(n_trx, curview->heap);
573 
574  view = curview->read_view;
575  view->undo_no = cr_trx->undo_no;
576  view->type = VIEW_HIGH_GRANULARITY;
577  view->creator_trx_id = UINT64_UNDEFINED;
578 
579  /* No future transactions should be visible in the view */
580 
582  view->low_limit_id = view->low_limit_no;
583 
584  /* No active transaction should be visible */
585 
587 
588  view->creator_trx_id = cr_trx->id;
589 
590  if (view->n_trx_ids > 0) {
591  /* The last active transaction has the smallest id: */
592 
593  view->up_limit_id = view->trx_ids[view->n_trx_ids - 1];
594  } else {
595  view->up_limit_id = view->low_limit_id;
596  }
597 
598  read_view_add(view);
599 
600  mutex_exit(&trx_sys->mutex);
601 
602  return(curview);
603 }
604 
605 /*********************************************************************/
608 UNIV_INTERN
609 void
611 /*=============================*/
612  trx_t* trx,
613  cursor_view_t* curview)
614 {
615  ut_a(curview);
616  ut_a(curview->read_view);
617  ut_a(curview->heap);
618 
619  /* Add cursor's tables to the global count of active tables that
620  belong to this transaction */
622 
623  read_view_remove(curview->read_view, false);
624 
625  trx->read_view = trx->global_read_view;
626 
627  mem_heap_free(curview->heap);
628 }
629 
630 /*********************************************************************/
634 UNIV_INTERN
635 void
637 /*======================*/
638  trx_t* trx,
639  cursor_view_t* curview)
640 {
641  ut_a(trx);
642 
643  mutex_enter(&trx_sys->mutex);
644 
645  if (UNIV_LIKELY(curview != NULL)) {
646  trx->read_view = curview->read_view;
647  } else {
648  trx->read_view = trx->global_read_view;
649  }
650 
651  ut_ad(read_view_validate(trx->read_view));
652 
653  mutex_exit(&trx_sys->mutex);
654 }