Drizzled Public API Documentation

trx0i_s.cc
1 /*****************************************************************************
2 
3 Copyright (C) 2007, 2010, 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 /**************************************************/
31 #include <config.h>
32 /* Found during the build of 5.5.3 on Linux 2.4 and early 2.6 kernels:
33  The includes "univ.i" -> "my_global.h" cause a different path
34  to be taken further down with pthread functions and types,
35  so they must come first.
36  From the symptoms, this is related to bug#46587 in the MySQL bug DB.
37 */
38 #include "univ.i"
39 
40 #if !defined(BUILD_DRIZZLE)
41 # include <mysql/plugin.h>
42 #endif
43 
44 #include "buf0buf.h"
45 #include "dict0dict.h"
46 #include "ha0storage.h"
47 #include "ha_prototypes.h"
48 #include "hash0hash.h"
49 #include "lock0iter.h"
50 #include "lock0lock.h"
51 #include "mem0mem.h"
52 #include "page0page.h"
53 #include "rem0rec.h"
54 #include "row0row.h"
55 #include "srv0srv.h"
56 #include "sync0rw.h"
57 #include "sync0sync.h"
58 #include "sync0types.h"
59 #include "trx0i_s.h"
60 #include "trx0sys.h"
61 #include "trx0trx.h"
62 #include "ut0mem.h"
63 #include "ut0ut.h"
64 
65 #include <drizzled/session.h>
66 
68 #define TABLE_CACHE_INITIAL_ROWSNUM 1024
69 
78 #define MEM_CHUNKS_IN_TABLE_CACHE 39
79 
82 /* @{ */
83 
84 #if 0
85 
89 #define TEST_LOCK_FOLD_ALWAYS_DIFFERENT
90 #endif
91 
92 #if 0
93 
97 #define TEST_NO_LOCKS_ROW_IS_EVER_EQUAL_TO_LOCK_T
98 #endif
99 
100 #if 0
101 
104 #define TEST_ADD_EACH_LOCKS_ROW_MANY_TIMES
105 #endif
106 
107 #if 0
108 
110 #define TEST_DO_NOT_CHECK_FOR_DUPLICATE_ROWS
111 #endif
112 
113 #if 0
114 
117 #define TEST_DO_NOT_INSERT_INTO_THE_HASH_TABLE
118 #endif
119 /* @} */
120 
124 #define MAX_ALLOWED_FOR_STORAGE(cache) \
125  (TRX_I_S_MEM_LIMIT \
126  - (cache)->mem_allocd)
127 
131 #define MAX_ALLOWED_FOR_ALLOC(cache) \
132  (TRX_I_S_MEM_LIMIT \
133  - (cache)->mem_allocd \
134  - ha_storage_get_size((cache)->storage))
135 
139 typedef struct i_s_mem_chunk_struct {
140  ulint offset;
141  ulint rows_allocd;
143  void* base;
145 
147 typedef struct i_s_table_cache_struct {
148  ulint rows_used;
149  ulint rows_allocd;
150  ulint row_size;
151  i_s_mem_chunk_t chunks[MEM_CHUNKS_IN_TABLE_CACHE];
155 
160  ullint last_read;
171 #define LOCKS_HASH_CELLS_NUM 10000
176 #define CACHE_STORAGE_INITIAL_SIZE 1024
177 
178 #define CACHE_STORAGE_HASH_CELLS 2048
183  ulint mem_allocd;
185  ibool is_truncated;
188 };
189 
193 static trx_i_s_cache_t trx_i_s_cache_static;
197 UNIV_INTERN trx_i_s_cache_t* trx_i_s_cache = &trx_i_s_cache_static;
198 
199 /* Key to register the lock/mutex with performance schema */
200 #ifdef UNIV_PFS_RWLOCK
201 UNIV_INTERN mysql_pfs_key_t trx_i_s_cache_lock_key;
202 #endif /* UNIV_PFS_RWLOCK */
203 
204 #ifdef UNIV_PFS_MUTEX
205 UNIV_INTERN mysql_pfs_key_t cache_last_read_mutex_key;
206 #endif /* UNIV_PFS_MUTEX */
207 
208 /*******************************************************************/
212 static
213 ulint
214 wait_lock_get_heap_no(
215 /*==================*/
216  const lock_t* lock)
217 {
218  ulint ret;
219 
220  switch (lock_get_type(lock)) {
221  case LOCK_REC:
222  ret = lock_rec_find_set_bit(lock);
223  ut_a(ret != ULINT_UNDEFINED);
224  break;
225  case LOCK_TABLE:
226  ret = ULINT_UNDEFINED;
227  break;
228  default:
229  ut_error;
230  }
231 
232  return(ret);
233 }
234 
235 /*******************************************************************/
237 static
238 void
239 table_cache_init(
240 /*=============*/
241  i_s_table_cache_t* table_cache,
242  size_t row_size)
244 {
245  ulint i;
246 
247  table_cache->rows_used = 0;
248  table_cache->rows_allocd = 0;
249  table_cache->row_size = row_size;
250 
251  for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
252 
253  /* the memory is actually allocated in
254  table_cache_create_empty_row() */
255  table_cache->chunks[i].base = NULL;
256  }
257 }
258 
259 /*******************************************************************/
261 static
262 void
263 table_cache_free(
264 /*=============*/
265  i_s_table_cache_t* table_cache)
266 {
267  ulint i;
268 
269  for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
270 
271  /* the memory is actually allocated in
272  table_cache_create_empty_row() */
273  if (table_cache->chunks[i].base) {
274  mem_free(table_cache->chunks[i].base);
275  table_cache->chunks[i].base = NULL;
276  }
277  }
278 }
279 
280 /*******************************************************************/
286 static
287 void*
288 table_cache_create_empty_row(
289 /*=========================*/
290  i_s_table_cache_t* table_cache,
291  trx_i_s_cache_t* cache)
294 {
295  ulint i;
296  void* row;
297 
298  ut_a(table_cache->rows_used <= table_cache->rows_allocd);
299 
300  if (table_cache->rows_used == table_cache->rows_allocd) {
301 
302  /* rows_used == rows_allocd means that new chunk needs
303  to be allocated: either no more empty rows in the
304  last allocated chunk or nothing has been allocated yet
305  (rows_num == rows_allocd == 0); */
306 
307  i_s_mem_chunk_t* chunk;
308  ulint req_bytes;
309  ulint got_bytes;
310  ulint req_rows;
311  ulint got_rows;
312 
313  /* find the first not allocated chunk */
314  for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
315 
316  if (table_cache->chunks[i].base == NULL) {
317 
318  break;
319  }
320  }
321 
322  /* i == MEM_CHUNKS_IN_TABLE_CACHE means that all chunks
323  have been allocated :-X */
324  ut_a(i < MEM_CHUNKS_IN_TABLE_CACHE);
325 
326  /* allocate the chunk we just found */
327 
328  if (i == 0) {
329 
330  /* first chunk, nothing is allocated yet */
331  req_rows = TABLE_CACHE_INITIAL_ROWSNUM;
332  } else {
333 
334  /* Memory is increased by the formula
335  new = old + old / 2; We are trying not to be
336  aggressive here (= using the common new = old * 2)
337  because the allocated memory will not be freed
338  until InnoDB exit (it is reused). So it is better
339  to once allocate the memory in more steps, but
340  have less unused/wasted memory than to use less
341  steps in allocation (which is done once in a
342  lifetime) but end up with lots of unused/wasted
343  memory. */
344  req_rows = table_cache->rows_allocd / 2;
345  }
346  req_bytes = req_rows * table_cache->row_size;
347 
348  if (req_bytes > MAX_ALLOWED_FOR_ALLOC(cache)) {
349 
350  return(NULL);
351  }
352 
353  chunk = &table_cache->chunks[i];
354 
355  chunk->base = mem_alloc2(req_bytes, &got_bytes);
356 
357  got_rows = got_bytes / table_cache->row_size;
358 
359  cache->mem_allocd += got_bytes;
360 
361 #if 0
362  printf("allocating chunk %d req bytes=%lu, got bytes=%lu, "
363  "row size=%lu, "
364  "req rows=%lu, got rows=%lu\n",
365  i, req_bytes, got_bytes,
366  table_cache->row_size,
367  req_rows, got_rows);
368 #endif
369 
370  chunk->rows_allocd = got_rows;
371 
372  table_cache->rows_allocd += got_rows;
373 
374  /* adjust the offset of the next chunk */
375  if (i < MEM_CHUNKS_IN_TABLE_CACHE - 1) {
376 
377  table_cache->chunks[i + 1].offset
378  = chunk->offset + chunk->rows_allocd;
379  }
380 
381  /* return the first empty row in the newly allocated
382  chunk */
383  row = chunk->base;
384  } else {
385 
386  char* chunk_start;
387  ulint offset;
388 
389  /* there is an empty row, no need to allocate new
390  chunks */
391 
392  /* find the first chunk that contains allocated but
393  empty/unused rows */
394  for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
395 
396  if (table_cache->chunks[i].offset
397  + table_cache->chunks[i].rows_allocd
398  > table_cache->rows_used) {
399 
400  break;
401  }
402  }
403 
404  /* i == MEM_CHUNKS_IN_TABLE_CACHE means that all chunks
405  are full, but
406  table_cache->rows_used != table_cache->rows_allocd means
407  exactly the opposite - there are allocated but
408  empty/unused rows :-X */
409  ut_a(i < MEM_CHUNKS_IN_TABLE_CACHE);
410 
411  chunk_start = (char*) table_cache->chunks[i].base;
412  offset = table_cache->rows_used
413  - table_cache->chunks[i].offset;
414 
415  row = chunk_start + offset * table_cache->row_size;
416  }
417 
418  table_cache->rows_used++;
419 
420  return(row);
421 }
422 
423 #ifdef UNIV_DEBUG
424 /*******************************************************************/
427 static
428 ibool
429 i_s_locks_row_validate(
430 /*===================*/
431  const i_s_locks_row_t* row)
432 {
433  ut_ad(row->lock_trx_id != 0);
434  ut_ad(row->lock_mode != NULL);
435  ut_ad(row->lock_type != NULL);
436  ut_ad(row->lock_table != NULL);
437  ut_ad(row->lock_table_id != 0);
438 
439  if (row->lock_space == ULINT_UNDEFINED) {
440  /* table lock */
441  ut_ad(!strcmp("TABLE", row->lock_type));
442  ut_ad(row->lock_index == NULL);
443  ut_ad(row->lock_data == NULL);
444  ut_ad(row->lock_page == ULINT_UNDEFINED);
445  ut_ad(row->lock_rec == ULINT_UNDEFINED);
446  } else {
447  /* record lock */
448  ut_ad(!strcmp("RECORD", row->lock_type));
449  ut_ad(row->lock_index != NULL);
450  ut_ad(row->lock_data != NULL);
451  ut_ad(row->lock_page != ULINT_UNDEFINED);
452  ut_ad(row->lock_rec != ULINT_UNDEFINED);
453  }
454 
455  return(TRUE);
456 }
457 #endif /* UNIV_DEBUG */
458 
459 /*******************************************************************/
463 static
464 ibool
465 fill_trx_row(
466 /*=========*/
467  i_s_trx_row_t* row,
469  const trx_t* trx,
471  const i_s_locks_row_t* requested_lock_row,
476  trx_i_s_cache_t* cache)
479 {
480  const char* stmt;
481  size_t stmt_len;
482  const char* s;
483 
484  ut_ad(mutex_own(&kernel_mutex));
485 
486  row->trx_id = trx->id;
487  row->trx_started = (ib_time_t) trx->start_time;
488  row->trx_state = trx_get_que_state_str(trx);
489  row->requested_lock_row = requested_lock_row;
490  ut_ad(requested_lock_row == NULL
491  || i_s_locks_row_validate(requested_lock_row));
492 
493  if (trx->wait_lock != NULL) {
494  ut_a(requested_lock_row != NULL);
496  } else {
497  ut_a(requested_lock_row == NULL);
498  row->trx_wait_started = 0;
499  }
500 
501  row->trx_weight = (ullint) TRX_WEIGHT(trx);
502 
503  if (trx->mysql_thd == NULL) {
504  /* For internal transactions e.g., purge and transactions
505  being recovered at startup there is no associated MySQL
506  thread data structure. */
507  row->trx_mysql_thread_id = 0;
508  row->trx_query = NULL;
509  goto thd_done;
510  }
511 
512  row->trx_mysql_thread_id = trx->session()->getSessionId();
513  stmt= trx->mysql_thd->getQueryStringCopy(stmt_len);
514 
515  if (stmt != NULL) {
516 
517  char query[TRX_I_S_TRX_QUERY_MAX_LEN + 1];
518 
519  if (stmt_len > TRX_I_S_TRX_QUERY_MAX_LEN) {
520  stmt_len = TRX_I_S_TRX_QUERY_MAX_LEN;
521  }
522 
523  memcpy(query, stmt, stmt_len);
524  query[stmt_len] = '\0';
525 
526  row->trx_query = static_cast<const char *>(ha_storage_put_memlim(
527  cache->storage, stmt, stmt_len + 1,
528  MAX_ALLOWED_FOR_STORAGE(cache)));
529 
530  if (row->trx_query == NULL) {
531 
532  return(FALSE);
533  }
534  } else {
535 
536  row->trx_query = NULL;
537  }
538 
539 thd_done:
540  s = trx->op_info;
541 
542  if (s != NULL && s[0] != '\0') {
543 
546 
547  if (row->trx_operation_state == NULL) {
548 
549  return(FALSE);
550  }
551  } else {
552 
553  row->trx_operation_state = NULL;
554  }
555 
556 // row->trx_tables_in_use = trx->n_mysql_tables_in_use;
557 
558  row->trx_tables_locked = trx->mysql_n_tables_locked;
559 
561 
563 
565 
566  row->trx_rows_modified = trx->undo_no;
567 
568  row->trx_concurrency_tickets = trx->n_tickets_to_enter_innodb;
569 
570  switch (trx->isolation_level) {
571  case TRX_ISO_READ_UNCOMMITTED:
572  row->trx_isolation_level = "READ UNCOMMITTED";
573  break;
574  case TRX_ISO_READ_COMMITTED:
575  row->trx_isolation_level = "READ COMMITTED";
576  break;
577  case TRX_ISO_REPEATABLE_READ:
578  row->trx_isolation_level = "REPEATABLE READ";
579  break;
580  case TRX_ISO_SERIALIZABLE:
581  row->trx_isolation_level = "SERIALIZABLE";
582  break;
583  /* Should not happen as TRX_ISO_READ_COMMITTED is default */
584  default:
585  row->trx_isolation_level = "UNKNOWN";
586  }
587 
588  row->trx_unique_checks = (ibool) trx->check_unique_secondary;
589 
590  row->trx_foreign_key_checks = (ibool) trx->check_foreigns;
591 
592  s = trx->detailed_error;
593 
594  if (s != NULL && s[0] != '\0') {
595 
599 
600  if (row->trx_foreign_key_error == NULL) {
601 
602  return(FALSE);
603  }
604  } else {
605  row->trx_foreign_key_error = NULL;
606  }
607 
608  row->trx_has_search_latch = (ibool) trx->has_search_latch;
609 
610  row->trx_search_latch_timeout = trx->search_latch_timeout;
611 
612  return(TRUE);
613 }
614 
615 /*******************************************************************/
620 static
621 ulint
622 put_nth_field(
623 /*==========*/
624  char* buf,
625  ulint buf_size,
626  ulint n,
627  const dict_index_t* index,
628  const rec_t* rec,
629  const ulint* offsets)
631 {
632  const byte* data;
633  ulint data_len;
634  dict_field_t* dict_field;
635  ulint ret;
636 
637  ut_ad(rec_offs_validate(rec, NULL, offsets));
638 
639  if (buf_size == 0) {
640 
641  return(0);
642  }
643 
644  ret = 0;
645 
646  if (n > 0) {
647  /* we must append ", " before the actual data */
648 
649  if (buf_size < 3) {
650 
651  buf[0] = '\0';
652  return(1);
653  }
654 
655  memcpy(buf, ", ", 3);
656 
657  buf += 2;
658  buf_size -= 2;
659  ret += 2;
660  }
661 
662  /* now buf_size >= 1 */
663 
664  data = rec_get_nth_field(rec, offsets, n, &data_len);
665 
666  dict_field = dict_index_get_nth_field(index, n);
667 
668  ret += row_raw_format((const char*) data, data_len,
669  dict_field, buf, buf_size);
670 
671  return(ret);
672 }
673 
674 /*******************************************************************/
678 static
679 ibool
680 fill_lock_data(
681 /*===========*/
682  const char** lock_data,
683  const lock_t* lock,
684  ulint heap_no,
685  trx_i_s_cache_t* cache)
687 {
688  mtr_t mtr;
689 
690  const buf_block_t* block;
691  const page_t* page;
692  const rec_t* rec;
693 
694  ut_a(lock_get_type(lock) == LOCK_REC);
695 
696  mtr_start(&mtr);
697 
699  lock_rec_get_page_no(lock),
700  &mtr);
701 
702  if (block == NULL) {
703 
704  *lock_data = NULL;
705 
706  mtr_commit(&mtr);
707 
708  return(TRUE);
709  }
710 
711  page = (const page_t*) buf_block_get_frame(block);
712 
713  rec = page_find_rec_with_heap_no(page, heap_no);
714 
715  if (page_rec_is_infimum(rec)) {
716 
717  *lock_data = ha_storage_put_str_memlim(
718  cache->storage, "infimum pseudo-record",
719  MAX_ALLOWED_FOR_STORAGE(cache));
720  } else if (page_rec_is_supremum(rec)) {
721 
722  *lock_data = ha_storage_put_str_memlim(
723  cache->storage, "supremum pseudo-record",
724  MAX_ALLOWED_FOR_STORAGE(cache));
725  } else {
726 
727  const dict_index_t* index;
728  ulint n_fields;
729  mem_heap_t* heap;
730  ulint offsets_onstack[REC_OFFS_NORMAL_SIZE];
731  ulint* offsets;
732  char buf[TRX_I_S_LOCK_DATA_MAX_LEN];
733  ulint buf_used;
734  ulint i;
735 
736  rec_offs_init(offsets_onstack);
737  offsets = offsets_onstack;
738 
739  index = lock_rec_get_index(lock);
740 
741  n_fields = dict_index_get_n_unique(index);
742 
743  ut_a(n_fields > 0);
744 
745  heap = NULL;
746  offsets = rec_get_offsets(rec, index, offsets, n_fields,
747  &heap);
748 
749  /* format and store the data */
750 
751  buf_used = 0;
752  for (i = 0; i < n_fields; i++) {
753 
754  buf_used += put_nth_field(
755  buf + buf_used, sizeof(buf) - buf_used,
756  i, index, rec, offsets) - 1;
757  }
758 
759  *lock_data = (const char*) ha_storage_put_memlim(
760  cache->storage, buf, buf_used + 1,
761  MAX_ALLOWED_FOR_STORAGE(cache));
762 
763  if (UNIV_UNLIKELY(heap != NULL)) {
764 
765  /* this means that rec_get_offsets() has created a new
766  heap and has stored offsets in it; check that this is
767  really the case and free the heap */
768  ut_a(offsets != offsets_onstack);
769  mem_heap_free(heap);
770  }
771  }
772 
773  mtr_commit(&mtr);
774 
775  if (*lock_data == NULL) {
776 
777  return(FALSE);
778  }
779 
780  return(TRUE);
781 }
782 
783 /*******************************************************************/
787 static
788 ibool
789 fill_locks_row(
790 /*===========*/
791  i_s_locks_row_t* row,
792  const lock_t* lock,
793  ulint heap_no,
796  trx_i_s_cache_t* cache)
798 {
799  row->lock_trx_id = lock_get_trx_id(lock);
800  row->lock_mode = lock_get_mode_str(lock);
801  row->lock_type = lock_get_type_str(lock);
802 
804  cache->storage, lock_get_table_name(lock),
805  MAX_ALLOWED_FOR_STORAGE(cache));
806 
807  /* memory could not be allocated */
808  if (row->lock_table == NULL) {
809 
810  return(FALSE);
811  }
812 
813  switch (lock_get_type(lock)) {
814  case LOCK_REC:
816  cache->storage, lock_rec_get_index_name(lock),
817  MAX_ALLOWED_FOR_STORAGE(cache));
818 
819  /* memory could not be allocated */
820  if (row->lock_index == NULL) {
821 
822  return(FALSE);
823  }
824 
825  row->lock_space = lock_rec_get_space_id(lock);
826  row->lock_page = lock_rec_get_page_no(lock);
827  row->lock_rec = heap_no;
828 
829  if (!fill_lock_data(&row->lock_data, lock, heap_no, cache)) {
830 
831  /* memory could not be allocated */
832  return(FALSE);
833  }
834 
835  break;
836  case LOCK_TABLE:
837  row->lock_index = NULL;
838 
839  row->lock_space = ULINT_UNDEFINED;
840  row->lock_page = ULINT_UNDEFINED;
841  row->lock_rec = ULINT_UNDEFINED;
842 
843  row->lock_data = NULL;
844 
845  break;
846  default:
847  ut_error;
848  }
849 
850  row->lock_table_id = lock_get_table_id(lock);
851 
852  row->hash_chain.value = row;
853  ut_ad(i_s_locks_row_validate(row));
854 
855  return(TRUE);
856 }
857 
858 /*******************************************************************/
861 static
863 fill_lock_waits_row(
864 /*================*/
865  i_s_lock_waits_row_t* row,
867  const i_s_locks_row_t* requested_lock_row,
870  const i_s_locks_row_t* blocking_lock_row)
873 {
874  ut_ad(i_s_locks_row_validate(requested_lock_row));
875  ut_ad(i_s_locks_row_validate(blocking_lock_row));
876 
877  row->requested_lock_row = requested_lock_row;
878  row->blocking_lock_row = blocking_lock_row;
879 
880  return(row);
881 }
882 
883 /*******************************************************************/
889 static
890 ulint
891 fold_lock(
892 /*======*/
893  const lock_t* lock,
894  ulint heap_no)
897 {
898 #ifdef TEST_LOCK_FOLD_ALWAYS_DIFFERENT
899  static ulint fold = 0;
900 
901  return(fold++);
902 #else
903  ulint ret;
904 
905  switch (lock_get_type(lock)) {
906  case LOCK_REC:
907  ut_a(heap_no != ULINT_UNDEFINED);
908 
909  ret = ut_fold_ulint_pair((ulint) lock_get_trx_id(lock),
910  lock_rec_get_space_id(lock));
911 
912  ret = ut_fold_ulint_pair(ret,
913  lock_rec_get_page_no(lock));
914 
915  ret = ut_fold_ulint_pair(ret, heap_no);
916 
917  break;
918  case LOCK_TABLE:
919  /* this check is actually not necessary for continuing
920  correct operation, but something must have gone wrong if
921  it fails. */
922  ut_a(heap_no == ULINT_UNDEFINED);
923 
924  ret = (ulint) lock_get_table_id(lock);
925 
926  break;
927  default:
928  ut_error;
929  }
930 
931  return(ret);
932 #endif
933 }
934 
935 /*******************************************************************/
938 static
939 ibool
940 locks_row_eq_lock(
941 /*==============*/
942  const i_s_locks_row_t* row,
943  const lock_t* lock,
944  ulint heap_no)
947 {
948  ut_ad(i_s_locks_row_validate(row));
949 #ifdef TEST_NO_LOCKS_ROW_IS_EVER_EQUAL_TO_LOCK_T
950  return(0);
951 #else
952  switch (lock_get_type(lock)) {
953  case LOCK_REC:
954  ut_a(heap_no != ULINT_UNDEFINED);
955 
956  return(row->lock_trx_id == lock_get_trx_id(lock)
957  && row->lock_space == lock_rec_get_space_id(lock)
958  && row->lock_page == lock_rec_get_page_no(lock)
959  && row->lock_rec == heap_no);
960 
961  case LOCK_TABLE:
962  /* this check is actually not necessary for continuing
963  correct operation, but something must have gone wrong if
964  it fails. */
965  ut_a(heap_no == ULINT_UNDEFINED);
966 
967  return(row->lock_trx_id == lock_get_trx_id(lock)
968  && row->lock_table_id == lock_get_table_id(lock));
969 
970  default:
971  ut_error;
972  return(FALSE);
973  }
974 #endif
975 }
976 
977 /*******************************************************************/
982 static
984 search_innodb_locks(
985 /*================*/
986  trx_i_s_cache_t* cache,
987  const lock_t* lock,
988  ulint heap_no)
991 {
992  i_s_hash_chain_t* hash_chain;
993 
994  HASH_SEARCH(
995  /* hash_chain->"next" */
996  next,
997  /* the hash table */
998  cache->locks_hash,
999  /* fold */
1000  fold_lock(lock, heap_no),
1001  /* the type of the next variable */
1003  /* auxiliary variable */
1004  hash_chain,
1005  /* assertion on every traversed item */
1006  ut_ad(i_s_locks_row_validate(hash_chain->value)),
1007  /* this determines if we have found the lock */
1008  locks_row_eq_lock(hash_chain->value, lock, heap_no));
1009 
1010  if (hash_chain == NULL) {
1011 
1012  return(NULL);
1013  }
1014  /* else */
1015 
1016  return(hash_chain->value);
1017 }
1018 
1019 /*******************************************************************/
1025 static
1027 add_lock_to_cache(
1028 /*==============*/
1029  trx_i_s_cache_t* cache,
1030  const lock_t* lock,
1031  ulint heap_no)
1034 {
1035  i_s_locks_row_t* dst_row;
1036 
1037 #ifdef TEST_ADD_EACH_LOCKS_ROW_MANY_TIMES
1038  ulint i;
1039  for (i = 0; i < 10000; i++) {
1040 #endif
1041 #ifndef TEST_DO_NOT_CHECK_FOR_DUPLICATE_ROWS
1042  /* quit if this lock is already present */
1043  dst_row = search_innodb_locks(cache, lock, heap_no);
1044  if (dst_row != NULL) {
1045 
1046  ut_ad(i_s_locks_row_validate(dst_row));
1047  return(dst_row);
1048  }
1049 #endif
1050 
1051  dst_row = (i_s_locks_row_t*)
1052  table_cache_create_empty_row(&cache->innodb_locks, cache);
1053 
1054  /* memory could not be allocated */
1055  if (dst_row == NULL) {
1056 
1057  return(NULL);
1058  }
1059 
1060  if (!fill_locks_row(dst_row, lock, heap_no, cache)) {
1061 
1062  /* memory could not be allocated */
1063  cache->innodb_locks.rows_used--;
1064  return(NULL);
1065  }
1066 
1067 #ifndef TEST_DO_NOT_INSERT_INTO_THE_HASH_TABLE
1068  HASH_INSERT(
1069  /* the type used in the hash chain */
1071  /* hash_chain->"next" */
1072  next,
1073  /* the hash table */
1074  cache->locks_hash,
1075  /* fold */
1076  fold_lock(lock, heap_no),
1077  /* add this data to the hash */
1078  &dst_row->hash_chain);
1079 #endif
1080 #ifdef TEST_ADD_EACH_LOCKS_ROW_MANY_TIMES
1081  } /* for()-loop */
1082 #endif
1083 
1084  ut_ad(i_s_locks_row_validate(dst_row));
1085  return(dst_row);
1086 }
1087 
1088 /*******************************************************************/
1092 static
1093 ibool
1094 add_lock_wait_to_cache(
1095 /*===================*/
1096  trx_i_s_cache_t* cache,
1097  const i_s_locks_row_t* requested_lock_row,
1100  const i_s_locks_row_t* blocking_lock_row)
1103 {
1104  i_s_lock_waits_row_t* dst_row;
1105 
1106  dst_row = (i_s_lock_waits_row_t*)
1107  table_cache_create_empty_row(&cache->innodb_lock_waits,
1108  cache);
1109 
1110  /* memory could not be allocated */
1111  if (dst_row == NULL) {
1112 
1113  return(FALSE);
1114  }
1115 
1116  fill_lock_waits_row(dst_row, requested_lock_row, blocking_lock_row);
1117 
1118  return(TRUE);
1119 }
1120 
1121 /*******************************************************************/
1129 static
1130 ibool
1131 add_trx_relevant_locks_to_cache(
1132 /*============================*/
1133  trx_i_s_cache_t* cache,
1134  const trx_t* trx,
1135  i_s_locks_row_t** requested_lock_row)
1138 {
1139  ut_ad(mutex_own(&kernel_mutex));
1140 
1141  /* If transaction is waiting we add the wait lock and all locks
1142  from another transactions that are blocking the wait lock. */
1143  if (trx->que_state == TRX_QUE_LOCK_WAIT) {
1144 
1145  const lock_t* curr_lock;
1146  ulint wait_lock_heap_no;
1147  i_s_locks_row_t* blocking_lock_row;
1148  lock_queue_iterator_t iter;
1149 
1150  ut_a(trx->wait_lock != NULL);
1151 
1152  wait_lock_heap_no
1153  = wait_lock_get_heap_no(trx->wait_lock);
1154 
1155  /* add the requested lock */
1156  *requested_lock_row
1157  = add_lock_to_cache(cache, trx->wait_lock,
1158  wait_lock_heap_no);
1159 
1160  /* memory could not be allocated */
1161  if (*requested_lock_row == NULL) {
1162 
1163  return(FALSE);
1164  }
1165 
1166  /* then iterate over the locks before the wait lock and
1167  add the ones that are blocking it */
1168 
1170  ULINT_UNDEFINED);
1171 
1172  curr_lock = lock_queue_iterator_get_prev(&iter);
1173  while (curr_lock != NULL) {
1174 
1175  if (lock_has_to_wait(trx->wait_lock,
1176  curr_lock)) {
1177 
1178  /* add the lock that is
1179  blocking trx->wait_lock */
1180  blocking_lock_row
1181  = add_lock_to_cache(
1182  cache, curr_lock,
1183  /* heap_no is the same
1184  for the wait and waited
1185  locks */
1186  wait_lock_heap_no);
1187 
1188  /* memory could not be allocated */
1189  if (blocking_lock_row == NULL) {
1190 
1191  return(FALSE);
1192  }
1193 
1194  /* add the relation between both locks
1195  to innodb_lock_waits */
1196  if (!add_lock_wait_to_cache(
1197  cache, *requested_lock_row,
1198  blocking_lock_row)) {
1199 
1200  /* memory could not be allocated */
1201  return(FALSE);
1202  }
1203  }
1204 
1205  curr_lock = lock_queue_iterator_get_prev(&iter);
1206  }
1207  } else {
1208 
1209  *requested_lock_row = NULL;
1210  }
1211 
1212  return(TRUE);
1213 }
1214 
1219 #define CACHE_MIN_IDLE_TIME_US 100000 /* 0.1 sec */
1220 
1221 /*******************************************************************/
1224 static
1225 ibool
1226 can_cache_be_updated(
1227 /*=================*/
1228  trx_i_s_cache_t* cache)
1229 {
1230  ullint now;
1231 
1232  /* Here we read cache->last_read without acquiring its mutex
1233  because last_read is only updated when a shared rw lock on the
1234  whole cache is being held (see trx_i_s_cache_end_read()) and
1235  we are currently holding an exclusive rw lock on the cache.
1236  So it is not possible for last_read to be updated while we are
1237  reading it. */
1238 
1239 #ifdef UNIV_SYNC_DEBUG
1240  ut_a(rw_lock_own(&cache->rw_lock, RW_LOCK_EX));
1241 #endif
1242 
1243  now = ut_time_us(NULL);
1244  if (now - cache->last_read > CACHE_MIN_IDLE_TIME_US) {
1245 
1246  return(TRUE);
1247  }
1248 
1249  return(FALSE);
1250 }
1251 
1252 /*******************************************************************/
1255 static
1256 void
1257 trx_i_s_cache_clear(
1258 /*================*/
1259  trx_i_s_cache_t* cache)
1260 {
1261  cache->innodb_trx.rows_used = 0;
1262  cache->innodb_locks.rows_used = 0;
1263  cache->innodb_lock_waits.rows_used = 0;
1264 
1265  hash_table_clear(cache->locks_hash);
1266 
1267  ha_storage_empty(&cache->storage);
1268 }
1269 
1270 /*******************************************************************/
1273 static
1274 void
1275 fetch_data_into_cache(
1276 /*==================*/
1277  trx_i_s_cache_t* cache)
1278 {
1279  trx_t* trx;
1280  i_s_trx_row_t* trx_row;
1281  i_s_locks_row_t* requested_lock_row;
1282 
1283  ut_ad(mutex_own(&kernel_mutex));
1284 
1285  trx_i_s_cache_clear(cache);
1286 
1287  /* We iterate over the list of all transactions and add each one
1288  to innodb_trx's cache. We also add all locks that are relevant
1289  to each transaction into innodb_locks' and innodb_lock_waits'
1290  caches. */
1291 
1292  for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
1293  trx != NULL;
1294  trx = UT_LIST_GET_NEXT(trx_list, trx)) {
1295 
1296  if (!add_trx_relevant_locks_to_cache(cache, trx,
1297  &requested_lock_row)) {
1298 
1299  cache->is_truncated = TRUE;
1300  return;
1301  }
1302 
1303  trx_row = (i_s_trx_row_t*)
1304  table_cache_create_empty_row(&cache->innodb_trx,
1305  cache);
1306 
1307  /* memory could not be allocated */
1308  if (trx_row == NULL) {
1309 
1310  cache->is_truncated = TRUE;
1311  return;
1312  }
1313 
1314  if (!fill_trx_row(trx_row, trx, requested_lock_row, cache)) {
1315 
1316  /* memory could not be allocated */
1317  cache->innodb_trx.rows_used--;
1318  cache->is_truncated = TRUE;
1319  return;
1320  }
1321  }
1322 
1323  cache->is_truncated = FALSE;
1324 }
1325 
1326 /*******************************************************************/
1330 UNIV_INTERN
1331 int
1333 /*===================================*/
1334  trx_i_s_cache_t* cache)
1335 {
1336  if (!can_cache_be_updated(cache)) {
1337 
1338  return(1);
1339  }
1340 
1341  /* We need to read trx_sys and record/table lock queues */
1342  mutex_enter(&kernel_mutex);
1343 
1344  fetch_data_into_cache(cache);
1345 
1346  mutex_exit(&kernel_mutex);
1347 
1348  return(0);
1349 }
1350 
1351 /*******************************************************************/
1355 UNIV_INTERN
1356 ibool
1358 /*=======================*/
1359  trx_i_s_cache_t* cache)
1360 {
1361  return(cache->is_truncated);
1362 }
1363 
1364 /*******************************************************************/
1366 UNIV_INTERN
1367 void
1369 /*===============*/
1370  trx_i_s_cache_t* cache)
1371 {
1372  /* The latching is done in the following order:
1373  acquire trx_i_s_cache_t::rw_lock, X
1374  acquire kernel_mutex
1375  release kernel_mutex
1376  release trx_i_s_cache_t::rw_lock
1377  acquire trx_i_s_cache_t::rw_lock, S
1378  acquire trx_i_s_cache_t::last_read_mutex
1379  release trx_i_s_cache_t::last_read_mutex
1380  release trx_i_s_cache_t::rw_lock */
1381 
1382  rw_lock_create(trx_i_s_cache_lock_key, &cache->rw_lock,
1383  SYNC_TRX_I_S_RWLOCK);
1384 
1385  cache->last_read = 0;
1386 
1387  mutex_create(cache_last_read_mutex_key,
1388  &cache->last_read_mutex, SYNC_TRX_I_S_LAST_READ);
1389 
1390  table_cache_init(&cache->innodb_trx, sizeof(i_s_trx_row_t));
1391  table_cache_init(&cache->innodb_locks, sizeof(i_s_locks_row_t));
1392  table_cache_init(&cache->innodb_lock_waits,
1393  sizeof(i_s_lock_waits_row_t));
1394 
1395  cache->locks_hash = hash_create(LOCKS_HASH_CELLS_NUM);
1396 
1397  cache->storage = ha_storage_create(CACHE_STORAGE_INITIAL_SIZE,
1398  CACHE_STORAGE_HASH_CELLS);
1399 
1400  cache->mem_allocd = 0;
1401 
1402  cache->is_truncated = FALSE;
1403 }
1404 
1405 /*******************************************************************/
1407 UNIV_INTERN
1408 void
1410 /*===============*/
1411  trx_i_s_cache_t* cache)
1412 {
1413  hash_table_free(cache->locks_hash);
1414  ha_storage_free(cache->storage);
1415  table_cache_free(&cache->innodb_trx);
1416  table_cache_free(&cache->innodb_locks);
1417  table_cache_free(&cache->innodb_lock_waits);
1418  memset(cache, 0, sizeof *cache);
1419 }
1420 
1421 /*******************************************************************/
1423 UNIV_INTERN
1424 void
1426 /*=====================*/
1427  trx_i_s_cache_t* cache)
1428 {
1429  rw_lock_s_lock(&cache->rw_lock);
1430 }
1431 
1432 /*******************************************************************/
1434 UNIV_INTERN
1435 void
1437 /*===================*/
1438  trx_i_s_cache_t* cache)
1439 {
1440  ullint now;
1441 
1442 #ifdef UNIV_SYNC_DEBUG
1443  ut_a(rw_lock_own(&cache->rw_lock, RW_LOCK_SHARED));
1444 #endif
1445 
1446  /* update cache last read time */
1447  now = ut_time_us(NULL);
1448  mutex_enter(&cache->last_read_mutex);
1449  cache->last_read = now;
1450  mutex_exit(&cache->last_read_mutex);
1451 
1452  rw_lock_s_unlock(&cache->rw_lock);
1453 }
1454 
1455 /*******************************************************************/
1457 UNIV_INTERN
1458 void
1460 /*======================*/
1461  trx_i_s_cache_t* cache)
1462 {
1463  rw_lock_x_lock(&cache->rw_lock);
1464 }
1465 
1466 /*******************************************************************/
1468 UNIV_INTERN
1469 void
1471 /*====================*/
1472  trx_i_s_cache_t* cache)
1473 {
1474 #ifdef UNIV_SYNC_DEBUG
1475  ut_a(rw_lock_own(&cache->rw_lock, RW_LOCK_EX));
1476 #endif
1477 
1478  rw_lock_x_unlock(&cache->rw_lock);
1479 }
1480 
1481 /*******************************************************************/
1484 static
1486 cache_select_table(
1487 /*===============*/
1488  trx_i_s_cache_t* cache,
1489  enum i_s_table table)
1490 {
1491  i_s_table_cache_t* table_cache;
1492 
1493 #ifdef UNIV_SYNC_DEBUG
1494  ut_a(rw_lock_own(&cache->rw_lock, RW_LOCK_SHARED)
1495  || rw_lock_own(&cache->rw_lock, RW_LOCK_EX));
1496 #endif
1497 
1498  switch (table) {
1499  case I_S_INNODB_TRX:
1500  table_cache = &cache->innodb_trx;
1501  break;
1502  case I_S_INNODB_LOCKS:
1503  table_cache = &cache->innodb_locks;
1504  break;
1505  case I_S_INNODB_LOCK_WAITS:
1506  table_cache = &cache->innodb_lock_waits;
1507  break;
1508  default:
1509  ut_error;
1510  }
1511 
1512  return(table_cache);
1513 }
1514 
1515 /*******************************************************************/
1519 UNIV_INTERN
1520 ulint
1522 /*========================*/
1523  trx_i_s_cache_t* cache,
1524  enum i_s_table table)
1525 {
1526  i_s_table_cache_t* table_cache;
1527 
1528  table_cache = cache_select_table(cache, table);
1529 
1530  return(table_cache->rows_used);
1531 }
1532 
1533 /*******************************************************************/
1537 UNIV_INTERN
1538 void*
1540 /*======================*/
1541  trx_i_s_cache_t* cache,
1542  enum i_s_table table,
1543  ulint n)
1544 {
1545  i_s_table_cache_t* table_cache;
1546  ulint i;
1547  void* row;
1548 
1549  table_cache = cache_select_table(cache, table);
1550 
1551  ut_a(n < table_cache->rows_used);
1552 
1553  row = NULL;
1554 
1555  for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
1556 
1557  if (table_cache->chunks[i].offset
1558  + table_cache->chunks[i].rows_allocd > n) {
1559 
1560  row = (char*) table_cache->chunks[i].base
1561  + (n - table_cache->chunks[i].offset)
1562  * table_cache->row_size;
1563  break;
1564  }
1565  }
1566 
1567  ut_a(row != NULL);
1568 
1569  return(row);
1570 }
1571 
1572 /*******************************************************************/
1578 UNIV_INTERN
1579 char*
1581 /*===================*/
1582  const i_s_locks_row_t* row,
1583  char* lock_id,
1584  ulint lock_id_size)
1586 {
1587  int res_len;
1588 
1589  /* please adjust TRX_I_S_LOCK_ID_MAX_LEN if you change this */
1590 
1591  if (row->lock_space != ULINT_UNDEFINED) {
1592  /* record lock */
1593  res_len = ut_snprintf(lock_id, lock_id_size,
1594  TRX_ID_FMT ":%lu:%lu:%lu",
1595  row->lock_trx_id, row->lock_space,
1596  row->lock_page, row->lock_rec);
1597  } else {
1598  /* table lock */
1599  res_len = ut_snprintf(lock_id, lock_id_size,
1600  TRX_ID_FMT ":" TRX_ID_FMT,
1601  row->lock_trx_id,
1602  row->lock_table_id);
1603  }
1604 
1605  /* the typecast is safe because snprintf(3) never returns
1606  negative result */
1607  ut_a(res_len >= 0);
1608  ut_a((ulint) res_len < lock_id_size);
1609 
1610  return(lock_id);
1611 }