summaryrefslogtreecommitdiff
path: root/storage/xtradb/trx/trx0i_s.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/xtradb/trx/trx0i_s.cc')
-rw-r--r--storage/xtradb/trx/trx0i_s.cc1677
1 files changed, 0 insertions, 1677 deletions
diff --git a/storage/xtradb/trx/trx0i_s.cc b/storage/xtradb/trx/trx0i_s.cc
deleted file mode 100644
index 16b91a6b2a8..00000000000
--- a/storage/xtradb/trx/trx0i_s.cc
+++ /dev/null
@@ -1,1677 +0,0 @@
-/*****************************************************************************
-
-Copyright (c) 2007, 2012, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2019, MariaDB Corporation.
-
-This program is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation; version 2 of the License.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with
-this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
-
-*****************************************************************************/
-
-/**************************************************//**
-@file trx/trx0i_s.cc
-INFORMATION SCHEMA innodb_trx, innodb_locks and
-innodb_lock_waits tables fetch code.
-
-The code below fetches information needed to fill those
-3 dynamic tables and uploads it into a "transactions
-table cache" for later retrieval.
-
-Created July 17, 2007 Vasil Dimov
-*******************************************************/
-
-/* Found during the build of 5.5.3 on Linux 2.4 and early 2.6 kernels:
- The includes "univ.i" -> "my_global.h" cause a different path
- to be taken further down with pthread functions and types,
- so they must come first.
- From the symptoms, this is related to bug#46587 in the MySQL bug DB.
-*/
-#include "univ.i"
-
-#include <mysql/plugin.h>
-
-#include "buf0buf.h"
-#include "dict0dict.h"
-#include "ha0storage.h"
-#include "ha_prototypes.h"
-#include "hash0hash.h"
-#include "lock0iter.h"
-#include "lock0lock.h"
-#include "mem0mem.h"
-#include "page0page.h"
-#include "rem0rec.h"
-#include "row0row.h"
-#include "srv0srv.h"
-#include "sync0rw.h"
-#include "sync0sync.h"
-#include "sync0types.h"
-#include "trx0i_s.h"
-#include "trx0sys.h"
-#include "trx0trx.h"
-#include "ut0mem.h"
-#include "ut0ut.h"
-
-/** Initial number of rows in the table cache */
-#define TABLE_CACHE_INITIAL_ROWSNUM 1024
-
-/** @brief The maximum number of chunks to allocate for a table cache.
-
-The rows of a table cache are stored in a set of chunks. When a new
-row is added a new chunk is allocated if necessary. Assuming that the
-first one is 1024 rows (TABLE_CACHE_INITIAL_ROWSNUM) and each
-subsequent is N/2 where N is the number of rows we have allocated till
-now, then 39th chunk would accommodate 1677416425 rows and all chunks
-would accommodate 3354832851 rows. */
-#define MEM_CHUNKS_IN_TABLE_CACHE 39
-
-/** The following are some testing auxiliary macros. Do not enable them
-in a production environment. */
-/* @{ */
-
-#if 0
-/** If this is enabled then lock folds will always be different
-resulting in equal rows being put in a different cells of the hash
-table. Checking for duplicates will be flawed because different
-fold will be calculated when a row is searched in the hash table. */
-#define TEST_LOCK_FOLD_ALWAYS_DIFFERENT
-#endif
-
-#if 0
-/** This effectively kills the search-for-duplicate-before-adding-a-row
-function, but searching in the hash is still performed. It will always
-be assumed that lock is not present and insertion will be performed in
-the hash table. */
-#define TEST_NO_LOCKS_ROW_IS_EVER_EQUAL_TO_LOCK_T
-#endif
-
-#if 0
-/** This aggressively repeats adding each row many times. Depending on
-the above settings this may be noop or may result in lots of rows being
-added. */
-#define TEST_ADD_EACH_LOCKS_ROW_MANY_TIMES
-#endif
-
-#if 0
-/** Very similar to TEST_NO_LOCKS_ROW_IS_EVER_EQUAL_TO_LOCK_T but hash
-table search is not performed at all. */
-#define TEST_DO_NOT_CHECK_FOR_DUPLICATE_ROWS
-#endif
-
-#if 0
-/** Do not insert each row into the hash table, duplicates may appear
-if this is enabled, also if this is enabled searching into the hash is
-noop because it will be empty. */
-#define TEST_DO_NOT_INSERT_INTO_THE_HASH_TABLE
-#endif
-/* @} */
-
-/** Memory limit passed to ha_storage_put_memlim().
-@param cache hash storage
-@return maximum allowed allocation size */
-#define MAX_ALLOWED_FOR_STORAGE(cache) \
- (TRX_I_S_MEM_LIMIT \
- - (cache)->mem_allocd)
-
-/** Memory limit in table_cache_create_empty_row().
-@param cache hash storage
-@return maximum allowed allocation size */
-#define MAX_ALLOWED_FOR_ALLOC(cache) \
- (TRX_I_S_MEM_LIMIT \
- - (cache)->mem_allocd \
- - ha_storage_get_size((cache)->storage))
-
-/** Memory for each table in the intermediate buffer is allocated in
-separate chunks. These chunks are considered to be concatenated to
-represent one flat array of rows. */
-struct i_s_mem_chunk_t {
- ulint offset; /*!< offset, in number of rows */
- ulint rows_allocd; /*!< the size of this chunk, in number
- of rows */
- void* base; /*!< start of the chunk */
-};
-
-/** This represents one table's cache. */
-struct i_s_table_cache_t {
- ulint rows_used; /*!< number of used rows */
- ulint rows_allocd; /*!< number of allocated rows */
- ulint row_size; /*!< size of a single row */
- i_s_mem_chunk_t chunks[MEM_CHUNKS_IN_TABLE_CACHE]; /*!< array of
- memory chunks that stores the
- rows */
-};
-
-/** This structure describes the intermediate buffer */
-struct trx_i_s_cache_t {
- rw_lock_t rw_lock; /*!< read-write lock protecting
- the rest of this structure */
- ulonglong last_read; /*!< last time the cache was read;
- measured in nanoseconds */
- ib_mutex_t last_read_mutex;/*!< mutex protecting the
- last_read member - it is updated
- inside a shared lock of the
- rw_lock member */
- i_s_table_cache_t innodb_trx; /*!< innodb_trx table */
- i_s_table_cache_t innodb_locks; /*!< innodb_locks table */
- i_s_table_cache_t innodb_lock_waits;/*!< innodb_lock_waits table */
-/** the hash table size is LOCKS_HASH_CELLS_NUM * sizeof(void*) bytes */
-#define LOCKS_HASH_CELLS_NUM 10000
- hash_table_t* locks_hash; /*!< hash table used to eliminate
- duplicate entries in the
- innodb_locks table */
-/** Initial size of the cache storage */
-#define CACHE_STORAGE_INITIAL_SIZE 1024
-/** Number of hash cells in the cache storage */
-#define CACHE_STORAGE_HASH_CELLS 2048
- ha_storage_t* storage; /*!< storage for external volatile
- data that may become unavailable
- when we release
- lock_sys->mutex or trx_sys->mutex */
- ulint mem_allocd; /*!< the amount of memory
- allocated with mem_alloc*() */
- ibool is_truncated; /*!< this is TRUE if the memory
- limit was hit and thus the data
- in the cache is truncated */
-};
-
-/** This is the intermediate buffer where data needed to fill the
-INFORMATION SCHEMA tables is fetched and later retrieved by the C++
-code in handler/i_s.cc. */
-static trx_i_s_cache_t trx_i_s_cache_static;
-/** This is the intermediate buffer where data needed to fill the
-INFORMATION SCHEMA tables is fetched and later retrieved by the C++
-code in handler/i_s.cc. */
-UNIV_INTERN trx_i_s_cache_t* trx_i_s_cache = &trx_i_s_cache_static;
-
-/* Key to register the lock/mutex with performance schema */
-#ifdef UNIV_PFS_RWLOCK
-UNIV_INTERN mysql_pfs_key_t trx_i_s_cache_lock_key;
-#endif /* UNIV_PFS_RWLOCK */
-
-#ifdef UNIV_PFS_MUTEX
-UNIV_INTERN mysql_pfs_key_t cache_last_read_mutex_key;
-#endif /* UNIV_PFS_MUTEX */
-
-/*******************************************************************//**
-For a record lock that is in waiting state retrieves the only bit that
-is set, for a table lock returns ULINT_UNDEFINED.
-@return record number within the heap */
-static
-ulint
-wait_lock_get_heap_no(
-/*==================*/
- const lock_t* lock) /*!< in: lock */
-{
- ulint ret;
-
- switch (lock_get_type(lock)) {
- case LOCK_REC:
- ret = lock_rec_find_set_bit(lock);
- ut_a(ret != ULINT_UNDEFINED);
- break;
- case LOCK_TABLE:
- ret = ULINT_UNDEFINED;
- break;
- default:
- ut_error;
- }
-
- return(ret);
-}
-
-/*******************************************************************//**
-Initializes the members of a table cache. */
-static
-void
-table_cache_init(
-/*=============*/
- i_s_table_cache_t* table_cache, /*!< out: table cache */
- size_t row_size) /*!< in: the size of a
- row */
-{
- ulint i;
-
- table_cache->rows_used = 0;
- table_cache->rows_allocd = 0;
- table_cache->row_size = row_size;
-
- for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
-
- /* the memory is actually allocated in
- table_cache_create_empty_row() */
- table_cache->chunks[i].base = NULL;
- }
-}
-
-/*******************************************************************//**
-Frees a table cache. */
-static
-void
-table_cache_free(
-/*=============*/
- i_s_table_cache_t* table_cache) /*!< in/out: table cache */
-{
- ulint i;
-
- for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
-
- /* the memory is actually allocated in
- table_cache_create_empty_row() */
- if (table_cache->chunks[i].base) {
- mem_free(table_cache->chunks[i].base);
- table_cache->chunks[i].base = NULL;
- }
- }
-}
-
-/*******************************************************************//**
-Returns an empty row from a table cache. The row is allocated if no more
-empty rows are available. The number of used rows is incremented.
-If the memory limit is hit then NULL is returned and nothing is
-allocated.
-@return empty row, or NULL if out of memory */
-static
-void*
-table_cache_create_empty_row(
-/*=========================*/
- i_s_table_cache_t* table_cache, /*!< in/out: table cache */
- trx_i_s_cache_t* cache) /*!< in/out: cache to record
- how many bytes are
- allocated */
-{
- ulint i;
- void* row;
-
- ut_a(table_cache->rows_used <= table_cache->rows_allocd);
-
- if (table_cache->rows_used == table_cache->rows_allocd) {
-
- /* rows_used == rows_allocd means that new chunk needs
- to be allocated: either no more empty rows in the
- last allocated chunk or nothing has been allocated yet
- (rows_num == rows_allocd == 0); */
-
- i_s_mem_chunk_t* chunk;
- ulint req_bytes;
- ulint got_bytes;
- ulint req_rows;
- ulint got_rows;
-
- /* find the first not allocated chunk */
- for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
-
- if (table_cache->chunks[i].base == NULL) {
-
- break;
- }
- }
-
- /* i == MEM_CHUNKS_IN_TABLE_CACHE means that all chunks
- have been allocated :-X */
- ut_a(i < MEM_CHUNKS_IN_TABLE_CACHE);
-
- /* allocate the chunk we just found */
-
- if (i == 0) {
-
- /* first chunk, nothing is allocated yet */
- req_rows = TABLE_CACHE_INITIAL_ROWSNUM;
- } else {
-
- /* Memory is increased by the formula
- new = old + old / 2; We are trying not to be
- aggressive here (= using the common new = old * 2)
- because the allocated memory will not be freed
- until InnoDB exit (it is reused). So it is better
- to once allocate the memory in more steps, but
- have less unused/wasted memory than to use less
- steps in allocation (which is done once in a
- lifetime) but end up with lots of unused/wasted
- memory. */
- req_rows = table_cache->rows_allocd / 2;
- }
- req_bytes = req_rows * table_cache->row_size;
-
- if (req_bytes > MAX_ALLOWED_FOR_ALLOC(cache)) {
-
- return(NULL);
- }
-
- chunk = &table_cache->chunks[i];
-
- chunk->base = mem_alloc2(req_bytes, &got_bytes);
-
- got_rows = got_bytes / table_cache->row_size;
-
- cache->mem_allocd += got_bytes;
-
-#if 0
- printf("allocating chunk %d req bytes=%lu, got bytes=%lu, "
- "row size=%lu, "
- "req rows=%lu, got rows=%lu\n",
- i, req_bytes, got_bytes,
- table_cache->row_size,
- req_rows, got_rows);
-#endif
-
- chunk->rows_allocd = got_rows;
-
- table_cache->rows_allocd += got_rows;
-
- /* adjust the offset of the next chunk */
- if (i < MEM_CHUNKS_IN_TABLE_CACHE - 1) {
-
- table_cache->chunks[i + 1].offset
- = chunk->offset + chunk->rows_allocd;
- }
-
- /* return the first empty row in the newly allocated
- chunk */
- row = chunk->base;
- } else {
-
- char* chunk_start;
- ulint offset;
-
- /* there is an empty row, no need to allocate new
- chunks */
-
- /* find the first chunk that contains allocated but
- empty/unused rows */
- for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
-
- if (table_cache->chunks[i].offset
- + table_cache->chunks[i].rows_allocd
- > table_cache->rows_used) {
-
- break;
- }
- }
-
- /* i == MEM_CHUNKS_IN_TABLE_CACHE means that all chunks
- are full, but
- table_cache->rows_used != table_cache->rows_allocd means
- exactly the opposite - there are allocated but
- empty/unused rows :-X */
- ut_a(i < MEM_CHUNKS_IN_TABLE_CACHE);
-
- chunk_start = (char*) table_cache->chunks[i].base;
- offset = table_cache->rows_used
- - table_cache->chunks[i].offset;
-
- row = chunk_start + offset * table_cache->row_size;
- }
-
- table_cache->rows_used++;
-
- return(row);
-}
-
-#ifdef UNIV_DEBUG
-/*******************************************************************//**
-Validates a row in the locks cache.
-@return TRUE if valid */
-static
-ibool
-i_s_locks_row_validate(
-/*===================*/
- const i_s_locks_row_t* row) /*!< in: row to validate */
-{
- ut_ad(row->lock_trx_id != 0);
- ut_ad(row->lock_mode != NULL);
- ut_ad(row->lock_type != NULL);
- ut_ad(row->lock_table != NULL);
- ut_ad(row->lock_table_id != 0);
-
- if (row->lock_space == ULINT_UNDEFINED) {
- /* table lock */
- ut_ad(!strcmp("TABLE", row->lock_type));
- ut_ad(row->lock_index == NULL);
- ut_ad(row->lock_data == NULL);
- ut_ad(row->lock_page == ULINT_UNDEFINED);
- ut_ad(row->lock_rec == ULINT_UNDEFINED);
- } else {
- /* record lock */
- ut_ad(!strcmp("RECORD", row->lock_type));
- ut_ad(row->lock_index != NULL);
- /* row->lock_data == NULL if buf_page_try_get() == NULL */
- ut_ad(row->lock_page != ULINT_UNDEFINED);
- ut_ad(row->lock_rec != ULINT_UNDEFINED);
- }
-
- return(TRUE);
-}
-#endif /* UNIV_DEBUG */
-
-/*******************************************************************//**
-Fills i_s_trx_row_t object.
-If memory can not be allocated then FALSE is returned.
-@return FALSE if allocation fails */
-static
-ibool
-fill_trx_row(
-/*=========*/
- i_s_trx_row_t* row, /*!< out: result object
- that's filled */
- const trx_t* trx, /*!< in: transaction to
- get data from */
- const i_s_locks_row_t* requested_lock_row,/*!< in: pointer to the
- corresponding row in
- innodb_locks if trx is
- waiting or NULL if trx
- is not waiting */
- trx_i_s_cache_t* cache) /*!< in/out: cache into
- which to copy volatile
- strings */
-{
- const char* stmt;
- size_t stmt_len;
- const char* s;
-
- ut_ad(lock_mutex_own());
-
- row->trx_id = trx->id;
- row->trx_started = trx->start_time;
- row->trx_state = trx_get_que_state_str(trx);
- row->requested_lock_row = requested_lock_row;
- ut_ad(requested_lock_row == NULL
- || i_s_locks_row_validate(requested_lock_row));
-
- if (trx->lock.wait_lock != NULL) {
-
- ut_a(requested_lock_row != NULL);
- row->trx_wait_started = trx->lock.wait_started;
- } else {
- ut_a(requested_lock_row == NULL);
- row->trx_wait_started = 0;
- }
-
- row->trx_weight = (ullint) TRX_WEIGHT(trx);
-
- if (trx->mysql_thd == NULL) {
- /* For internal transactions e.g., purge and transactions
- being recovered at startup there is no associated MySQL
- thread data structure. */
- row->trx_mysql_thread_id = 0;
- row->trx_query = NULL;
- goto thd_done;
- }
-
- row->trx_mysql_thread_id = thd_get_thread_id(trx->mysql_thd);
-
- stmt = trx->mysql_thd
- ? innobase_get_stmt(trx->mysql_thd, &stmt_len)
- : NULL;
-
- if (stmt != NULL) {
- char query[TRX_I_S_TRX_QUERY_MAX_LEN + 1];
-
- if (stmt_len > TRX_I_S_TRX_QUERY_MAX_LEN) {
- stmt_len = TRX_I_S_TRX_QUERY_MAX_LEN;
- }
-
- memcpy(query, stmt, stmt_len);
- query[stmt_len] = '\0';
-
- row->trx_query = static_cast<const char*>(
- ha_storage_put_memlim(
- cache->storage, query, stmt_len + 1,
- MAX_ALLOWED_FOR_STORAGE(cache)));
-
- row->trx_query_cs = innobase_get_charset(trx->mysql_thd);
-
- if (row->trx_query == NULL) {
-
- return(FALSE);
- }
- } else {
-
- row->trx_query = NULL;
- }
-
-thd_done:
- s = trx->op_info;
-
- if (s != NULL && s[0] != '\0') {
-
- TRX_I_S_STRING_COPY(s, row->trx_operation_state,
- TRX_I_S_TRX_OP_STATE_MAX_LEN, cache);
-
- if (row->trx_operation_state == NULL) {
-
- return(FALSE);
- }
- } else {
-
- row->trx_operation_state = NULL;
- }
-
- row->trx_tables_in_use = trx->n_mysql_tables_in_use;
-
- row->trx_tables_locked = trx->mysql_n_tables_locked;
-
- /* These are protected by both trx->mutex or lock_sys->mutex,
- or just lock_sys->mutex. For reading, it suffices to hold
- lock_sys->mutex. */
-
- row->trx_lock_structs = UT_LIST_GET_LEN(trx->lock.trx_locks);
-
- row->trx_lock_memory_bytes = mem_heap_get_size(trx->lock.lock_heap);
-
- row->trx_rows_locked = lock_number_of_rows_locked(&trx->lock);
-
- row->trx_rows_modified = trx->undo_no;
-
- row->trx_concurrency_tickets = trx->n_tickets_to_enter_innodb;
-
- switch (trx->isolation_level) {
- case TRX_ISO_READ_UNCOMMITTED:
- row->trx_isolation_level = "READ UNCOMMITTED";
- break;
- case TRX_ISO_READ_COMMITTED:
- row->trx_isolation_level = "READ COMMITTED";
- break;
- case TRX_ISO_REPEATABLE_READ:
- row->trx_isolation_level = "REPEATABLE READ";
- break;
- case TRX_ISO_SERIALIZABLE:
- row->trx_isolation_level = "SERIALIZABLE";
- break;
- /* Should not happen as TRX_ISO_READ_COMMITTED is default */
- default:
- row->trx_isolation_level = "UNKNOWN";
- }
-
- row->trx_unique_checks = (ibool) trx->check_unique_secondary;
-
- row->trx_foreign_key_checks = (ibool) trx->check_foreigns;
-
- s = trx->detailed_error;
-
- if (s != NULL && s[0] != '\0') {
-
- TRX_I_S_STRING_COPY(s,
- row->trx_foreign_key_error,
- TRX_I_S_TRX_FK_ERROR_MAX_LEN, cache);
-
- if (row->trx_foreign_key_error == NULL) {
-
- return(FALSE);
- }
- } else {
- row->trx_foreign_key_error = NULL;
- }
-
- row->trx_has_search_latch = (ibool) trx->has_search_latch;
-
- row->trx_search_latch_timeout = trx->search_latch_timeout;
-
- row->trx_is_read_only = trx->read_only;
-
- row->trx_is_autocommit_non_locking = trx_is_autocommit_non_locking(trx);
-
- return(TRUE);
-}
-
-/*******************************************************************//**
-Format the nth field of "rec" and put it in "buf". The result is always
-NUL-terminated. Returns the number of bytes that were written to "buf"
-(including the terminating NUL).
-@return end of the result */
-static
-ulint
-put_nth_field(
-/*==========*/
- char* buf, /*!< out: buffer */
- ulint buf_size,/*!< in: buffer size in bytes */
- ulint n, /*!< in: number of field */
- const dict_index_t* index, /*!< in: index */
- const rec_t* rec, /*!< in: record */
- const ulint* offsets)/*!< in: record offsets, returned
- by rec_get_offsets() */
-{
- const byte* data;
- ulint data_len;
- dict_field_t* dict_field;
- ulint ret;
-
- ut_ad(rec_offs_validate(rec, NULL, offsets));
-
- if (buf_size == 0) {
-
- return(0);
- }
-
- ret = 0;
-
- if (n > 0) {
- /* we must append ", " before the actual data */
-
- if (buf_size < 3) {
-
- buf[0] = '\0';
- return(1);
- }
-
- memcpy(buf, ", ", 3);
-
- buf += 2;
- buf_size -= 2;
- ret += 2;
- }
-
- /* now buf_size >= 1 */
-
- data = rec_get_nth_field(rec, offsets, n, &data_len);
-
- dict_field = dict_index_get_nth_field(index, n);
-
- ret += row_raw_format((const char*) data, data_len,
- dict_field, buf, buf_size);
-
- return(ret);
-}
-
-/*******************************************************************//**
-Fills the "lock_data" member of i_s_locks_row_t object.
-If memory can not be allocated then FALSE is returned.
-@return FALSE if allocation fails */
-static
-ibool
-fill_lock_data(
-/*===========*/
- const char** lock_data,/*!< out: "lock_data" to fill */
- const lock_t* lock, /*!< in: lock used to find the data */
- ulint heap_no,/*!< in: rec num used to find the data */
- trx_i_s_cache_t* cache) /*!< in/out: cache where to store
- volatile data */
-{
- mtr_t mtr;
-
- const buf_block_t* block;
- const page_t* page;
- const rec_t* rec;
-
- ut_a(lock_get_type(lock) == LOCK_REC);
-
- mtr_start(&mtr);
-
- block = buf_page_try_get(lock_rec_get_space_id(lock),
- lock_rec_get_page_no(lock),
- &mtr);
-
- if (block == NULL) {
-
- *lock_data = NULL;
-
- mtr_commit(&mtr);
-
- return(TRUE);
- }
-
- page = (const page_t*) buf_block_get_frame(block);
-
- rec = page_find_rec_with_heap_no(page, heap_no);
-
- if (page_rec_is_infimum(rec)) {
-
- *lock_data = ha_storage_put_str_memlim(
- cache->storage, "infimum pseudo-record",
- MAX_ALLOWED_FOR_STORAGE(cache));
- } else if (page_rec_is_supremum(rec)) {
-
- *lock_data = ha_storage_put_str_memlim(
- cache->storage, "supremum pseudo-record",
- MAX_ALLOWED_FOR_STORAGE(cache));
- } else {
-
- const dict_index_t* index;
- ulint n_fields;
- mem_heap_t* heap;
- ulint offsets_onstack[REC_OFFS_NORMAL_SIZE];
- ulint* offsets;
- char buf[TRX_I_S_LOCK_DATA_MAX_LEN];
- ulint buf_used;
- ulint i;
-
- rec_offs_init(offsets_onstack);
- offsets = offsets_onstack;
-
- index = lock_rec_get_index(lock);
-
- n_fields = dict_index_get_n_unique(index);
-
- ut_a(n_fields > 0);
-
- heap = NULL;
- offsets = rec_get_offsets(rec, index, offsets, n_fields,
- &heap);
-
- /* format and store the data */
-
- buf_used = 0;
- for (i = 0; i < n_fields; i++) {
-
- buf_used += put_nth_field(
- buf + buf_used, sizeof(buf) - buf_used,
- i, index, rec, offsets) - 1;
- }
-
- *lock_data = (const char*) ha_storage_put_memlim(
- cache->storage, buf, buf_used + 1,
- MAX_ALLOWED_FOR_STORAGE(cache));
-
- if (UNIV_UNLIKELY(heap != NULL)) {
-
- /* this means that rec_get_offsets() has created a new
- heap and has stored offsets in it; check that this is
- really the case and free the heap */
- ut_a(offsets != offsets_onstack);
- mem_heap_free(heap);
- }
- }
-
- mtr_commit(&mtr);
-
- if (*lock_data == NULL) {
-
- return(FALSE);
- }
-
- return(TRUE);
-}
-
-/*******************************************************************//**
-Fills i_s_locks_row_t object. Returns its first argument.
-If memory can not be allocated then FALSE is returned.
-@return FALSE if allocation fails */
-static
-ibool
-fill_locks_row(
-/*===========*/
- i_s_locks_row_t* row, /*!< out: result object that's filled */
- const lock_t* lock, /*!< in: lock to get data from */
- ulint heap_no,/*!< in: lock's record number
- or ULINT_UNDEFINED if the lock
- is a table lock */
- trx_i_s_cache_t* cache) /*!< in/out: cache into which to copy
- volatile strings */
-{
- row->lock_trx_id = lock_get_trx_id(lock);
- row->lock_mode = lock_get_mode_str(lock);
- row->lock_type = lock_get_type_str(lock);
-
- row->lock_table = ha_storage_put_str_memlim(
- cache->storage, lock_get_table_name(lock),
- MAX_ALLOWED_FOR_STORAGE(cache));
-
- /* memory could not be allocated */
- if (row->lock_table == NULL) {
-
- return(FALSE);
- }
-
- switch (lock_get_type(lock)) {
- case LOCK_REC:
- row->lock_index = ha_storage_put_str_memlim(
- cache->storage, lock_rec_get_index_name(lock),
- MAX_ALLOWED_FOR_STORAGE(cache));
-
- /* memory could not be allocated */
- if (row->lock_index == NULL) {
-
- return(FALSE);
- }
-
- row->lock_space = lock_rec_get_space_id(lock);
- row->lock_page = lock_rec_get_page_no(lock);
- row->lock_rec = heap_no;
-
- if (!fill_lock_data(&row->lock_data, lock, heap_no, cache)) {
-
- /* memory could not be allocated */
- return(FALSE);
- }
-
- break;
- case LOCK_TABLE:
- row->lock_index = NULL;
-
- row->lock_space = ULINT_UNDEFINED;
- row->lock_page = ULINT_UNDEFINED;
- row->lock_rec = ULINT_UNDEFINED;
-
- row->lock_data = NULL;
-
- break;
- default:
- ut_error;
- }
-
- row->lock_table_id = lock_get_table_id(lock);
-
- row->hash_chain.value = row;
- ut_ad(i_s_locks_row_validate(row));
-
- return(TRUE);
-}
-
-/*******************************************************************//**
-Fills i_s_lock_waits_row_t object. Returns its first argument.
-@return result object that's filled */
-static
-i_s_lock_waits_row_t*
-fill_lock_waits_row(
-/*================*/
- i_s_lock_waits_row_t* row, /*!< out: result object
- that's filled */
- const i_s_locks_row_t* requested_lock_row,/*!< in: pointer to the
- relevant requested lock
- row in innodb_locks */
- const i_s_locks_row_t* blocking_lock_row)/*!< in: pointer to the
- relevant blocking lock
- row in innodb_locks */
-{
- ut_ad(i_s_locks_row_validate(requested_lock_row));
- ut_ad(i_s_locks_row_validate(blocking_lock_row));
-
- row->requested_lock_row = requested_lock_row;
- row->blocking_lock_row = blocking_lock_row;
-
- return(row);
-}
-
-/*******************************************************************//**
-Calculates a hash fold for a lock. For a record lock the fold is
-calculated from 4 elements, which uniquely identify a lock at a given
-point in time: transaction id, space id, page number, record number.
-For a table lock the fold is table's id.
-@return fold */
-static
-ulint
-fold_lock(
-/*======*/
- const lock_t* lock, /*!< in: lock object to fold */
- ulint heap_no)/*!< in: lock's record number
- or ULINT_UNDEFINED if the lock
- is a table lock */
-{
-#ifdef TEST_LOCK_FOLD_ALWAYS_DIFFERENT
- static ulint fold = 0;
-
- return(fold++);
-#else
- ulint ret;
-
- switch (lock_get_type(lock)) {
- case LOCK_REC:
- ut_a(heap_no != ULINT_UNDEFINED);
-
- ret = ut_fold_ulint_pair((ulint) lock_get_trx_id(lock),
- lock_rec_get_space_id(lock));
-
- ret = ut_fold_ulint_pair(ret,
- lock_rec_get_page_no(lock));
-
- ret = ut_fold_ulint_pair(ret, heap_no);
-
- break;
- case LOCK_TABLE:
- /* this check is actually not necessary for continuing
- correct operation, but something must have gone wrong if
- it fails. */
- ut_a(heap_no == ULINT_UNDEFINED);
-
- ret = (ulint) lock_get_table_id(lock);
-
- break;
- default:
- ut_error;
- }
-
- return(ret);
-#endif
-}
-
-/*******************************************************************//**
-Checks whether i_s_locks_row_t object represents a lock_t object.
-@return TRUE if they match */
-static
-ibool
-locks_row_eq_lock(
-/*==============*/
- const i_s_locks_row_t* row, /*!< in: innodb_locks row */
- const lock_t* lock, /*!< in: lock object */
- ulint heap_no)/*!< in: lock's record number
- or ULINT_UNDEFINED if the lock
- is a table lock */
-{
- ut_ad(i_s_locks_row_validate(row));
-#ifdef TEST_NO_LOCKS_ROW_IS_EVER_EQUAL_TO_LOCK_T
- return(0);
-#else
- switch (lock_get_type(lock)) {
- case LOCK_REC:
- ut_a(heap_no != ULINT_UNDEFINED);
-
- return(row->lock_trx_id == lock_get_trx_id(lock)
- && row->lock_space == lock_rec_get_space_id(lock)
- && row->lock_page == lock_rec_get_page_no(lock)
- && row->lock_rec == heap_no);
-
- case LOCK_TABLE:
- /* this check is actually not necessary for continuing
- correct operation, but something must have gone wrong if
- it fails. */
- ut_a(heap_no == ULINT_UNDEFINED);
-
- return(row->lock_trx_id == lock_get_trx_id(lock)
- && row->lock_table_id == lock_get_table_id(lock));
-
- default:
- ut_error;
- return(FALSE);
- }
-#endif
-}
-
-/*******************************************************************//**
-Searches for a row in the innodb_locks cache that has a specified id.
-This happens in O(1) time since a hash table is used. Returns pointer to
-the row or NULL if none is found.
-@return row or NULL */
-static
-i_s_locks_row_t*
-search_innodb_locks(
-/*================*/
- trx_i_s_cache_t* cache, /*!< in: cache */
- const lock_t* lock, /*!< in: lock to search for */
- ulint heap_no)/*!< in: lock's record number
- or ULINT_UNDEFINED if the lock
- is a table lock */
-{
- i_s_hash_chain_t* hash_chain;
-
- HASH_SEARCH(
- /* hash_chain->"next" */
- next,
- /* the hash table */
- cache->locks_hash,
- /* fold */
- fold_lock(lock, heap_no),
- /* the type of the next variable */
- i_s_hash_chain_t*,
- /* auxiliary variable */
- hash_chain,
- /* assertion on every traversed item */
- ut_ad(i_s_locks_row_validate(hash_chain->value)),
- /* this determines if we have found the lock */
- locks_row_eq_lock(hash_chain->value, lock, heap_no));
-
- if (hash_chain == NULL) {
-
- return(NULL);
- }
- /* else */
-
- return(hash_chain->value);
-}
-
-/*******************************************************************//**
-Adds new element to the locks cache, enlarging it if necessary.
-Returns a pointer to the added row. If the row is already present then
-no row is added and a pointer to the existing row is returned.
-If row can not be allocated then NULL is returned.
-@return row */
-static
-i_s_locks_row_t*
-add_lock_to_cache(
-/*==============*/
- trx_i_s_cache_t* cache, /*!< in/out: cache */
- const lock_t* lock, /*!< in: the element to add */
- ulint heap_no)/*!< in: lock's record number
- or ULINT_UNDEFINED if the lock
- is a table lock */
-{
- i_s_locks_row_t* dst_row;
-
-#ifdef TEST_ADD_EACH_LOCKS_ROW_MANY_TIMES
- ulint i;
- for (i = 0; i < 10000; i++) {
-#endif
-#ifndef TEST_DO_NOT_CHECK_FOR_DUPLICATE_ROWS
- /* quit if this lock is already present */
- dst_row = search_innodb_locks(cache, lock, heap_no);
- if (dst_row != NULL) {
-
- ut_ad(i_s_locks_row_validate(dst_row));
- return(dst_row);
- }
-#endif
-
- dst_row = (i_s_locks_row_t*)
- table_cache_create_empty_row(&cache->innodb_locks, cache);
-
- /* memory could not be allocated */
- if (dst_row == NULL) {
-
- return(NULL);
- }
-
- if (!fill_locks_row(dst_row, lock, heap_no, cache)) {
-
- /* memory could not be allocated */
- cache->innodb_locks.rows_used--;
- return(NULL);
- }
-
-#ifndef TEST_DO_NOT_INSERT_INTO_THE_HASH_TABLE
- HASH_INSERT(
- /* the type used in the hash chain */
- i_s_hash_chain_t,
- /* hash_chain->"next" */
- next,
- /* the hash table */
- cache->locks_hash,
- /* fold */
- fold_lock(lock, heap_no),
- /* add this data to the hash */
- &dst_row->hash_chain);
-#endif
-#ifdef TEST_ADD_EACH_LOCKS_ROW_MANY_TIMES
- } /* for()-loop */
-#endif
-
- ut_ad(i_s_locks_row_validate(dst_row));
- return(dst_row);
-}
-
-/*******************************************************************//**
-Adds new pair of locks to the lock waits cache.
-If memory can not be allocated then FALSE is returned.
-@return FALSE if allocation fails */
-static
-ibool
-add_lock_wait_to_cache(
-/*===================*/
- trx_i_s_cache_t* cache, /*!< in/out: cache */
- const i_s_locks_row_t* requested_lock_row,/*!< in: pointer to the
- relevant requested lock
- row in innodb_locks */
- const i_s_locks_row_t* blocking_lock_row)/*!< in: pointer to the
- relevant blocking lock
- row in innodb_locks */
-{
- i_s_lock_waits_row_t* dst_row;
-
- dst_row = (i_s_lock_waits_row_t*)
- table_cache_create_empty_row(&cache->innodb_lock_waits,
- cache);
-
- /* memory could not be allocated */
- if (dst_row == NULL) {
-
- return(FALSE);
- }
-
- fill_lock_waits_row(dst_row, requested_lock_row, blocking_lock_row);
-
- return(TRUE);
-}
-
-/*******************************************************************//**
-Adds transaction's relevant (important) locks to cache.
-If the transaction is waiting, then the wait lock is added to
-innodb_locks and a pointer to the added row is returned in
-requested_lock_row, otherwise requested_lock_row is set to NULL.
-If rows can not be allocated then FALSE is returned and the value of
-requested_lock_row is undefined.
-@return FALSE if allocation fails */
-static
-ibool
-add_trx_relevant_locks_to_cache(
-/*============================*/
- trx_i_s_cache_t* cache, /*!< in/out: cache */
- const trx_t* trx, /*!< in: transaction */
- i_s_locks_row_t** requested_lock_row)/*!< out: pointer to the
- requested lock row, or NULL or
- undefined */
-{
- ut_ad(lock_mutex_own());
-
- /* If transaction is waiting we add the wait lock and all locks
- from another transactions that are blocking the wait lock. */
- if (trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
-
- const lock_t* curr_lock;
- ulint wait_lock_heap_no;
- i_s_locks_row_t* blocking_lock_row;
- lock_queue_iterator_t iter;
-
- ut_a(trx->lock.wait_lock != NULL);
-
- wait_lock_heap_no
- = wait_lock_get_heap_no(trx->lock.wait_lock);
-
- /* add the requested lock */
- *requested_lock_row
- = add_lock_to_cache(cache, trx->lock.wait_lock,
- wait_lock_heap_no);
-
- /* memory could not be allocated */
- if (*requested_lock_row == NULL) {
-
- return(FALSE);
- }
-
- /* then iterate over the locks before the wait lock and
- add the ones that are blocking it */
-
- lock_queue_iterator_reset(&iter, trx->lock.wait_lock,
- ULINT_UNDEFINED);
-
- for (curr_lock = lock_queue_iterator_get_prev(&iter);
- curr_lock != NULL;
- curr_lock = lock_queue_iterator_get_prev(&iter)) {
-
- if (lock_has_to_wait(trx->lock.wait_lock,
- curr_lock)) {
-
- /* add the lock that is
- blocking trx->lock.wait_lock */
- blocking_lock_row
- = add_lock_to_cache(
- cache, curr_lock,
- /* heap_no is the same
- for the wait and waited
- locks */
- wait_lock_heap_no);
-
- /* memory could not be allocated */
- if (blocking_lock_row == NULL) {
-
- return(FALSE);
- }
-
- /* add the relation between both locks
- to innodb_lock_waits */
- if (!add_lock_wait_to_cache(
- cache, *requested_lock_row,
- blocking_lock_row)) {
-
- /* memory could not be allocated */
- return(FALSE);
- }
- }
- }
- } else {
-
- *requested_lock_row = NULL;
- }
-
- return(TRUE);
-}
-
-/** The minimum time that a cache must not be updated after it has been
-read for the last time; measured in nanoseconds. We use this technique
-to ensure that SELECTs which join several INFORMATION SCHEMA tables read
-the same version of the cache. */
-#define CACHE_MIN_IDLE_TIME_NS 100000000 /* 0.1 sec */
-
-/*******************************************************************//**
-Checks if the cache can safely be updated.
-@return whether the cache can be updated */
-static bool can_cache_be_updated(trx_i_s_cache_t* cache)
-{
- /* Here we read cache->last_read without acquiring its mutex
- because last_read is only updated when a shared rw lock on the
- whole cache is being held (see trx_i_s_cache_end_read()) and
- we are currently holding an exclusive rw lock on the cache.
- So it is not possible for last_read to be updated while we are
- reading it. */
-
-#ifdef UNIV_SYNC_DEBUG
- ut_a(rw_lock_own(&cache->rw_lock, RW_LOCK_EX));
-#endif
- return my_interval_timer() - cache->last_read > CACHE_MIN_IDLE_TIME_NS;
-}
-
-/*******************************************************************//**
-Declare a cache empty, preparing it to be filled up. Not all resources
-are freed because they can be reused. */
-static
-void
-trx_i_s_cache_clear(
-/*================*/
- trx_i_s_cache_t* cache) /*!< out: cache to clear */
-{
- cache->innodb_trx.rows_used = 0;
- cache->innodb_locks.rows_used = 0;
- cache->innodb_lock_waits.rows_used = 0;
-
- hash_table_clear(cache->locks_hash);
-
- ha_storage_empty(&cache->storage);
-}
-
-/*******************************************************************//**
-Fetches the data needed to fill the 3 INFORMATION SCHEMA tables into the
-table cache buffer. Cache must be locked for write. */
-static
-void
-fetch_data_into_cache_low(
-/*======================*/
- trx_i_s_cache_t* cache, /*!< in/out: cache */
- ibool only_ac_nl, /*!< in: only select non-locking
- autocommit transactions */
- trx_list_t* trx_list) /*!< in: trx list */
-{
- const trx_t* trx;
-
- ut_ad(trx_list == &trx_sys->rw_trx_list
- || trx_list == &trx_sys->ro_trx_list
- || trx_list == &trx_sys->mysql_trx_list);
-
- ut_ad(only_ac_nl == (trx_list == &trx_sys->mysql_trx_list));
-
- /* Iterate over the transaction list and add each one
- to innodb_trx's cache. We also add all locks that are relevant
- to each transaction into innodb_locks' and innodb_lock_waits'
- caches. */
-
- for (trx = UT_LIST_GET_FIRST(*trx_list);
- trx != NULL;
- trx =
- (trx_list == &trx_sys->mysql_trx_list
- ? UT_LIST_GET_NEXT(mysql_trx_list, trx)
- : UT_LIST_GET_NEXT(trx_list, trx))) {
-
- i_s_trx_row_t* trx_row;
- i_s_locks_row_t* requested_lock_row;
-
- if (trx->state == TRX_STATE_NOT_STARTED
- || (only_ac_nl && !trx_is_autocommit_non_locking(trx))) {
-
- continue;
- }
-
- assert_trx_nonlocking_or_in_list(trx);
-
- ut_ad(trx->in_ro_trx_list
- == (trx_list == &trx_sys->ro_trx_list));
-
- ut_ad(trx->in_rw_trx_list
- == (trx_list == &trx_sys->rw_trx_list));
-
- if (!add_trx_relevant_locks_to_cache(cache, trx,
- &requested_lock_row)) {
-
- cache->is_truncated = TRUE;
- return;
- }
-
- trx_row = (i_s_trx_row_t*)
- table_cache_create_empty_row(&cache->innodb_trx,
- cache);
-
- /* memory could not be allocated */
- if (trx_row == NULL) {
-
- cache->is_truncated = TRUE;
- return;
- }
-
- if (!fill_trx_row(trx_row, trx, requested_lock_row, cache)) {
-
- /* memory could not be allocated */
- cache->innodb_trx.rows_used--;
- cache->is_truncated = TRUE;
- return;
- }
- }
-}
-
-/*******************************************************************//**
-Fetches the data needed to fill the 3 INFORMATION SCHEMA tables into the
-table cache buffer. Cache must be locked for write. */
-static
-void
-fetch_data_into_cache(
-/*==================*/
- trx_i_s_cache_t* cache) /*!< in/out: cache */
-{
- ut_ad(lock_mutex_own());
- ut_ad(mutex_own(&trx_sys->mutex));
-
- trx_i_s_cache_clear(cache);
-
- fetch_data_into_cache_low(cache, FALSE, &trx_sys->rw_trx_list);
- fetch_data_into_cache_low(cache, FALSE, &trx_sys->ro_trx_list);
-
- /* Only select autocommit non-locking selects because they can
- only be on the MySQL transaction list (TRUE). */
- fetch_data_into_cache_low(cache, TRUE, &trx_sys->mysql_trx_list);
-
- cache->is_truncated = FALSE;
-}
-
-/*******************************************************************//**
-Update the transactions cache if it has not been read for some time.
-Called from handler/i_s.cc.
-@return 0 - fetched, 1 - not */
-UNIV_INTERN
-int
-trx_i_s_possibly_fetch_data_into_cache(
-/*===================================*/
- trx_i_s_cache_t* cache) /*!< in/out: cache */
-{
- if (!can_cache_be_updated(cache)) {
-
- return(1);
- }
-
- /* We need to read trx_sys and record/table lock queues */
-
- lock_mutex_enter();
-
- mutex_enter(&trx_sys->mutex);
-
- fetch_data_into_cache(cache);
-
- mutex_exit(&trx_sys->mutex);
-
- lock_mutex_exit();
-
- return(0);
-}
-
-/*******************************************************************//**
-Returns TRUE if the data in the cache is truncated due to the memory
-limit posed by TRX_I_S_MEM_LIMIT.
-@return TRUE if truncated */
-UNIV_INTERN
-ibool
-trx_i_s_cache_is_truncated(
-/*=======================*/
- trx_i_s_cache_t* cache) /*!< in: cache */
-{
- return(cache->is_truncated);
-}
-
-/*******************************************************************//**
-Initialize INFORMATION SCHEMA trx related cache. */
-UNIV_INTERN
-void
-trx_i_s_cache_init(
-/*===============*/
- trx_i_s_cache_t* cache) /*!< out: cache to init */
-{
- /* The latching is done in the following order:
- acquire trx_i_s_cache_t::rw_lock, X
- acquire lock mutex
- release lock mutex
- release trx_i_s_cache_t::rw_lock
- acquire trx_i_s_cache_t::rw_lock, S
- acquire trx_i_s_cache_t::last_read_mutex
- release trx_i_s_cache_t::last_read_mutex
- release trx_i_s_cache_t::rw_lock */
-
- rw_lock_create(trx_i_s_cache_lock_key, &cache->rw_lock,
- SYNC_TRX_I_S_RWLOCK);
-
- cache->last_read = 0;
-
- mutex_create(cache_last_read_mutex_key,
- &cache->last_read_mutex, SYNC_TRX_I_S_LAST_READ);
-
- table_cache_init(&cache->innodb_trx, sizeof(i_s_trx_row_t));
- table_cache_init(&cache->innodb_locks, sizeof(i_s_locks_row_t));
- table_cache_init(&cache->innodb_lock_waits,
- sizeof(i_s_lock_waits_row_t));
-
- cache->locks_hash = hash_create(LOCKS_HASH_CELLS_NUM);
-
- cache->storage = ha_storage_create(CACHE_STORAGE_INITIAL_SIZE,
- CACHE_STORAGE_HASH_CELLS);
-
- cache->mem_allocd = 0;
-
- cache->is_truncated = FALSE;
-}
-
-/*******************************************************************//**
-Free the INFORMATION SCHEMA trx related cache. */
-UNIV_INTERN
-void
-trx_i_s_cache_free(
-/*===============*/
- trx_i_s_cache_t* cache) /*!< in, own: cache to free */
-{
- rw_lock_free(&cache->rw_lock);
- mutex_free(&cache->last_read_mutex);
- hash_table_free(cache->locks_hash);
- ha_storage_free(cache->storage);
- table_cache_free(&cache->innodb_trx);
- table_cache_free(&cache->innodb_locks);
- table_cache_free(&cache->innodb_lock_waits);
- memset(cache, 0, sizeof *cache);
-}
-
-/*******************************************************************//**
-Issue a shared/read lock on the tables cache. */
-UNIV_INTERN
-void
-trx_i_s_cache_start_read(
-/*=====================*/
- trx_i_s_cache_t* cache) /*!< in: cache */
-{
- rw_lock_s_lock(&cache->rw_lock);
-}
-
-/*******************************************************************//**
-Release a shared/read lock on the tables cache. */
-UNIV_INTERN
-void
-trx_i_s_cache_end_read(
-/*===================*/
- trx_i_s_cache_t* cache) /*!< in: cache */
-{
-#ifdef UNIV_SYNC_DEBUG
- ut_a(rw_lock_own(&cache->rw_lock, RW_LOCK_SHARED));
-#endif
-
- /* update cache last read time */
- const ulonglong now = my_interval_timer();
- mutex_enter(&cache->last_read_mutex);
- cache->last_read = now;
- mutex_exit(&cache->last_read_mutex);
-
- rw_lock_s_unlock(&cache->rw_lock);
-}
-
-/*******************************************************************//**
-Issue an exclusive/write lock on the tables cache. */
-UNIV_INTERN
-void
-trx_i_s_cache_start_write(
-/*======================*/
- trx_i_s_cache_t* cache) /*!< in: cache */
-{
- rw_lock_x_lock(&cache->rw_lock);
-}
-
-/*******************************************************************//**
-Release an exclusive/write lock on the tables cache. */
-UNIV_INTERN
-void
-trx_i_s_cache_end_write(
-/*====================*/
- trx_i_s_cache_t* cache) /*!< in: cache */
-{
-#ifdef UNIV_SYNC_DEBUG
- ut_a(rw_lock_own(&cache->rw_lock, RW_LOCK_EX));
-#endif
-
- rw_lock_x_unlock(&cache->rw_lock);
-}
-
-/*******************************************************************//**
-Selects a INFORMATION SCHEMA table cache from the whole cache.
-@return table cache */
-static
-i_s_table_cache_t*
-cache_select_table(
-/*===============*/
- trx_i_s_cache_t* cache, /*!< in: whole cache */
- enum i_s_table table) /*!< in: which table */
-{
- i_s_table_cache_t* table_cache;
-
-#ifdef UNIV_SYNC_DEBUG
- ut_a(rw_lock_own(&cache->rw_lock, RW_LOCK_SHARED)
- || rw_lock_own(&cache->rw_lock, RW_LOCK_EX));
-#endif
-
- switch (table) {
- case I_S_INNODB_TRX:
- table_cache = &cache->innodb_trx;
- break;
- case I_S_INNODB_LOCKS:
- table_cache = &cache->innodb_locks;
- break;
- case I_S_INNODB_LOCK_WAITS:
- table_cache = &cache->innodb_lock_waits;
- break;
- default:
- ut_error;
- }
-
- return(table_cache);
-}
-
-/*******************************************************************//**
-Retrieves the number of used rows in the cache for a given
-INFORMATION SCHEMA table.
-@return number of rows */
-UNIV_INTERN
-ulint
-trx_i_s_cache_get_rows_used(
-/*========================*/
- trx_i_s_cache_t* cache, /*!< in: cache */
- enum i_s_table table) /*!< in: which table */
-{
- i_s_table_cache_t* table_cache;
-
- table_cache = cache_select_table(cache, table);
-
- return(table_cache->rows_used);
-}
-
-/*******************************************************************//**
-Retrieves the nth row (zero-based) in the cache for a given
-INFORMATION SCHEMA table.
-@return row */
-UNIV_INTERN
-void*
-trx_i_s_cache_get_nth_row(
-/*======================*/
- trx_i_s_cache_t* cache, /*!< in: cache */
- enum i_s_table table, /*!< in: which table */
- ulint n) /*!< in: row number */
-{
- i_s_table_cache_t* table_cache;
- ulint i;
- void* row;
-
- table_cache = cache_select_table(cache, table);
-
- ut_a(n < table_cache->rows_used);
-
- row = NULL;
-
- for (i = 0; i < MEM_CHUNKS_IN_TABLE_CACHE; i++) {
-
- if (table_cache->chunks[i].offset
- + table_cache->chunks[i].rows_allocd > n) {
-
- row = (char*) table_cache->chunks[i].base
- + (n - table_cache->chunks[i].offset)
- * table_cache->row_size;
- break;
- }
- }
-
- ut_a(row != NULL);
-
- return(row);
-}
-
-/*******************************************************************//**
-Crafts a lock id string from a i_s_locks_row_t object. Returns its
-second argument. This function aborts if there is not enough space in
-lock_id. Be sure to provide at least TRX_I_S_LOCK_ID_MAX_LEN + 1 if you
-want to be 100% sure that it will not abort.
-@return resulting lock id */
-UNIV_INTERN
-char*
-trx_i_s_create_lock_id(
-/*===================*/
- const i_s_locks_row_t* row, /*!< in: innodb_locks row */
- char* lock_id,/*!< out: resulting lock_id */
- ulint lock_id_size)/*!< in: size of the lock id
- buffer */
-{
- int res_len;
-
- /* please adjust TRX_I_S_LOCK_ID_MAX_LEN if you change this */
-
- if (row->lock_space != ULINT_UNDEFINED) {
- /* record lock */
- res_len = ut_snprintf(lock_id, lock_id_size,
- TRX_ID_FMT ":%lu:%lu:%lu",
- row->lock_trx_id, row->lock_space,
- row->lock_page, row->lock_rec);
- } else {
- /* table lock */
- res_len = ut_snprintf(lock_id, lock_id_size,
- TRX_ID_FMT ":" UINT64PF,
- row->lock_trx_id,
- row->lock_table_id);
- }
-
- /* the typecast is safe because snprintf(3) never returns
- negative result */
- ut_a(res_len >= 0);
- ut_a((ulint) res_len < lock_id_size);
-
- return(lock_id);
-}
-
-UNIV_INTERN
-void
-trx_i_s_get_lock_sys_memory_usage(ulint *constant, ulint *variable)
-{
- trx_t* trx;
-
- *constant = lock_sys->rec_hash->n_cells * sizeof(hash_cell_t);
- *variable = 0;
-
- if (trx_sys) {
- mutex_enter(&trx_sys->mutex);
- trx = UT_LIST_GET_FIRST(trx_sys->mysql_trx_list);
- while (trx) {
- *variable += ((trx->lock.lock_heap) ? mem_heap_get_size(trx->lock.lock_heap) : 0);
- trx = UT_LIST_GET_NEXT(mysql_trx_list, trx);
- }
- mutex_exit(&trx_sys->mutex);
- }
-
-}