1 /*****************************************************************************
2
3 Copyright (c) 2007, 2021, Oracle and/or its affiliates.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation. The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License, version 2.0, for more details.
20
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24
25 *****************************************************************************/
26
27 /**************************************************//**
28 @file trx/trx0i_s.cc
29 INFORMATION SCHEMA innodb_trx, innodb_locks and
30 innodb_lock_waits tables fetch code.
31
32 The code below fetches information needed to fill those
33 3 dynamic tables and uploads it into a "transactions
34 table cache" for later retrieval.
35
36 Created July 17, 2007 Vasil Dimov
37 *******************************************************/
38
39 /* Found during the build of 5.5.3 on Linux 2.4 and early 2.6 kernels:
40 The includes "univ.i" -> "my_global.h" cause a different path
41 to be taken further down with pthread functions and types,
42 so they must come first.
43 From the symptoms, this is related to bug#46587 in the MySQL bug DB.
44 */
45
46 #include "ha_prototypes.h"
47 #include <sql_class.h>
48
49 #include "buf0buf.h"
50 #include "dict0dict.h"
51 #include "ha0storage.h"
52 #include "hash0hash.h"
53 #include "lock0iter.h"
54 #include "lock0lock.h"
55 #include "mem0mem.h"
56 #include "page0page.h"
57 #include "rem0rec.h"
58 #include "row0row.h"
59 #include "srv0srv.h"
60 #include "sync0rw.h"
61 #include "sync0sync.h"
62 #include "trx0i_s.h"
63 #include "trx0sys.h"
64 #include "trx0trx.h"
65 #include "ut0mem.h"
66
67 /** Initial number of rows in the table cache */
68 #define TABLE_CACHE_INITIAL_ROWSNUM 1024
69
70 /** @brief The maximum number of chunks to allocate for a table cache.
71
72 The rows of a table cache are stored in a set of chunks. When a new
73 row is added a new chunk is allocated if necessary. Assuming that the
74 first one is 1024 rows (TABLE_CACHE_INITIAL_ROWSNUM) and each
75 subsequent is N/2 where N is the number of rows we have allocated till
76 now, then 39th chunk would accommodate 1677416425 rows and all chunks
77 would accommodate 3354832851 rows. */
78 #define MEM_CHUNKS_IN_TABLE_CACHE 39
79
80 /** The following are some testing auxiliary macros. Do not enable them
81 in a production environment. */
82 /* @{ */
83
84 #if 0
85 /** If this is enabled then lock folds will always be different
86 resulting in equal rows being put in a different cells of the hash
87 table. Checking for duplicates will be flawed because different
88 fold will be calculated when a row is searched in the hash table. */
89 #define TEST_LOCK_FOLD_ALWAYS_DIFFERENT
90 #endif
91
92 #if 0
93 /** This effectively kills the search-for-duplicate-before-adding-a-row
94 function, but searching in the hash is still performed. It will always
95 be assumed that lock is not present and insertion will be performed in
96 the hash table. */
97 #define TEST_NO_LOCKS_ROW_IS_EVER_EQUAL_TO_LOCK_T
98 #endif
99
100 #if 0
101 /** This aggressively repeats adding each row many times. Depending on
102 the above settings this may be noop or may result in lots of rows being
103 added. */
104 #define TEST_ADD_EACH_LOCKS_ROW_MANY_TIMES
105 #endif
106
107 #if 0
108 /** Very similar to TEST_NO_LOCKS_ROW_IS_EVER_EQUAL_TO_LOCK_T but hash
109 table search is not performed at all. */
110 #define TEST_DO_NOT_CHECK_FOR_DUPLICATE_ROWS
111 #endif
112
113 #if 0
114 /** Do not insert each row into the hash table, duplicates may appear
115 if this is enabled, also if this is enabled searching into the hash is
116 noop because it will be empty. */
117 #define TEST_DO_NOT_INSERT_INTO_THE_HASH_TABLE
118 #endif
119 /* @} */
120
121 /** Memory limit passed to ha_storage_put_memlim().
122 @param cache hash storage
123 @return maximum allowed allocation size */
124 #define MAX_ALLOWED_FOR_STORAGE(cache) \
125 (TRX_I_S_MEM_LIMIT \
126 - (cache)->mem_allocd)
127
128 /** Memory limit in table_cache_create_empty_row().
129 @param cache hash storage
130 @return maximum allowed allocation size */
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
136 /** Memory for each table in the intermediate buffer is allocated in
137 separate chunks. These chunks are considered to be concatenated to
138 represent one flat array of rows. */
139 struct i_s_mem_chunk_t {
140 ulint offset; /*!< offset, in number of rows */
141 ulint rows_allocd; /*!< the size of this chunk, in number
142 of rows */
143 void* base; /*!< start of the chunk */
144 };
145
146 /** This represents one table's cache. */
147 struct i_s_table_cache_t {
148 ulint rows_used; /*!< number of used rows */
149 ulint rows_allocd; /*!< number of allocated rows */
150 ulint row_size; /*!< size of a single row */
151 i_s_mem_chunk_t chunks[MEM_CHUNKS_IN_TABLE_CACHE]; /*!< array of
152 memory chunks that stores the
153 rows */
154 };
155
156 /** This structure describes the intermediate buffer */
157 struct trx_i_s_cache_t {
158 rw_lock_t* rw_lock; /*!< read-write lock protecting
159 the rest of this structure */
160 ib_time_monotonic_us_t last_read;/*!< last time the cache was read;
161 measured in microseconds since
162 epoch */
163 ib_mutex_t last_read_mutex;/*!< mutex protecting the
164 last_read member - it is updated
165 inside a shared lock of the
166 rw_lock member */
167 i_s_table_cache_t innodb_trx; /*!< innodb_trx table */
168 i_s_table_cache_t innodb_locks; /*!< innodb_locks table */
169 i_s_table_cache_t innodb_lock_waits;/*!< innodb_lock_waits table */
170 /** the hash table size is LOCKS_HASH_CELLS_NUM * sizeof(void*) bytes */
171 #define LOCKS_HASH_CELLS_NUM 10000
172 hash_table_t* locks_hash; /*!< hash table used to eliminate
173 duplicate entries in the
174 innodb_locks table */
175 /** Initial size of the cache storage */
176 #define CACHE_STORAGE_INITIAL_SIZE 1024
177 /** Number of hash cells in the cache storage */
178 #define CACHE_STORAGE_HASH_CELLS 2048
179 ha_storage_t* storage; /*!< storage for external volatile
180 data that may become unavailable
181 when we release
182 lock_sys->mutex or trx_sys->mutex */
183 ulint mem_allocd; /*!< the amount of memory
184 allocated with mem_alloc*() */
185 ibool is_truncated; /*!< this is TRUE if the memory
186 limit was hit and thus the data
187 in the cache is truncated */
188 };
189
190 /** This is the intermediate buffer where data needed to fill the
191 INFORMATION SCHEMA tables is fetched and later retrieved by the C++
192 code in handler/i_s.cc. */
193 static trx_i_s_cache_t trx_i_s_cache_static;
194 /** This is the intermediate buffer where data needed to fill the
195 INFORMATION SCHEMA tables is fetched and later retrieved by the C++
196 code in handler/i_s.cc. */
197 trx_i_s_cache_t* trx_i_s_cache = &trx_i_s_cache_static;
198
199 /*******************************************************************//**
200 For a record lock that is in waiting state retrieves the only bit that
201 is set, for a table lock returns ULINT_UNDEFINED.
202 @return record number within the heap */
203 static
204 ulint
wait_lock_get_heap_no(const lock_t * lock)205 wait_lock_get_heap_no(
206 /*==================*/
207 const lock_t* lock) /*!< in: lock */
208 {
209 ulint ret;
210
211 switch (lock_get_type(lock)) {
212 case LOCK_REC:
213 ret = lock_rec_find_set_bit(lock);
214 ut_a(ret != ULINT_UNDEFINED);
215 break;
216 case LOCK_TABLE:
217 ret = ULINT_UNDEFINED;
218 break;
219 default:
220 ut_error;
221 }
222
223 return(ret);
224 }
225
226 /*******************************************************************//**
227 Initializes the members of a table cache. */
228 static
229 void
table_cache_init(i_s_table_cache_t * table_cache,size_t row_size)230 table_cache_init(
231 /*=============*/
232 i_s_table_cache_t* table_cache, /*!< out: table cache */
233 size_t row_size) /*!< in: the size of a
234 row */
235 {
236 ulint i;
237
238 table_cache->rows_used = 0;
239 table_cache->rows_allocd = 0;
240 table_cache->row_size = row_size;
241
242 for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
243
244 /* the memory is actually allocated in
245 table_cache_create_empty_row() */
246 table_cache->chunks[i].base = NULL;
247 }
248 }
249
250 /*******************************************************************//**
251 Frees a table cache. */
252 static
253 void
table_cache_free(i_s_table_cache_t * table_cache)254 table_cache_free(
255 /*=============*/
256 i_s_table_cache_t* table_cache) /*!< in/out: table cache */
257 {
258 ulint i;
259
260 for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
261
262 /* the memory is actually allocated in
263 table_cache_create_empty_row() */
264 if (table_cache->chunks[i].base) {
265 ut_free(table_cache->chunks[i].base);
266 table_cache->chunks[i].base = NULL;
267 }
268 }
269 }
270
271 /*******************************************************************//**
272 Returns an empty row from a table cache. The row is allocated if no more
273 empty rows are available. The number of used rows is incremented.
274 If the memory limit is hit then NULL is returned and nothing is
275 allocated.
276 @return empty row, or NULL if out of memory */
277 static
278 void*
table_cache_create_empty_row(i_s_table_cache_t * table_cache,trx_i_s_cache_t * cache)279 table_cache_create_empty_row(
280 /*=========================*/
281 i_s_table_cache_t* table_cache, /*!< in/out: table cache */
282 trx_i_s_cache_t* cache) /*!< in/out: cache to record
283 how many bytes are
284 allocated */
285 {
286 ulint i;
287 void* row;
288
289 ut_a(table_cache->rows_used <= table_cache->rows_allocd);
290
291 if (table_cache->rows_used == table_cache->rows_allocd) {
292
293 /* rows_used == rows_allocd means that new chunk needs
294 to be allocated: either no more empty rows in the
295 last allocated chunk or nothing has been allocated yet
296 (rows_num == rows_allocd == 0); */
297
298 i_s_mem_chunk_t* chunk;
299 ulint req_bytes;
300 ulint got_bytes;
301 ulint req_rows;
302 ulint got_rows;
303
304 /* find the first not allocated chunk */
305 for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
306
307 if (table_cache->chunks[i].base == NULL) {
308
309 break;
310 }
311 }
312
313 /* i == MEM_CHUNKS_IN_TABLE_CACHE means that all chunks
314 have been allocated :-X */
315 ut_a(i < MEM_CHUNKS_IN_TABLE_CACHE);
316
317 /* allocate the chunk we just found */
318
319 if (i == 0) {
320
321 /* first chunk, nothing is allocated yet */
322 req_rows = TABLE_CACHE_INITIAL_ROWSNUM;
323 } else {
324
325 /* Memory is increased by the formula
326 new = old + old / 2; We are trying not to be
327 aggressive here (= using the common new = old * 2)
328 because the allocated memory will not be freed
329 until InnoDB exit (it is reused). So it is better
330 to once allocate the memory in more steps, but
331 have less unused/wasted memory than to use less
332 steps in allocation (which is done once in a
333 lifetime) but end up with lots of unused/wasted
334 memory. */
335 req_rows = table_cache->rows_allocd / 2;
336 }
337 req_bytes = req_rows * table_cache->row_size;
338
339 if (req_bytes > MAX_ALLOWED_FOR_ALLOC(cache)) {
340
341 return(NULL);
342 }
343
344 chunk = &table_cache->chunks[i];
345
346 got_bytes = req_bytes;
347 chunk->base = ut_malloc_nokey(req_bytes);
348
349 got_rows = got_bytes / table_cache->row_size;
350
351 cache->mem_allocd += got_bytes;
352
353 #if 0
354 printf("allocating chunk %d req bytes=%lu, got bytes=%lu,"
355 " row size=%lu,"
356 " req rows=%lu, got rows=%lu\n",
357 i, req_bytes, got_bytes,
358 table_cache->row_size,
359 req_rows, got_rows);
360 #endif
361
362 chunk->rows_allocd = got_rows;
363
364 table_cache->rows_allocd += got_rows;
365
366 /* adjust the offset of the next chunk */
367 if (i < MEM_CHUNKS_IN_TABLE_CACHE - 1) {
368
369 table_cache->chunks[i + 1].offset
370 = chunk->offset + chunk->rows_allocd;
371 }
372
373 /* return the first empty row in the newly allocated
374 chunk */
375 row = chunk->base;
376 } else {
377
378 char* chunk_start;
379 ulint offset;
380
381 /* there is an empty row, no need to allocate new
382 chunks */
383
384 /* find the first chunk that contains allocated but
385 empty/unused rows */
386 for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
387
388 if (table_cache->chunks[i].offset
389 + table_cache->chunks[i].rows_allocd
390 > table_cache->rows_used) {
391
392 break;
393 }
394 }
395
396 /* i == MEM_CHUNKS_IN_TABLE_CACHE means that all chunks
397 are full, but
398 table_cache->rows_used != table_cache->rows_allocd means
399 exactly the opposite - there are allocated but
400 empty/unused rows :-X */
401 ut_a(i < MEM_CHUNKS_IN_TABLE_CACHE);
402
403 chunk_start = (char*) table_cache->chunks[i].base;
404 offset = table_cache->rows_used
405 - table_cache->chunks[i].offset;
406
407 row = chunk_start + offset * table_cache->row_size;
408 }
409
410 table_cache->rows_used++;
411
412 return(row);
413 }
414
415 #ifdef UNIV_DEBUG
416 /*******************************************************************//**
417 Validates a row in the locks cache.
418 @return TRUE if valid */
419 static
420 ibool
i_s_locks_row_validate(const i_s_locks_row_t * row)421 i_s_locks_row_validate(
422 /*===================*/
423 const i_s_locks_row_t* row) /*!< in: row to validate */
424 {
425 ut_ad(row->lock_mode != NULL);
426 ut_ad(row->lock_type != NULL);
427 ut_ad(row->lock_table != NULL);
428 ut_ad(row->lock_table_id != 0);
429
430 if (row->lock_space == ULINT_UNDEFINED) {
431 /* table lock */
432 ut_ad(!strcmp("TABLE", row->lock_type));
433 ut_ad(row->lock_index == NULL);
434 ut_ad(row->lock_data == NULL);
435 ut_ad(row->lock_page == ULINT_UNDEFINED);
436 ut_ad(row->lock_rec == ULINT_UNDEFINED);
437 } else {
438 /* record lock */
439 ut_ad(!strcmp("RECORD", row->lock_type));
440 ut_ad(row->lock_index != NULL);
441 /* row->lock_data == NULL if buf_page_try_get() == NULL */
442 ut_ad(row->lock_page != ULINT_UNDEFINED);
443 ut_ad(row->lock_rec != ULINT_UNDEFINED);
444 }
445
446 return(TRUE);
447 }
448 #endif /* UNIV_DEBUG */
449
450 /*******************************************************************//**
451 Fills i_s_trx_row_t object.
452 If memory can not be allocated then FALSE is returned.
453 @return FALSE if allocation fails */
454 static
455 ibool
fill_trx_row(i_s_trx_row_t * row,const trx_t * trx,const i_s_locks_row_t * requested_lock_row,trx_i_s_cache_t * cache)456 fill_trx_row(
457 /*=========*/
458 i_s_trx_row_t* row, /*!< out: result object
459 that's filled */
460 const trx_t* trx, /*!< in: transaction to
461 get data from */
462 const i_s_locks_row_t* requested_lock_row,/*!< in: pointer to the
463 corresponding row in
464 innodb_locks if trx is
465 waiting or NULL if trx
466 is not waiting */
467 trx_i_s_cache_t* cache) /*!< in/out: cache into
468 which to copy volatile
469 strings */
470 {
471 size_t stmt_len;
472 const char* s;
473
474 ut_ad(lock_mutex_own());
475
476 row->trx_id = trx_get_id_for_print(trx);
477 row->trx_started = (ib_time_t) trx->start_time;
478 row->trx_state = trx_get_que_state_str(trx);
479 row->requested_lock_row = requested_lock_row;
480 ut_ad(requested_lock_row == NULL
481 || i_s_locks_row_validate(requested_lock_row));
482
483 if (trx->lock.wait_lock != NULL) {
484
485 ut_a(requested_lock_row != NULL);
486 row->trx_wait_started = (ib_time_t) trx->lock.wait_started;
487 } else {
488 ut_a(requested_lock_row == NULL);
489 row->trx_wait_started = 0;
490 }
491
492 row->trx_weight = static_cast<uintmax_t>(TRX_WEIGHT(trx));
493
494 if (trx->mysql_thd == NULL) {
495 /* For internal transactions e.g., purge and transactions
496 being recovered at startup there is no associated MySQL
497 thread data structure. */
498 row->trx_mysql_thread_id = 0;
499 row->trx_query = NULL;
500 goto thd_done;
501 }
502
503 row->trx_mysql_thread_id = thd_get_thread_id(trx->mysql_thd);
504
505 char query[TRX_I_S_TRX_QUERY_MAX_LEN + 1];
506 stmt_len = innobase_get_stmt_safe(trx->mysql_thd, query, sizeof(query));
507
508 if (stmt_len > 0) {
509
510 row->trx_query = static_cast<const char*>(
511 ha_storage_put_memlim(
512 cache->storage, query, stmt_len + 1,
513 MAX_ALLOWED_FOR_STORAGE(cache)));
514
515 row->trx_query_cs = innobase_get_charset(trx->mysql_thd);
516
517 if (row->trx_query == NULL) {
518
519 return(FALSE);
520 }
521 } else {
522
523 row->trx_query = NULL;
524 }
525
526 thd_done:
527 s = trx->op_info;
528
529 if (s != NULL && s[0] != '\0') {
530
531 TRX_I_S_STRING_COPY(s, row->trx_operation_state,
532 TRX_I_S_TRX_OP_STATE_MAX_LEN, cache);
533
534 if (row->trx_operation_state == NULL) {
535
536 return(FALSE);
537 }
538 } else {
539
540 row->trx_operation_state = NULL;
541 }
542
543 row->trx_tables_in_use = trx->n_mysql_tables_in_use;
544
545 row->trx_tables_locked = lock_number_of_tables_locked(&trx->lock);
546
547 /* These are protected by both trx->mutex or lock_sys->mutex,
548 or just lock_sys->mutex. For reading, it suffices to hold
549 lock_sys->mutex. */
550
551 row->trx_lock_structs = UT_LIST_GET_LEN(trx->lock.trx_locks);
552
553 row->trx_lock_memory_bytes = mem_heap_get_size(trx->lock.lock_heap);
554
555 row->trx_rows_locked = lock_number_of_rows_locked(&trx->lock);
556
557 row->trx_rows_modified = trx->undo_no;
558
559 row->trx_concurrency_tickets = trx->n_tickets_to_enter_innodb;
560
561 switch (trx->isolation_level) {
562 case TRX_ISO_READ_UNCOMMITTED:
563 row->trx_isolation_level = "READ UNCOMMITTED";
564 break;
565 case TRX_ISO_READ_COMMITTED:
566 row->trx_isolation_level = "READ COMMITTED";
567 break;
568 case TRX_ISO_REPEATABLE_READ:
569 row->trx_isolation_level = "REPEATABLE READ";
570 break;
571 case TRX_ISO_SERIALIZABLE:
572 row->trx_isolation_level = "SERIALIZABLE";
573 break;
574 /* Should not happen as TRX_ISO_READ_COMMITTED is default */
575 default:
576 row->trx_isolation_level = "UNKNOWN";
577 }
578
579 row->trx_unique_checks = (ibool) trx->check_unique_secondary;
580
581 row->trx_foreign_key_checks = (ibool) trx->check_foreigns;
582
583 s = trx->detailed_error;
584
585 if (s != NULL && s[0] != '\0') {
586
587 TRX_I_S_STRING_COPY(s,
588 row->trx_foreign_key_error,
589 TRX_I_S_TRX_FK_ERROR_MAX_LEN, cache);
590
591 if (row->trx_foreign_key_error == NULL) {
592
593 return(FALSE);
594 }
595 } else {
596 row->trx_foreign_key_error = NULL;
597 }
598
599 row->trx_has_search_latch = (ibool) trx->has_search_latch;
600
601 row->trx_is_read_only = trx->read_only;
602
603 row->trx_is_autocommit_non_locking = trx_is_autocommit_non_locking(trx);
604
605 return(TRUE);
606 }
607
608 /*******************************************************************//**
609 Format the nth field of "rec" and put it in "buf". The result is always
610 NUL-terminated. Returns the number of bytes that were written to "buf"
611 (including the terminating NUL).
612 @return end of the result */
613 static
614 ulint
put_nth_field(char * buf,ulint buf_size,ulint n,const dict_index_t * index,const rec_t * rec,const ulint * offsets)615 put_nth_field(
616 /*==========*/
617 char* buf, /*!< out: buffer */
618 ulint buf_size,/*!< in: buffer size in bytes */
619 ulint n, /*!< in: number of field */
620 const dict_index_t* index, /*!< in: index */
621 const rec_t* rec, /*!< in: record */
622 const ulint* offsets)/*!< in: record offsets, returned
623 by rec_get_offsets() */
624 {
625 const byte* data;
626 ulint data_len;
627 dict_field_t* dict_field;
628 ulint ret;
629
630 ut_ad(rec_offs_validate(rec, NULL, offsets));
631
632 if (buf_size == 0) {
633
634 return(0);
635 }
636
637 ret = 0;
638
639 if (n > 0) {
640 /* we must append ", " before the actual data */
641
642 if (buf_size < 3) {
643
644 buf[0] = '\0';
645 return(1);
646 }
647
648 memcpy(buf, ", ", 3);
649
650 buf += 2;
651 buf_size -= 2;
652 ret += 2;
653 }
654
655 /* now buf_size >= 1 */
656
657 data = rec_get_nth_field(rec, offsets, n, &data_len);
658
659 dict_field = dict_index_get_nth_field(index, n);
660
661 ret += row_raw_format((const char*) data, data_len,
662 dict_field, buf, buf_size);
663
664 return(ret);
665 }
666
667 /*******************************************************************//**
668 Fills the "lock_data" member of i_s_locks_row_t object.
669 If memory can not be allocated then FALSE is returned.
670 @return FALSE if allocation fails */
671 static
672 ibool
fill_lock_data(const char ** lock_data,const lock_t * lock,ulint heap_no,trx_i_s_cache_t * cache)673 fill_lock_data(
674 /*===========*/
675 const char** lock_data,/*!< out: "lock_data" to fill */
676 const lock_t* lock, /*!< in: lock used to find the data */
677 ulint heap_no,/*!< in: rec num used to find the data */
678 trx_i_s_cache_t* cache) /*!< in/out: cache where to store
679 volatile data */
680 {
681 ut_a(lock_get_type(lock) == LOCK_REC);
682
683 switch (heap_no) {
684 case PAGE_HEAP_NO_INFIMUM:
685 case PAGE_HEAP_NO_SUPREMUM:
686 *lock_data = ha_storage_put_str_memlim(
687 cache->storage,
688 heap_no == PAGE_HEAP_NO_INFIMUM
689 ? "infimum pseudo-record"
690 : "supremum pseudo-record",
691 MAX_ALLOWED_FOR_STORAGE(cache));
692 return(*lock_data != NULL);
693 }
694
695 mtr_t mtr;
696
697 const buf_block_t* block;
698 const page_t* page;
699 const rec_t* rec;
700 const dict_index_t* index;
701 ulint n_fields;
702 mem_heap_t* heap;
703 ulint offsets_onstack[REC_OFFS_NORMAL_SIZE];
704 ulint* offsets;
705 char buf[TRX_I_S_LOCK_DATA_MAX_LEN];
706 ulint buf_used;
707 ulint i;
708
709 mtr_start(&mtr);
710
711 block = buf_page_try_get(page_id_t(lock_rec_get_space_id(lock),
712 lock_rec_get_page_no(lock)),
713 &mtr);
714
715 if (block == NULL) {
716
717 *lock_data = NULL;
718
719 mtr_commit(&mtr);
720
721 return(TRUE);
722 }
723
724 page = reinterpret_cast<const page_t*>(buf_block_get_frame(block));
725
726
727 rec_offs_init(offsets_onstack);
728 offsets = offsets_onstack;
729
730 rec = page_find_rec_with_heap_no(page, heap_no);
731
732 index = lock_rec_get_index(lock);
733
734 n_fields = dict_index_get_n_unique(index);
735
736 ut_a(n_fields > 0);
737
738 heap = NULL;
739 offsets = rec_get_offsets(rec, index, offsets, n_fields,
740 &heap);
741
742 /* format and store the data */
743
744 buf_used = 0;
745 for (i = 0; i < n_fields; i++) {
746
747 buf_used += put_nth_field(
748 buf + buf_used, sizeof(buf) - buf_used,
749 i, index, rec, offsets) - 1;
750 }
751
752 *lock_data = (const char*) ha_storage_put_memlim(
753 cache->storage, buf, buf_used + 1,
754 MAX_ALLOWED_FOR_STORAGE(cache));
755
756 if (heap != NULL) {
757
758 /* this means that rec_get_offsets() has created a new
759 heap and has stored offsets in it; check that this is
760 really the case and free the heap */
761 ut_a(offsets != offsets_onstack);
762 mem_heap_free(heap);
763 }
764
765 mtr_commit(&mtr);
766
767 if (*lock_data == NULL) {
768
769 return(FALSE);
770 }
771
772 return(TRUE);
773 }
774
775 /*******************************************************************//**
776 Fills i_s_locks_row_t object. Returns its first argument.
777 If memory can not be allocated then FALSE is returned.
778 @return FALSE if allocation fails */
779 static
780 ibool
fill_locks_row(i_s_locks_row_t * row,const lock_t * lock,ulint heap_no,trx_i_s_cache_t * cache)781 fill_locks_row(
782 /*===========*/
783 i_s_locks_row_t* row, /*!< out: result object that's filled */
784 const lock_t* lock, /*!< in: lock to get data from */
785 ulint heap_no,/*!< in: lock's record number
786 or ULINT_UNDEFINED if the lock
787 is a table lock */
788 trx_i_s_cache_t* cache) /*!< in/out: cache into which to copy
789 volatile strings */
790 {
791 row->lock_trx_id = lock_get_trx_id(lock);
792 row->lock_mode = lock_get_mode_str(lock);
793 row->lock_type = lock_get_type_str(lock);
794
795 row->lock_table = ha_storage_put_str_memlim(
796 cache->storage, lock_get_table_name(lock).m_name,
797 MAX_ALLOWED_FOR_STORAGE(cache));
798
799 /* memory could not be allocated */
800 if (row->lock_table == NULL) {
801
802 return(FALSE);
803 }
804
805 switch (lock_get_type(lock)) {
806 case LOCK_REC:
807 row->lock_index = ha_storage_put_str_memlim(
808 cache->storage, lock_rec_get_index_name(lock),
809 MAX_ALLOWED_FOR_STORAGE(cache));
810
811 /* memory could not be allocated */
812 if (row->lock_index == NULL) {
813
814 return(FALSE);
815 }
816
817 row->lock_space = lock_rec_get_space_id(lock);
818 row->lock_page = lock_rec_get_page_no(lock);
819 row->lock_rec = heap_no;
820
821 if (!fill_lock_data(&row->lock_data, lock, heap_no, cache)) {
822
823 /* memory could not be allocated */
824 return(FALSE);
825 }
826
827 break;
828 case LOCK_TABLE:
829 row->lock_index = NULL;
830
831 row->lock_space = ULINT_UNDEFINED;
832 row->lock_page = ULINT_UNDEFINED;
833 row->lock_rec = ULINT_UNDEFINED;
834
835 row->lock_data = NULL;
836
837 break;
838 default:
839 ut_error;
840 }
841
842 row->lock_table_id = lock_get_table_id(lock);
843
844 row->hash_chain.value = row;
845 ut_ad(i_s_locks_row_validate(row));
846
847 return(TRUE);
848 }
849
850 /*******************************************************************//**
851 Fills i_s_lock_waits_row_t object. Returns its first argument.
852 @return result object that's filled */
853 static
854 i_s_lock_waits_row_t*
fill_lock_waits_row(i_s_lock_waits_row_t * row,const i_s_locks_row_t * requested_lock_row,const i_s_locks_row_t * blocking_lock_row)855 fill_lock_waits_row(
856 /*================*/
857 i_s_lock_waits_row_t* row, /*!< out: result object
858 that's filled */
859 const i_s_locks_row_t* requested_lock_row,/*!< in: pointer to the
860 relevant requested lock
861 row in innodb_locks */
862 const i_s_locks_row_t* blocking_lock_row)/*!< in: pointer to the
863 relevant blocking lock
864 row in innodb_locks */
865 {
866 ut_ad(i_s_locks_row_validate(requested_lock_row));
867 ut_ad(i_s_locks_row_validate(blocking_lock_row));
868
869 row->requested_lock_row = requested_lock_row;
870 row->blocking_lock_row = blocking_lock_row;
871
872 return(row);
873 }
874
875 /*******************************************************************//**
876 Calculates a hash fold for a lock. For a record lock the fold is
877 calculated from 4 elements, which uniquely identify a lock at a given
878 point in time: transaction id, space id, page number, record number.
879 For a table lock the fold is table's id.
880 @return fold */
881 static
882 ulint
fold_lock(const lock_t * lock,ulint heap_no)883 fold_lock(
884 /*======*/
885 const lock_t* lock, /*!< in: lock object to fold */
886 ulint heap_no)/*!< in: lock's record number
887 or ULINT_UNDEFINED if the lock
888 is a table lock */
889 {
890 #ifdef TEST_LOCK_FOLD_ALWAYS_DIFFERENT
891 static ulint fold = 0;
892
893 return(fold++);
894 #else
895 ulint ret;
896
897 switch (lock_get_type(lock)) {
898 case LOCK_REC:
899 ut_a(heap_no != ULINT_UNDEFINED);
900
901 ret = ut_fold_ulint_pair((ulint) lock_get_trx_id(lock),
902 lock_rec_get_space_id(lock));
903
904 ret = ut_fold_ulint_pair(ret,
905 lock_rec_get_page_no(lock));
906
907 ret = ut_fold_ulint_pair(ret, heap_no);
908
909 break;
910 case LOCK_TABLE:
911 /* this check is actually not necessary for continuing
912 correct operation, but something must have gone wrong if
913 it fails. */
914 ut_a(heap_no == ULINT_UNDEFINED);
915
916 ret = (ulint) lock_get_table_id(lock);
917
918 break;
919 default:
920 ut_error;
921 }
922
923 return(ret);
924 #endif
925 }
926
927 /*******************************************************************//**
928 Checks whether i_s_locks_row_t object represents a lock_t object.
929 @return TRUE if they match */
930 static
931 ibool
locks_row_eq_lock(const i_s_locks_row_t * row,const lock_t * lock,ulint heap_no)932 locks_row_eq_lock(
933 /*==============*/
934 const i_s_locks_row_t* row, /*!< in: innodb_locks row */
935 const lock_t* lock, /*!< in: lock object */
936 ulint heap_no)/*!< in: lock's record number
937 or ULINT_UNDEFINED if the lock
938 is a table lock */
939 {
940 ut_ad(i_s_locks_row_validate(row));
941 #ifdef TEST_NO_LOCKS_ROW_IS_EVER_EQUAL_TO_LOCK_T
942 return(0);
943 #else
944 switch (lock_get_type(lock)) {
945 case LOCK_REC:
946 ut_a(heap_no != ULINT_UNDEFINED);
947
948 return(row->lock_trx_id == lock_get_trx_id(lock)
949 && row->lock_space == lock_rec_get_space_id(lock)
950 && row->lock_page == lock_rec_get_page_no(lock)
951 && row->lock_rec == heap_no);
952
953 case LOCK_TABLE:
954 /* this check is actually not necessary for continuing
955 correct operation, but something must have gone wrong if
956 it fails. */
957 ut_a(heap_no == ULINT_UNDEFINED);
958
959 return(row->lock_trx_id == lock_get_trx_id(lock)
960 && row->lock_table_id == lock_get_table_id(lock));
961
962 default:
963 ut_error;
964 return(FALSE);
965 }
966 #endif
967 }
968
969 /*******************************************************************//**
970 Searches for a row in the innodb_locks cache that has a specified id.
971 This happens in O(1) time since a hash table is used. Returns pointer to
972 the row or NULL if none is found.
973 @return row or NULL */
974 static
975 i_s_locks_row_t*
search_innodb_locks(trx_i_s_cache_t * cache,const lock_t * lock,ulint heap_no)976 search_innodb_locks(
977 /*================*/
978 trx_i_s_cache_t* cache, /*!< in: cache */
979 const lock_t* lock, /*!< in: lock to search for */
980 ulint heap_no)/*!< in: lock's record number
981 or ULINT_UNDEFINED if the lock
982 is a table lock */
983 {
984 i_s_hash_chain_t* hash_chain;
985
986 HASH_SEARCH(
987 /* hash_chain->"next" */
988 next,
989 /* the hash table */
990 cache->locks_hash,
991 /* fold */
992 fold_lock(lock, heap_no),
993 /* the type of the next variable */
994 i_s_hash_chain_t*,
995 /* auxiliary variable */
996 hash_chain,
997 /* assertion on every traversed item */
998 ut_ad(i_s_locks_row_validate(hash_chain->value)),
999 /* this determines if we have found the lock */
1000 locks_row_eq_lock(hash_chain->value, lock, heap_no));
1001
1002 if (hash_chain == NULL) {
1003
1004 return(NULL);
1005 }
1006 /* else */
1007
1008 return(hash_chain->value);
1009 }
1010
1011 /*******************************************************************//**
1012 Adds new element to the locks cache, enlarging it if necessary.
1013 Returns a pointer to the added row. If the row is already present then
1014 no row is added and a pointer to the existing row is returned.
1015 If row can not be allocated then NULL is returned.
1016 @return row */
1017 static
1018 i_s_locks_row_t*
add_lock_to_cache(trx_i_s_cache_t * cache,const lock_t * lock,ulint heap_no)1019 add_lock_to_cache(
1020 /*==============*/
1021 trx_i_s_cache_t* cache, /*!< in/out: cache */
1022 const lock_t* lock, /*!< in: the element to add */
1023 ulint heap_no)/*!< in: lock's record number
1024 or ULINT_UNDEFINED if the lock
1025 is a table lock */
1026 {
1027 i_s_locks_row_t* dst_row;
1028
1029 #ifdef TEST_ADD_EACH_LOCKS_ROW_MANY_TIMES
1030 ulint i;
1031 for (i = 0; i < 10000; i++) {
1032 #endif
1033 #ifndef TEST_DO_NOT_CHECK_FOR_DUPLICATE_ROWS
1034 /* quit if this lock is already present */
1035 dst_row = search_innodb_locks(cache, lock, heap_no);
1036 if (dst_row != NULL) {
1037
1038 ut_ad(i_s_locks_row_validate(dst_row));
1039 return(dst_row);
1040 }
1041 #endif
1042
1043 dst_row = (i_s_locks_row_t*)
1044 table_cache_create_empty_row(&cache->innodb_locks, cache);
1045
1046 /* memory could not be allocated */
1047 if (dst_row == NULL) {
1048
1049 return(NULL);
1050 }
1051
1052 if (!fill_locks_row(dst_row, lock, heap_no, cache)) {
1053
1054 /* memory could not be allocated */
1055 cache->innodb_locks.rows_used--;
1056 return(NULL);
1057 }
1058
1059 #ifndef TEST_DO_NOT_INSERT_INTO_THE_HASH_TABLE
1060 HASH_INSERT(
1061 /* the type used in the hash chain */
1062 i_s_hash_chain_t,
1063 /* hash_chain->"next" */
1064 next,
1065 /* the hash table */
1066 cache->locks_hash,
1067 /* fold */
1068 fold_lock(lock, heap_no),
1069 /* add this data to the hash */
1070 &dst_row->hash_chain);
1071 #endif
1072 #ifdef TEST_ADD_EACH_LOCKS_ROW_MANY_TIMES
1073 } /* for()-loop */
1074 #endif
1075
1076 ut_ad(i_s_locks_row_validate(dst_row));
1077 return(dst_row);
1078 }
1079
1080 /*******************************************************************//**
1081 Adds new pair of locks to the lock waits cache.
1082 If memory can not be allocated then FALSE is returned.
1083 @return FALSE if allocation fails */
1084 static
1085 ibool
add_lock_wait_to_cache(trx_i_s_cache_t * cache,const i_s_locks_row_t * requested_lock_row,const i_s_locks_row_t * blocking_lock_row)1086 add_lock_wait_to_cache(
1087 /*===================*/
1088 trx_i_s_cache_t* cache, /*!< in/out: cache */
1089 const i_s_locks_row_t* requested_lock_row,/*!< in: pointer to the
1090 relevant requested lock
1091 row in innodb_locks */
1092 const i_s_locks_row_t* blocking_lock_row)/*!< in: pointer to the
1093 relevant blocking lock
1094 row in innodb_locks */
1095 {
1096 i_s_lock_waits_row_t* dst_row;
1097
1098 dst_row = (i_s_lock_waits_row_t*)
1099 table_cache_create_empty_row(&cache->innodb_lock_waits,
1100 cache);
1101
1102 /* memory could not be allocated */
1103 if (dst_row == NULL) {
1104
1105 return(FALSE);
1106 }
1107
1108 fill_lock_waits_row(dst_row, requested_lock_row, blocking_lock_row);
1109
1110 return(TRUE);
1111 }
1112
1113 /*******************************************************************//**
1114 Adds transaction's relevant (important) locks to cache.
1115 If the transaction is waiting, then the wait lock is added to
1116 innodb_locks and a pointer to the added row is returned in
1117 requested_lock_row, otherwise requested_lock_row is set to NULL.
1118 If rows can not be allocated then FALSE is returned and the value of
1119 requested_lock_row is undefined.
1120 @return FALSE if allocation fails */
1121 static
1122 ibool
add_trx_relevant_locks_to_cache(trx_i_s_cache_t * cache,const trx_t * trx,i_s_locks_row_t ** requested_lock_row)1123 add_trx_relevant_locks_to_cache(
1124 /*============================*/
1125 trx_i_s_cache_t* cache, /*!< in/out: cache */
1126 const trx_t* trx, /*!< in: transaction */
1127 i_s_locks_row_t** requested_lock_row)/*!< out: pointer to the
1128 requested lock row, or NULL or
1129 undefined */
1130 {
1131 ut_ad(lock_mutex_own());
1132
1133 /* If transaction is waiting we add the wait lock and all locks
1134 from another transactions that are blocking the wait lock. */
1135 if (trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
1136
1137 const lock_t* curr_lock;
1138 ulint wait_lock_heap_no;
1139 i_s_locks_row_t* blocking_lock_row;
1140 lock_queue_iterator_t iter;
1141
1142 ut_a(trx->lock.wait_lock != NULL);
1143
1144 wait_lock_heap_no
1145 = wait_lock_get_heap_no(trx->lock.wait_lock);
1146
1147 /* add the requested lock */
1148 *requested_lock_row
1149 = add_lock_to_cache(cache, trx->lock.wait_lock,
1150 wait_lock_heap_no);
1151
1152 /* memory could not be allocated */
1153 if (*requested_lock_row == NULL) {
1154
1155 return(FALSE);
1156 }
1157
1158 /* then iterate over the locks before the wait lock and
1159 add the ones that are blocking it */
1160
1161 lock_queue_iterator_reset(&iter, trx->lock.wait_lock,
1162 ULINT_UNDEFINED);
1163
1164 for (curr_lock = lock_queue_iterator_get_prev(&iter);
1165 curr_lock != NULL;
1166 curr_lock = lock_queue_iterator_get_prev(&iter)) {
1167
1168 if (lock_has_to_wait(trx->lock.wait_lock,
1169 curr_lock)) {
1170
1171 /* add the lock that is
1172 blocking trx->lock.wait_lock */
1173 blocking_lock_row
1174 = add_lock_to_cache(
1175 cache, curr_lock,
1176 /* heap_no is the same
1177 for the wait and waited
1178 locks */
1179 wait_lock_heap_no);
1180
1181 /* memory could not be allocated */
1182 if (blocking_lock_row == NULL) {
1183
1184 return(FALSE);
1185 }
1186
1187 /* add the relation between both locks
1188 to innodb_lock_waits */
1189 if (!add_lock_wait_to_cache(
1190 cache, *requested_lock_row,
1191 blocking_lock_row)) {
1192
1193 /* memory could not be allocated */
1194 return(FALSE);
1195 }
1196 }
1197 }
1198 } else {
1199
1200 *requested_lock_row = NULL;
1201 }
1202
1203 return(TRUE);
1204 }
1205
1206 /** The minimum time that a cache must not be updated after it has been
1207 read for the last time; measured in microseconds. We use this technique
1208 to ensure that SELECTs which join several INFORMATION SCHEMA tables read
1209 the same version of the cache. */
1210 #define CACHE_MIN_IDLE_TIME_US 100000 /* 0.1 sec */
1211
1212 /*******************************************************************//**
1213 Checks if the cache can safely be updated.
1214 @return TRUE if can be updated */
1215 static
1216 ibool
can_cache_be_updated(trx_i_s_cache_t * cache)1217 can_cache_be_updated(
1218 /*=================*/
1219 trx_i_s_cache_t* cache) /*!< in: cache */
1220 {
1221 ib_time_monotonic_us_t now;
1222
1223 /* Here we read cache->last_read without acquiring its mutex
1224 because last_read is only updated when a shared rw lock on the
1225 whole cache is being held (see trx_i_s_cache_end_read()) and
1226 we are currently holding an exclusive rw lock on the cache.
1227 So it is not possible for last_read to be updated while we are
1228 reading it. */
1229
1230 ut_ad(rw_lock_own(cache->rw_lock, RW_LOCK_X));
1231
1232 now = ut_time_monotonic_us();
1233 if (now - cache->last_read > CACHE_MIN_IDLE_TIME_US) {
1234
1235 return(TRUE);
1236 }
1237
1238 return(FALSE);
1239 }
1240
1241 /*******************************************************************//**
1242 Declare a cache empty, preparing it to be filled up. Not all resources
1243 are freed because they can be reused. */
1244 static
1245 void
trx_i_s_cache_clear(trx_i_s_cache_t * cache)1246 trx_i_s_cache_clear(
1247 /*================*/
1248 trx_i_s_cache_t* cache) /*!< out: cache to clear */
1249 {
1250 cache->innodb_trx.rows_used = 0;
1251 cache->innodb_locks.rows_used = 0;
1252 cache->innodb_lock_waits.rows_used = 0;
1253
1254 hash_table_clear(cache->locks_hash);
1255
1256 ha_storage_empty(&cache->storage);
1257 }
1258
1259 /*******************************************************************//**
1260 Fetches the data needed to fill the 3 INFORMATION SCHEMA tables into the
1261 table cache buffer. Cache must be locked for write. */
1262 static
1263 void
fetch_data_into_cache_low(trx_i_s_cache_t * cache,bool read_write,trx_ut_list_t * trx_list)1264 fetch_data_into_cache_low(
1265 /*======================*/
1266 trx_i_s_cache_t* cache, /*!< in/out: cache */
1267 bool read_write, /*!< in: only read-write
1268 transactions */
1269 trx_ut_list_t* trx_list) /*!< in: trx list */
1270 {
1271 const trx_t* trx;
1272 bool rw_trx_list = trx_list == &trx_sys->rw_trx_list;
1273
1274 ut_ad(rw_trx_list || trx_list == &trx_sys->mysql_trx_list);
1275
1276 /* Iterate over the transaction list and add each one
1277 to innodb_trx's cache. We also add all locks that are relevant
1278 to each transaction into innodb_locks' and innodb_lock_waits'
1279 caches. */
1280
1281 for (trx = UT_LIST_GET_FIRST(*trx_list);
1282 trx != NULL;
1283 trx =
1284 (rw_trx_list
1285 ? UT_LIST_GET_NEXT(trx_list, trx)
1286 : UT_LIST_GET_NEXT(mysql_trx_list, trx))) {
1287
1288 i_s_trx_row_t* trx_row;
1289 i_s_locks_row_t* requested_lock_row;
1290
1291 /* Note: Read only transactions that modify temporary
1292 tables an have a transaction ID */
1293 if (!trx_is_started(trx)
1294 || (!rw_trx_list && trx->id != 0 && !trx->read_only)) {
1295
1296 continue;
1297 }
1298
1299 assert_trx_nonlocking_or_in_list(trx);
1300
1301 ut_ad(trx->in_rw_trx_list == rw_trx_list);
1302
1303 if (!add_trx_relevant_locks_to_cache(cache, trx,
1304 &requested_lock_row)) {
1305
1306 cache->is_truncated = TRUE;
1307 return;
1308 }
1309
1310 trx_row = reinterpret_cast<i_s_trx_row_t*>(
1311 table_cache_create_empty_row(
1312 &cache->innodb_trx, cache));
1313
1314 /* memory could not be allocated */
1315 if (trx_row == NULL) {
1316
1317 cache->is_truncated = TRUE;
1318 return;
1319 }
1320
1321 if (!fill_trx_row(trx_row, trx, requested_lock_row, cache)) {
1322
1323 /* memory could not be allocated */
1324 --cache->innodb_trx.rows_used;
1325 cache->is_truncated = TRUE;
1326 return;
1327 }
1328 }
1329 }
1330
1331 /*******************************************************************//**
1332 Fetches the data needed to fill the 3 INFORMATION SCHEMA tables into the
1333 table cache buffer. Cache must be locked for write. */
1334 static
1335 void
fetch_data_into_cache(trx_i_s_cache_t * cache)1336 fetch_data_into_cache(
1337 /*==================*/
1338 trx_i_s_cache_t* cache) /*!< in/out: cache */
1339 {
1340 ut_ad(lock_mutex_own());
1341 ut_ad(trx_sys_mutex_own());
1342
1343 trx_i_s_cache_clear(cache);
1344
1345 /* Capture the state of the read-write transactions. This includes
1346 internal transactions too. They are not on mysql_trx_list */
1347 fetch_data_into_cache_low(cache, true, &trx_sys->rw_trx_list);
1348
1349 /* Capture the state of the read-only active transactions */
1350 fetch_data_into_cache_low(cache, false, &trx_sys->mysql_trx_list);
1351
1352 cache->is_truncated = FALSE;
1353 }
1354
1355 /*******************************************************************//**
1356 Update the transactions cache if it has not been read for some time.
1357 Called from handler/i_s.cc.
1358 @return 0 - fetched, 1 - not */
1359 int
trx_i_s_possibly_fetch_data_into_cache(trx_i_s_cache_t * cache)1360 trx_i_s_possibly_fetch_data_into_cache(
1361 /*===================================*/
1362 trx_i_s_cache_t* cache) /*!< in/out: cache */
1363 {
1364 if (!can_cache_be_updated(cache)) {
1365
1366 return(1);
1367 }
1368
1369 /* We need to read trx_sys and record/table lock queues */
1370
1371 lock_mutex_enter();
1372
1373 trx_sys_mutex_enter();
1374
1375 fetch_data_into_cache(cache);
1376
1377 trx_sys_mutex_exit();
1378
1379 lock_mutex_exit();
1380
1381 return(0);
1382 }
1383
1384 /*******************************************************************//**
1385 Returns TRUE if the data in the cache is truncated due to the memory
1386 limit posed by TRX_I_S_MEM_LIMIT.
1387 @return TRUE if truncated */
1388 ibool
trx_i_s_cache_is_truncated(trx_i_s_cache_t * cache)1389 trx_i_s_cache_is_truncated(
1390 /*=======================*/
1391 trx_i_s_cache_t* cache) /*!< in: cache */
1392 {
1393 return(cache->is_truncated);
1394 }
1395
1396 /*******************************************************************//**
1397 Initialize INFORMATION SCHEMA trx related cache. */
1398 void
trx_i_s_cache_init(trx_i_s_cache_t * cache)1399 trx_i_s_cache_init(
1400 /*===============*/
1401 trx_i_s_cache_t* cache) /*!< out: cache to init */
1402 {
1403 /* The latching is done in the following order:
1404 acquire trx_i_s_cache_t::rw_lock, X
1405 acquire lock mutex
1406 release lock mutex
1407 release trx_i_s_cache_t::rw_lock
1408 acquire trx_i_s_cache_t::rw_lock, S
1409 acquire trx_i_s_cache_t::last_read_mutex
1410 release trx_i_s_cache_t::last_read_mutex
1411 release trx_i_s_cache_t::rw_lock */
1412
1413 cache->rw_lock = static_cast<rw_lock_t*>(
1414 ut_malloc_nokey(sizeof(*cache->rw_lock)));
1415
1416 rw_lock_create(trx_i_s_cache_lock_key, cache->rw_lock,
1417 SYNC_TRX_I_S_RWLOCK);
1418
1419 cache->last_read = 0;
1420
1421 mutex_create(LATCH_ID_CACHE_LAST_READ, &cache->last_read_mutex);
1422
1423 table_cache_init(&cache->innodb_trx, sizeof(i_s_trx_row_t));
1424 table_cache_init(&cache->innodb_locks, sizeof(i_s_locks_row_t));
1425 table_cache_init(&cache->innodb_lock_waits,
1426 sizeof(i_s_lock_waits_row_t));
1427
1428 cache->locks_hash = hash_create(LOCKS_HASH_CELLS_NUM);
1429
1430 cache->storage = ha_storage_create(CACHE_STORAGE_INITIAL_SIZE,
1431 CACHE_STORAGE_HASH_CELLS);
1432
1433 cache->mem_allocd = 0;
1434
1435 cache->is_truncated = FALSE;
1436 }
1437
1438 /*******************************************************************//**
1439 Free the INFORMATION SCHEMA trx related cache. */
1440 void
trx_i_s_cache_free(trx_i_s_cache_t * cache)1441 trx_i_s_cache_free(
1442 /*===============*/
1443 trx_i_s_cache_t* cache) /*!< in, own: cache to free */
1444 {
1445 rw_lock_free(cache->rw_lock);
1446 ut_free(cache->rw_lock);
1447 cache->rw_lock = NULL;
1448
1449 mutex_free(&cache->last_read_mutex);
1450
1451 hash_table_free(cache->locks_hash);
1452 ha_storage_free(cache->storage);
1453 table_cache_free(&cache->innodb_trx);
1454 table_cache_free(&cache->innodb_locks);
1455 table_cache_free(&cache->innodb_lock_waits);
1456 }
1457
1458 /*******************************************************************//**
1459 Issue a shared/read lock on the tables cache. */
1460 void
trx_i_s_cache_start_read(trx_i_s_cache_t * cache)1461 trx_i_s_cache_start_read(
1462 /*=====================*/
1463 trx_i_s_cache_t* cache) /*!< in: cache */
1464 {
1465 rw_lock_s_lock(cache->rw_lock);
1466 }
1467
1468 /*******************************************************************//**
1469 Release a shared/read lock on the tables cache. */
1470 void
trx_i_s_cache_end_read(trx_i_s_cache_t * cache)1471 trx_i_s_cache_end_read(
1472 /*===================*/
1473 trx_i_s_cache_t* cache) /*!< in: cache */
1474 {
1475 ib_time_monotonic_us_t now;
1476
1477 ut_ad(rw_lock_own(cache->rw_lock, RW_LOCK_S));
1478
1479 /* update cache last read time */
1480 now = ut_time_monotonic_us();
1481 mutex_enter(&cache->last_read_mutex);
1482 cache->last_read = now;
1483 mutex_exit(&cache->last_read_mutex);
1484
1485 rw_lock_s_unlock(cache->rw_lock);
1486 }
1487
1488 /*******************************************************************//**
1489 Issue an exclusive/write lock on the tables cache. */
1490 void
trx_i_s_cache_start_write(trx_i_s_cache_t * cache)1491 trx_i_s_cache_start_write(
1492 /*======================*/
1493 trx_i_s_cache_t* cache) /*!< in: cache */
1494 {
1495 rw_lock_x_lock(cache->rw_lock);
1496 }
1497
1498 /*******************************************************************//**
1499 Release an exclusive/write lock on the tables cache. */
1500 void
trx_i_s_cache_end_write(trx_i_s_cache_t * cache)1501 trx_i_s_cache_end_write(
1502 /*====================*/
1503 trx_i_s_cache_t* cache) /*!< in: cache */
1504 {
1505 ut_ad(rw_lock_own(cache->rw_lock, RW_LOCK_X));
1506
1507 rw_lock_x_unlock(cache->rw_lock);
1508 }
1509
1510 /*******************************************************************//**
1511 Selects a INFORMATION SCHEMA table cache from the whole cache.
1512 @return table cache */
1513 static
1514 i_s_table_cache_t*
cache_select_table(trx_i_s_cache_t * cache,enum i_s_table table)1515 cache_select_table(
1516 /*===============*/
1517 trx_i_s_cache_t* cache, /*!< in: whole cache */
1518 enum i_s_table table) /*!< in: which table */
1519 {
1520 i_s_table_cache_t* table_cache;
1521
1522 ut_ad(rw_lock_own(cache->rw_lock, RW_LOCK_S)
1523 || rw_lock_own(cache->rw_lock, RW_LOCK_X));
1524
1525 switch (table) {
1526 case I_S_INNODB_TRX:
1527 table_cache = &cache->innodb_trx;
1528 break;
1529 case I_S_INNODB_LOCKS:
1530 table_cache = &cache->innodb_locks;
1531 break;
1532 case I_S_INNODB_LOCK_WAITS:
1533 table_cache = &cache->innodb_lock_waits;
1534 break;
1535 default:
1536 ut_error;
1537 }
1538
1539 return(table_cache);
1540 }
1541
1542 /*******************************************************************//**
1543 Retrieves the number of used rows in the cache for a given
1544 INFORMATION SCHEMA table.
1545 @return number of rows */
1546 ulint
trx_i_s_cache_get_rows_used(trx_i_s_cache_t * cache,enum i_s_table table)1547 trx_i_s_cache_get_rows_used(
1548 /*========================*/
1549 trx_i_s_cache_t* cache, /*!< in: cache */
1550 enum i_s_table table) /*!< in: which table */
1551 {
1552 i_s_table_cache_t* table_cache;
1553
1554 table_cache = cache_select_table(cache, table);
1555
1556 return(table_cache->rows_used);
1557 }
1558
1559 /*******************************************************************//**
1560 Retrieves the nth row (zero-based) in the cache for a given
1561 INFORMATION SCHEMA table.
1562 @return row */
1563 void*
trx_i_s_cache_get_nth_row(trx_i_s_cache_t * cache,enum i_s_table table,ulint n)1564 trx_i_s_cache_get_nth_row(
1565 /*======================*/
1566 trx_i_s_cache_t* cache, /*!< in: cache */
1567 enum i_s_table table, /*!< in: which table */
1568 ulint n) /*!< in: row number */
1569 {
1570 i_s_table_cache_t* table_cache;
1571 ulint i;
1572 void* row;
1573
1574 table_cache = cache_select_table(cache, table);
1575
1576 ut_a(n < table_cache->rows_used);
1577
1578 row = NULL;
1579
1580 for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
1581
1582 if (table_cache->chunks[i].offset
1583 + table_cache->chunks[i].rows_allocd > n) {
1584
1585 row = (char*) table_cache->chunks[i].base
1586 + (n - table_cache->chunks[i].offset)
1587 * table_cache->row_size;
1588 break;
1589 }
1590 }
1591
1592 ut_a(row != NULL);
1593
1594 return(row);
1595 }
1596
1597 /*******************************************************************//**
1598 Crafts a lock id string from a i_s_locks_row_t object. Returns its
1599 second argument. This function aborts if there is not enough space in
1600 lock_id. Be sure to provide at least TRX_I_S_LOCK_ID_MAX_LEN + 1 if you
1601 want to be 100% sure that it will not abort.
1602 @return resulting lock id */
1603 char*
trx_i_s_create_lock_id(const i_s_locks_row_t * row,char * lock_id,ulint lock_id_size)1604 trx_i_s_create_lock_id(
1605 /*===================*/
1606 const i_s_locks_row_t* row, /*!< in: innodb_locks row */
1607 char* lock_id,/*!< out: resulting lock_id */
1608 ulint lock_id_size)/*!< in: size of the lock id
1609 buffer */
1610 {
1611 int res_len;
1612
1613 /* please adjust TRX_I_S_LOCK_ID_MAX_LEN if you change this */
1614
1615 if (row->lock_space != ULINT_UNDEFINED) {
1616 /* record lock */
1617 res_len = ut_snprintf(lock_id, lock_id_size,
1618 TRX_ID_FMT ":%lu:%lu:%lu",
1619 row->lock_trx_id, row->lock_space,
1620 row->lock_page, row->lock_rec);
1621 } else {
1622 /* table lock */
1623 res_len = ut_snprintf(lock_id, lock_id_size,
1624 TRX_ID_FMT":" UINT64PF,
1625 row->lock_trx_id,
1626 row->lock_table_id);
1627 }
1628
1629 /* the typecast is safe because snprintf(3) never returns
1630 negative result */
1631 ut_a(res_len >= 0);
1632 ut_a((ulint) res_len < lock_id_size);
1633
1634 return(lock_id);
1635 }
1636
1637 void
trx_i_s_get_lock_sys_memory_usage(ulint * constant,ulint * variable)1638 trx_i_s_get_lock_sys_memory_usage(ulint *constant, ulint *variable)
1639 {
1640 trx_t* trx;
1641
1642 *constant = lock_sys->rec_hash->n_cells * sizeof(hash_cell_t);
1643 *variable = 0;
1644
1645 if (trx_sys) {
1646 mutex_enter(&trx_sys->mutex);
1647 trx = UT_LIST_GET_FIRST(trx_sys->mysql_trx_list);
1648 while (trx) {
1649 *variable += ((trx->lock.lock_heap)
1650 ? mem_heap_get_size(trx->lock.lock_heap)
1651 : 0);
1652 trx = UT_LIST_GET_NEXT(mysql_trx_list, trx);
1653 }
1654 mutex_exit(&trx_sys->mutex);
1655 }
1656
1657 }
1658