Drizzled Public API Documentation

read0read.cc

00001 /*****************************************************************************
00002 
00003 Copyright (C) 1997, 2009, Innobase Oy. All Rights Reserved.
00004 
00005 This program is free software; you can redistribute it and/or modify it under
00006 the terms of the GNU General Public License as published by the Free Software
00007 Foundation; version 2 of the License.
00008 
00009 This program is distributed in the hope that it will be useful, but WITHOUT
00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00011 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00012 
00013 You should have received a copy of the GNU General Public License along with
00014 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
00015 St, Fifth Floor, Boston, MA 02110-1301 USA
00016 
00017 *****************************************************************************/
00018 
00019 /**************************************************/
00026 #include "read0read.h"
00027 
00028 #ifdef UNIV_NONINL
00029 #include "read0read.ic"
00030 #endif
00031 
00032 #include "srv0srv.h"
00033 #include "trx0sys.h"
00034 
00035 /*
00036 -------------------------------------------------------------------------------
00037 FACT A: Cursor read view on a secondary index sees only committed versions
00038 -------
00039 of the records in the secondary index or those versions of rows created
00040 by transaction which created a cursor before cursor was created even
00041 if transaction which created the cursor has changed that clustered index page.
00042 
00043 PROOF: We must show that read goes always to the clustered index record
00044 to see that record is visible in the cursor read view. Consider e.g.
00045 following table and SQL-clauses:
00046 
00047 create table t1(a int not null, b int, primary key(a), index(b));
00048 insert into t1 values (1,1),(2,2);
00049 commit;
00050 
00051 Now consider that we have a cursor for a query
00052 
00053 select b from t1 where b >= 1;
00054 
00055 This query will use secondary key on the table t1. Now after the first fetch
00056 on this cursor if we do a update:
00057 
00058 update t1 set b = 5 where b = 2;
00059 
00060 Now second fetch of the cursor should not see record (2,5) instead it should
00061 see record (2,2).
00062 
00063 We also should show that if we have delete t1 where b = 5; we still
00064 can see record (2,2).
00065 
00066 When we access a secondary key record maximum transaction id is fetched
00067 from this record and this trx_id is compared to up_limit_id in the view.
00068 If trx_id in the record is greater or equal than up_limit_id in the view
00069 cluster record is accessed.  Because trx_id of the creating
00070 transaction is stored when this view was created to the list of
00071 trx_ids not seen by this read view previous version of the
00072 record is requested to be built. This is build using clustered record.
00073 If the secondary key record is delete  marked it's corresponding
00074 clustered record can be already be purged only if records
00075 trx_id < low_limit_no. Purge can't remove any record deleted by a
00076 transaction which was active when cursor was created. But, we still
00077 may have a deleted secondary key record but no clustered record. But,
00078 this is not a problem because this case is handled in
00079 row_sel_get_clust_rec() function which is called
00080 whenever we note that this read view does not see trx_id in the
00081 record. Thus, we see correct version. Q. E. D.
00082 
00083 -------------------------------------------------------------------------------
00084 FACT B: Cursor read view on a clustered index sees only committed versions
00085 -------
00086 of the records in the clustered index or those versions of rows created
00087 by transaction which created a cursor before cursor was created even
00088 if transaction which created the cursor has changed that clustered index page.
00089 
00090 PROOF:  Consider e.g.following table and SQL-clauses:
00091 
00092 create table t1(a int not null, b int, primary key(a));
00093 insert into t1 values (1),(2);
00094 commit;
00095 
00096 Now consider that we have a cursor for a query
00097 
00098 select a from t1 where a >= 1;
00099 
00100 This query will use clustered key on the table t1. Now after the first fetch
00101 on this cursor if we do a update:
00102 
00103 update t1 set a = 5 where a = 2;
00104 
00105 Now second fetch of the cursor should not see record (5) instead it should
00106 see record (2).
00107 
00108 We also should show that if we have execute delete t1 where a = 5; after
00109 the cursor is opened we still can see record (2).
00110 
00111 When accessing clustered record we always check if this read view sees
00112 trx_id stored to clustered record. By default we don't see any changes
00113 if record trx_id >= low_limit_id i.e. change was made transaction
00114 which started after transaction which created the cursor. If row
00115 was changed by the future transaction a previous version of the
00116 clustered record is created. Thus we see only committed version in
00117 this case. We see all changes made by committed transactions i.e.
00118 record trx_id < up_limit_id. In this case we don't need to do anything,
00119 we already see correct version of the record. We don't see any changes
00120 made by active transaction except creating transaction. We have stored
00121 trx_id of creating transaction to list of trx_ids when this view was
00122 created. Thus we can easily see if this record was changed by the
00123 creating transaction. Because we already have clustered record we can
00124 access roll_ptr. Using this roll_ptr we can fetch undo record.
00125 We can now check that undo_no of the undo record is less than undo_no of the
00126 trancaction which created a view when cursor was created. We see this
00127 clustered record only in case when record undo_no is less than undo_no
00128 in the view. If this is not true we build based on undo_rec previous
00129 version of the record. This record is found because purge can't remove
00130 records accessed by active transaction. Thus we see correct version. Q. E. D.
00131 -------------------------------------------------------------------------------
00132 FACT C: Purge does not remove any delete marked row that is visible
00133 -------
00134 to cursor view.
00135 
00136 TODO: proof this
00137 
00138 */
00139 
00140 /*********************************************************************/
00143 UNIV_INLINE
00144 read_view_t*
00145 read_view_create_low(
00146 /*=================*/
00147   ulint   n,  
00148   mem_heap_t* heap) 
00149 {
00150   read_view_t*  view;
00151 
00152   view = static_cast<read_view_t *>(mem_heap_alloc(heap, sizeof(read_view_t)));
00153 
00154   view->n_trx_ids = n;
00155   view->trx_ids = static_cast<trx_id_t *>(mem_heap_alloc(heap, n * sizeof *view->trx_ids));
00156 
00157   return(view);
00158 }
00159 
00160 /*********************************************************************/
00166 UNIV_INTERN
00167 read_view_t*
00168 read_view_oldest_copy_or_open_new(
00169 /*==============================*/
00170   trx_id_t  cr_trx_id,  
00172   mem_heap_t* heap)   
00174 {
00175   read_view_t*  old_view;
00176   read_view_t*  view_copy;
00177   ibool   needs_insert  = TRUE;
00178   ulint   insert_done = 0;
00179   ulint   n;
00180   ulint   i;
00181 
00182   ut_ad(mutex_own(&kernel_mutex));
00183 
00184   old_view = UT_LIST_GET_LAST(trx_sys->view_list);
00185 
00186   if (old_view == NULL) {
00187 
00188     return(read_view_open_now(cr_trx_id, heap));
00189   }
00190 
00191   n = old_view->n_trx_ids;
00192 
00193   if (old_view->creator_trx_id) {
00194     n++;
00195   } else {
00196     needs_insert = FALSE;
00197   }
00198 
00199   view_copy = read_view_create_low(n, heap);
00200 
00201   /* Insert the id of the creator in the right place of the descending
00202   array of ids, if needs_insert is TRUE: */
00203 
00204   i = 0;
00205   while (i < n) {
00206     if (needs_insert
00207         && (i >= old_view->n_trx_ids
00208       || old_view->creator_trx_id
00209       > read_view_get_nth_trx_id(old_view, i))) {
00210 
00211       read_view_set_nth_trx_id(view_copy, i,
00212              old_view->creator_trx_id);
00213       needs_insert = FALSE;
00214       insert_done = 1;
00215     } else {
00216       read_view_set_nth_trx_id(view_copy, i,
00217              read_view_get_nth_trx_id(
00218                old_view,
00219                i - insert_done));
00220     }
00221 
00222     i++;
00223   }
00224 
00225   view_copy->creator_trx_id = cr_trx_id;
00226 
00227   view_copy->low_limit_no = old_view->low_limit_no;
00228   view_copy->low_limit_id = old_view->low_limit_id;
00229 
00230 
00231   if (n > 0) {
00232     /* The last active transaction has the smallest id: */
00233     view_copy->up_limit_id = read_view_get_nth_trx_id(
00234       view_copy, n - 1);
00235   } else {
00236     view_copy->up_limit_id = old_view->up_limit_id;
00237   }
00238 
00239   UT_LIST_ADD_LAST(view_list, trx_sys->view_list, view_copy);
00240 
00241   return(view_copy);
00242 }
00243 
00244 /*********************************************************************/
00248 UNIV_INTERN
00249 read_view_t*
00250 read_view_open_now(
00251 /*===============*/
00252   trx_id_t  cr_trx_id,  
00254   mem_heap_t* heap)   
00256 {
00257   read_view_t*  view;
00258   trx_t*    trx;
00259   ulint   n;
00260 
00261   ut_ad(mutex_own(&kernel_mutex));
00262 
00263   view = read_view_create_low(UT_LIST_GET_LEN(trx_sys->trx_list), heap);
00264 
00265   view->creator_trx_id = cr_trx_id;
00266   view->type = VIEW_NORMAL;
00267   view->undo_no = 0;
00268 
00269   /* No future transactions should be visible in the view */
00270 
00271   view->low_limit_no = trx_sys->max_trx_id;
00272   view->low_limit_id = view->low_limit_no;
00273 
00274   n = 0;
00275   trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
00276 
00277   /* No active transaction should be visible, except cr_trx */
00278 
00279   while (trx) {
00280     if (trx->id != cr_trx_id
00281         && (trx->conc_state == TRX_ACTIVE
00282       || trx->conc_state == TRX_PREPARED)) {
00283 
00284       read_view_set_nth_trx_id(view, n, trx->id);
00285 
00286       n++;
00287 
00288       /* NOTE that a transaction whose trx number is <
00289       trx_sys->max_trx_id can still be active, if it is
00290       in the middle of its commit! Note that when a
00291       transaction starts, we initialize trx->no to
00292       IB_ULONGLONG_MAX. */
00293 
00294       if (view->low_limit_no > trx->no) {
00295 
00296         view->low_limit_no = trx->no;
00297       }
00298     }
00299 
00300     trx = UT_LIST_GET_NEXT(trx_list, trx);
00301   }
00302 
00303   view->n_trx_ids = n;
00304 
00305   if (n > 0) {
00306     /* The last active transaction has the smallest id: */
00307     view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
00308   } else {
00309     view->up_limit_id = view->low_limit_id;
00310   }
00311 
00312 
00313   UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
00314 
00315   return(view);
00316 }
00317 
00318 /*********************************************************************/
00320 UNIV_INTERN
00321 void
00322 read_view_close(
00323 /*============*/
00324   read_view_t*  view) 
00325 {
00326   ut_ad(mutex_own(&kernel_mutex));
00327 
00328   UT_LIST_REMOVE(view_list, trx_sys->view_list, view);
00329 }
00330 
00331 /*********************************************************************/
00334 UNIV_INTERN
00335 void
00336 read_view_close_for_mysql(
00337 /*======================*/
00338   trx_t*  trx)  
00339 {
00340   ut_a(trx->global_read_view);
00341 
00342   mutex_enter(&kernel_mutex);
00343 
00344   read_view_close(trx->global_read_view);
00345 
00346   mem_heap_empty(trx->global_read_view_heap);
00347 
00348   trx->read_view = NULL;
00349   trx->global_read_view = NULL;
00350 
00351   mutex_exit(&kernel_mutex);
00352 }
00353 
00354 /*********************************************************************/
00356 UNIV_INTERN
00357 void
00358 read_view_print(
00359 /*============*/
00360   const read_view_t*  view) 
00361 {
00362   ulint n_ids;
00363   ulint i;
00364 
00365   if (view->type == VIEW_HIGH_GRANULARITY) {
00366     fprintf(stderr,
00367       "High-granularity read view undo_n:o %llu\n",
00368       (ullint) view->undo_no);
00369   } else {
00370     fprintf(stderr, "Normal read view\n");
00371   }
00372 
00373   fprintf(stderr, "Read view low limit trx n:o " TRX_ID_FMT "\n",
00374     view->low_limit_no);
00375 
00376   fprintf(stderr, "Read view up limit trx id " TRX_ID_FMT "\n",
00377     view->up_limit_id);
00378 
00379   fprintf(stderr, "Read view low limit trx id " TRX_ID_FMT "\n",
00380     view->low_limit_id);
00381 
00382   fprintf(stderr, "Read view individually stored trx ids:\n");
00383 
00384   n_ids = view->n_trx_ids;
00385 
00386   for (i = 0; i < n_ids; i++) {
00387     fprintf(stderr, "Read view trx id " TRX_ID_FMT "\n",
00388       read_view_get_nth_trx_id(view, i));
00389   }
00390 }
00391 
00392 /*********************************************************************/
00397 UNIV_INTERN
00398 cursor_view_t*
00399 read_cursor_view_create_for_mysql(
00400 /*==============================*/
00401   trx_t*  cr_trx) 
00402 {
00403   cursor_view_t*  curview;
00404   read_view_t*  view;
00405   mem_heap_t* heap;
00406   trx_t*    trx;
00407   ulint   n;
00408 
00409   ut_a(cr_trx);
00410 
00411   /* Use larger heap than in trx_create when creating a read_view
00412   because cursors are quite long. */
00413 
00414   heap = mem_heap_create(512);
00415 
00416   curview = (cursor_view_t*) mem_heap_alloc(heap, sizeof(cursor_view_t));
00417   curview->heap = heap;
00418 
00419   mutex_enter(&kernel_mutex);
00420 
00421   curview->read_view = read_view_create_low(
00422     UT_LIST_GET_LEN(trx_sys->trx_list), curview->heap);
00423 
00424   view = curview->read_view;
00425   view->creator_trx_id = cr_trx->id;
00426   view->type = VIEW_HIGH_GRANULARITY;
00427   view->undo_no = cr_trx->undo_no;
00428 
00429   /* No future transactions should be visible in the view */
00430 
00431   view->low_limit_no = trx_sys->max_trx_id;
00432   view->low_limit_id = view->low_limit_no;
00433 
00434   n = 0;
00435   trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
00436 
00437   /* No active transaction should be visible */
00438 
00439   while (trx) {
00440 
00441     if (trx->conc_state == TRX_ACTIVE
00442         || trx->conc_state == TRX_PREPARED) {
00443 
00444       read_view_set_nth_trx_id(view, n, trx->id);
00445 
00446       n++;
00447 
00448       /* NOTE that a transaction whose trx number is <
00449       trx_sys->max_trx_id can still be active, if it is
00450       in the middle of its commit! Note that when a
00451       transaction starts, we initialize trx->no to
00452       IB_ULONGLONG_MAX. */
00453 
00454       if (view->low_limit_no > trx->no) {
00455 
00456         view->low_limit_no = trx->no;
00457       }
00458     }
00459 
00460     trx = UT_LIST_GET_NEXT(trx_list, trx);
00461   }
00462 
00463   view->n_trx_ids = n;
00464 
00465   if (n > 0) {
00466     /* The last active transaction has the smallest id: */
00467     view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
00468   } else {
00469     view->up_limit_id = view->low_limit_id;
00470   }
00471 
00472   UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
00473 
00474   mutex_exit(&kernel_mutex);
00475 
00476   return(curview);
00477 }
00478 
00479 /*********************************************************************/
00482 UNIV_INTERN
00483 void
00484 read_cursor_view_close_for_mysql(
00485 /*=============================*/
00486   trx_t*    trx,  
00487   cursor_view_t*  curview)
00488 {
00489   ut_a(curview);
00490   ut_a(curview->read_view);
00491   ut_a(curview->heap);
00492 
00493   /* Add cursor's tables to the global count of active tables that
00494   belong to this transaction */
00495 
00496   mutex_enter(&kernel_mutex);
00497 
00498   read_view_close(curview->read_view);
00499   trx->read_view = trx->global_read_view;
00500 
00501   mutex_exit(&kernel_mutex);
00502 
00503   mem_heap_free(curview->heap);
00504 }
00505 
00506 /*********************************************************************/
00510 UNIV_INTERN
00511 void
00512 read_cursor_set_for_mysql(
00513 /*======================*/
00514   trx_t*    trx,  
00515   cursor_view_t*  curview)
00516 {
00517   ut_a(trx);
00518 
00519   mutex_enter(&kernel_mutex);
00520 
00521   if (UNIV_LIKELY(curview != NULL)) {
00522     trx->read_view = curview->read_view;
00523   } else {
00524     trx->read_view = trx->global_read_view;
00525   }
00526 
00527   mutex_exit(&kernel_mutex);
00528 }