/*****************************************************************************

Copyright (c) 2007, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2021, 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 include/trx0i_s.h
INFORMATION SCHEMA innodb_trx, innodb_locks and
innodb_lock_waits tables cache structures and public
functions.

Created July 17, 2007 Vasil Dimov
*******************************************************/

#ifndef trx0i_s_h
#define trx0i_s_h

#include "trx0types.h"
#include "dict0types.h"
#include "buf0types.h"

/** The maximum amount of memory that can be consumed by innodb_trx,
innodb_locks and innodb_lock_waits information schema tables. */
#define TRX_I_S_MEM_LIMIT		16777216 /* 16 MiB */

/** The maximum length of a string that can be stored in
i_s_locks_row_t::lock_data */
#define TRX_I_S_LOCK_DATA_MAX_LEN	8192

/** The maximum length of a string that can be stored in
i_s_trx_row_t::trx_query */
#define TRX_I_S_TRX_QUERY_MAX_LEN	1024

/** The maximum length of a string that can be stored in
i_s_trx_row_t::trx_foreign_key_error */
#define TRX_I_S_TRX_FK_ERROR_MAX_LEN	256

/** Safely copy strings in to the INNODB_TRX table's
string based columns */
#define TRX_I_S_STRING_COPY(data, field, constraint, tcache)	\
do {								\
	if (strlen(data) > constraint) {			\
		char	buff[constraint + 1];			\
		strncpy(buff, data, constraint);		\
		buff[constraint] = '\0';			\
								\
		field = static_cast<const char*>(		\
			ha_storage_put_memlim(			\
			(tcache)->storage, buff, constraint + 1,\
			MAX_ALLOWED_FOR_STORAGE(tcache)));	\
	} else {						\
		field = static_cast<const char*>(		\
			ha_storage_put_str_memlim(		\
			(tcache)->storage, data,		\
			MAX_ALLOWED_FOR_STORAGE(tcache)));	\
	}							\
} while (0)

/** This structure represents INFORMATION_SCHEMA.innodb_locks row */
struct i_s_locks_row_t {
	trx_id_t	lock_trx_id;	/*!< transaction identifier */
	const char*	lock_table;	/*!< table name from
					lock_get_table_name() */
	/** index name of a record lock; NULL for table locks */
	const char*	lock_index;
	/** page identifier of the record; (0,0) if !lock_index */
	page_id_t	lock_page;
	/** heap number of the record; 0 if !lock_index */
	uint16_t	lock_rec;
	/** lock mode corresponding to lock_mode_values_typelib */
	uint8_t		lock_mode;
	/** (some) content of the record, if available in the buffer pool;
	NULL if !lock_index */
	const char*	lock_data;

	/** The following are auxiliary and not included in the table */
	/* @{ */
	table_id_t	lock_table_id;
					/*!< table identifier from
					lock_get_table_id */
	i_s_locks_row_t *next;		/*!< hash table chain node for
					trx_i_s_cache_t::locks_hash */
	/* @} */
};

/** This structure represents INFORMATION_SCHEMA.innodb_trx row */
struct i_s_trx_row_t {
	trx_id_t		trx_id;		/*!< transaction identifier */
	const char*		trx_state;
	time_t			trx_started;	/*!< trx_t::start_time */
	const i_s_locks_row_t*	requested_lock_row;
					/*!< pointer to a row
					in innodb_locks if trx
					is waiting, or NULL */
	time_t		trx_wait_started; /*!< trx_t->lock.wait_started */
	uintmax_t	trx_weight;	/*!< TRX_WEIGHT() */
	my_thread_id	trx_mysql_thread_id; /*!< thd_get_thread_id() */
	const char*	trx_query;	/*!< MySQL statement being
					executed in the transaction */
	CHARSET_INFO*	trx_query_cs;	/*!< the charset of trx_query */
	const char*	trx_operation_state; /*!< trx_t::op_info */
	ulint		trx_tables_in_use;/*!< n_mysql_tables_in_use in
					 trx_t */
	ulint		trx_tables_locked;
					/*!< mysql_n_tables_locked in
					trx_t */
	ulint		trx_lock_structs;/*!< list len of trx_locks in
					trx_t */
	ulint		trx_lock_memory_bytes;
					/*!< mem_heap_get_size(
					trx->lock_heap) */
	ulint		trx_rows_locked;/*!< trx_lock_t::n_rec_locks */
	uintmax_t	trx_rows_modified;/*!< trx_t::undo_no */
	uint		trx_isolation_level;
					/*!< trx_t::isolation_level */
	bool		trx_unique_checks;
					/*!< check_unique_secondary in trx_t*/
	bool		trx_foreign_key_checks;
					/*!< check_foreigns in trx_t */
	const char*	trx_foreign_key_error;
					/*!< detailed_error in trx_t */
	bool		trx_is_read_only;
					/*!< trx_t::read_only */
	bool		trx_is_autocommit_non_locking;
					/*!< trx:t::is_autocommit_non_locking()
					*/
};

/** This structure represents INFORMATION_SCHEMA.innodb_lock_waits row */
struct i_s_lock_waits_row_t {
	const i_s_locks_row_t*	requested_lock_row;	/*!< requested lock */
	const i_s_locks_row_t*	blocking_lock_row;	/*!< blocking lock */
};

/** Auxiliary enum used by functions that need to select one of the
INFORMATION_SCHEMA tables */
enum i_s_table {
	I_S_INNODB_TRX,		/*!< INFORMATION_SCHEMA.innodb_trx */
	I_S_INNODB_LOCKS,	/*!< INFORMATION_SCHEMA.innodb_locks */
	I_S_INNODB_LOCK_WAITS	/*!< INFORMATION_SCHEMA.innodb_lock_waits */
};

/*******************************************************************//**
Initialize INFORMATION SCHEMA trx related cache. */
void trx_i_s_cache_init();
/*******************************************************************//**
Free the INFORMATION SCHEMA trx related cache. */
void trx_i_s_cache_free();

/*******************************************************************//**
Issue a shared/read lock on the tables cache. */
void trx_i_s_cache_start_read() noexcept;

/*******************************************************************//**
Release a shared/read lock on the tables cache. */
void trx_i_s_cache_end_read() noexcept;

/*******************************************************************//**
Retrieves the number of used rows in the cache for a given
INFORMATION SCHEMA table.
@param table  table to be read
@return number of rows */
ulint trx_i_s_cache_get_rows_used(i_s_table table);

/*******************************************************************//**
Retrieves the nth row in the cache for a given INFORMATION SCHEMA
table.
@param table table to read
@param n     row number
@return row */
void *trx_i_s_cache_get_nth_row(i_s_table table, ulint n);

/*******************************************************************//**
Update the transactions cache if it has not been read for some time.
@retval false when fetched or cached
@retval true if fetched but the cache was truncated */
bool trx_i_s_possibly_fetch_data_into_cache();

/** The maximum length of a resulting lock_id_size in
trx_i_s_create_lock_id(), not including the terminating NUL.
":%lu:%lu:%lu" -> 63 chars */
#define TRX_I_S_LOCK_ID_MAX_LEN	(TRX_ID_MAX_LEN + 63)

/*******************************************************************//**
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 */
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 */

#endif /* trx0i_s_h */
