mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			6516 lines
		
	
	
	
		
			168 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			6516 lines
		
	
	
	
		
			168 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*****************************************************************************
 | |
| 
 | |
| Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved.
 | |
| Copyright (c) 2014, 2023, 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 handler/i_s.cc
 | |
| InnoDB INFORMATION SCHEMA tables interface to MySQL.
 | |
| 
 | |
| Created July 18, 2007 Vasil Dimov
 | |
| *******************************************************/
 | |
| 
 | |
| #include "univ.i"
 | |
| #include <mysql_version.h>
 | |
| #include <field.h>
 | |
| 
 | |
| #include <sql_acl.h>
 | |
| #include <sql_show.h>
 | |
| #include <sql_time.h>
 | |
| 
 | |
| #include "i_s.h"
 | |
| #include "btr0pcur.h"
 | |
| #include "btr0types.h"
 | |
| #include "dict0dict.h"
 | |
| #include "dict0load.h"
 | |
| #include "buf0buddy.h"
 | |
| #include "buf0buf.h"
 | |
| #include "dict0mem.h"
 | |
| #include "dict0types.h"
 | |
| #include "srv0start.h"
 | |
| #include "trx0i_s.h"
 | |
| #include "trx0trx.h"
 | |
| #include "srv0mon.h"
 | |
| #include "pars0pars.h"
 | |
| #include "fts0types.h"
 | |
| #include "fts0opt.h"
 | |
| #include "fts0priv.h"
 | |
| #include "btr0btr.h"
 | |
| #include "page0zip.h"
 | |
| #include "fil0fil.h"
 | |
| #include "fil0crypt.h"
 | |
| #include "dict0crea.h"
 | |
| #include "fts0vlc.h"
 | |
| #include "scope.h"
 | |
| #include "log.h"
 | |
| 
 | |
| /** The latest successfully looked up innodb_fts_aux_table */
 | |
| table_id_t innodb_ft_aux_table_id;
 | |
| 
 | |
| /** structure associates a name string with a file page type and/or buffer
 | |
| page state. */
 | |
| struct buf_page_desc_t{
 | |
| 	const char*	type_str;	/*!< String explain the page
 | |
| 					type/state */
 | |
| 	ulint		type_value;	/*!< Page type or page state */
 | |
| };
 | |
| 
 | |
| /** We also define I_S_PAGE_TYPE_INDEX as the Index Page's position
 | |
| in i_s_page_type[] array */
 | |
| #define I_S_PAGE_TYPE_INDEX		1
 | |
| 
 | |
| /** Any unassigned FIL_PAGE_TYPE will be treated as unknown. */
 | |
| #define	I_S_PAGE_TYPE_UNKNOWN		FIL_PAGE_TYPE_UNKNOWN
 | |
| 
 | |
| /** R-tree index page */
 | |
| #define	I_S_PAGE_TYPE_RTREE		(FIL_PAGE_TYPE_LAST + 1)
 | |
| 
 | |
| #define I_S_PAGE_TYPE_LAST		I_S_PAGE_TYPE_RTREE
 | |
| 
 | |
| #define I_S_PAGE_TYPE_BITS		4
 | |
| 
 | |
| /** Name string for File Page Types */
 | |
| static buf_page_desc_t	i_s_page_type[] = {
 | |
| 	{"ALLOCATED", FIL_PAGE_TYPE_ALLOCATED},
 | |
| 	{"INDEX", FIL_PAGE_INDEX},
 | |
| 	{"UNDO_LOG", FIL_PAGE_UNDO_LOG},
 | |
| 	{"INODE", FIL_PAGE_INODE},
 | |
| 	{"IBUF_FREE_LIST", FIL_PAGE_IBUF_FREE_LIST},
 | |
| 	{"IBUF_BITMAP", FIL_PAGE_IBUF_BITMAP},
 | |
| 	{"SYSTEM", FIL_PAGE_TYPE_SYS},
 | |
| 	{"TRX_SYSTEM", FIL_PAGE_TYPE_TRX_SYS},
 | |
| 	{"FILE_SPACE_HEADER", FIL_PAGE_TYPE_FSP_HDR},
 | |
| 	{"EXTENT_DESCRIPTOR", FIL_PAGE_TYPE_XDES},
 | |
| 	{"BLOB", FIL_PAGE_TYPE_BLOB},
 | |
| 	{"COMPRESSED_BLOB", FIL_PAGE_TYPE_ZBLOB},
 | |
| 	{"COMPRESSED_BLOB2", FIL_PAGE_TYPE_ZBLOB2},
 | |
| 	{"UNKNOWN", I_S_PAGE_TYPE_UNKNOWN},
 | |
| 	{"RTREE_INDEX", I_S_PAGE_TYPE_RTREE},
 | |
| };
 | |
| 
 | |
| /** This structure defines information we will fetch from pages
 | |
| currently cached in the buffer pool. It will be used to populate
 | |
| table INFORMATION_SCHEMA.INNODB_BUFFER_PAGE */
 | |
| struct buf_page_info_t{
 | |
| 	ulint		block_id;	/*!< Buffer Pool block ID */
 | |
| 	/** page identifier */
 | |
| 	page_id_t	id;
 | |
| 	uint32_t	access_time;	/*!< Time of first access */
 | |
| 	uint32_t	state;		/*!< buf_page_t::state() */
 | |
| #ifdef BTR_CUR_HASH_ADAPT
 | |
| 	unsigned	hashed:1;	/*!< Whether hash index has been
 | |
| 					built on this page */
 | |
| #endif /* BTR_CUR_HASH_ADAPT */
 | |
| 	unsigned	is_old:1;	/*!< TRUE if the block is in the old
 | |
| 					blocks in buf_pool.LRU_old */
 | |
| 	unsigned	freed_page_clock:31; /*!< the value of
 | |
| 					buf_pool.freed_page_clock */
 | |
| 	unsigned	zip_ssize:PAGE_ZIP_SSIZE_BITS;
 | |
| 					/*!< Compressed page size */
 | |
| 	unsigned	compressed_only:1; /*!< ROW_FORMAT=COMPRESSED only */
 | |
| 	unsigned	page_type:I_S_PAGE_TYPE_BITS;	/*!< Page type */
 | |
| 	unsigned	num_recs:UNIV_PAGE_SIZE_SHIFT_MAX-2;
 | |
| 					/*!< Number of records on Page */
 | |
| 	unsigned	data_size:UNIV_PAGE_SIZE_SHIFT_MAX;
 | |
| 					/*!< Sum of the sizes of the records */
 | |
| 	lsn_t		newest_mod;	/*!< Log sequence number of
 | |
| 					the youngest modification */
 | |
| 	lsn_t		oldest_mod;	/*!< Log sequence number of
 | |
| 					the oldest modification */
 | |
| 	index_id_t	index_id;	/*!< Index ID if a index page */
 | |
| };
 | |
| 
 | |
| /*
 | |
| Use the following types mapping:
 | |
| 
 | |
| C type	ST_FIELD_INFO::field_type
 | |
| ---------------------------------
 | |
| long			MYSQL_TYPE_LONGLONG
 | |
| (field_length=MY_INT64_NUM_DECIMAL_DIGITS)
 | |
| 
 | |
| long unsigned		MYSQL_TYPE_LONGLONG
 | |
| (field_length=MY_INT64_NUM_DECIMAL_DIGITS, field_flags=MY_I_S_UNSIGNED)
 | |
| 
 | |
| char*			MYSQL_TYPE_STRING
 | |
| (field_length=n)
 | |
| 
 | |
| float			MYSQL_TYPE_FLOAT
 | |
| (field_length=0 is ignored)
 | |
| 
 | |
| void*			MYSQL_TYPE_LONGLONG
 | |
| (field_length=MY_INT64_NUM_DECIMAL_DIGITS, field_flags=MY_I_S_UNSIGNED)
 | |
| 
 | |
| boolean (if else)	MYSQL_TYPE_LONG
 | |
| (field_length=1)
 | |
| 
 | |
| time_t			MYSQL_TYPE_DATETIME
 | |
| (field_length=0 ignored)
 | |
| ---------------------------------
 | |
| */
 | |
| 
 | |
| /**
 | |
| Common function to fill any of the dynamic tables:
 | |
| INFORMATION_SCHEMA.innodb_trx
 | |
| INFORMATION_SCHEMA.innodb_locks
 | |
| INFORMATION_SCHEMA.innodb_lock_waits
 | |
| @retval false if access to the table is blocked
 | |
| @retval true  if something should be filled in */
 | |
| static bool trx_i_s_common_fill_table(THD *thd, TABLE_LIST *tables)
 | |
| {
 | |
|   DBUG_ENTER("trx_i_s_common_fill_table");
 | |
| 
 | |
|   /* deny access to non-superusers */
 | |
|   if (check_global_access(thd, PROCESS_ACL))
 | |
|     DBUG_RETURN(false);
 | |
| 
 | |
|   RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
|   /* update the cache */
 | |
|   trx_i_s_cache_start_write(trx_i_s_cache);
 | |
|   trx_i_s_possibly_fetch_data_into_cache(trx_i_s_cache);
 | |
|   trx_i_s_cache_end_write(trx_i_s_cache);
 | |
| 
 | |
|   if (trx_i_s_cache_is_truncated(trx_i_s_cache))
 | |
|     sql_print_warning("InnoDB: Data in %.*s truncated due to memory limit"
 | |
|                       " of %u bytes",
 | |
|                       int(tables->schema_table_name.length),
 | |
|                       tables->schema_table_name.str,
 | |
|                       TRX_I_S_MEM_LIMIT);
 | |
| 
 | |
|   DBUG_RETURN(true);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Unbind a dynamic INFORMATION_SCHEMA table.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_common_deinit(
 | |
| /*==============*/
 | |
| 	void*	p);	/*!< in/out: table schema object */
 | |
| /*******************************************************************//**
 | |
| Auxiliary function to store time_t value in MYSQL_TYPE_DATETIME
 | |
| field.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| field_store_time_t(
 | |
| /*===============*/
 | |
| 	Field*	field,	/*!< in/out: target field for storage */
 | |
| 	time_t	time)	/*!< in: value to store */
 | |
| {
 | |
| 	MYSQL_TIME	my_time;
 | |
| 	struct tm	tm_time;
 | |
| 
 | |
| 	if (time) {
 | |
| #if 0
 | |
| 		/* use this if you are sure that `variables' and `time_zone'
 | |
| 		are always initialized */
 | |
| 		thd->variables.time_zone->gmt_sec_to_TIME(
 | |
| 			&my_time, (my_time_t) time);
 | |
| #else
 | |
| 		localtime_r(&time, &tm_time);
 | |
| 		localtime_to_TIME(&my_time, &tm_time);
 | |
| 		my_time.time_type = MYSQL_TIMESTAMP_DATETIME;
 | |
| #endif
 | |
| 	} else {
 | |
| 		memset(&my_time, 0, sizeof(my_time));
 | |
| 	}
 | |
| 
 | |
| 	/* JAN: TODO: MySQL 5.7
 | |
| 	return(field->store_time(&my_time, MYSQL_TIMESTAMP_DATETIME));
 | |
| 	*/
 | |
| 	return(field->store_time(&my_time));
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Auxiliary function to store char* value in MYSQL_TYPE_STRING field.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| field_store_string(
 | |
| /*===============*/
 | |
| 	Field*		field,	/*!< in/out: target field for storage */
 | |
| 	const char*	str)	/*!< in: NUL-terminated utf-8 string,
 | |
| 				or NULL */
 | |
| {
 | |
| 	if (!str) {
 | |
| 		field->set_null();
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	field->set_notnull();
 | |
| 	return field->store(str, uint(strlen(str)), system_charset_info);
 | |
| }
 | |
| 
 | |
| #ifdef BTR_CUR_HASH_ADAPT
 | |
| # define I_S_AHI 1 /* Include the IS_HASHED column */
 | |
| #else
 | |
| # define I_S_AHI 0 /* Omit the IS_HASHED column */
 | |
| #endif
 | |
| 
 | |
| static const LEX_CSTRING isolation_level_values[] =
 | |
| {
 | |
| 	{ STRING_WITH_LEN("READ UNCOMMITTED") },
 | |
| 	{ STRING_WITH_LEN("READ COMMITTED") },
 | |
| 	{ STRING_WITH_LEN("REPEATABLE READ") },
 | |
| 	{ STRING_WITH_LEN("SERIALIZABLE") }
 | |
| };
 | |
| 
 | |
| static TypelibBuffer<4> isolation_level_values_typelib(isolation_level_values);
 | |
| 
 | |
| namespace Show {
 | |
| 
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.innodb_trx */
 | |
| static ST_FIELD_INFO innodb_trx_fields_info[]=
 | |
| {
 | |
| #define IDX_TRX_ID		0
 | |
|   Column("trx_id", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_STATE		1
 | |
|   Column("trx_state", Varchar(13), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_STARTED		2
 | |
|   Column("trx_started", Datetime(0), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_REQUESTED_LOCK_ID	3
 | |
|   Column("trx_requested_lock_id",
 | |
|          Varchar(TRX_I_S_LOCK_ID_MAX_LEN + 1), NULLABLE),
 | |
| 
 | |
| #define IDX_TRX_WAIT_STARTED	4
 | |
|  Column("trx_wait_started", Datetime(0), NULLABLE),
 | |
| 
 | |
| #define IDX_TRX_WEIGHT		5
 | |
|  Column("trx_weight", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_MYSQL_THREAD_ID	6
 | |
|   Column("trx_mysql_thread_id", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_QUERY		7
 | |
|   Column("trx_query", Varchar(TRX_I_S_TRX_QUERY_MAX_LEN), NULLABLE),
 | |
| 
 | |
| #define IDX_TRX_OPERATION_STATE	8
 | |
|   Column("trx_operation_state", Varchar(64), NULLABLE),
 | |
| 
 | |
| #define IDX_TRX_TABLES_IN_USE	9
 | |
|   Column("trx_tables_in_use", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_TABLES_LOCKED	10
 | |
|   Column("trx_tables_locked", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_LOCK_STRUCTS	11
 | |
|   Column("trx_lock_structs", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_LOCK_MEMORY_BYTES	12
 | |
|   Column("trx_lock_memory_bytes", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_ROWS_LOCKED	13
 | |
|   Column("trx_rows_locked", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_ROWS_MODIFIED	14
 | |
|   Column("trx_rows_modified", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_CONNCURRENCY_TICKETS	15
 | |
|   Column("trx_concurrency_tickets", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_ISOLATION_LEVEL	16
 | |
|   Column("trx_isolation_level",
 | |
|          Enum(&isolation_level_values_typelib), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_UNIQUE_CHECKS	17
 | |
|   Column("trx_unique_checks", SLong(1), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_FOREIGN_KEY_CHECKS	18
 | |
|   Column("trx_foreign_key_checks", SLong(1), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_LAST_FOREIGN_KEY_ERROR	19
 | |
|   Column("trx_last_foreign_key_error",
 | |
|          Varchar(TRX_I_S_TRX_FK_ERROR_MAX_LEN),NULLABLE),
 | |
| 
 | |
| #define IDX_TRX_READ_ONLY		20
 | |
|   Column("trx_is_read_only", SLong(1), NOT_NULL),
 | |
| 
 | |
| #define IDX_TRX_AUTOCOMMIT_NON_LOCKING	21
 | |
|   Column("trx_autocommit_non_locking", SLong(1), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| 
 | |
| } // namespace Show
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Read data from cache buffer and fill the INFORMATION_SCHEMA.innodb_trx
 | |
| table with it.
 | |
| @retval 0 on success
 | |
| @retval 1 on failure */
 | |
| static int fill_innodb_trx_from_cache(THD *thd, TABLE_LIST *tables, Item*)
 | |
| {
 | |
| 	ulint	rows_num;
 | |
| 	char	lock_id[TRX_I_S_LOCK_ID_MAX_LEN + 1];
 | |
| 	ulint	i;
 | |
| 
 | |
| 	DBUG_ENTER("fill_innodb_trx_from_cache");
 | |
| 
 | |
| 	if (!trx_i_s_common_fill_table(thd, tables)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	struct cache
 | |
| 	{
 | |
| 		cache() { trx_i_s_cache_start_read(trx_i_s_cache); }
 | |
| 		~cache() { trx_i_s_cache_end_read(trx_i_s_cache); }
 | |
| 	} c;
 | |
| 
 | |
| 	Field** fields = tables->table->field;
 | |
| 
 | |
| 	rows_num = trx_i_s_cache_get_rows_used(trx_i_s_cache,
 | |
| 					       I_S_INNODB_TRX);
 | |
| 
 | |
| 	for (i = 0; i < rows_num; i++) {
 | |
| 
 | |
| 		i_s_trx_row_t*	row;
 | |
| 
 | |
| 		row = (i_s_trx_row_t*)
 | |
| 			trx_i_s_cache_get_nth_row(
 | |
| 				trx_i_s_cache, I_S_INNODB_TRX, i);
 | |
| 
 | |
| 		/* trx_id */
 | |
| 		OK(fields[IDX_TRX_ID]->store(row->trx_id, true));
 | |
| 
 | |
| 		/* trx_state */
 | |
| 		OK(field_store_string(fields[IDX_TRX_STATE],
 | |
| 				      row->trx_state));
 | |
| 
 | |
| 		/* trx_started */
 | |
| 		OK(field_store_time_t(fields[IDX_TRX_STARTED],
 | |
| 				      (time_t) row->trx_started));
 | |
| 
 | |
| 		/* trx_requested_lock_id */
 | |
| 		/* trx_wait_started */
 | |
| 		if (row->trx_wait_started != 0) {
 | |
| 
 | |
| 			OK(field_store_string(
 | |
| 				   fields[IDX_TRX_REQUESTED_LOCK_ID],
 | |
| 				   trx_i_s_create_lock_id(
 | |
| 					   row->requested_lock_row,
 | |
| 					   lock_id, sizeof(lock_id))));
 | |
| 			/* field_store_string() sets it no notnull */
 | |
| 
 | |
| 			OK(field_store_time_t(
 | |
| 				   fields[IDX_TRX_WAIT_STARTED],
 | |
| 				   (time_t) row->trx_wait_started));
 | |
| 			fields[IDX_TRX_WAIT_STARTED]->set_notnull();
 | |
| 		} else {
 | |
| 
 | |
| 			fields[IDX_TRX_REQUESTED_LOCK_ID]->set_null();
 | |
| 			fields[IDX_TRX_WAIT_STARTED]->set_null();
 | |
| 		}
 | |
| 
 | |
| 		/* trx_weight */
 | |
| 		OK(fields[IDX_TRX_WEIGHT]->store(row->trx_weight, true));
 | |
| 
 | |
| 		/* trx_mysql_thread_id */
 | |
| 		OK(fields[IDX_TRX_MYSQL_THREAD_ID]->store(
 | |
| 			   row->trx_mysql_thread_id, true));
 | |
| 
 | |
| 		/* trx_query */
 | |
| 		if (row->trx_query) {
 | |
| 			/* store will do appropriate character set
 | |
| 			conversion check */
 | |
| 			fields[IDX_TRX_QUERY]->store(
 | |
| 				row->trx_query,
 | |
| 				static_cast<uint>(strlen(row->trx_query)),
 | |
| 				row->trx_query_cs);
 | |
| 			fields[IDX_TRX_QUERY]->set_notnull();
 | |
| 		} else {
 | |
| 			fields[IDX_TRX_QUERY]->set_null();
 | |
| 		}
 | |
| 
 | |
| 		/* trx_operation_state */
 | |
| 		OK(field_store_string(fields[IDX_TRX_OPERATION_STATE],
 | |
| 				      row->trx_operation_state));
 | |
| 
 | |
| 		/* trx_tables_in_use */
 | |
| 		OK(fields[IDX_TRX_TABLES_IN_USE]->store(
 | |
| 			   row->trx_tables_in_use, true));
 | |
| 
 | |
| 		/* trx_tables_locked */
 | |
| 		OK(fields[IDX_TRX_TABLES_LOCKED]->store(
 | |
| 			   row->trx_tables_locked, true));
 | |
| 
 | |
| 		/* trx_lock_structs */
 | |
| 		OK(fields[IDX_TRX_LOCK_STRUCTS]->store(
 | |
| 			   row->trx_lock_structs, true));
 | |
| 
 | |
| 		/* trx_lock_memory_bytes */
 | |
| 		OK(fields[IDX_TRX_LOCK_MEMORY_BYTES]->store(
 | |
| 			   row->trx_lock_memory_bytes, true));
 | |
| 
 | |
| 		/* trx_rows_locked */
 | |
| 		OK(fields[IDX_TRX_ROWS_LOCKED]->store(
 | |
| 			   row->trx_rows_locked, true));
 | |
| 
 | |
| 		/* trx_rows_modified */
 | |
| 		OK(fields[IDX_TRX_ROWS_MODIFIED]->store(
 | |
| 			   row->trx_rows_modified, true));
 | |
| 
 | |
| 		/* trx_concurrency_tickets */
 | |
| 		OK(fields[IDX_TRX_CONNCURRENCY_TICKETS]->store(0, true));
 | |
| 
 | |
| 		/* trx_isolation_level */
 | |
| 		OK(fields[IDX_TRX_ISOLATION_LEVEL]->store(
 | |
| 			   1 + row->trx_isolation_level, true));
 | |
| 
 | |
| 		/* trx_unique_checks */
 | |
| 		OK(fields[IDX_TRX_UNIQUE_CHECKS]->store(
 | |
| 			   row->trx_unique_checks, true));
 | |
| 
 | |
| 		/* trx_foreign_key_checks */
 | |
| 		OK(fields[IDX_TRX_FOREIGN_KEY_CHECKS]->store(
 | |
| 			   row->trx_foreign_key_checks, true));
 | |
| 
 | |
| 		/* trx_last_foreign_key_error */
 | |
| 		OK(field_store_string(fields[IDX_TRX_LAST_FOREIGN_KEY_ERROR],
 | |
| 				      row->trx_foreign_key_error));
 | |
| 
 | |
| 		/* trx_is_read_only*/
 | |
| 		OK(fields[IDX_TRX_READ_ONLY]->store(
 | |
| 			   row->trx_is_read_only, true));
 | |
| 
 | |
| 		/* trx_is_autocommit_non_locking */
 | |
| 		OK(fields[IDX_TRX_AUTOCOMMIT_NON_LOCKING]->store(
 | |
| 			   row->trx_is_autocommit_non_locking, true));
 | |
| 
 | |
| 		OK(schema_table_store_record(thd, tables->table));
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_trx
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_trx_init(
 | |
| /*============*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_trx_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_trx_fields_info;
 | |
| 	schema->fill_table = fill_innodb_trx_from_cache;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| static struct st_mysql_information_schema	i_s_info =
 | |
| {
 | |
| 	MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
 | |
| };
 | |
| 
 | |
| /** version number reported by SHOW PLUGINS */
 | |
| constexpr unsigned i_s_version= MYSQL_VERSION_MAJOR << 8 | MYSQL_VERSION_MINOR;
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_trx =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_TRX",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB transactions",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_trx_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| static const LEX_CSTRING lock_mode_values[] =
 | |
| {
 | |
| 	{ STRING_WITH_LEN("S") },
 | |
| 	{ STRING_WITH_LEN("S,GAP") },
 | |
| 	{ STRING_WITH_LEN("X") },
 | |
| 	{ STRING_WITH_LEN("X,GAP") },
 | |
| 	{ STRING_WITH_LEN("IS") },
 | |
| 	{ STRING_WITH_LEN("IS,GAP") },
 | |
| 	{ STRING_WITH_LEN("IX") },
 | |
| 	{ STRING_WITH_LEN("IX,GAP") },
 | |
| 	{ STRING_WITH_LEN("AUTO_INC") }
 | |
| };
 | |
| 
 | |
| static TypelibBuffer<9> lock_mode_values_typelib(lock_mode_values);
 | |
| 
 | |
| static const LEX_CSTRING lock_type_values[] =
 | |
| {
 | |
| 	{ STRING_WITH_LEN("RECORD") },
 | |
| 	{ STRING_WITH_LEN("TABLE") }
 | |
| };
 | |
| 
 | |
| static TypelibBuffer<2> lock_type_values_typelib(lock_type_values);
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.innodb_locks */
 | |
| static ST_FIELD_INFO innodb_locks_fields_info[]=
 | |
| {
 | |
| #define IDX_LOCK_ID		0
 | |
|   Column("lock_id",     Varchar(TRX_I_S_LOCK_ID_MAX_LEN + 1),  NOT_NULL),
 | |
| 
 | |
| #define IDX_LOCK_TRX_ID		1
 | |
|   Column("lock_trx_id", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_LOCK_MODE		2
 | |
|   Column("lock_mode",   Enum(&lock_mode_values_typelib), NOT_NULL),
 | |
| 
 | |
| #define IDX_LOCK_TYPE		3
 | |
|   Column("lock_type",   Enum(&lock_type_values_typelib), NOT_NULL),
 | |
| 
 | |
| #define IDX_LOCK_TABLE		4
 | |
|   Column("lock_table",  Varchar(1024), NOT_NULL),
 | |
| 
 | |
| #define IDX_LOCK_INDEX		5
 | |
|   Column("lock_index",  Varchar(1024), NULLABLE),
 | |
| 
 | |
| #define IDX_LOCK_SPACE		6
 | |
|   Column("lock_space",  ULong(),   NULLABLE),
 | |
| 
 | |
| #define IDX_LOCK_PAGE		7
 | |
|   Column("lock_page",   ULong(),   NULLABLE),
 | |
| 
 | |
| #define IDX_LOCK_REC		8
 | |
|   Column("lock_rec",    ULong(),   NULLABLE),
 | |
| 
 | |
| #define IDX_LOCK_DATA		9
 | |
|   Column("lock_data",   Varchar(TRX_I_S_LOCK_DATA_MAX_LEN), NULLABLE),
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Read data from cache buffer and fill the INFORMATION_SCHEMA.innodb_locks
 | |
| table with it.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| fill_innodb_locks_from_cache(
 | |
| /*=========================*/
 | |
| 	THD*			thd,	/*!< in: MySQL client connection */
 | |
| 	TABLE_LIST*		tables,	/*!< in/out: fill this table */
 | |
| 	Item*)
 | |
| {
 | |
| 	ulint	rows_num;
 | |
| 	char	lock_id[TRX_I_S_LOCK_ID_MAX_LEN + 1];
 | |
| 	ulint	i;
 | |
| 
 | |
| 	DBUG_ENTER("fill_innodb_locks_from_cache");
 | |
| 
 | |
| 	if (!trx_i_s_common_fill_table(thd, tables)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	struct cache
 | |
| 	{
 | |
| 		cache() { trx_i_s_cache_start_read(trx_i_s_cache); }
 | |
| 		~cache() { trx_i_s_cache_end_read(trx_i_s_cache); }
 | |
| 	} c;
 | |
| 
 | |
| 	Field** fields = tables->table->field;
 | |
| 
 | |
| 	rows_num = trx_i_s_cache_get_rows_used(trx_i_s_cache,
 | |
| 					       I_S_INNODB_LOCKS);
 | |
| 
 | |
| 	for (i = 0; i < rows_num; i++) {
 | |
| 
 | |
| 		i_s_locks_row_t*	row;
 | |
| 		char			buf[MAX_FULL_NAME_LEN + 1];
 | |
| 		const char*		bufend;
 | |
| 
 | |
| 		row = (i_s_locks_row_t*)
 | |
| 			trx_i_s_cache_get_nth_row(
 | |
| 				trx_i_s_cache, I_S_INNODB_LOCKS, i);
 | |
| 
 | |
| 		/* lock_id */
 | |
| 		trx_i_s_create_lock_id(row, lock_id, sizeof(lock_id));
 | |
| 		OK(field_store_string(fields[IDX_LOCK_ID],
 | |
| 				      lock_id));
 | |
| 
 | |
| 		/* lock_trx_id */
 | |
| 		OK(fields[IDX_LOCK_TRX_ID]->store(row->lock_trx_id, true));
 | |
| 
 | |
| 		/* lock_mode */
 | |
| 		OK(fields[IDX_LOCK_MODE]->store(row->lock_mode, true));
 | |
| 
 | |
| 		/* lock_type */
 | |
| 		OK(fields[IDX_LOCK_TYPE]->store(
 | |
| 			   row->lock_index ? 1 : 2, true));
 | |
| 
 | |
| 		/* lock_table */
 | |
| 		bufend = innobase_convert_name(buf, sizeof(buf),
 | |
| 					       row->lock_table,
 | |
| 					       strlen(row->lock_table),
 | |
| 					       thd);
 | |
| 		OK(fields[IDX_LOCK_TABLE]->store(
 | |
| 			buf, uint(bufend - buf), system_charset_info));
 | |
| 
 | |
| 		if (row->lock_index) {
 | |
| 			/* record lock */
 | |
| 			OK(field_store_string(fields[IDX_LOCK_INDEX],
 | |
| 					      row->lock_index));
 | |
| 			OK(fields[IDX_LOCK_SPACE]->store(
 | |
| 				   row->lock_page.space(), true));
 | |
| 			fields[IDX_LOCK_SPACE]->set_notnull();
 | |
| 			OK(fields[IDX_LOCK_PAGE]->store(
 | |
| 				   row->lock_page.page_no(), true));
 | |
| 			fields[IDX_LOCK_PAGE]->set_notnull();
 | |
| 			OK(fields[IDX_LOCK_REC]->store(
 | |
| 				   row->lock_rec, true));
 | |
| 			fields[IDX_LOCK_REC]->set_notnull();
 | |
| 			OK(field_store_string(fields[IDX_LOCK_DATA],
 | |
| 					      row->lock_data));
 | |
| 		} else {
 | |
| 			fields[IDX_LOCK_INDEX]->set_null();
 | |
| 			fields[IDX_LOCK_SPACE]->set_null();
 | |
| 			fields[IDX_LOCK_REC]->set_null();
 | |
| 			fields[IDX_LOCK_DATA]->set_null();
 | |
| 		}
 | |
| 
 | |
| 		OK(schema_table_store_record(thd, tables->table));
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_locks
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_locks_init(
 | |
| /*==============*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_locks_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_locks_fields_info;
 | |
| 	schema->fill_table = fill_innodb_locks_from_cache;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_locks =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_LOCKS",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB conflicting locks",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_locks_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.innodb_lock_waits */
 | |
| static ST_FIELD_INFO innodb_lock_waits_fields_info[]=
 | |
| {
 | |
| #define IDX_REQUESTING_TRX_ID	0
 | |
|   Column("requesting_trx_id", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_REQUESTED_LOCK_ID	1
 | |
|   Column("requested_lock_id", Varchar(TRX_I_S_LOCK_ID_MAX_LEN + 1), NOT_NULL),
 | |
| 
 | |
| #define IDX_BLOCKING_TRX_ID	2
 | |
|   Column("blocking_trx_id",   ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BLOCKING_LOCK_ID	3
 | |
|   Column("blocking_lock_id",  Varchar(TRX_I_S_LOCK_ID_MAX_LEN + 1), NOT_NULL),
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Read data from cache buffer and fill the
 | |
| INFORMATION_SCHEMA.innodb_lock_waits table with it.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| fill_innodb_lock_waits_from_cache(
 | |
| /*==============================*/
 | |
| 	THD*			thd,	/*!< in: used to call
 | |
| 					schema_table_store_record() */
 | |
| 	TABLE_LIST*		tables,	/*!< in/out: fill this table */
 | |
| 	Item*)
 | |
| {
 | |
| 	ulint	rows_num;
 | |
| 	char	requested_lock_id[TRX_I_S_LOCK_ID_MAX_LEN + 1];
 | |
| 	char	blocking_lock_id[TRX_I_S_LOCK_ID_MAX_LEN + 1];
 | |
| 	ulint	i;
 | |
| 
 | |
| 	DBUG_ENTER("fill_innodb_lock_waits_from_cache");
 | |
| 
 | |
| 	if (!trx_i_s_common_fill_table(thd, tables)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	struct cache
 | |
| 	{
 | |
| 		cache() { trx_i_s_cache_start_read(trx_i_s_cache); }
 | |
| 		~cache() { trx_i_s_cache_end_read(trx_i_s_cache); }
 | |
| 	} c;
 | |
| 
 | |
| 	Field** fields = tables->table->field;
 | |
| 
 | |
| 	rows_num = trx_i_s_cache_get_rows_used(trx_i_s_cache,
 | |
| 					       I_S_INNODB_LOCK_WAITS);
 | |
| 
 | |
| 	for (i = 0; i < rows_num; i++) {
 | |
| 
 | |
| 		i_s_lock_waits_row_t*	row;
 | |
| 
 | |
| 		row = (i_s_lock_waits_row_t*)
 | |
| 			trx_i_s_cache_get_nth_row(
 | |
| 				trx_i_s_cache, I_S_INNODB_LOCK_WAITS, i);
 | |
| 
 | |
| 		/* requesting_trx_id */
 | |
| 		OK(fields[IDX_REQUESTING_TRX_ID]->store(
 | |
| 				      row->requested_lock_row->lock_trx_id, true));
 | |
| 
 | |
| 		/* requested_lock_id */
 | |
| 		OK(field_store_string(
 | |
| 			   fields[IDX_REQUESTED_LOCK_ID],
 | |
| 			   trx_i_s_create_lock_id(
 | |
| 				   row->requested_lock_row,
 | |
| 				   requested_lock_id,
 | |
| 				   sizeof(requested_lock_id))));
 | |
| 
 | |
| 		/* blocking_trx_id */
 | |
| 		OK(fields[IDX_BLOCKING_TRX_ID]->store(
 | |
| 				      row->blocking_lock_row->lock_trx_id, true));
 | |
| 
 | |
| 		/* blocking_lock_id */
 | |
| 		OK(field_store_string(
 | |
| 			   fields[IDX_BLOCKING_LOCK_ID],
 | |
| 			   trx_i_s_create_lock_id(
 | |
| 				   row->blocking_lock_row,
 | |
| 				   blocking_lock_id,
 | |
| 				   sizeof(blocking_lock_id))));
 | |
| 
 | |
| 		OK(schema_table_store_record(thd, tables->table));
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_lock_waits
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_lock_waits_init(
 | |
| /*===================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_lock_waits_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_lock_waits_fields_info;
 | |
| 	schema->fill_table = fill_innodb_lock_waits_from_cache;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_lock_waits =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_LOCK_WAITS",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB which lock is blocking which",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_lock_waits_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table information_schema.innodb_cmp. */
 | |
| static ST_FIELD_INFO i_s_cmp_fields_info[] =
 | |
| {
 | |
|   Column("page_size",      SLong(5),NOT_NULL, "Compressed Page Size"),
 | |
|   Column("compress_ops",   SLong(), NOT_NULL, "Total Number of Compressions"),
 | |
|   Column("compress_ops_ok",SLong(), NOT_NULL, "Total Number of "
 | |
|                                               "Successful Compressions"),
 | |
|   Column("compress_time",  SLong(), NOT_NULL, "Total Duration of "
 | |
|                                               "Compressions, in Seconds"),
 | |
|   Column("uncompress_ops", SLong(), NOT_NULL, "Total Number of Decompressions"),
 | |
|   Column("uncompress_time",SLong(), NOT_NULL, "Total Duration of "
 | |
|                                               "Decompressions, in Seconds"),
 | |
|   CEnd(),
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table information_schema.innodb_cmp or
 | |
| innodb_cmp_reset.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_cmp_fill_low(
 | |
| /*=============*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		,	/*!< in: condition (ignored) */
 | |
| 	ibool		reset)	/*!< in: TRUE=reset cumulated counts */
 | |
| {
 | |
| 	TABLE*	table	= (TABLE*) tables->table;
 | |
| 	int	status	= 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_cmp_fill_low");
 | |
| 
 | |
| 	/* deny access to non-superusers */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	for (uint i = 0; i < PAGE_ZIP_SSIZE_MAX; i++) {
 | |
| 		page_zip_stat_t*	zip_stat = &page_zip_stat[i];
 | |
| 
 | |
| 		table->field[0]->store(UNIV_ZIP_SIZE_MIN << i);
 | |
| 
 | |
| 		/* The cumulated counts are not protected by any
 | |
| 		mutex.  Thus, some operation in page0zip.cc could
 | |
| 		increment a counter between the time we read it and
 | |
| 		clear it.  We could introduce mutex protection, but it
 | |
| 		could cause a measureable performance hit in
 | |
| 		page0zip.cc. */
 | |
| 		table->field[1]->store(zip_stat->compressed, true);
 | |
| 		table->field[2]->store(zip_stat->compressed_ok, true);
 | |
| 		table->field[3]->store(zip_stat->compressed_usec / 1000000,
 | |
| 				       true);
 | |
| 		table->field[4]->store(zip_stat->decompressed, true);
 | |
| 		table->field[5]->store(zip_stat->decompressed_usec / 1000000,
 | |
| 				       true);
 | |
| 
 | |
| 		if (reset) {
 | |
| 			new (zip_stat) page_zip_stat_t();
 | |
| 		}
 | |
| 
 | |
| 		if (schema_table_store_record(thd, table)) {
 | |
| 			status = 1;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(status);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table information_schema.innodb_cmp.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_cmp_fill(
 | |
| /*=========*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		cond)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	return(i_s_cmp_fill_low(thd, tables, cond, FALSE));
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table information_schema.innodb_cmp_reset.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_cmp_reset_fill(
 | |
| /*===============*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		cond)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	return(i_s_cmp_fill_low(thd, tables, cond, TRUE));
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table information_schema.innodb_cmp.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_cmp_init(
 | |
| /*=========*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_cmp_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_cmp_fields_info;
 | |
| 	schema->fill_table = i_s_cmp_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table information_schema.innodb_cmp_reset.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_cmp_reset_init(
 | |
| /*===============*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_cmp_reset_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_cmp_fields_info;
 | |
| 	schema->fill_table = i_s_cmp_reset_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_cmp =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_CMP",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"Statistics for the InnoDB compression",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_cmp_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_cmp_reset =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_CMP_RESET",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"Statistics for the InnoDB compression;"
 | |
| 		   " reset cumulated counts",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_cmp_reset_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic tables
 | |
| information_schema.innodb_cmp_per_index and
 | |
| information_schema.innodb_cmp_per_index_reset. */
 | |
| static ST_FIELD_INFO i_s_cmp_per_index_fields_info[]=
 | |
| {
 | |
| #define IDX_DATABASE_NAME	0
 | |
|   Column("database_name",   Varchar(NAME_CHAR_LEN), NOT_NULL),
 | |
| 
 | |
| #define IDX_TABLE_NAME		1 /* FIXME: this is in my_charset_filename! */
 | |
|   Column("table_name",      Varchar(NAME_CHAR_LEN), NOT_NULL),
 | |
| 
 | |
| #define IDX_INDEX_NAME		2
 | |
|   Column("index_name",      Varchar(NAME_CHAR_LEN), NOT_NULL),
 | |
| 
 | |
| #define IDX_COMPRESS_OPS	3
 | |
|   Column("compress_ops",    SLong(),      NOT_NULL),
 | |
| 
 | |
| #define IDX_COMPRESS_OPS_OK	4
 | |
|   Column("compress_ops_ok", SLong(),      NOT_NULL),
 | |
| 
 | |
| #define IDX_COMPRESS_TIME	5
 | |
|   Column("compress_time",   SLong(),      NOT_NULL),
 | |
| 
 | |
| #define IDX_UNCOMPRESS_OPS	6
 | |
|   Column("uncompress_ops",  SLong(),      NOT_NULL),
 | |
| 
 | |
| #define IDX_UNCOMPRESS_TIME	7
 | |
|   Column("uncompress_time", SLong(),      NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| 
 | |
| } // namespace Show
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table
 | |
| information_schema.innodb_cmp_per_index or
 | |
| information_schema.innodb_cmp_per_index_reset.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_cmp_per_index_fill_low(
 | |
| /*=======================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		,	/*!< in: condition (ignored) */
 | |
| 	ibool		reset)	/*!< in: TRUE=reset cumulated counts */
 | |
| {
 | |
| 	TABLE*	table = tables->table;
 | |
| 	Field**	fields = table->field;
 | |
| 	int	status = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_cmp_per_index_fill_low");
 | |
| 
 | |
| 	/* deny access to non-superusers */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* Create a snapshot of the stats so we do not bump into lock
 | |
| 	order violations with dict_sys.latch below. */
 | |
| 	mysql_mutex_lock(&page_zip_stat_per_index_mutex);
 | |
| 	page_zip_stat_per_index_t		snap (page_zip_stat_per_index);
 | |
| 	mysql_mutex_unlock(&page_zip_stat_per_index_mutex);
 | |
| 
 | |
| 	dict_sys.freeze(SRW_LOCK_CALL);
 | |
| 
 | |
| 	page_zip_stat_per_index_t::iterator	iter;
 | |
| 	ulint					i;
 | |
| 
 | |
| 	for (iter = snap.begin(), i = 0; iter != snap.end(); iter++, i++) {
 | |
| 
 | |
| 		if (dict_index_t* index
 | |
| 		    = dict_index_get_if_in_cache_low(iter->first)) {
 | |
| 			char	db_utf8[MAX_DB_UTF8_LEN];
 | |
| 			char	table_utf8[MAX_TABLE_UTF8_LEN];
 | |
| 
 | |
| 			dict_fs2utf8(index->table->name.m_name,
 | |
| 				     db_utf8, sizeof(db_utf8),
 | |
| 				     table_utf8, sizeof(table_utf8));
 | |
| 
 | |
| 			status = field_store_string(fields[IDX_DATABASE_NAME],
 | |
| 						    db_utf8)
 | |
| 				|| field_store_string(fields[IDX_TABLE_NAME],
 | |
| 						      table_utf8)
 | |
| 				|| field_store_string(fields[IDX_INDEX_NAME],
 | |
| 						      index->name);
 | |
| 		} else {
 | |
| 			/* index not found */
 | |
| 			char name[MY_INT64_NUM_DECIMAL_DIGITS
 | |
| 				  + sizeof "index_id: "];
 | |
| 			fields[IDX_DATABASE_NAME]->set_null();
 | |
| 			fields[IDX_TABLE_NAME]->set_null();
 | |
| 			fields[IDX_INDEX_NAME]->set_notnull();
 | |
| 			status = fields[IDX_INDEX_NAME]->store(
 | |
| 				name,
 | |
| 				uint(snprintf(name, sizeof name,
 | |
| 					      "index_id: " IB_ID_FMT,
 | |
| 					      iter->first)),
 | |
| 				system_charset_info);
 | |
| 		}
 | |
| 
 | |
| 		if (status
 | |
| 		    || fields[IDX_COMPRESS_OPS]->store(
 | |
| 			    iter->second.compressed, true)
 | |
| 		    || fields[IDX_COMPRESS_OPS_OK]->store(
 | |
| 			    iter->second.compressed_ok, true)
 | |
| 		    || fields[IDX_COMPRESS_TIME]->store(
 | |
| 			    iter->second.compressed_usec / 1000000, true)
 | |
| 		    || fields[IDX_UNCOMPRESS_OPS]->store(
 | |
| 			    iter->second.decompressed, true)
 | |
| 		    || fields[IDX_UNCOMPRESS_TIME]->store(
 | |
| 			    iter->second.decompressed_usec / 1000000, true)
 | |
| 		    || schema_table_store_record(thd, table)) {
 | |
| 			status = 1;
 | |
| 			break;
 | |
| 		}
 | |
| 		/* Release and reacquire the dict_sys.latch to allow other
 | |
| 		threads to proceed. This could eventually result in the
 | |
| 		contents of INFORMATION_SCHEMA.innodb_cmp_per_index being
 | |
| 		inconsistent, but it is an acceptable compromise. */
 | |
| 		if (i == 1000) {
 | |
| 			dict_sys.unfreeze();
 | |
| 			i = 0;
 | |
| 			dict_sys.freeze(SRW_LOCK_CALL);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	dict_sys.unfreeze();
 | |
| 
 | |
| 	if (reset) {
 | |
| 		page_zip_reset_stat_per_index();
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(status);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table information_schema.innodb_cmp_per_index.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_cmp_per_index_fill(
 | |
| /*===================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		cond)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	return(i_s_cmp_per_index_fill_low(thd, tables, cond, FALSE));
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table information_schema.innodb_cmp_per_index_reset.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_cmp_per_index_reset_fill(
 | |
| /*=========================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		cond)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	return(i_s_cmp_per_index_fill_low(thd, tables, cond, TRUE));
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table information_schema.innodb_cmp_per_index.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_cmp_per_index_init(
 | |
| /*===================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_cmp_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_cmp_per_index_fields_info;
 | |
| 	schema->fill_table = i_s_cmp_per_index_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table information_schema.innodb_cmp_per_index_reset.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_cmp_per_index_reset_init(
 | |
| /*=========================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_cmp_reset_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_cmp_per_index_fields_info;
 | |
| 	schema->fill_table = i_s_cmp_per_index_reset_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_cmp_per_index =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_CMP_PER_INDEX",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"Statistics for the InnoDB compression (per index)",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_cmp_per_index_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_cmp_per_index_reset =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_CMP_PER_INDEX_RESET",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"Statistics for the InnoDB compression (per index);"
 | |
| 		   " reset cumulated counts",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_cmp_per_index_reset_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table information_schema.innodb_cmpmem. */
 | |
| static ST_FIELD_INFO i_s_cmpmem_fields_info[] =
 | |
| {
 | |
|   Column("page_size",           SLong(5), NOT_NULL, "Buddy Block Size"),
 | |
|   Column("buffer_pool_instance", SLong(), NOT_NULL, "Buffer Pool Id"),
 | |
|   Column("pages_used",           SLong(), NOT_NULL, "Currently in Use"),
 | |
|   Column("pages_free",           SLong(), NOT_NULL, "Currently Available"),
 | |
|   Column("relocation_ops",   SLonglong(), NOT_NULL, "Total Number of Relocations"),
 | |
|   Column("relocation_time",      SLong(), NOT_NULL, "Total Duration of Relocations,"
 | |
|                                                     " in Seconds"),
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table information_schema.innodb_cmpmem or
 | |
| innodb_cmpmem_reset.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_cmpmem_fill_low(
 | |
| /*================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		,	/*!< in: condition (ignored) */
 | |
| 	ibool		reset)	/*!< in: TRUE=reset cumulated counts */
 | |
| {
 | |
| 	TABLE*	table	= (TABLE*) tables->table;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_cmpmem_fill_low");
 | |
| 
 | |
| 	/* deny access to non-superusers */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	ulint			zip_free_len_local[BUF_BUDDY_SIZES_MAX + 1];
 | |
| 	buf_buddy_stat_t	buddy_stat_local[BUF_BUDDY_SIZES_MAX + 1];
 | |
| 
 | |
| 	/* Save buddy stats for buffer pool in local variables. */
 | |
| 	mysql_mutex_lock(&buf_pool.mutex);
 | |
| 
 | |
| 	for (uint x = 0; x <= BUF_BUDDY_SIZES; x++) {
 | |
| 		zip_free_len_local[x] = (x < BUF_BUDDY_SIZES) ?
 | |
| 			UT_LIST_GET_LEN(buf_pool.zip_free[x]) : 0;
 | |
| 
 | |
| 		buddy_stat_local[x] = buf_pool.buddy_stat[x];
 | |
| 
 | |
| 		if (reset) {
 | |
| 			/* This is protected by buf_pool.mutex. */
 | |
| 			buf_pool.buddy_stat[x].relocated = 0;
 | |
| 			buf_pool.buddy_stat[x].relocated_usec = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	mysql_mutex_unlock(&buf_pool.mutex);
 | |
| 
 | |
| 	for (uint x = 0; x <= BUF_BUDDY_SIZES; x++) {
 | |
| 		buf_buddy_stat_t* buddy_stat = &buddy_stat_local[x];
 | |
| 
 | |
| 		Field **field = table->field;
 | |
| 
 | |
| 		(*field++)->store(BUF_BUDDY_LOW << x);
 | |
| 		(*field++)->store(0, true);
 | |
| 		(*field++)->store(buddy_stat->used, true);
 | |
| 		(*field++)->store(zip_free_len_local[x], true);
 | |
| 		(*field++)->store(buddy_stat->relocated, true);
 | |
| 		(*field)->store(buddy_stat->relocated_usec / 1000000, true);
 | |
| 
 | |
| 		if (schema_table_store_record(thd, table)) {
 | |
| 			DBUG_RETURN(1);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table information_schema.innodb_cmpmem.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_cmpmem_fill(
 | |
| /*============*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		cond)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	return(i_s_cmpmem_fill_low(thd, tables, cond, FALSE));
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table information_schema.innodb_cmpmem_reset.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_cmpmem_reset_fill(
 | |
| /*==================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		cond)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	return(i_s_cmpmem_fill_low(thd, tables, cond, TRUE));
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table information_schema.innodb_cmpmem.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_cmpmem_init(
 | |
| /*============*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_cmpmem_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_cmpmem_fields_info;
 | |
| 	schema->fill_table = i_s_cmpmem_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table information_schema.innodb_cmpmem_reset.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_cmpmem_reset_init(
 | |
| /*==================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_cmpmem_reset_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_cmpmem_fields_info;
 | |
| 	schema->fill_table = i_s_cmpmem_reset_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_cmpmem =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_CMPMEM",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"Statistics for the InnoDB compressed buffer pool",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_cmpmem_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_cmpmem_reset =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_CMPMEM_RESET",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"Statistics for the InnoDB compressed buffer pool;"
 | |
| 		   " reset cumulated counts",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_cmpmem_reset_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| 
 | |
| static const LEX_CSTRING metric_type_values[] =
 | |
| {
 | |
| 	{ STRING_WITH_LEN("value") },
 | |
| 	{ STRING_WITH_LEN("status_counter") },
 | |
| 	{ STRING_WITH_LEN("set_owner") },
 | |
| 	{ STRING_WITH_LEN("set_member") },
 | |
| 	{ STRING_WITH_LEN("counter") }
 | |
| };
 | |
| 
 | |
| static TypelibBuffer<5> metric_type_values_typelib(metric_type_values);
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.innodb_metrics */
 | |
| static ST_FIELD_INFO innodb_metrics_fields_info[]=
 | |
| {
 | |
| #define	METRIC_NAME		0
 | |
|   Column("NAME",            Varchar(NAME_LEN + 1),       NOT_NULL),
 | |
| 
 | |
| #define	METRIC_SUBSYS		1
 | |
|   Column("SUBSYSTEM",       Varchar(NAME_LEN + 1),       NOT_NULL),
 | |
| 
 | |
| #define	METRIC_VALUE_START	2
 | |
|   Column("COUNT",           SLonglong(),                 NOT_NULL),
 | |
| 
 | |
| #define	METRIC_MAX_VALUE_START	3
 | |
|   Column("MAX_COUNT",       SLonglong(),                 NULLABLE),
 | |
| 
 | |
| #define	METRIC_MIN_VALUE_START	4
 | |
|   Column("MIN_COUNT",       SLonglong(),                 NULLABLE),
 | |
| 
 | |
| #define	METRIC_AVG_VALUE_START	5
 | |
|   Column("AVG_COUNT",       Float(MAX_FLOAT_STR_LENGTH), NULLABLE),
 | |
| 
 | |
| #define	METRIC_VALUE_RESET	6
 | |
|   Column("COUNT_RESET",     SLonglong(),                 NOT_NULL),
 | |
| 
 | |
| #define	METRIC_MAX_VALUE_RESET	7
 | |
|   Column("MAX_COUNT_RESET", SLonglong(),                 NULLABLE),
 | |
| 
 | |
| #define	METRIC_MIN_VALUE_RESET	8
 | |
|   Column("MIN_COUNT_RESET", SLonglong(),                 NULLABLE),
 | |
| 
 | |
| #define	METRIC_AVG_VALUE_RESET	9
 | |
|   Column("AVG_COUNT_RESET", Float(MAX_FLOAT_STR_LENGTH), NULLABLE),
 | |
| 
 | |
| #define	METRIC_START_TIME	10
 | |
|   Column("TIME_ENABLED",    Datetime(0),                 NULLABLE),
 | |
| 
 | |
| #define	METRIC_STOP_TIME	11
 | |
|   Column("TIME_DISABLED",   Datetime(0),                 NULLABLE),
 | |
| 
 | |
| #define	METRIC_TIME_ELAPSED	12
 | |
|   Column("TIME_ELAPSED",    SLonglong(),                 NULLABLE),
 | |
| 
 | |
| #define	METRIC_RESET_TIME	13
 | |
|   Column("TIME_RESET",      Datetime(0),                 NULLABLE),
 | |
| 
 | |
| #define	METRIC_STATUS		14
 | |
|   Column("ENABLED", SLong(1), NOT_NULL),
 | |
| 
 | |
| #define	METRIC_TYPE		15
 | |
|   Column("TYPE",    Enum(&metric_type_values_typelib), NOT_NULL),
 | |
| 
 | |
| #define	METRIC_DESC		16
 | |
|   Column("COMMENT",         Varchar(NAME_LEN + 1),       NOT_NULL),
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /**********************************************************************//**
 | |
| Fill the information schema metrics table.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_metrics_fill(
 | |
| /*=============*/
 | |
| 	THD*		thd,		/*!< in: thread */
 | |
| 	TABLE*		table_to_fill)	/*!< in/out: fill this table */
 | |
| {
 | |
| 	int		count;
 | |
| 	Field**		fields;
 | |
| 	double		time_diff = 0;
 | |
| 	monitor_info_t*	monitor_info;
 | |
| 	mon_type_t	min_val;
 | |
| 	mon_type_t	max_val;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_metrics_fill");
 | |
| 	fields = table_to_fill->field;
 | |
| 
 | |
| 	for (count = 0; count < NUM_MONITOR; count++) {
 | |
| 		monitor_info = srv_mon_get_info((monitor_id_t) count);
 | |
| 
 | |
| 		/* A good place to sanity check the Monitor ID */
 | |
| 		ut_a(count == monitor_info->monitor_id);
 | |
| 
 | |
| 		/* If the item refers to a Module, nothing to fill,
 | |
| 		continue. */
 | |
| 		if ((monitor_info->monitor_type & MONITOR_MODULE)
 | |
| 		    || (monitor_info->monitor_type & MONITOR_HIDDEN)) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* If this is an existing "status variable", and
 | |
| 		its corresponding counter is still on, we need
 | |
| 		to calculate the result from its corresponding
 | |
| 		counter. */
 | |
| 		if (monitor_info->monitor_type & MONITOR_EXISTING
 | |
| 		    && MONITOR_IS_ON(count)) {
 | |
| 			srv_mon_process_existing_counter((monitor_id_t) count,
 | |
| 							 MONITOR_GET_VALUE);
 | |
| 		}
 | |
| 
 | |
| 		/* Fill in counter's basic information */
 | |
| 		OK(field_store_string(fields[METRIC_NAME],
 | |
| 				      monitor_info->monitor_name));
 | |
| 
 | |
| 		OK(field_store_string(fields[METRIC_SUBSYS],
 | |
| 				      monitor_info->monitor_module));
 | |
| 
 | |
| 		OK(field_store_string(fields[METRIC_DESC],
 | |
| 				      monitor_info->monitor_desc));
 | |
| 
 | |
| 		/* Fill in counter values */
 | |
| 		OK(fields[METRIC_VALUE_RESET]->store(
 | |
| 			MONITOR_VALUE(count), FALSE));
 | |
| 
 | |
| 		OK(fields[METRIC_VALUE_START]->store(
 | |
| 			MONITOR_VALUE_SINCE_START(count), FALSE));
 | |
| 
 | |
| 		/* If the max value is MAX_RESERVED, counter max
 | |
| 		value has not been updated. Set the column value
 | |
| 		to NULL. */
 | |
| 		if (MONITOR_MAX_VALUE(count) == MAX_RESERVED
 | |
| 		    || MONITOR_MAX_MIN_NOT_INIT(count)) {
 | |
| 			fields[METRIC_MAX_VALUE_RESET]->set_null();
 | |
| 		} else {
 | |
| 			OK(fields[METRIC_MAX_VALUE_RESET]->store(
 | |
| 				MONITOR_MAX_VALUE(count), FALSE));
 | |
| 			fields[METRIC_MAX_VALUE_RESET]->set_notnull();
 | |
| 		}
 | |
| 
 | |
| 		/* If the min value is MAX_RESERVED, counter min
 | |
| 		value has not been updated. Set the column value
 | |
| 		to NULL. */
 | |
| 		if (MONITOR_MIN_VALUE(count) == MIN_RESERVED
 | |
| 		    || MONITOR_MAX_MIN_NOT_INIT(count)) {
 | |
| 			fields[METRIC_MIN_VALUE_RESET]->set_null();
 | |
| 		} else {
 | |
| 			OK(fields[METRIC_MIN_VALUE_RESET]->store(
 | |
| 				MONITOR_MIN_VALUE(count), FALSE));
 | |
| 			fields[METRIC_MIN_VALUE_RESET]->set_notnull();
 | |
| 		}
 | |
| 
 | |
| 		/* Calculate the max value since counter started */
 | |
| 		max_val = srv_mon_calc_max_since_start((monitor_id_t) count);
 | |
| 
 | |
| 		if (max_val == MAX_RESERVED
 | |
| 		    || MONITOR_MAX_MIN_NOT_INIT(count)) {
 | |
| 			fields[METRIC_MAX_VALUE_START]->set_null();
 | |
| 		} else {
 | |
| 			OK(fields[METRIC_MAX_VALUE_START]->store(
 | |
| 				max_val, FALSE));
 | |
| 			fields[METRIC_MAX_VALUE_START]->set_notnull();
 | |
| 		}
 | |
| 
 | |
| 		/* Calculate the min value since counter started */
 | |
| 		min_val = srv_mon_calc_min_since_start((monitor_id_t) count);
 | |
| 
 | |
| 		if (min_val == MIN_RESERVED
 | |
| 		    || MONITOR_MAX_MIN_NOT_INIT(count)) {
 | |
| 			fields[METRIC_MIN_VALUE_START]->set_null();
 | |
| 		} else {
 | |
| 			OK(fields[METRIC_MIN_VALUE_START]->store(
 | |
| 				min_val, FALSE));
 | |
| 
 | |
| 			fields[METRIC_MIN_VALUE_START]->set_notnull();
 | |
| 		}
 | |
| 
 | |
| 		/* If monitor has been enabled (no matter it is disabled
 | |
| 		or not now), fill METRIC_START_TIME and METRIC_TIME_ELAPSED
 | |
| 		field */
 | |
| 		if (MONITOR_FIELD(count, mon_start_time)) {
 | |
| 			OK(field_store_time_t(fields[METRIC_START_TIME],
 | |
| 				(time_t)MONITOR_FIELD(count, mon_start_time)));
 | |
| 			fields[METRIC_START_TIME]->set_notnull();
 | |
| 
 | |
| 			/* If monitor is enabled, the TIME_ELAPSED is the
 | |
| 			time difference between current and time when monitor
 | |
| 			is enabled. Otherwise, it is the time difference
 | |
| 			between time when monitor is enabled and time
 | |
| 			when it is disabled */
 | |
| 			if (MONITOR_IS_ON(count)) {
 | |
| 				time_diff = difftime(time(NULL),
 | |
| 					MONITOR_FIELD(count, mon_start_time));
 | |
| 			} else {
 | |
| 				time_diff =  difftime(
 | |
| 					MONITOR_FIELD(count, mon_stop_time),
 | |
| 					MONITOR_FIELD(count, mon_start_time));
 | |
| 			}
 | |
| 
 | |
| 			OK(fields[METRIC_TIME_ELAPSED]->store(
 | |
| 				time_diff));
 | |
| 			fields[METRIC_TIME_ELAPSED]->set_notnull();
 | |
| 		} else {
 | |
| 			fields[METRIC_START_TIME]->set_null();
 | |
| 			fields[METRIC_TIME_ELAPSED]->set_null();
 | |
| 			time_diff = 0;
 | |
| 		}
 | |
| 
 | |
| 		/* Unless MONITOR_NO_AVERAGE is set, we must
 | |
| 		to calculate the average value. If this is a monitor set
 | |
| 		owner marked by MONITOR_SET_OWNER, divide
 | |
| 		the value by another counter (number of calls) designated
 | |
| 		by monitor_info->monitor_related_id.
 | |
| 		Otherwise average the counter value by the time between the
 | |
| 		time that the counter is enabled and time it is disabled
 | |
| 		or time it is sampled. */
 | |
| 		if ((monitor_info->monitor_type
 | |
| 		     & (MONITOR_NO_AVERAGE | MONITOR_SET_OWNER))
 | |
| 		    == MONITOR_SET_OWNER
 | |
| 		    && monitor_info->monitor_related_id) {
 | |
| 			mon_type_t	value_start
 | |
| 				 = MONITOR_VALUE_SINCE_START(
 | |
| 					monitor_info->monitor_related_id);
 | |
| 
 | |
| 			if (value_start) {
 | |
| 				OK(fields[METRIC_AVG_VALUE_START]->store(
 | |
| 					MONITOR_VALUE_SINCE_START(count)
 | |
| 					/ value_start, FALSE));
 | |
| 
 | |
| 				fields[METRIC_AVG_VALUE_START]->set_notnull();
 | |
| 			} else {
 | |
| 				fields[METRIC_AVG_VALUE_START]->set_null();
 | |
| 			}
 | |
| 
 | |
| 			if (mon_type_t related_value =
 | |
| 			    MONITOR_VALUE(monitor_info->monitor_related_id)) {
 | |
| 				OK(fields[METRIC_AVG_VALUE_RESET]
 | |
| 				   ->store(MONITOR_VALUE(count)
 | |
| 					   / related_value, false));
 | |
| 				fields[METRIC_AVG_VALUE_RESET]->set_notnull();
 | |
| 			} else {
 | |
| 				fields[METRIC_AVG_VALUE_RESET]->set_null();
 | |
| 			}
 | |
| 		} else if (!(monitor_info->monitor_type
 | |
| 			     & (MONITOR_NO_AVERAGE
 | |
| 				| MONITOR_DISPLAY_CURRENT))) {
 | |
| 			if (time_diff != 0) {
 | |
| 				OK(fields[METRIC_AVG_VALUE_START]->store(
 | |
| 					(double) MONITOR_VALUE_SINCE_START(
 | |
| 						count) / time_diff));
 | |
| 				fields[METRIC_AVG_VALUE_START]->set_notnull();
 | |
| 			} else {
 | |
| 				fields[METRIC_AVG_VALUE_START]->set_null();
 | |
| 			}
 | |
| 
 | |
| 			if (MONITOR_FIELD(count, mon_reset_time)) {
 | |
| 				/* calculate the time difference since last
 | |
| 				reset */
 | |
| 				if (MONITOR_IS_ON(count)) {
 | |
| 					time_diff = difftime(
 | |
| 						time(NULL), MONITOR_FIELD(
 | |
| 							count, mon_reset_time));
 | |
| 				} else {
 | |
| 					time_diff =  difftime(
 | |
| 					MONITOR_FIELD(count, mon_stop_time),
 | |
| 					MONITOR_FIELD(count, mon_reset_time));
 | |
| 				}
 | |
| 			} else {
 | |
| 				time_diff = 0;
 | |
| 			}
 | |
| 
 | |
| 			if (time_diff != 0) {
 | |
| 				OK(fields[METRIC_AVG_VALUE_RESET]->store(
 | |
| 					static_cast<double>(
 | |
| 						MONITOR_VALUE(count))
 | |
| 					/ time_diff));
 | |
| 				fields[METRIC_AVG_VALUE_RESET]->set_notnull();
 | |
| 			} else {
 | |
| 				fields[METRIC_AVG_VALUE_RESET]->set_null();
 | |
| 			}
 | |
| 		} else {
 | |
| 			fields[METRIC_AVG_VALUE_START]->set_null();
 | |
| 			fields[METRIC_AVG_VALUE_RESET]->set_null();
 | |
| 		}
 | |
| 
 | |
| 		if (MONITOR_IS_ON(count)) {
 | |
| 			/* If monitor is on, the stop time will set to NULL */
 | |
| 			fields[METRIC_STOP_TIME]->set_null();
 | |
| 
 | |
| 			/* Display latest Monitor Reset Time only if Monitor
 | |
| 			counter is on. */
 | |
| 			if (MONITOR_FIELD(count, mon_reset_time)) {
 | |
| 				OK(field_store_time_t(
 | |
| 					fields[METRIC_RESET_TIME],
 | |
| 					(time_t)MONITOR_FIELD(
 | |
| 						count, mon_reset_time)));
 | |
| 				fields[METRIC_RESET_TIME]->set_notnull();
 | |
| 			} else {
 | |
| 				fields[METRIC_RESET_TIME]->set_null();
 | |
| 			}
 | |
| 
 | |
| 			OK(fields[METRIC_STATUS]->store(1, true));
 | |
| 		} else {
 | |
| 			if (MONITOR_FIELD(count, mon_stop_time)) {
 | |
| 				OK(field_store_time_t(fields[METRIC_STOP_TIME],
 | |
| 				(time_t)MONITOR_FIELD(count, mon_stop_time)));
 | |
| 				fields[METRIC_STOP_TIME]->set_notnull();
 | |
| 			} else {
 | |
| 				fields[METRIC_STOP_TIME]->set_null();
 | |
| 			}
 | |
| 
 | |
| 			fields[METRIC_RESET_TIME]->set_null();
 | |
| 
 | |
| 			OK(fields[METRIC_STATUS]->store(0, true));
 | |
| 		}
 | |
| 
 | |
| 		uint metric_type;
 | |
| 
 | |
| 		if (monitor_info->monitor_type & MONITOR_DISPLAY_CURRENT) {
 | |
| 			metric_type = 1; /* "value" */
 | |
| 		} else if (monitor_info->monitor_type & MONITOR_EXISTING) {
 | |
| 			metric_type = 2; /* "status_counter" */
 | |
| 		} else if (monitor_info->monitor_type & MONITOR_SET_OWNER) {
 | |
| 			metric_type = 3; /* "set_owner" */
 | |
| 		} else if (monitor_info->monitor_type & MONITOR_SET_MEMBER) {
 | |
| 			metric_type = 4; /* "set_member" */
 | |
| 		} else {
 | |
| 			metric_type = 5; /* "counter" */
 | |
| 		}
 | |
| 
 | |
| 		OK(fields[METRIC_TYPE]->store(metric_type, true));
 | |
| 
 | |
| 		OK(schema_table_store_record(thd, table_to_fill));
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Function to fill information schema metrics tables.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_metrics_fill_table(
 | |
| /*===================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (not used) */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_metrics_fill_table");
 | |
| 
 | |
| 	/* deny access to non-superusers */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	i_s_metrics_fill(thd, tables->table);
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_metrics
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_metrics_init(
 | |
| /*================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_metrics_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_metrics_fields_info;
 | |
| 	schema->fill_table = i_s_metrics_fill_table;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_metrics =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_METRICS",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB Metrics Info",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_metrics_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.innodb_ft_default_stopword */
 | |
| static ST_FIELD_INFO i_s_stopword_fields_info[]=
 | |
| {
 | |
| #define STOPWORD_VALUE	0
 | |
|   Column("value", Varchar(TRX_ID_MAX_LEN + 1), NOT_NULL),
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table information_schema.innodb_ft_default_stopword.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_stopword_fill(
 | |
| /*==============*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (not used) */
 | |
| {
 | |
| 	Field**	fields;
 | |
| 	ulint	i = 0;
 | |
| 	TABLE*	table = (TABLE*) tables->table;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_stopword_fill");
 | |
| 
 | |
| 	fields = table->field;
 | |
| 
 | |
| 	/* Fill with server default stopword list in array
 | |
| 	fts_default_stopword */
 | |
| 	while (fts_default_stopword[i]) {
 | |
| 		OK(field_store_string(fields[STOPWORD_VALUE],
 | |
| 				      fts_default_stopword[i]));
 | |
| 
 | |
| 		OK(schema_table_store_record(thd, table));
 | |
| 		i++;
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table information_schema.innodb_ft_default_stopword.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_stopword_init(
 | |
| /*==============*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_stopword_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_stopword_fields_info;
 | |
| 	schema->fill_table = i_s_stopword_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_ft_default_stopword =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_FT_DEFAULT_STOPWORD",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"Default stopword list for InnoDB Full Text Search",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_stopword_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_FT_DELETED
 | |
| INFORMATION_SCHEMA.INNODB_FT_BEING_DELETED */
 | |
| static ST_FIELD_INFO i_s_fts_doc_fields_info[]=
 | |
| {
 | |
| #define	I_S_FTS_DOC_ID			0
 | |
|   Column("DOC_ID", ULonglong(), NOT_NULL),
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table INFORMATION_SCHEMA.INNODB_FT_DELETED or
 | |
| INFORMATION_SCHEMA.INNODB_FT_BEING_DELETED
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_fts_deleted_generic_fill(
 | |
| /*=========================*/
 | |
| 	THD*		thd,		/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,		/*!< in/out: tables to fill */
 | |
| 	ibool		being_deleted)	/*!< in: BEING_DELTED table */
 | |
| {
 | |
| 	Field**			fields;
 | |
| 	TABLE*			table = (TABLE*) tables->table;
 | |
| 	trx_t*			trx;
 | |
| 	fts_table_t		fts_table;
 | |
| 	fts_doc_ids_t*		deleted;
 | |
| 	dict_table_t*		user_table;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_fts_deleted_generic_fill");
 | |
| 
 | |
| 	/* deny access to non-superusers */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	MDL_ticket* mdl_ticket = nullptr;
 | |
| 	user_table = dict_table_open_on_id(
 | |
| 		innodb_ft_aux_table_id, false, DICT_TABLE_OP_NORMAL,
 | |
| 		thd, &mdl_ticket);
 | |
| 
 | |
| 	if (!user_table) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	} else if (!dict_table_has_fts_index(user_table)
 | |
| 		   || !user_table->is_readable()) {
 | |
| 		dict_table_close(user_table, thd, mdl_ticket);
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	deleted = fts_doc_ids_create();
 | |
| 
 | |
| 	trx = trx_create();
 | |
| 	trx->op_info = "Select for FTS DELETE TABLE";
 | |
| 
 | |
| 	FTS_INIT_FTS_TABLE(&fts_table,
 | |
| 			   (being_deleted) ? "BEING_DELETED" : "DELETED",
 | |
| 			   FTS_COMMON_TABLE, user_table);
 | |
| 
 | |
| 	fts_table_fetch_doc_ids(trx, &fts_table, deleted);
 | |
| 
 | |
| 	dict_table_close(user_table, thd, mdl_ticket);
 | |
| 
 | |
| 	trx->free();
 | |
| 
 | |
| 	fields = table->field;
 | |
| 
 | |
| 	int	ret = 0;
 | |
| 
 | |
| 	for (ulint j = 0; j < ib_vector_size(deleted->doc_ids); ++j) {
 | |
| 		doc_id_t	doc_id;
 | |
| 
 | |
| 		doc_id = *(doc_id_t*) ib_vector_get_const(deleted->doc_ids, j);
 | |
| 
 | |
| 		BREAK_IF(ret = fields[I_S_FTS_DOC_ID]->store(doc_id, true));
 | |
| 
 | |
| 		BREAK_IF(ret = schema_table_store_record(thd, table));
 | |
| 	}
 | |
| 
 | |
| 	fts_doc_ids_free(deleted);
 | |
| 
 | |
| 	DBUG_RETURN(ret);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table INFORMATION_SCHEMA.INNODB_FT_DELETED
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_fts_deleted_fill(
 | |
| /*=================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_fts_deleted_fill");
 | |
| 
 | |
| 	DBUG_RETURN(i_s_fts_deleted_generic_fill(thd, tables, FALSE));
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.INNODB_FT_DELETED
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_fts_deleted_init(
 | |
| /*=================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_fts_deleted_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_fts_doc_fields_info;
 | |
| 	schema->fill_table = i_s_fts_deleted_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_ft_deleted =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_FT_DELETED",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"INNODB AUXILIARY FTS DELETED TABLE",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_fts_deleted_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table INFORMATION_SCHEMA.INNODB_FT_BEING_DELETED
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_fts_being_deleted_fill(
 | |
| /*=======================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_fts_being_deleted_fill");
 | |
| 
 | |
| 	DBUG_RETURN(i_s_fts_deleted_generic_fill(thd, tables, TRUE));
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.INNODB_FT_BEING_DELETED
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_fts_being_deleted_init(
 | |
| /*=======================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_fts_deleted_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_fts_doc_fields_info;
 | |
| 	schema->fill_table = i_s_fts_being_deleted_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_ft_being_deleted =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_FT_BEING_DELETED",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"INNODB AUXILIARY FTS BEING DELETED TABLE",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_fts_being_deleted_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHED and
 | |
| INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE */
 | |
| static ST_FIELD_INFO i_s_fts_index_fields_info[]=
 | |
| {
 | |
| #define	I_S_FTS_WORD			0
 | |
|   Column("WORD",         Varchar(FTS_MAX_WORD_LEN + 1), NOT_NULL),
 | |
| 
 | |
| #define	I_S_FTS_FIRST_DOC_ID		1
 | |
|   Column("FIRST_DOC_ID", ULonglong(),                   NOT_NULL),
 | |
| 
 | |
| #define	I_S_FTS_LAST_DOC_ID		2
 | |
|   Column("LAST_DOC_ID",  ULonglong(),                   NOT_NULL),
 | |
| 
 | |
| #define	I_S_FTS_DOC_COUNT		3
 | |
|   Column("DOC_COUNT",    ULonglong(),                   NOT_NULL),
 | |
| 
 | |
| #define	I_S_FTS_ILIST_DOC_ID		4
 | |
|   Column("DOC_ID",       ULonglong(),                   NOT_NULL),
 | |
| 
 | |
| #define	I_S_FTS_ILIST_DOC_POS		5
 | |
|   Column("POSITION",     ULonglong(),                   NOT_NULL),
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Go through the Doc Node and its ilist, fill the dynamic table
 | |
| INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHED for one FTS index on the table.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_fts_index_cache_fill_one_index(
 | |
| /*===============================*/
 | |
| 	fts_index_cache_t*	index_cache,	/*!< in: FTS index cache */
 | |
| 	THD*			thd,		/*!< in: thread */
 | |
| 	fts_string_t*		conv_str,	/*!< in/out: buffer */
 | |
| 	TABLE_LIST*		tables)		/*!< in/out: tables to fill */
 | |
| {
 | |
| 	TABLE*			table = (TABLE*) tables->table;
 | |
| 	Field**			fields;
 | |
| 	CHARSET_INFO*		index_charset;
 | |
| 	const ib_rbt_node_t*	rbt_node;
 | |
| 	uint			dummy_errors;
 | |
| 	char*			word_str;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_fts_index_cache_fill_one_index");
 | |
| 
 | |
| 	fields = table->field;
 | |
| 
 | |
| 	index_charset = index_cache->charset;
 | |
| 	conv_str->f_n_char = 0;
 | |
| 
 | |
| 	int	ret = 0;
 | |
| 
 | |
| 	/* Go through each word in the index cache */
 | |
| 	for (rbt_node = rbt_first(index_cache->words);
 | |
| 	     rbt_node;
 | |
| 	     rbt_node = rbt_next(index_cache->words, rbt_node)) {
 | |
| 		fts_tokenizer_word_t* word;
 | |
| 
 | |
| 		word = rbt_value(fts_tokenizer_word_t, rbt_node);
 | |
| 
 | |
| 		/* Convert word from index charset to system_charset_info */
 | |
| 		if (index_charset->cset != system_charset_info->cset) {
 | |
| 			conv_str->f_n_char = my_convert(
 | |
| 				reinterpret_cast<char*>(conv_str->f_str),
 | |
| 				static_cast<uint32>(conv_str->f_len),
 | |
| 				system_charset_info,
 | |
| 				reinterpret_cast<char*>(word->text.f_str),
 | |
| 				static_cast<uint32>(word->text.f_len),
 | |
| 				index_charset, &dummy_errors);
 | |
| 			ut_ad(conv_str->f_n_char <= conv_str->f_len);
 | |
| 			conv_str->f_str[conv_str->f_n_char] = 0;
 | |
| 			word_str = reinterpret_cast<char*>(conv_str->f_str);
 | |
| 		} else {
 | |
| 			word_str = reinterpret_cast<char*>(word->text.f_str);
 | |
| 		}
 | |
| 
 | |
| 		/* Decrypt the ilist, and display Dod ID and word position */
 | |
| 		for (ulint i = 0; i < ib_vector_size(word->nodes); i++) {
 | |
| 			fts_node_t*	node;
 | |
| 			const byte*	ptr;
 | |
| 			ulint		decoded = 0;
 | |
| 			doc_id_t	doc_id = 0;
 | |
| 
 | |
| 			node = static_cast<fts_node_t*> (ib_vector_get(
 | |
| 				word->nodes, i));
 | |
| 
 | |
| 			ptr = node->ilist;
 | |
| 
 | |
| 			while (decoded < node->ilist_size) {
 | |
| 
 | |
| 				doc_id += fts_decode_vlc(&ptr);
 | |
| 
 | |
| 				/* Get position info */
 | |
| 				while (*ptr) {
 | |
| 
 | |
| 					OK(field_store_string(
 | |
| 						   fields[I_S_FTS_WORD],
 | |
| 						   word_str));
 | |
| 
 | |
| 					OK(fields[I_S_FTS_FIRST_DOC_ID]->store(
 | |
| 						   node->first_doc_id,
 | |
| 						   true));
 | |
| 
 | |
| 					OK(fields[I_S_FTS_LAST_DOC_ID]->store(
 | |
| 						   node->last_doc_id,
 | |
| 						   true));
 | |
| 
 | |
| 					OK(fields[I_S_FTS_DOC_COUNT]->store(
 | |
| 						   node->doc_count, true));
 | |
| 
 | |
| 					OK(fields[I_S_FTS_ILIST_DOC_ID]->store(
 | |
| 						   doc_id, true));
 | |
| 
 | |
| 					OK(fields[I_S_FTS_ILIST_DOC_POS]->store(
 | |
| 						   fts_decode_vlc(&ptr), true));
 | |
| 
 | |
| 					OK(schema_table_store_record(
 | |
| 						   thd, table));
 | |
| 				}
 | |
| 
 | |
| 				++ptr;
 | |
| 
 | |
| 				decoded = ptr - (byte*) node->ilist;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(ret);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHED
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_fts_index_cache_fill(
 | |
| /*=====================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	dict_table_t*		user_table;
 | |
| 	fts_cache_t*		cache;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_fts_index_cache_fill");
 | |
| 
 | |
| 	/* deny access to non-superusers */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	MDL_ticket* mdl_ticket = nullptr;
 | |
| 	user_table = dict_table_open_on_id(
 | |
| 		innodb_ft_aux_table_id, false, DICT_TABLE_OP_NORMAL,
 | |
| 		thd, &mdl_ticket);
 | |
| 
 | |
| 	if (!user_table) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	if (!user_table->fts || !user_table->fts->cache) {
 | |
| 		dict_table_close(user_table, thd, mdl_ticket);
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	cache = user_table->fts->cache;
 | |
| 
 | |
| 	int			ret = 0;
 | |
| 	fts_string_t		conv_str;
 | |
| 	byte			word[HA_FT_MAXBYTELEN + 1];
 | |
| 	conv_str.f_len = sizeof word;
 | |
| 	conv_str.f_str = word;
 | |
| 
 | |
| 	mysql_mutex_lock(&cache->lock);
 | |
| 
 | |
| 	for (ulint i = 0; i < ib_vector_size(cache->indexes); i++) {
 | |
| 		fts_index_cache_t*      index_cache;
 | |
| 
 | |
| 		index_cache = static_cast<fts_index_cache_t*> (
 | |
| 			ib_vector_get(cache->indexes, i));
 | |
| 
 | |
| 		BREAK_IF(ret = i_s_fts_index_cache_fill_one_index(
 | |
| 				 index_cache, thd, &conv_str, tables));
 | |
| 	}
 | |
| 
 | |
| 	mysql_mutex_unlock(&cache->lock);
 | |
| 	dict_table_close(user_table, thd, mdl_ticket);
 | |
| 
 | |
| 	DBUG_RETURN(ret);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_fts_index_cache_init(
 | |
| /*=====================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_fts_index_cache_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_fts_index_fields_info;
 | |
| 	schema->fill_table = i_s_fts_index_cache_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_ft_index_cache =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_FT_INDEX_CACHE",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"INNODB AUXILIARY FTS INDEX CACHED",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_fts_index_cache_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Go through a FTS index auxiliary table, fetch its rows and fill
 | |
| FTS word cache structure.
 | |
| @return DB_SUCCESS on success, otherwise error code */
 | |
| static
 | |
| dberr_t
 | |
| i_s_fts_index_table_fill_selected(
 | |
| /*==============================*/
 | |
| 	dict_index_t*		index,		/*!< in: FTS index */
 | |
| 	ib_vector_t*		words,		/*!< in/out: vector to hold
 | |
| 						fetched words */
 | |
| 	ulint			selected,	/*!< in: selected FTS index */
 | |
| 	fts_string_t*		word)		/*!< in: word to select */
 | |
| {
 | |
| 	pars_info_t*		info;
 | |
| 	fts_table_t		fts_table;
 | |
| 	trx_t*			trx;
 | |
| 	que_t*			graph;
 | |
| 	dberr_t			error;
 | |
| 	fts_fetch_t		fetch;
 | |
| 	char			table_name[MAX_FULL_NAME_LEN];
 | |
| 
 | |
| 	info = pars_info_create();
 | |
| 
 | |
| 	fetch.read_arg = words;
 | |
| 	fetch.read_record = fts_optimize_index_fetch_node;
 | |
| 	fetch.total_memory = 0;
 | |
| 
 | |
| 	DBUG_EXECUTE_IF("fts_instrument_result_cache_limit",
 | |
| 	        fts_result_cache_limit = 8192;
 | |
| 	);
 | |
| 
 | |
| 	trx = trx_create();
 | |
| 
 | |
| 	trx->op_info = "fetching FTS index nodes";
 | |
| 
 | |
| 	pars_info_bind_function(info, "my_func", fetch.read_record, &fetch);
 | |
| 	pars_info_bind_varchar_literal(info, "word", word->f_str, word->f_len);
 | |
| 
 | |
| 	FTS_INIT_INDEX_TABLE(&fts_table, fts_get_suffix(selected),
 | |
| 			     FTS_INDEX_TABLE, index);
 | |
| 	fts_get_table_name(&fts_table, table_name);
 | |
| 	pars_info_bind_id(info, "table_name", table_name);
 | |
| 
 | |
| 	graph = fts_parse_sql(
 | |
| 		&fts_table, info,
 | |
| 		"DECLARE FUNCTION my_func;\n"
 | |
| 		"DECLARE CURSOR c IS"
 | |
| 		" SELECT word, doc_count, first_doc_id, last_doc_id,"
 | |
| 		" ilist\n"
 | |
| 		" FROM $table_name WHERE word >= :word;\n"
 | |
| 		"BEGIN\n"
 | |
| 		"\n"
 | |
| 		"OPEN c;\n"
 | |
| 		"WHILE 1 = 1 LOOP\n"
 | |
| 		"  FETCH c INTO my_func();\n"
 | |
| 		"  IF c % NOTFOUND THEN\n"
 | |
| 		"    EXIT;\n"
 | |
| 		"  END IF;\n"
 | |
| 		"END LOOP;\n"
 | |
| 		"CLOSE c;");
 | |
| 
 | |
| 	for (;;) {
 | |
| 		error = fts_eval_sql(trx, graph);
 | |
| 
 | |
| 		if (UNIV_LIKELY(error == DB_SUCCESS)) {
 | |
| 			fts_sql_commit(trx);
 | |
| 
 | |
| 			break;
 | |
| 		} else {
 | |
| 			fts_sql_rollback(trx);
 | |
| 
 | |
| 			if (error == DB_LOCK_WAIT_TIMEOUT) {
 | |
| 				ib::warn() << "Lock wait timeout reading"
 | |
| 					" FTS index. Retrying!";
 | |
| 
 | |
| 				trx->error_state = DB_SUCCESS;
 | |
| 			} else {
 | |
| 				ib::error() << "Error occurred while reading"
 | |
| 					" FTS index: " << error;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	que_graph_free(graph);
 | |
| 
 | |
| 	trx->free();
 | |
| 
 | |
| 	if (fetch.total_memory >= fts_result_cache_limit) {
 | |
| 		error = DB_FTS_EXCEED_RESULT_CACHE_LIMIT;
 | |
| 	}
 | |
| 
 | |
| 	return(error);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Free words. */
 | |
| static
 | |
| void
 | |
| i_s_fts_index_table_free_one_fetch(
 | |
| /*===============================*/
 | |
| 	ib_vector_t*		words)		/*!< in: words fetched */
 | |
| {
 | |
| 	for (ulint i = 0; i < ib_vector_size(words); i++) {
 | |
| 		fts_word_t*	word;
 | |
| 
 | |
| 		word = static_cast<fts_word_t*>(ib_vector_get(words, i));
 | |
| 
 | |
| 		for (ulint j = 0; j < ib_vector_size(word->nodes); j++) {
 | |
| 			fts_node_t*     node;
 | |
| 
 | |
| 			node = static_cast<fts_node_t*> (ib_vector_get(
 | |
| 				word->nodes, j));
 | |
| 			ut_free(node->ilist);
 | |
| 		}
 | |
| 
 | |
| 		fts_word_free(word);
 | |
| 	}
 | |
| 
 | |
| 	ib_vector_reset(words);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Go through words, fill INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE.
 | |
| @return	0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_fts_index_table_fill_one_fetch(
 | |
| /*===============================*/
 | |
| 	CHARSET_INFO*		index_charset,	/*!< in: FTS index charset */
 | |
| 	THD*			thd,		/*!< in: thread */
 | |
| 	TABLE_LIST*		tables,		/*!< in/out: tables to fill */
 | |
| 	ib_vector_t*		words,		/*!< in: words fetched */
 | |
| 	fts_string_t*		conv_str,	/*!< in: string for conversion*/
 | |
| 	bool			has_more)	/*!< in: has more to fetch */
 | |
| {
 | |
| 	TABLE*			table = (TABLE*) tables->table;
 | |
| 	Field**			fields;
 | |
| 	uint			dummy_errors;
 | |
| 	char*			word_str;
 | |
| 	ulint			words_size;
 | |
| 	int			ret = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_fts_index_table_fill_one_fetch");
 | |
| 
 | |
| 	fields = table->field;
 | |
| 
 | |
| 	words_size = ib_vector_size(words);
 | |
| 	if (has_more) {
 | |
| 		/* the last word is not fetched completely. */
 | |
| 		ut_ad(words_size > 1);
 | |
| 		words_size -= 1;
 | |
| 	}
 | |
| 
 | |
| 	/* Go through each word in the index cache */
 | |
| 	for (ulint i = 0; i < words_size; i++) {
 | |
| 		fts_word_t*	word;
 | |
| 
 | |
| 		word = static_cast<fts_word_t*>(ib_vector_get(words, i));
 | |
| 
 | |
| 		word->text.f_str[word->text.f_len] = 0;
 | |
| 
 | |
| 		/* Convert word from index charset to system_charset_info */
 | |
| 		if (index_charset->cset != system_charset_info->cset) {
 | |
| 			conv_str->f_n_char = my_convert(
 | |
| 				reinterpret_cast<char*>(conv_str->f_str),
 | |
| 				static_cast<uint32>(conv_str->f_len),
 | |
| 				system_charset_info,
 | |
| 				reinterpret_cast<char*>(word->text.f_str),
 | |
| 				static_cast<uint32>(word->text.f_len),
 | |
| 				index_charset, &dummy_errors);
 | |
| 			ut_ad(conv_str->f_n_char <= conv_str->f_len);
 | |
| 			conv_str->f_str[conv_str->f_n_char] = 0;
 | |
| 			word_str = reinterpret_cast<char*>(conv_str->f_str);
 | |
| 		} else {
 | |
| 			word_str = reinterpret_cast<char*>(word->text.f_str);
 | |
| 		}
 | |
| 
 | |
| 		/* Decrypt the ilist, and display Dod ID and word position */
 | |
| 		for (ulint i = 0; i < ib_vector_size(word->nodes); i++) {
 | |
| 			fts_node_t*	node;
 | |
| 			const byte*	ptr;
 | |
| 			ulint		decoded = 0;
 | |
| 			doc_id_t	doc_id = 0;
 | |
| 
 | |
| 			node = static_cast<fts_node_t*> (ib_vector_get(
 | |
| 				word->nodes, i));
 | |
| 
 | |
| 			ptr = node->ilist;
 | |
| 
 | |
| 			while (decoded < node->ilist_size) {
 | |
| 				doc_id += fts_decode_vlc(&ptr);
 | |
| 
 | |
| 				/* Get position info */
 | |
| 				while (*ptr) {
 | |
| 
 | |
| 					OK(field_store_string(
 | |
| 						   fields[I_S_FTS_WORD],
 | |
| 						   word_str));
 | |
| 
 | |
| 					OK(fields[I_S_FTS_FIRST_DOC_ID]->store(
 | |
| 						longlong(node->first_doc_id), true));
 | |
| 
 | |
| 					OK(fields[I_S_FTS_LAST_DOC_ID]->store(
 | |
| 						longlong(node->last_doc_id), true));
 | |
| 
 | |
| 					OK(fields[I_S_FTS_DOC_COUNT]->store(
 | |
| 						   node->doc_count, true));
 | |
| 
 | |
| 					OK(fields[I_S_FTS_ILIST_DOC_ID]->store(
 | |
| 						longlong(doc_id), true));
 | |
| 
 | |
| 					OK(fields[I_S_FTS_ILIST_DOC_POS]->store(
 | |
| 						   fts_decode_vlc(&ptr), true));
 | |
| 
 | |
| 					OK(schema_table_store_record(
 | |
| 						   thd, table));
 | |
| 				}
 | |
| 
 | |
| 				++ptr;
 | |
| 
 | |
| 				decoded = ptr - (byte*) node->ilist;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(ret);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Go through a FTS index and its auxiliary tables, fetch rows in each table
 | |
| and fill INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_fts_index_table_fill_one_index(
 | |
| /*===============================*/
 | |
| 	dict_index_t*		index,		/*!< in: FTS index */
 | |
| 	THD*			thd,		/*!< in: thread */
 | |
| 	fts_string_t*		conv_str,	/*!< in/out: buffer */
 | |
| 	TABLE_LIST*		tables)		/*!< in/out: tables to fill */
 | |
| {
 | |
| 	ib_vector_t*		words;
 | |
| 	mem_heap_t*		heap;
 | |
| 	CHARSET_INFO*		index_charset;
 | |
| 	dberr_t			error;
 | |
| 	int			ret = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_fts_index_table_fill_one_index");
 | |
| 	DBUG_ASSERT(!dict_index_is_online_ddl(index));
 | |
| 
 | |
| 	heap = mem_heap_create(1024);
 | |
| 
 | |
| 	words = ib_vector_create(ib_heap_allocator_create(heap),
 | |
| 				 sizeof(fts_word_t), 256);
 | |
| 
 | |
| 	index_charset = fts_index_get_charset(index);
 | |
| 
 | |
| 	/* Iterate through each auxiliary table as described in
 | |
| 	fts_index_selector */
 | |
| 	for (ulint selected = 0; selected < FTS_NUM_AUX_INDEX; selected++) {
 | |
| 		fts_string_t	word;
 | |
| 		bool		has_more = false;
 | |
| 
 | |
| 		word.f_str = NULL;
 | |
| 		word.f_len = 0;
 | |
| 		word.f_n_char = 0;
 | |
| 
 | |
| 		do {
 | |
| 			/* Fetch from index */
 | |
| 			error = i_s_fts_index_table_fill_selected(
 | |
| 				index, words, selected, &word);
 | |
| 
 | |
| 			if (error == DB_SUCCESS) {
 | |
| 				has_more = false;
 | |
| 			} else if (error == DB_FTS_EXCEED_RESULT_CACHE_LIMIT) {
 | |
| 				has_more = true;
 | |
| 			} else {
 | |
| 				i_s_fts_index_table_free_one_fetch(words);
 | |
| 				ret = 1;
 | |
| 				goto func_exit;
 | |
| 			}
 | |
| 
 | |
| 			if (has_more) {
 | |
| 				fts_word_t*	last_word;
 | |
| 
 | |
| 				/* Prepare start point for next fetch */
 | |
| 				last_word = static_cast<fts_word_t*>(ib_vector_last(words));
 | |
| 				ut_ad(last_word != NULL);
 | |
| 				fts_string_dup(&word, &last_word->text, heap);
 | |
| 			}
 | |
| 
 | |
| 			/* Fill into tables */
 | |
| 			ret = i_s_fts_index_table_fill_one_fetch(
 | |
| 				index_charset, thd, tables, words, conv_str,
 | |
| 				has_more);
 | |
| 			i_s_fts_index_table_free_one_fetch(words);
 | |
| 
 | |
| 			if (ret != 0) {
 | |
| 				goto func_exit;
 | |
| 			}
 | |
| 		} while (has_more);
 | |
| 	}
 | |
| 
 | |
| func_exit:
 | |
| 	mem_heap_free(heap);
 | |
| 
 | |
| 	DBUG_RETURN(ret);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_fts_index_table_fill(
 | |
| /*=====================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	dict_table_t*		user_table;
 | |
| 	dict_index_t*		index;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_fts_index_table_fill");
 | |
| 
 | |
| 	/* deny access to non-superusers */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	MDL_ticket* mdl_ticket = nullptr;
 | |
| 	user_table = dict_table_open_on_id(
 | |
| 		innodb_ft_aux_table_id, false, DICT_TABLE_OP_NORMAL,
 | |
| 		thd, &mdl_ticket);
 | |
| 
 | |
| 	if (!user_table) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	int		ret = 0;
 | |
| 	fts_string_t	conv_str;
 | |
| 	conv_str.f_len = system_charset_info->mbmaxlen
 | |
| 		* FTS_MAX_WORD_LEN_IN_CHAR;
 | |
| 	conv_str.f_str = static_cast<byte*>(ut_malloc_nokey(conv_str.f_len));
 | |
| 
 | |
| 	for (index = dict_table_get_first_index(user_table);
 | |
| 	     index; index = dict_table_get_next_index(index)) {
 | |
| 		if (index->type & DICT_FTS) {
 | |
| 			BREAK_IF(ret = i_s_fts_index_table_fill_one_index(
 | |
| 					 index, thd, &conv_str, tables));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	dict_table_close(user_table, thd, mdl_ticket);
 | |
| 
 | |
| 	ut_free(conv_str.f_str);
 | |
| 
 | |
| 	DBUG_RETURN(ret);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_fts_index_table_init(
 | |
| /*=====================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_fts_index_table_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_fts_index_fields_info;
 | |
| 	schema->fill_table = i_s_fts_index_table_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_ft_index_table =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_FT_INDEX_TABLE",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"INNODB AUXILIARY FTS INDEX TABLE",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_fts_index_table_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_FT_CONFIG */
 | |
| static ST_FIELD_INFO i_s_fts_config_fields_info[]=
 | |
| {
 | |
| #define	FTS_CONFIG_KEY			0
 | |
|   Column("KEY",   Varchar(NAME_LEN + 1),  NOT_NULL),
 | |
| 
 | |
| #define	FTS_CONFIG_VALUE		1
 | |
|   Column("VALUE", Varchar(NAME_LEN + 1),  NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| static const char* fts_config_key[] = {
 | |
| 	FTS_OPTIMIZE_LIMIT_IN_SECS,
 | |
| 	FTS_SYNCED_DOC_ID,
 | |
| 	FTS_STOPWORD_TABLE_NAME,
 | |
| 	FTS_USE_STOPWORD,
 | |
|         NULL
 | |
| };
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill the dynamic table INFORMATION_SCHEMA.INNODB_FT_CONFIG
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_fts_config_fill(
 | |
| /*================*/
 | |
| 	THD*		thd,		/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,		/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (ignored) */
 | |
| {
 | |
| 	Field**			fields;
 | |
| 	TABLE*			table = (TABLE*) tables->table;
 | |
| 	trx_t*			trx;
 | |
| 	fts_table_t		fts_table;
 | |
| 	dict_table_t*		user_table;
 | |
| 	ulint			i = 0;
 | |
| 	dict_index_t*		index = NULL;
 | |
| 	unsigned char		str[FTS_MAX_CONFIG_VALUE_LEN + 1];
 | |
| 
 | |
| 	DBUG_ENTER("i_s_fts_config_fill");
 | |
| 
 | |
| 	/* deny access to non-superusers */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	MDL_ticket* mdl_ticket = nullptr;
 | |
| 	user_table = dict_table_open_on_id(
 | |
| 		innodb_ft_aux_table_id, false, DICT_TABLE_OP_NORMAL,
 | |
| 		thd, &mdl_ticket);
 | |
| 
 | |
| 	if (!user_table) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	if (!dict_table_has_fts_index(user_table)) {
 | |
| 		dict_table_close(user_table, thd, mdl_ticket);
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	fields = table->field;
 | |
| 
 | |
| 	trx = trx_create();
 | |
| 	trx->op_info = "Select for FTS CONFIG TABLE";
 | |
| 
 | |
| 	FTS_INIT_FTS_TABLE(&fts_table, "CONFIG", FTS_COMMON_TABLE, user_table);
 | |
| 
 | |
| 	if (!ib_vector_is_empty(user_table->fts->indexes)) {
 | |
| 		index = (dict_index_t*) ib_vector_getp_const(
 | |
| 				user_table->fts->indexes, 0);
 | |
| 		DBUG_ASSERT(!dict_index_is_online_ddl(index));
 | |
| 	}
 | |
| 
 | |
| 	int	ret = 0;
 | |
| 
 | |
| 	while (fts_config_key[i]) {
 | |
| 		fts_string_t	value;
 | |
| 		char*		key_name;
 | |
| 		ulint		allocated = FALSE;
 | |
| 
 | |
| 		value.f_len = FTS_MAX_CONFIG_VALUE_LEN;
 | |
| 
 | |
| 		value.f_str = str;
 | |
| 
 | |
| 		if (index
 | |
| 		    && strcmp(fts_config_key[i], FTS_TOTAL_WORD_COUNT) == 0) {
 | |
| 			key_name = fts_config_create_index_param_name(
 | |
| 				fts_config_key[i], index);
 | |
| 			allocated = TRUE;
 | |
| 		} else {
 | |
| 			key_name = (char*) fts_config_key[i];
 | |
| 		}
 | |
| 
 | |
| 		fts_config_get_value(trx, &fts_table, key_name, &value);
 | |
| 
 | |
| 		if (allocated) {
 | |
| 			ut_free(key_name);
 | |
| 		}
 | |
| 
 | |
| 		BREAK_IF(ret = field_store_string(
 | |
| 				 fields[FTS_CONFIG_KEY], fts_config_key[i]));
 | |
| 
 | |
| 		BREAK_IF(ret = field_store_string(
 | |
| 				 fields[FTS_CONFIG_VALUE],
 | |
| 				 reinterpret_cast<const char*>(value.f_str)));
 | |
| 
 | |
| 		BREAK_IF(ret = schema_table_store_record(thd, table));
 | |
| 
 | |
| 		i++;
 | |
| 	}
 | |
| 
 | |
| 	fts_sql_commit(trx);
 | |
| 
 | |
| 	dict_table_close(user_table, thd, mdl_ticket);
 | |
| 
 | |
| 	trx->free();
 | |
| 
 | |
| 	DBUG_RETURN(ret);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.INNODB_FT_CONFIG
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_fts_config_init(
 | |
| /*=================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_fts_config_init");
 | |
| 	ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_fts_config_fields_info;
 | |
| 	schema->fill_table = i_s_fts_config_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_ft_config =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_FT_CONFIG",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"INNODB AUXILIARY FTS CONFIG TABLE",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_fts_config_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table INNODB_BUFFER_POOL_STATS. */
 | |
| static ST_FIELD_INFO i_s_innodb_buffer_stats_fields_info[]=
 | |
| {
 | |
| #define IDX_BUF_STATS_POOL_ID		0
 | |
|   Column("POOL_ID", ULong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_POOL_SIZE		1
 | |
|   Column("POOL_SIZE", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_FREE_BUFFERS	2
 | |
|   Column("FREE_BUFFERS", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_LRU_LEN		3
 | |
|   Column("DATABASE_PAGES", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_OLD_LRU_LEN	4
 | |
|   Column("OLD_DATABASE_PAGES", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_FLUSH_LIST_LEN	5
 | |
|   Column("MODIFIED_DATABASE_PAGES", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_PENDING_ZIP	6
 | |
|   Column("PENDING_DECOMPRESS", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_PENDING_READ	7
 | |
|   Column("PENDING_READS",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_FLUSH_LRU		8
 | |
|   Column("PENDING_FLUSH_LRU",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_FLUSH_LIST	9
 | |
|   Column("PENDING_FLUSH_LIST", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_PAGE_YOUNG	10
 | |
|   Column("PAGES_MADE_YOUNG",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_PAGE_NOT_YOUNG	11
 | |
|   Column("PAGES_NOT_MADE_YOUNG",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define	IDX_BUF_STATS_PAGE_YOUNG_RATE	12
 | |
|   Column("PAGES_MADE_YOUNG_RATE", Float(MAX_FLOAT_STR_LENGTH), NOT_NULL),
 | |
| 
 | |
| #define	IDX_BUF_STATS_PAGE_NOT_YOUNG_RATE 13
 | |
|   Column("PAGES_MADE_NOT_YOUNG_RATE", Float(MAX_FLOAT_STR_LENGTH), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_PAGE_READ		14
 | |
|   Column("NUMBER_PAGES_READ",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_PAGE_CREATED	15
 | |
|   Column("NUMBER_PAGES_CREATED",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_PAGE_WRITTEN	16
 | |
|   Column("NUMBER_PAGES_WRITTEN",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define	IDX_BUF_STATS_PAGE_READ_RATE	17
 | |
|   Column("PAGES_READ_RATE", Float(MAX_FLOAT_STR_LENGTH), NOT_NULL),
 | |
| 
 | |
| #define	IDX_BUF_STATS_PAGE_CREATE_RATE	18
 | |
|   Column("PAGES_CREATE_RATE", Float(MAX_FLOAT_STR_LENGTH), NOT_NULL),
 | |
| 
 | |
| #define	IDX_BUF_STATS_PAGE_WRITTEN_RATE	19
 | |
|   Column("PAGES_WRITTEN_RATE",Float(MAX_FLOAT_STR_LENGTH), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_GET		20
 | |
|   Column("NUMBER_PAGES_GET", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_HIT_RATE		21
 | |
|   Column("HIT_RATE", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_MADE_YOUNG_PCT	22
 | |
|   Column("YOUNG_MAKE_PER_THOUSAND_GETS", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_NOT_MADE_YOUNG_PCT 23
 | |
|   Column("NOT_YOUNG_MAKE_PER_THOUSAND_GETS", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_READ_AHEAD	24
 | |
|   Column("NUMBER_PAGES_READ_AHEAD", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_READ_AHEAD_EVICTED 25
 | |
|   Column("NUMBER_READ_AHEAD_EVICTED", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define	IDX_BUF_STATS_READ_AHEAD_RATE	26
 | |
|   Column("READ_AHEAD_RATE", Float(MAX_FLOAT_STR_LENGTH), NOT_NULL),
 | |
| 
 | |
| #define	IDX_BUF_STATS_READ_AHEAD_EVICT_RATE 27
 | |
|   Column("READ_AHEAD_EVICTED_RATE",Float(MAX_FLOAT_STR_LENGTH), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_LRU_IO_SUM	28
 | |
|   Column("LRU_IO_TOTAL", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_LRU_IO_CUR	29
 | |
|   Column("LRU_IO_CURRENT", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_UNZIP_SUM		30
 | |
|   Column("UNCOMPRESS_TOTAL",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_STATS_UNZIP_CUR		31
 | |
|   Column("UNCOMPRESS_CURRENT", ULonglong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /** Fill INFORMATION_SCHEMA.INNODB_BUFFER_POOL_STATS
 | |
| @param[in,out]	thd	connection
 | |
| @param[in,out]	tables	tables to fill
 | |
| @return 0 on success, 1 on failure */
 | |
| static int i_s_innodb_stats_fill(THD *thd, TABLE_LIST * tables, Item *)
 | |
| {
 | |
| 	TABLE*		table;
 | |
| 	Field**		fields;
 | |
| 	buf_pool_info_t	info;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_innodb_stats_fill");
 | |
| 
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* Only allow the PROCESS privilege holder to access the stats */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	buf_pool.get_info(&info);
 | |
| 
 | |
| 	table = tables->table;
 | |
| 
 | |
| 	fields = table->field;
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_POOL_ID]->store(0, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_POOL_SIZE]->store(info.pool_size, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_LRU_LEN]->store(info.lru_len, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_OLD_LRU_LEN]->store(info.old_lru_len, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_FREE_BUFFERS]->store(
 | |
| 		   info.free_list_len, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_FLUSH_LIST_LEN]->store(
 | |
| 		   info.flush_list_len, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PENDING_ZIP]->store(info.n_pend_unzip, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PENDING_READ]->store(info.n_pend_reads, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_FLUSH_LRU]->store(
 | |
| 		   info.n_pending_flush_lru, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_FLUSH_LIST]->store(
 | |
| 		   info.n_pending_flush_list, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PAGE_YOUNG]->store(
 | |
| 		   info.n_pages_made_young, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PAGE_NOT_YOUNG]->store(
 | |
| 		   info.n_pages_not_made_young, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PAGE_YOUNG_RATE]->store(
 | |
| 		   info.page_made_young_rate));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PAGE_NOT_YOUNG_RATE]->store(
 | |
| 		   info.page_not_made_young_rate));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PAGE_READ]->store(info.n_pages_read, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PAGE_CREATED]->store(
 | |
| 		   info.n_pages_created, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PAGE_WRITTEN]->store(
 | |
| 		   info.n_pages_written, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_GET]->store(info.n_page_gets, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PAGE_READ_RATE]->store(
 | |
| 		   info.pages_read_rate));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PAGE_CREATE_RATE]->store(
 | |
| 		   info.pages_created_rate));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_PAGE_WRITTEN_RATE]->store(
 | |
| 		   info.pages_written_rate));
 | |
| 
 | |
| 	if (info.n_page_get_delta) {
 | |
| 		if (info.page_read_delta <= info.n_page_get_delta) {
 | |
| 			OK(fields[IDX_BUF_STATS_HIT_RATE]->store(
 | |
| 				static_cast<double>(
 | |
| 					1000 - (1000 * info.page_read_delta
 | |
| 					/ info.n_page_get_delta))));
 | |
| 		} else {
 | |
| 			OK(fields[IDX_BUF_STATS_HIT_RATE]->store(0));
 | |
| 		}
 | |
| 
 | |
| 		OK(fields[IDX_BUF_STATS_MADE_YOUNG_PCT]->store(
 | |
| 			   1000 * info.young_making_delta
 | |
| 			   / info.n_page_get_delta, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_STATS_NOT_MADE_YOUNG_PCT]->store(
 | |
| 			   1000 * info.not_young_making_delta
 | |
| 			   / info.n_page_get_delta, true));
 | |
| 	} else {
 | |
| 		OK(fields[IDX_BUF_STATS_HIT_RATE]->store(0, true));
 | |
| 		OK(fields[IDX_BUF_STATS_MADE_YOUNG_PCT]->store(0, true));
 | |
| 		OK(fields[IDX_BUF_STATS_NOT_MADE_YOUNG_PCT]->store(0, true));
 | |
| 	}
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_READ_AHEAD]->store(
 | |
| 		   info.n_ra_pages_read, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_READ_AHEAD_EVICTED]->store(
 | |
| 		   info.n_ra_pages_evicted, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_READ_AHEAD_RATE]->store(
 | |
| 		   info.pages_readahead_rate));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_READ_AHEAD_EVICT_RATE]->store(
 | |
| 		   info.pages_evicted_rate));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_LRU_IO_SUM]->store(info.io_sum, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_LRU_IO_CUR]->store(info.io_cur, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_UNZIP_SUM]->store(info.unzip_sum, true));
 | |
| 
 | |
| 	OK(fields[IDX_BUF_STATS_UNZIP_CUR]->store(info.unzip_cur, true));
 | |
| 
 | |
| 	DBUG_RETURN(schema_table_store_record(thd, table));
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.INNODB_BUFFER_POOL_STATS.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_innodb_buffer_pool_stats_init(
 | |
| /*==============================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_innodb_buffer_pool_stats_init");
 | |
| 
 | |
| 	schema = reinterpret_cast<ST_SCHEMA_TABLE*>(p);
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_innodb_buffer_stats_fields_info;
 | |
| 	schema->fill_table = i_s_innodb_stats_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_buffer_stats =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_BUFFER_POOL_STATS",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB Buffer Pool Statistics Information ",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_innodb_buffer_pool_stats_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| /** These must correspond to the first values of buf_page_state */
 | |
| static const LEX_CSTRING page_state_values[] =
 | |
| {
 | |
|   { STRING_WITH_LEN("NOT_USED") },
 | |
|   { STRING_WITH_LEN("MEMORY") },
 | |
|   { STRING_WITH_LEN("REMOVE_HASH") },
 | |
|   { STRING_WITH_LEN("FILE_PAGE") },
 | |
| };
 | |
| 
 | |
| static const TypelibBuffer<4> page_state_values_typelib(page_state_values);
 | |
| 
 | |
| static const LEX_CSTRING io_values[] =
 | |
| {
 | |
| 	{ STRING_WITH_LEN("IO_NONE") },
 | |
| 	{ STRING_WITH_LEN("IO_READ") },
 | |
| 	{ STRING_WITH_LEN("IO_WRITE") }
 | |
| };
 | |
| 
 | |
| 
 | |
| static TypelibBuffer<3> io_values_typelib(io_values);
 | |
| 
 | |
| namespace Show {
 | |
| /* Fields of the dynamic table INNODB_BUFFER_POOL_PAGE. */
 | |
| static ST_FIELD_INFO i_s_innodb_buffer_page_fields_info[]=
 | |
| {
 | |
| #define IDX_BUFFER_POOL_ID		0
 | |
|   Column("POOL_ID", ULong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_BLOCK_ID		1
 | |
|   Column("BLOCK_ID", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_SPACE		2
 | |
|   Column("SPACE", ULong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_NUM		3
 | |
|   Column("PAGE_NUMBER", ULong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_TYPE		4
 | |
|   Column("PAGE_TYPE", Varchar(64), NULLABLE),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_FLUSH_TYPE	5
 | |
|   Column("FLUSH_TYPE", ULong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_FIX_COUNT	6
 | |
|   Column("FIX_COUNT", ULong(), NOT_NULL),
 | |
| 
 | |
| #ifdef BTR_CUR_HASH_ADAPT
 | |
| #define IDX_BUFFER_PAGE_HASHED		7
 | |
|   Column("IS_HASHED", SLong(1), NOT_NULL),
 | |
| #endif /* BTR_CUR_HASH_ADAPT */
 | |
| #define IDX_BUFFER_PAGE_NEWEST_MOD	7 + I_S_AHI
 | |
|   Column("NEWEST_MODIFICATION", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_OLDEST_MOD	8 + I_S_AHI
 | |
|   Column("OLDEST_MODIFICATION", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_ACCESS_TIME	9 + I_S_AHI
 | |
|   Column("ACCESS_TIME", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_TABLE_NAME	10 + I_S_AHI
 | |
|   Column("TABLE_NAME", Varchar(1024), NULLABLE),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_INDEX_NAME	11 + I_S_AHI
 | |
|   Column("INDEX_NAME", Varchar(NAME_CHAR_LEN), NULLABLE),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_NUM_RECS	12 + I_S_AHI
 | |
|   Column("NUMBER_RECORDS", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_DATA_SIZE	13 + I_S_AHI
 | |
|   Column("DATA_SIZE", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_ZIP_SIZE	14 + I_S_AHI
 | |
|   Column("COMPRESSED_SIZE", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_STATE		15 + I_S_AHI
 | |
|   Column("PAGE_STATE", Enum(&page_state_values_typelib), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_IO_FIX		16 + I_S_AHI
 | |
|   Column("IO_FIX", Enum(&io_values_typelib), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_IS_OLD		17 + I_S_AHI
 | |
|   Column("IS_OLD", SLong(1), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUFFER_PAGE_FREE_CLOCK	18 + I_S_AHI
 | |
|   Column("FREE_PAGE_CLOCK", ULonglong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill Information Schema table INNODB_BUFFER_PAGE with information
 | |
| cached in the buf_page_info_t array
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_innodb_buffer_page_fill(
 | |
| /*========================*/
 | |
| 	THD*			thd,		/*!< in: thread */
 | |
| 	TABLE_LIST*		tables,		/*!< in/out: tables to fill */
 | |
| 	const buf_page_info_t*	info_array,	/*!< in: array cached page
 | |
| 						info */
 | |
| 	ulint			num_page)	/*!< in: number of page info
 | |
| 						cached */
 | |
| {
 | |
| 	TABLE*			table;
 | |
| 	Field**			fields;
 | |
| 
 | |
| 	compile_time_assert(I_S_PAGE_TYPE_LAST < 1 << I_S_PAGE_TYPE_BITS);
 | |
| 
 | |
| 	DBUG_ENTER("i_s_innodb_buffer_page_fill");
 | |
| 
 | |
| 	table = tables->table;
 | |
| 
 | |
| 	fields = table->field;
 | |
| 
 | |
| 	/* Iterate through the cached array and fill the I_S table rows */
 | |
| 	for (ulint i = 0; i < num_page; i++) {
 | |
| 		const buf_page_info_t*	page_info;
 | |
| 		char			table_name[MAX_FULL_NAME_LEN + 1];
 | |
| 		const char*		table_name_end = NULL;
 | |
| 
 | |
| 		page_info = info_array + i;
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_POOL_ID]->store(0, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_BLOCK_ID]->store(
 | |
| 			   page_info->block_id, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_SPACE]->store(
 | |
| 			   page_info->id.space(), true));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_NUM]->store(
 | |
| 			   page_info->id.page_no(), true));
 | |
| 
 | |
| 		OK(field_store_string(
 | |
| 			   fields[IDX_BUFFER_PAGE_TYPE],
 | |
| 			   i_s_page_type[page_info->page_type].type_str));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_FLUSH_TYPE]->store(0, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_FIX_COUNT]->store(
 | |
| 			   ~buf_page_t::LRU_MASK & page_info->state, true));
 | |
| 
 | |
| #ifdef BTR_CUR_HASH_ADAPT
 | |
| 		OK(fields[IDX_BUFFER_PAGE_HASHED]->store(
 | |
| 			   page_info->hashed, true));
 | |
| #endif /* BTR_CUR_HASH_ADAPT */
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_NEWEST_MOD]->store(
 | |
| 			   page_info->newest_mod, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_OLDEST_MOD]->store(
 | |
| 			   page_info->oldest_mod, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_ACCESS_TIME]->store(
 | |
| 			   page_info->access_time, true));
 | |
| 
 | |
| 		fields[IDX_BUFFER_PAGE_TABLE_NAME]->set_null();
 | |
| 
 | |
| 		fields[IDX_BUFFER_PAGE_INDEX_NAME]->set_null();
 | |
| 
 | |
| 		/* If this is an index page, fetch the index name
 | |
| 		and table name */
 | |
| 		if (page_info->page_type == I_S_PAGE_TYPE_INDEX) {
 | |
| 			bool ret = false;
 | |
| 
 | |
| 			dict_sys.freeze(SRW_LOCK_CALL);
 | |
| 
 | |
| 			const dict_index_t* index =
 | |
| 				dict_index_get_if_in_cache_low(
 | |
| 					page_info->index_id);
 | |
| 
 | |
| 			if (index) {
 | |
| 				table_name_end = innobase_convert_name(
 | |
| 					table_name, sizeof(table_name),
 | |
| 					index->table->name.m_name,
 | |
| 					strlen(index->table->name.m_name),
 | |
| 					thd);
 | |
| 
 | |
| 				ret = fields[IDX_BUFFER_PAGE_TABLE_NAME]
 | |
| 					->store(table_name,
 | |
| 						static_cast<uint>(
 | |
| 							table_name_end
 | |
| 							- table_name),
 | |
| 						system_charset_info)
 | |
| 					|| fields[IDX_BUFFER_PAGE_INDEX_NAME]
 | |
| 					->store(index->name,
 | |
| 						uint(strlen(index->name)),
 | |
| 						system_charset_info);
 | |
| 			}
 | |
| 
 | |
| 			dict_sys.unfreeze();
 | |
| 
 | |
| 			OK(ret);
 | |
| 
 | |
| 			if (index) {
 | |
| 				fields[IDX_BUFFER_PAGE_TABLE_NAME]
 | |
| 					->set_notnull();
 | |
| 				fields[IDX_BUFFER_PAGE_INDEX_NAME]
 | |
| 					->set_notnull();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_NUM_RECS]->store(
 | |
| 			   page_info->num_recs, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_DATA_SIZE]->store(
 | |
| 			   page_info->data_size, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_ZIP_SIZE]->store(
 | |
| 			   page_info->zip_ssize
 | |
| 			   ? (UNIV_ZIP_SIZE_MIN >> 1) << page_info->zip_ssize
 | |
| 			   : 0, true));
 | |
| 
 | |
| 		static_assert(buf_page_t::NOT_USED == 0, "compatibility");
 | |
| 		static_assert(buf_page_t::MEMORY == 1, "compatibility");
 | |
| 		static_assert(buf_page_t::REMOVE_HASH == 2, "compatibility");
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_STATE]->store(
 | |
| 			   std::min<uint32_t>(3, page_info->state) + 1, true));
 | |
| 
 | |
| 		static_assert(buf_page_t::UNFIXED == 2U << 29, "comp.");
 | |
| 		static_assert(buf_page_t::READ_FIX == 4U << 29, "comp.");
 | |
| 		static_assert(buf_page_t::WRITE_FIX == 6U << 29, "comp.");
 | |
| 
 | |
| 		unsigned io_fix = page_info->state >> 29;
 | |
| 		if (io_fix < 4) {
 | |
| 			io_fix = 1;
 | |
| 		} else if (io_fix == 4) {
 | |
| 			io_fix = 2;
 | |
| 		} else {
 | |
| 			io_fix = 3;
 | |
| 		}
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_IO_FIX]->store(io_fix, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_IS_OLD]->store(
 | |
| 			   page_info->is_old, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUFFER_PAGE_FREE_CLOCK]->store(
 | |
| 			   page_info->freed_page_clock, true));
 | |
| 
 | |
| 		OK(schema_table_store_record(thd, table));
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Set appropriate page type to a buf_page_info_t structure */
 | |
| static
 | |
| void
 | |
| i_s_innodb_set_page_type(
 | |
| /*=====================*/
 | |
| 	buf_page_info_t*page_info,	/*!< in/out: structure to fill with
 | |
| 					scanned info */
 | |
| 	const byte*	frame)		/*!< in: buffer frame */
 | |
| {
 | |
| 	uint16_t page_type = fil_page_get_type(frame);
 | |
| 
 | |
| 	if (fil_page_type_is_index(page_type)) {
 | |
| 		const page_t*	page = (const page_t*) frame;
 | |
| 
 | |
| 		page_info->index_id = btr_page_get_index_id(page);
 | |
| 
 | |
| 		/* FIL_PAGE_INDEX and FIL_PAGE_RTREE are a bit special,
 | |
| 		their values are defined as 17855 and 17854, so we cannot
 | |
| 		use them to index into i_s_page_type[] array, its array index
 | |
| 		in the i_s_page_type[] array is I_S_PAGE_TYPE_INDEX
 | |
| 		(1) for index pages */
 | |
| 		if (page_type == FIL_PAGE_RTREE) {
 | |
| 			page_info->page_type = I_S_PAGE_TYPE_RTREE;
 | |
| 		} else {
 | |
| 			ut_ad(page_type == FIL_PAGE_INDEX
 | |
| 			      || page_type == FIL_PAGE_TYPE_INSTANT);
 | |
| 			page_info->page_type = I_S_PAGE_TYPE_INDEX;
 | |
| 		}
 | |
| 
 | |
| 		page_info->data_size = uint16_t(page_header_get_field(
 | |
| 			page, PAGE_HEAP_TOP) - (page_is_comp(page)
 | |
| 						? PAGE_NEW_SUPREMUM_END
 | |
| 						: PAGE_OLD_SUPREMUM_END)
 | |
| 			- page_header_get_field(page, PAGE_GARBAGE));
 | |
| 
 | |
| 		page_info->num_recs = page_get_n_recs(page) & ((1U << 14) - 1);
 | |
| 	} else if (page_type > FIL_PAGE_TYPE_LAST) {
 | |
| 		/* Encountered an unknown page type */
 | |
| 		page_info->page_type = I_S_PAGE_TYPE_UNKNOWN;
 | |
| 	} else {
 | |
| 		/* Make sure we get the right index into the
 | |
| 		i_s_page_type[] array */
 | |
| 		ut_a(page_type == i_s_page_type[page_type].type_value);
 | |
| 
 | |
| 		page_info->page_type = page_type & 0xf;
 | |
| 	}
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Scans pages in the buffer cache, and collect their general information
 | |
| into the buf_page_info_t array which is zero-filled. So any fields
 | |
| that are not initialized in the function will default to 0 */
 | |
| static
 | |
| void
 | |
| i_s_innodb_buffer_page_get_info(
 | |
| /*============================*/
 | |
| 	const buf_page_t*bpage,		/*!< in: buffer pool page to scan */
 | |
| 	ulint		pos,		/*!< in: buffer block position in
 | |
| 					buffer pool or in the LRU list */
 | |
| 	buf_page_info_t*page_info)	/*!< in: zero filled info structure;
 | |
| 					out: structure filled with scanned
 | |
| 					info */
 | |
| {
 | |
| 	page_info->block_id = pos;
 | |
| 
 | |
| 	static_assert(buf_page_t::NOT_USED == 0, "compatibility");
 | |
| 	static_assert(buf_page_t::MEMORY == 1, "compatibility");
 | |
| 	static_assert(buf_page_t::REMOVE_HASH == 2, "compatibility");
 | |
| 	static_assert(buf_page_t::UNFIXED == 2U << 29, "compatibility");
 | |
| 	static_assert(buf_page_t::READ_FIX == 4U << 29, "compatibility");
 | |
| 	static_assert(buf_page_t::WRITE_FIX == 6U << 29, "compatibility");
 | |
| 
 | |
| 	page_info->state = bpage->state();
 | |
| 
 | |
| 	if (page_info->state < buf_page_t::UNFIXED) {
 | |
| 		page_info->page_type = I_S_PAGE_TYPE_UNKNOWN;
 | |
| 		page_info->compressed_only = false;
 | |
| 	} else {
 | |
| 		const byte*	frame;
 | |
| 
 | |
| 		page_info->id = bpage->id();
 | |
| 
 | |
| 		page_info->oldest_mod = bpage->oldest_modification();
 | |
| 
 | |
| 		page_info->access_time = bpage->access_time;
 | |
| 
 | |
| 		page_info->zip_ssize = bpage->zip.ssize;
 | |
| 
 | |
| 		page_info->is_old = bpage->old;
 | |
| 
 | |
| 		page_info->freed_page_clock = bpage->freed_page_clock;
 | |
| 
 | |
| 		if (page_info->state >= buf_page_t::READ_FIX
 | |
| 		    && page_info->state < buf_page_t::WRITE_FIX) {
 | |
| 			page_info->page_type = I_S_PAGE_TYPE_UNKNOWN;
 | |
| 			page_info->newest_mod = 0;
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		page_info->compressed_only = !bpage->frame,
 | |
| 		frame = bpage->frame;
 | |
| 		if (UNIV_LIKELY(frame != nullptr)) {
 | |
| #ifdef BTR_CUR_HASH_ADAPT
 | |
| 			/* Note: this may be a false positive, that
 | |
| 			is, block->index will not always be set to
 | |
| 			NULL when the last adaptive hash index
 | |
| 			reference is dropped. */
 | |
| 			page_info->hashed =
 | |
| 				reinterpret_cast<const buf_block_t*>(bpage)
 | |
| 				->index != nullptr;
 | |
| #endif /* BTR_CUR_HASH_ADAPT */
 | |
| 		} else {
 | |
| 			ut_ad(page_info->zip_ssize);
 | |
| 			frame = bpage->zip.data;
 | |
| 		}
 | |
| 
 | |
| 		page_info->newest_mod = mach_read_from_8(FIL_PAGE_LSN + frame);
 | |
| 		i_s_innodb_set_page_type(page_info, frame);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| This is the function that goes through each block of the buffer pool
 | |
| and fetch information to information schema tables: INNODB_BUFFER_PAGE.
 | |
| @param[in,out]	thd	connection
 | |
| @param[in,out]	tables	tables to fill
 | |
| @return 0 on success, 1 on failure */
 | |
| static int i_s_innodb_buffer_page_fill(THD *thd, TABLE_LIST *tables, Item *)
 | |
| {
 | |
|   DBUG_ENTER("i_s_innodb_buffer_page_fill");
 | |
|   RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
|   /* deny access to user without PROCESS privilege */
 | |
|   if (check_global_access(thd, PROCESS_ACL))
 | |
|     DBUG_RETURN(0);
 | |
| 
 | |
|   int status;
 | |
|   buf_page_info_t *b=
 | |
|     static_cast<buf_page_info_t*>(my_malloc(PSI_INSTRUMENT_ME,
 | |
|                                             MAX_BUF_INFO_CACHED * sizeof *b,
 | |
|                                             MYF(MY_WME)));
 | |
|   if (!b)
 | |
|     DBUG_RETURN(1);
 | |
|   for (size_t j= 0;;)
 | |
|   {
 | |
|     memset((void*) b, 0, MAX_BUF_INFO_CACHED * sizeof *b);
 | |
|     mysql_mutex_lock(&buf_pool.mutex);
 | |
|     const size_t N= buf_pool.curr_size();
 | |
|     const size_t n= std::min<size_t>(N, MAX_BUF_INFO_CACHED);
 | |
|     for (size_t i= 0; i < n && j < N; i++, j++)
 | |
|       i_s_innodb_buffer_page_get_info(&buf_pool.get_nth_page(j)->page, j,
 | |
|                                       &b[i]);
 | |
| 
 | |
|     mysql_mutex_unlock(&buf_pool.mutex);
 | |
|     status= i_s_innodb_buffer_page_fill(thd, tables, b, n);
 | |
|     if (status || j >= N)
 | |
|       break;
 | |
|   }
 | |
|   my_free(b);
 | |
|   DBUG_RETURN(status);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.INNODB_BUFFER_PAGE.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_innodb_buffer_page_init(
 | |
| /*========================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_innodb_buffer_page_init");
 | |
| 
 | |
| 	schema = reinterpret_cast<ST_SCHEMA_TABLE*>(p);
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_innodb_buffer_page_fields_info;
 | |
| 	schema->fill_table = i_s_innodb_buffer_page_fill;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_buffer_page =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_BUFFER_PAGE",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB Buffer Page Information",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_innodb_buffer_page_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| static ST_FIELD_INFO i_s_innodb_buf_page_lru_fields_info[] =
 | |
| {
 | |
| #define IDX_BUF_LRU_POOL_ID		0
 | |
|   Column("POOL_ID", ULong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_POS			1
 | |
|   Column("LRU_POSITION", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_SPACE		2
 | |
|   Column("SPACE", ULong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_NUM		3
 | |
|   Column("PAGE_NUMBER", ULong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_TYPE		4
 | |
|   Column("PAGE_TYPE", Varchar(64), NULLABLE),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_FLUSH_TYPE	5
 | |
|   Column("FLUSH_TYPE", ULong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_FIX_COUNT	6
 | |
|   Column("FIX_COUNT", ULong(), NOT_NULL),
 | |
| 
 | |
| #ifdef BTR_CUR_HASH_ADAPT
 | |
| #define IDX_BUF_LRU_PAGE_HASHED		7
 | |
|   Column("IS_HASHED", SLong(1), NOT_NULL),
 | |
| #endif /* BTR_CUR_HASH_ADAPT */
 | |
| #define IDX_BUF_LRU_PAGE_NEWEST_MOD	7 + I_S_AHI
 | |
|   Column("NEWEST_MODIFICATION",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_OLDEST_MOD	8 + I_S_AHI
 | |
|   Column("OLDEST_MODIFICATION",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_ACCESS_TIME	9 + I_S_AHI
 | |
|   Column("ACCESS_TIME",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_TABLE_NAME	10 + I_S_AHI
 | |
|   Column("TABLE_NAME", Varchar(1024), NULLABLE),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_INDEX_NAME	11 + I_S_AHI
 | |
|   Column("INDEX_NAME", Varchar(NAME_CHAR_LEN), NULLABLE),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_NUM_RECS	12 + I_S_AHI
 | |
|   Column("NUMBER_RECORDS", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_DATA_SIZE	13 + I_S_AHI
 | |
|   Column("DATA_SIZE", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_ZIP_SIZE	14 + I_S_AHI
 | |
|   Column("COMPRESSED_SIZE",ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_STATE		15 + I_S_AHI
 | |
|   Column("COMPRESSED", SLong(1), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_IO_FIX		16 + I_S_AHI
 | |
|   Column("IO_FIX", Enum(&io_values_typelib), NOT_NULL),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_IS_OLD		17 + I_S_AHI
 | |
|   Column("IS_OLD", SLong(1), NULLABLE),
 | |
| 
 | |
| #define IDX_BUF_LRU_PAGE_FREE_CLOCK	18 + I_S_AHI
 | |
|   Column("FREE_PAGE_CLOCK", ULonglong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Fill Information Schema table INNODB_BUFFER_PAGE_LRU with information
 | |
| cached in the buf_page_info_t array
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_innodb_buf_page_lru_fill(
 | |
| /*=========================*/
 | |
| 	THD*			thd,		/*!< in: thread */
 | |
| 	TABLE_LIST*		tables,		/*!< in/out: tables to fill */
 | |
| 	const buf_page_info_t*	info_array,	/*!< in: array cached page
 | |
| 						info */
 | |
| 	ulint			num_page)	/*!< in: number of page info
 | |
| 						 cached */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_innodb_buf_page_lru_fill");
 | |
| 
 | |
| 	TABLE*	table	= tables->table;
 | |
| 	Field**	fields	= table->field;
 | |
| 
 | |
| 	/* Iterate through the cached array and fill the I_S table rows */
 | |
| 	for (ulint i = 0; i < num_page; i++) {
 | |
| 		const buf_page_info_t*	page_info;
 | |
| 		char			table_name[MAX_FULL_NAME_LEN + 1];
 | |
| 		const char*		table_name_end = NULL;
 | |
| 
 | |
| 		page_info = info_array + i;
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_POOL_ID]->store(0, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_POS]->store(
 | |
| 			   page_info->block_id, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_SPACE]->store(
 | |
| 			   page_info->id.space(), true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_NUM]->store(
 | |
| 			   page_info->id.page_no(), true));
 | |
| 
 | |
| 		OK(field_store_string(
 | |
| 			   fields[IDX_BUF_LRU_PAGE_TYPE],
 | |
| 			   i_s_page_type[page_info->page_type].type_str));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_FLUSH_TYPE]->store(0, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_FIX_COUNT]->store(
 | |
| 			   ~buf_page_t::LRU_MASK & page_info->state, true));
 | |
| 
 | |
| #ifdef BTR_CUR_HASH_ADAPT
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_HASHED]->store(
 | |
| 			   page_info->hashed, true));
 | |
| #endif /* BTR_CUR_HASH_ADAPT */
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_NEWEST_MOD]->store(
 | |
| 			   page_info->newest_mod, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_OLDEST_MOD]->store(
 | |
| 			   page_info->oldest_mod, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_ACCESS_TIME]->store(
 | |
| 			   page_info->access_time, true));
 | |
| 
 | |
| 		fields[IDX_BUF_LRU_PAGE_TABLE_NAME]->set_null();
 | |
| 
 | |
| 		fields[IDX_BUF_LRU_PAGE_INDEX_NAME]->set_null();
 | |
| 
 | |
| 		/* If this is an index page, fetch the index name
 | |
| 		and table name */
 | |
| 		if (page_info->page_type == I_S_PAGE_TYPE_INDEX) {
 | |
| 			bool ret = false;
 | |
| 
 | |
| 			dict_sys.freeze(SRW_LOCK_CALL);
 | |
| 
 | |
| 			const dict_index_t* index =
 | |
| 				dict_index_get_if_in_cache_low(
 | |
| 					page_info->index_id);
 | |
| 
 | |
| 			if (index) {
 | |
| 				table_name_end = innobase_convert_name(
 | |
| 					table_name, sizeof(table_name),
 | |
| 					index->table->name.m_name,
 | |
| 					strlen(index->table->name.m_name),
 | |
| 					thd);
 | |
| 
 | |
| 				ret = fields[IDX_BUF_LRU_PAGE_TABLE_NAME]
 | |
| 					->store(table_name,
 | |
| 						static_cast<uint>(
 | |
| 							table_name_end
 | |
| 							- table_name),
 | |
| 						system_charset_info)
 | |
| 					|| fields[IDX_BUF_LRU_PAGE_INDEX_NAME]
 | |
| 					->store(index->name,
 | |
| 						uint(strlen(index->name)),
 | |
| 						system_charset_info);
 | |
| 			}
 | |
| 
 | |
| 			dict_sys.unfreeze();
 | |
| 
 | |
| 			OK(ret);
 | |
| 
 | |
| 			if (index) {
 | |
| 				fields[IDX_BUF_LRU_PAGE_TABLE_NAME]
 | |
| 					->set_notnull();
 | |
| 				fields[IDX_BUF_LRU_PAGE_INDEX_NAME]
 | |
| 					->set_notnull();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_NUM_RECS]->store(
 | |
| 			   page_info->num_recs, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_DATA_SIZE]->store(
 | |
| 			   page_info->data_size, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_ZIP_SIZE]->store(
 | |
| 			   page_info->zip_ssize
 | |
| 			   ? 512 << page_info->zip_ssize : 0, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_STATE]->store(
 | |
| 			   page_info->compressed_only, true));
 | |
| 
 | |
| 		static_assert(buf_page_t::UNFIXED == 2U << 29, "comp.");
 | |
| 		static_assert(buf_page_t::READ_FIX == 4U << 29, "comp.");
 | |
| 		static_assert(buf_page_t::WRITE_FIX == 6U << 29, "comp.");
 | |
| 
 | |
| 		unsigned io_fix = page_info->state >> 29;
 | |
| 		if (io_fix < 4) {
 | |
| 			io_fix = 1;
 | |
| 		} else if (io_fix == 4) {
 | |
| 			io_fix = 2;
 | |
| 		} else {
 | |
| 			io_fix = 3;
 | |
| 		}
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_IO_FIX]->store(io_fix, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_IS_OLD]->store(
 | |
| 			   page_info->is_old, true));
 | |
| 
 | |
| 		OK(fields[IDX_BUF_LRU_PAGE_FREE_CLOCK]->store(
 | |
| 			   page_info->freed_page_clock, true));
 | |
| 
 | |
| 		OK(schema_table_store_record(thd, table));
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /** Fill the table INFORMATION_SCHEMA.INNODB_BUFFER_PAGE_LRU.
 | |
| @param[in]	thd		thread
 | |
| @param[in,out]	tables		tables to fill
 | |
| @return 0 on success, 1 on failure */
 | |
| static int i_s_innodb_fill_buffer_lru(THD *thd, TABLE_LIST *tables, Item *)
 | |
| {
 | |
| 	int			status = 0;
 | |
| 	buf_page_info_t*	info_buffer;
 | |
| 	ulint			lru_pos = 0;
 | |
| 	const buf_page_t*	bpage;
 | |
| 	ulint			lru_len;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_innodb_fill_buffer_lru");
 | |
| 
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* deny access to any users that do not hold PROCESS_ACL */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	/* Aquire the mutex before allocating info_buffer, since
 | |
| 	UT_LIST_GET_LEN(buf_pool.LRU) could change */
 | |
| 	mysql_mutex_lock(&buf_pool.mutex);
 | |
| 
 | |
| 	lru_len = UT_LIST_GET_LEN(buf_pool.LRU);
 | |
| 
 | |
| 	/* Print error message if malloc fail */
 | |
| 	info_buffer = (buf_page_info_t*) my_malloc(PSI_INSTRUMENT_ME,
 | |
| 		lru_len * sizeof *info_buffer, MYF(MY_WME | MY_ZEROFILL));
 | |
| 
 | |
| 	if (!info_buffer) {
 | |
| 		status = 1;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	/* Walk through Pool's LRU list and print the buffer page
 | |
| 	information */
 | |
| 	bpage = UT_LIST_GET_LAST(buf_pool.LRU);
 | |
| 
 | |
| 	while (bpage != NULL) {
 | |
| 		/* Use the same function that collect buffer info for
 | |
| 		INNODB_BUFFER_PAGE to get buffer page info */
 | |
| 		i_s_innodb_buffer_page_get_info(bpage, lru_pos,
 | |
| 						(info_buffer + lru_pos));
 | |
| 
 | |
| 		bpage = UT_LIST_GET_PREV(LRU, bpage);
 | |
| 
 | |
| 		lru_pos++;
 | |
| 	}
 | |
| 
 | |
| 	ut_ad(lru_pos == lru_len);
 | |
| 	ut_ad(lru_pos == UT_LIST_GET_LEN(buf_pool.LRU));
 | |
| 
 | |
| exit:
 | |
| 	mysql_mutex_unlock(&buf_pool.mutex);
 | |
| 
 | |
| 	if (info_buffer) {
 | |
| 		status = i_s_innodb_buf_page_lru_fill(
 | |
| 			thd, tables, info_buffer, lru_len);
 | |
| 
 | |
| 		my_free(info_buffer);
 | |
| 	}
 | |
| 
 | |
| 	DBUG_RETURN(status);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.INNODB_BUFFER_PAGE_LRU.
 | |
| @return 0 on success, 1 on failure */
 | |
| static
 | |
| int
 | |
| i_s_innodb_buffer_page_lru_init(
 | |
| /*============================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_innodb_buffer_page_lru_init");
 | |
| 
 | |
| 	schema = reinterpret_cast<ST_SCHEMA_TABLE*>(p);
 | |
| 
 | |
| 	schema->fields_info = Show::i_s_innodb_buf_page_lru_fields_info;
 | |
| 	schema->fill_table = i_s_innodb_fill_buffer_lru;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_buffer_page_lru =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_BUFFER_PAGE_LRU",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB Buffer Page in LRU",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_innodb_buffer_page_lru_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Unbind a dynamic INFORMATION_SCHEMA table.
 | |
| @return 0 */
 | |
| static int i_s_common_deinit(void*)
 | |
| {
 | |
| 	DBUG_ENTER("i_s_common_deinit");
 | |
| 
 | |
| 	/* Do nothing */
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| static const LEX_CSTRING row_format_values[] =
 | |
| {
 | |
|   { STRING_WITH_LEN("Redundant") },
 | |
|   { STRING_WITH_LEN("Compact") },
 | |
|   { STRING_WITH_LEN("Compressed") },
 | |
|   { STRING_WITH_LEN("Dynamic") }
 | |
| };
 | |
| 
 | |
| static TypelibBuffer<4> row_format_values_typelib(row_format_values);
 | |
| 
 | |
| static const LEX_CSTRING space_type_values[] =
 | |
| {
 | |
| 	{ STRING_WITH_LEN("Single") },
 | |
| 	{ STRING_WITH_LEN("System") }
 | |
| };
 | |
| 
 | |
| static TypelibBuffer<2> space_type_values_typelib(space_type_values);
 | |
| 
 | |
| namespace Show {
 | |
| /**  SYS_TABLES  ***************************************************/
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.SYS_TABLES */
 | |
| static ST_FIELD_INFO innodb_sys_tables_fields_info[]=
 | |
| {
 | |
| #define SYS_TABLES_ID			0
 | |
|   Column("TABLE_ID", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLES_NAME			1
 | |
|   Column("NAME", Varchar(MAX_FULL_NAME_LEN + 1), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLES_FLAG			2
 | |
|   Column("FLAG", SLong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLES_NUM_COLUMN		3
 | |
|   Column("N_COLS", ULong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLES_SPACE		4
 | |
|   Column("SPACE", ULong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLES_ROW_FORMAT		5
 | |
|   Column("ROW_FORMAT", Enum(&row_format_values_typelib), NULLABLE),
 | |
| 
 | |
| #define SYS_TABLES_ZIP_PAGE_SIZE	6
 | |
|   Column("ZIP_PAGE_SIZE", ULong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLES_SPACE_TYPE		7
 | |
|   Column("SPACE_TYPE", Enum(&space_type_values_typelib), NULLABLE),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /**********************************************************************//**
 | |
| Populate information_schema.innodb_sys_tables table with information
 | |
| from SYS_TABLES.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_dict_fill_sys_tables(
 | |
| /*=====================*/
 | |
| 	THD*		thd,		/*!< in: thread */
 | |
| 	dict_table_t*	table,		/*!< in: table */
 | |
| 	TABLE*		table_to_fill)	/*!< in/out: fill this table */
 | |
| {
 | |
| 	Field**			fields;
 | |
| 	ulint			compact = DICT_TF_GET_COMPACT(table->flags);
 | |
| 	ulint			atomic_blobs = DICT_TF_HAS_ATOMIC_BLOBS(
 | |
| 								table->flags);
 | |
| 	const ulint zip_size = dict_tf_get_zip_size(table->flags);
 | |
| 	const char*		row_format;
 | |
| 
 | |
| 	if (!compact) {
 | |
| 		row_format = "Redundant";
 | |
| 	} else if (!atomic_blobs) {
 | |
| 		row_format = "Compact";
 | |
| 	} else if (DICT_TF_GET_ZIP_SSIZE(table->flags)) {
 | |
| 		row_format = "Compressed";
 | |
| 	} else {
 | |
| 		row_format = "Dynamic";
 | |
| 	}
 | |
| 
 | |
| 	DBUG_ENTER("i_s_dict_fill_sys_tables");
 | |
| 
 | |
| 	fields = table_to_fill->field;
 | |
| 
 | |
| 	OK(fields[SYS_TABLES_ID]->store(longlong(table->id), TRUE));
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_TABLES_NAME], table->name.m_name));
 | |
| 
 | |
| 	OK(fields[SYS_TABLES_FLAG]->store(table->flags));
 | |
| 
 | |
| 	OK(fields[SYS_TABLES_NUM_COLUMN]->store(table->n_cols));
 | |
| 
 | |
| 	OK(fields[SYS_TABLES_SPACE]->store(table->space_id, true));
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_TABLES_ROW_FORMAT], row_format));
 | |
| 
 | |
| 	OK(fields[SYS_TABLES_ZIP_PAGE_SIZE]->store(zip_size, true));
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_TABLES_SPACE_TYPE],
 | |
| 			      table->space_id ? "Single" : "System"));
 | |
| 
 | |
| 	OK(schema_table_store_record(thd, table_to_fill));
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /** Handle the error for information schema query
 | |
| @param  err  error value
 | |
| @param  thd  thread
 | |
| @return 0 if query is interrupted or error */
 | |
| static int i_s_sys_error_handling(int err, THD *thd)
 | |
| {
 | |
|   return thd_kill_level(thd) ? 0 : err;
 | |
| }
 | |
| 
 | |
| /** Convert one SYS_TABLES record to dict_table_t.
 | |
| @param pcur      persistent cursor position on SYS_TABLES record
 | |
| @param mtr       mini-transaction (nullptr=use the dict_sys cache)
 | |
| @param rec       record to read from (nullptr=use the dict_sys cache)
 | |
| @param table     the converted dict_table_t
 | |
| @return error message
 | |
| @retval nullptr on success */
 | |
| static const char *i_s_sys_tables_rec(const btr_pcur_t &pcur, mtr_t *mtr,
 | |
|                                       const rec_t *rec, dict_table_t **table)
 | |
| {
 | |
|   static_assert(DICT_FLD__SYS_TABLES__NAME == 0, "compatibility");
 | |
|   size_t len;
 | |
|   if (rec_get_1byte_offs_flag(pcur.old_rec))
 | |
|   {
 | |
|     len= rec_1_get_field_end_info(pcur.old_rec, 0);
 | |
|     if (len & REC_1BYTE_SQL_NULL_MASK)
 | |
|       return "corrupted SYS_TABLES.NAME";
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     len= rec_2_get_field_end_info(pcur.old_rec, 0);
 | |
|     static_assert(REC_2BYTE_EXTERN_MASK == 16384, "compatibility");
 | |
|     if (len >= REC_2BYTE_EXTERN_MASK)
 | |
|       return "corrupted SYS_TABLES.NAME";
 | |
|   }
 | |
| 
 | |
|   if (rec)
 | |
|     return dict_load_table_low(mtr, false, rec, table);
 | |
| 
 | |
|   *table= dict_sys.load_table
 | |
|     (span<const char>{reinterpret_cast<const char*>(pcur.old_rec), len});
 | |
|   return *table ? nullptr : "Table not found in cache";
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Function to go through each record in SYS_TABLES table, and fill the
 | |
| information_schema.innodb_sys_tables table with related table information
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_sys_tables_fill_table(
 | |
| /*======================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (not used) */
 | |
| {
 | |
| 	btr_pcur_t	pcur;
 | |
| 	mtr_t		mtr;
 | |
| 	int		err = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_sys_tables_fill_table");
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* deny access to user without PROCESS_ACL privilege */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	mtr.start();
 | |
| 	dict_sys.lock(SRW_LOCK_CALL);
 | |
| 
 | |
| 	for (const rec_t *rec = dict_startscan_system(&pcur, &mtr,
 | |
| 						      dict_sys.sys_tables);
 | |
| 	     rec; rec = dict_getnext_system(&pcur, &mtr)) {
 | |
| 		if (rec_get_deleted_flag(rec, 0)) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		const char*	err_msg;
 | |
| 		dict_table_t*	table_rec;
 | |
| 
 | |
| 		/* Create and populate a dict_table_t structure with
 | |
| 		information from SYS_TABLES row */
 | |
| 		err_msg = i_s_sys_tables_rec(pcur, &mtr, rec, &table_rec);
 | |
| 		mtr.commit();
 | |
| 		dict_sys.unlock();
 | |
| 
 | |
| 		if (!err_msg) {
 | |
| 			err = i_s_dict_fill_sys_tables(
 | |
| 				thd, table_rec, tables->table);
 | |
| 			if (err) {
 | |
| 				err = i_s_sys_error_handling(err, thd);
 | |
| 				if (table_rec) {
 | |
| 					dict_mem_table_free(table_rec);
 | |
| 				}
 | |
| 				goto func_exit;
 | |
| 			}
 | |
| 		} else {
 | |
| 			push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
| 					    ER_CANT_FIND_SYSTEM_REC, "%s",
 | |
| 					    err_msg);
 | |
| 		}
 | |
| 
 | |
| 		if (table_rec) {
 | |
| 			dict_mem_table_free(table_rec);
 | |
| 		}
 | |
| 
 | |
| 		/* Get the next record */
 | |
| 		mtr.start();
 | |
| 		dict_sys.lock(SRW_LOCK_CALL);
 | |
| 	}
 | |
| 
 | |
| 	mtr.commit();
 | |
| 	dict_sys.unlock();
 | |
| func_exit:
 | |
| 	ut_free(pcur.old_rec_buf);
 | |
| 
 | |
| 	DBUG_RETURN(err);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_sys_tables
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_sys_tables_init(
 | |
| /*===================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_sys_tables_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_sys_tables_fields_info;
 | |
| 	schema->fill_table = i_s_sys_tables_fill_table;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_sys_tables =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_SYS_TABLES",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB SYS_TABLES",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_sys_tables_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /**  SYS_TABLESTATS  ***********************************************/
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.SYS_TABLESTATS */
 | |
| static ST_FIELD_INFO innodb_sys_tablestats_fields_info[]=
 | |
| {
 | |
| #define SYS_TABLESTATS_ID		0
 | |
|   Column("TABLE_ID", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESTATS_NAME		1
 | |
|   Column("NAME", Varchar(NAME_CHAR_LEN), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESTATS_INIT		2
 | |
|   Column("STATS_INITIALIZED", SLong(1), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESTATS_NROW		3
 | |
|   Column("NUM_ROWS", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESTATS_CLUST_SIZE	4
 | |
|   Column("CLUST_INDEX_SIZE", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESTATS_INDEX_SIZE	5
 | |
|   Column("OTHER_INDEX_SIZE", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESTATS_MODIFIED		6
 | |
|   Column("MODIFIED_COUNTER", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESTATS_AUTONINC		7
 | |
|   Column("AUTOINC", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESTATS_TABLE_REF_COUNT	8
 | |
|   Column("REF_COUNT", SLong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /** Populate information_schema.innodb_sys_tablestats table with a table,
 | |
| and release exclusive dict_sys.latch.
 | |
| @param[in]	thd		connection
 | |
| @param[in,out]	table		InnoDB table metadata
 | |
| @param[in,out]	table_to_fill	INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_dict_fill_sys_tablestats(THD* thd, dict_table_t *table,
 | |
|                              TABLE* table_to_fill)
 | |
| {
 | |
|   DBUG_ENTER("i_s_dict_fill_sys_tablestats");
 | |
| 
 | |
|   Field **fields= table_to_fill->field;
 | |
| 
 | |
|   {
 | |
|     table->stats_shared_lock();
 | |
|     auto _ = make_scope_exit([table]() {
 | |
|       table->stats_shared_unlock(); dict_sys.unlock(); });
 | |
| 
 | |
|     OK(fields[SYS_TABLESTATS_ID]->store(longlong(table->id), TRUE));
 | |
| 
 | |
|     OK(field_store_string(fields[SYS_TABLESTATS_NAME],
 | |
|                           table->name.m_name));
 | |
|     OK(fields[SYS_TABLESTATS_INIT]->store(table->stat_initialized(), true));
 | |
| 
 | |
|     if (table->stat_initialized())
 | |
|     {
 | |
|       OK(fields[SYS_TABLESTATS_NROW]->store(table->stat_n_rows, true));
 | |
| 
 | |
|       OK(fields[SYS_TABLESTATS_CLUST_SIZE]->
 | |
|          store(table->stat_clustered_index_size, true));
 | |
| 
 | |
|       OK(fields[SYS_TABLESTATS_INDEX_SIZE]->
 | |
|          store(table->stat_sum_of_other_index_sizes, true));
 | |
| 
 | |
|       OK(fields[SYS_TABLESTATS_MODIFIED]->
 | |
|          store(table->stat_modified_counter, true));
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       OK(fields[SYS_TABLESTATS_NROW]->store(0, true));
 | |
|       OK(fields[SYS_TABLESTATS_CLUST_SIZE]->store(0, true));
 | |
|       OK(fields[SYS_TABLESTATS_INDEX_SIZE]->store(0, true));
 | |
|       OK(fields[SYS_TABLESTATS_MODIFIED]->store(0, true));
 | |
|     }
 | |
| 
 | |
|     OK(fields[SYS_TABLESTATS_AUTONINC]->store(table->autoinc, true));
 | |
| 
 | |
|     OK(fields[SYS_TABLESTATS_TABLE_REF_COUNT]->
 | |
|        store(table->get_ref_count(), true));
 | |
|   }
 | |
| 
 | |
|   OK(schema_table_store_record(thd, table_to_fill));
 | |
|   DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Function to go through each record in SYS_TABLES table, and fill the
 | |
| information_schema.innodb_sys_tablestats table with table statistics
 | |
| related information
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_sys_tables_fill_table_stats(
 | |
| /*============================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (not used) */
 | |
| {
 | |
| 	btr_pcur_t	pcur;
 | |
| 	const rec_t*	rec;
 | |
| 	mtr_t		mtr;
 | |
| 	int		err = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_sys_tables_fill_table_stats");
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* deny access to user without PROCESS_ACL privilege */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	mtr.start();
 | |
| 	dict_sys.lock(SRW_LOCK_CALL);
 | |
| 
 | |
| 	rec = dict_startscan_system(&pcur, &mtr, dict_sys.sys_tables);
 | |
| 
 | |
| 	while (rec) {
 | |
| 		const char*	err_msg;
 | |
| 		dict_table_t*	table_rec = nullptr;
 | |
| 
 | |
| 		mtr.commit();
 | |
| 		/* Fetch the dict_table_t structure corresponding to
 | |
| 		this SYS_TABLES record */
 | |
| 		err_msg = i_s_sys_tables_rec(pcur, nullptr, nullptr,
 | |
| 					     &table_rec);
 | |
| 
 | |
| 		if (UNIV_LIKELY(!err_msg)) {
 | |
| 			err = i_s_dict_fill_sys_tablestats(thd, table_rec,
 | |
| 						     tables->table);
 | |
| 			if (err) {
 | |
| 				err = i_s_sys_error_handling(err, thd);
 | |
| 				goto func_exit;
 | |
| 			}
 | |
| 		} else {
 | |
| 			ut_ad(!table_rec);
 | |
| 			dict_sys.unlock();
 | |
| 			push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
| 					    ER_CANT_FIND_SYSTEM_REC, "%s",
 | |
| 					    err_msg);
 | |
| 		}
 | |
| 
 | |
| 		/* Get the next record */
 | |
| 		mtr.start();
 | |
| 		dict_sys.lock(SRW_LOCK_CALL);
 | |
| 
 | |
| 		rec = dict_getnext_system(&pcur, &mtr);
 | |
| 	}
 | |
| 
 | |
| 	mtr.commit();
 | |
| 	dict_sys.unlock();
 | |
| func_exit:
 | |
| 	ut_free(pcur.old_rec_buf);
 | |
| 	DBUG_RETURN(err);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_sys_tablestats
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_sys_tablestats_init(
 | |
| /*=======================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_sys_tablestats_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_sys_tablestats_fields_info;
 | |
| 	schema->fill_table = i_s_sys_tables_fill_table_stats;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_sys_tablestats =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_SYS_TABLESTATS",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB SYS_TABLESTATS",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_sys_tablestats_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /**  SYS_INDEXES  **************************************************/
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.SYS_INDEXES */
 | |
| static ST_FIELD_INFO innodb_sysindex_fields_info[]=
 | |
| {
 | |
| #define SYS_INDEX_ID		0
 | |
|   Column("INDEX_ID", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_INDEX_NAME		1
 | |
|   Column("NAME", Varchar(NAME_CHAR_LEN), NOT_NULL),
 | |
| 
 | |
| #define SYS_INDEX_TABLE_ID	2
 | |
|   Column("TABLE_ID", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_INDEX_TYPE		3
 | |
|   Column("TYPE", SLong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_INDEX_NUM_FIELDS	4
 | |
|   Column("N_FIELDS", SLong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_INDEX_PAGE_NO	5
 | |
|   Column("PAGE_NO", SLong(), NULLABLE),
 | |
| 
 | |
| #define SYS_INDEX_SPACE		6
 | |
|   Column("SPACE", SLong(), NULLABLE),
 | |
| 
 | |
| #define SYS_INDEX_MERGE_THRESHOLD 7
 | |
|   Column("MERGE_THRESHOLD", SLong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /**********************************************************************//**
 | |
| Function to populate the information_schema.innodb_sys_indexes table with
 | |
| collected index information
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_dict_fill_sys_indexes(
 | |
| /*======================*/
 | |
| 	THD*		thd,		/*!< in: thread */
 | |
| 	table_id_t	table_id,	/*!< in: table id */
 | |
| 	ulint		space_id,	/*!< in: tablespace id */
 | |
| 	dict_index_t*	index,		/*!< in: populated dict_index_t
 | |
| 					struct with index info */
 | |
| 	TABLE*		table_to_fill)	/*!< in/out: fill this table */
 | |
| {
 | |
| 	Field**		fields;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_dict_fill_sys_indexes");
 | |
| 
 | |
| 	fields = table_to_fill->field;
 | |
| 
 | |
| 	if (*index->name == *TEMP_INDEX_PREFIX_STR) {
 | |
| 		/* Since TEMP_INDEX_PREFIX_STR is not valid UTF-8, we
 | |
| 		need to convert it to something else. */
 | |
| 		*const_cast<char*>(index->name()) = '?';
 | |
| 	}
 | |
| 
 | |
| 	OK(fields[SYS_INDEX_NAME]->store(index->name,
 | |
| 					 uint(strlen(index->name)),
 | |
| 					 system_charset_info));
 | |
| 
 | |
| 	OK(fields[SYS_INDEX_ID]->store(longlong(index->id), true));
 | |
| 
 | |
| 	OK(fields[SYS_INDEX_TABLE_ID]->store(longlong(table_id), true));
 | |
| 
 | |
| 	OK(fields[SYS_INDEX_TYPE]->store(index->type, true));
 | |
| 
 | |
| 	OK(fields[SYS_INDEX_NUM_FIELDS]->store(index->n_fields));
 | |
| 
 | |
| 	/* FIL_NULL is ULINT32_UNDEFINED */
 | |
| 	if (index->page == FIL_NULL) {
 | |
| 		fields[SYS_INDEX_PAGE_NO]->set_null();
 | |
| 	} else {
 | |
| 		fields[SYS_INDEX_PAGE_NO]->set_notnull();
 | |
| 		OK(fields[SYS_INDEX_PAGE_NO]->store(index->page, true));
 | |
| 	}
 | |
| 
 | |
| 	if (space_id == FIL_NULL) {
 | |
| 		fields[SYS_INDEX_SPACE]->set_null();
 | |
| 	} else {
 | |
| 		fields[SYS_INDEX_SPACE]->set_notnull();
 | |
| 		OK(fields[SYS_INDEX_SPACE]->store(space_id, true));
 | |
| 	}
 | |
| 
 | |
| 	OK(fields[SYS_INDEX_MERGE_THRESHOLD]->store(index->merge_threshold,
 | |
| 						    true));
 | |
| 
 | |
| 	OK(schema_table_store_record(thd, table_to_fill));
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Function to go through each record in SYS_INDEXES table, and fill the
 | |
| information_schema.innodb_sys_indexes table with related index information
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_sys_indexes_fill_table(
 | |
| /*=======================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (not used) */
 | |
| {
 | |
| 	btr_pcur_t		pcur;
 | |
| 	const rec_t*		rec;
 | |
| 	mem_heap_t*		heap;
 | |
| 	mtr_t			mtr;
 | |
| 	int			err = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_sys_indexes_fill_table");
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* deny access to user without PROCESS_ACL privilege */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	heap = mem_heap_create(1000);
 | |
| 	dict_sys.lock(SRW_LOCK_CALL);
 | |
| 	mtr_start(&mtr);
 | |
| 
 | |
| 	/* Start scan the SYS_INDEXES table */
 | |
| 	rec = dict_startscan_system(&pcur, &mtr, dict_sys.sys_indexes);
 | |
| 
 | |
| 	/* Process each record in the table */
 | |
| 	while (rec) {
 | |
| 		const char*	err_msg;
 | |
| 		table_id_t	table_id;
 | |
| 		ulint		space_id;
 | |
| 		dict_index_t	index_rec;
 | |
| 
 | |
| 		/* Populate a dict_index_t structure with information from
 | |
| 		a SYS_INDEXES row */
 | |
| 		err_msg = dict_process_sys_indexes_rec(heap, rec, &index_rec,
 | |
| 						       &table_id);
 | |
| 		const byte* field = rec_get_nth_field_old(
 | |
| 			rec, DICT_FLD__SYS_INDEXES__SPACE, &space_id);
 | |
| 		space_id = space_id == 4 ? mach_read_from_4(field)
 | |
| 			: ULINT_UNDEFINED;
 | |
| 		mtr.commit();
 | |
| 		dict_sys.unlock();
 | |
| 
 | |
| 		if (!err_msg) {
 | |
| 			err = i_s_dict_fill_sys_indexes(
 | |
| 				    thd, table_id, space_id,
 | |
| 				    &index_rec,
 | |
| 				    tables->table);
 | |
| 			if (err) {
 | |
| 				err = i_s_sys_error_handling(err, thd);
 | |
| 				goto func_exit;
 | |
| 			}
 | |
| 		} else {
 | |
| 			push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
| 					    ER_CANT_FIND_SYSTEM_REC, "%s",
 | |
| 					    err_msg);
 | |
| 		}
 | |
| 
 | |
| 		mem_heap_empty(heap);
 | |
| 
 | |
| 		/* Get the next record */
 | |
| 		mtr.start();
 | |
| 		dict_sys.lock(SRW_LOCK_CALL);
 | |
| 		rec = dict_getnext_system(&pcur, &mtr);
 | |
| 	}
 | |
| 
 | |
| 	mtr.commit();
 | |
| 	dict_sys.unlock();
 | |
| func_exit:
 | |
| 	mem_heap_free(heap);
 | |
| 	ut_free(pcur.old_rec_buf);
 | |
| 
 | |
| 	DBUG_RETURN(err);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_sys_indexes
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_sys_indexes_init(
 | |
| /*====================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_sys_indexes_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_sysindex_fields_info;
 | |
| 	schema->fill_table = i_s_sys_indexes_fill_table;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_sys_indexes =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_SYS_INDEXES",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB SYS_INDEXES",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_sys_indexes_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /**  SYS_COLUMNS  **************************************************/
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_SYS_COLUMNS */
 | |
| static ST_FIELD_INFO innodb_sys_columns_fields_info[]=
 | |
| {
 | |
| #define SYS_COLUMN_TABLE_ID		0
 | |
|   Column("TABLE_ID", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_COLUMN_NAME		1
 | |
|   Column("NAME", Varchar(NAME_CHAR_LEN), NOT_NULL),
 | |
| 
 | |
| #define SYS_COLUMN_POSITION	2
 | |
|   Column("POS", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_COLUMN_MTYPE		3
 | |
|   Column("MTYPE", SLong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_COLUMN__PRTYPE	4
 | |
|   Column("PRTYPE", SLong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_COLUMN_COLUMN_LEN	5
 | |
|   Column("LEN", SLong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /**********************************************************************//**
 | |
| Function to populate the information_schema.innodb_sys_columns with
 | |
| related column information
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_dict_fill_sys_columns(
 | |
| /*======================*/
 | |
| 	THD*		thd,		/*!< in: thread */
 | |
| 	table_id_t	table_id,	/*!< in: table ID */
 | |
| 	const char*	col_name,	/*!< in: column name */
 | |
| 	dict_col_t*	column,		/*!< in: dict_col_t struct holding
 | |
| 					more column information */
 | |
| 	ulint		nth_v_col,	/*!< in: virtual column, its
 | |
| 					sequence number (nth virtual col) */
 | |
| 	TABLE*		table_to_fill)	/*!< in/out: fill this table */
 | |
| {
 | |
| 	Field**		fields;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_dict_fill_sys_columns");
 | |
| 
 | |
| 	fields = table_to_fill->field;
 | |
| 
 | |
| 	OK(fields[SYS_COLUMN_TABLE_ID]->store((longlong) table_id, TRUE));
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_COLUMN_NAME], col_name));
 | |
| 
 | |
| 	if (column->is_virtual()) {
 | |
| 		ulint	pos = dict_create_v_col_pos(nth_v_col, column->ind);
 | |
| 		OK(fields[SYS_COLUMN_POSITION]->store(pos, true));
 | |
| 	} else {
 | |
| 		OK(fields[SYS_COLUMN_POSITION]->store(column->ind, true));
 | |
| 	}
 | |
| 
 | |
| 	OK(fields[SYS_COLUMN_MTYPE]->store(column->mtype));
 | |
| 
 | |
| 	OK(fields[SYS_COLUMN__PRTYPE]->store(column->prtype));
 | |
| 
 | |
| 	OK(fields[SYS_COLUMN_COLUMN_LEN]->store(column->len));
 | |
| 
 | |
| 	OK(schema_table_store_record(thd, table_to_fill));
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Function to fill information_schema.innodb_sys_columns with information
 | |
| collected by scanning SYS_COLUMNS table.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_sys_columns_fill_table(
 | |
| /*=======================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (not used) */
 | |
| {
 | |
| 	btr_pcur_t	pcur;
 | |
| 	const rec_t*	rec;
 | |
| 	const char*	col_name;
 | |
| 	mem_heap_t*	heap;
 | |
| 	mtr_t		mtr;
 | |
| 	int		err = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_sys_columns_fill_table");
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* deny access to user without PROCESS_ACL privilege */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	heap = mem_heap_create(1000);
 | |
| 	mtr.start();
 | |
| 	dict_sys.lock(SRW_LOCK_CALL);
 | |
| 
 | |
| 	rec = dict_startscan_system(&pcur, &mtr, dict_sys.sys_columns);
 | |
| 
 | |
| 	while (rec) {
 | |
| 		const char*	err_msg;
 | |
| 		dict_col_t	column_rec;
 | |
| 		table_id_t	table_id;
 | |
| 		ulint		nth_v_col;
 | |
| 
 | |
| 		/* populate a dict_col_t structure with information from
 | |
| 		a SYS_COLUMNS row */
 | |
| 		err_msg = dict_process_sys_columns_rec(heap, rec, &column_rec,
 | |
| 						       &table_id, &col_name,
 | |
| 						       &nth_v_col);
 | |
| 
 | |
| 		mtr.commit();
 | |
| 		dict_sys.unlock();
 | |
| 
 | |
| 		if (!err_msg) {
 | |
| 			err = i_s_dict_fill_sys_columns(
 | |
| 				thd, table_id, col_name,
 | |
| 				&column_rec, nth_v_col,
 | |
| 				tables->table);
 | |
| 			if (err) {
 | |
| 				err = i_s_sys_error_handling(err, thd);
 | |
| 				goto func_exit;
 | |
| 			}
 | |
| 		} else {
 | |
| 			push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
| 					    ER_CANT_FIND_SYSTEM_REC, "%s",
 | |
| 					    err_msg);
 | |
| 		}
 | |
| 
 | |
| 		mem_heap_empty(heap);
 | |
| 
 | |
| 		/* Get the next record */
 | |
| 		mtr.start();
 | |
| 		dict_sys.lock(SRW_LOCK_CALL);
 | |
| 		rec = dict_getnext_system(&pcur, &mtr);
 | |
| 	}
 | |
| 
 | |
| 	mtr.commit();
 | |
| 	dict_sys.unlock();
 | |
| func_exit:
 | |
| 	mem_heap_free(heap);
 | |
| 	ut_free(pcur.old_rec_buf);
 | |
| 
 | |
| 	DBUG_RETURN(err);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_sys_columns
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_sys_columns_init(
 | |
| /*====================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_sys_columns_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_sys_columns_fields_info;
 | |
| 	schema->fill_table = i_s_sys_columns_fill_table;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_sys_columns =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_SYS_COLUMNS",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB SYS_COLUMNS",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_sys_columns_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /**  SYS_VIRTUAL **************************************************/
 | |
| /** Fields of the dynamic table INFORMATION_SCHEMA.INNODB_SYS_VIRTUAL */
 | |
| static ST_FIELD_INFO innodb_sys_virtual_fields_info[]=
 | |
| {
 | |
| #define SYS_VIRTUAL_TABLE_ID		0
 | |
|   Column("TABLE_ID", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_VIRTUAL_POS			1
 | |
|   Column("POS", ULong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_VIRTUAL_BASE_POS		2
 | |
|   Column("BASE_POS", ULong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /** Function to populate the information_schema.innodb_sys_virtual with
 | |
| related information
 | |
| param[in]	thd		thread
 | |
| param[in]	table_id	table ID
 | |
| param[in]	pos		virtual column position
 | |
| param[in]	base_pos	base column position
 | |
| param[in,out]	table_to_fill	fill this table
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_dict_fill_sys_virtual(
 | |
| 	THD*		thd,
 | |
| 	table_id_t	table_id,
 | |
| 	ulint		pos,
 | |
| 	ulint		base_pos,
 | |
| 	TABLE*		table_to_fill)
 | |
| {
 | |
| 	Field**		fields;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_dict_fill_sys_virtual");
 | |
| 
 | |
| 	fields = table_to_fill->field;
 | |
| 
 | |
| 	OK(fields[SYS_VIRTUAL_TABLE_ID]->store(table_id, true));
 | |
| 
 | |
| 	OK(fields[SYS_VIRTUAL_POS]->store(pos, true));
 | |
| 
 | |
| 	OK(fields[SYS_VIRTUAL_BASE_POS]->store(base_pos, true));
 | |
| 
 | |
| 	OK(schema_table_store_record(thd, table_to_fill));
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /** Function to fill information_schema.innodb_sys_virtual with information
 | |
| collected by scanning SYS_VIRTUAL table.
 | |
| param[in]	thd		thread
 | |
| param[in,out]	tables		tables to fill
 | |
| param[in]	item		condition (not used)
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_sys_virtual_fill_table(
 | |
| 	THD*		thd,
 | |
| 	TABLE_LIST*	tables,
 | |
| 	Item*		)
 | |
| {
 | |
| 	btr_pcur_t	pcur;
 | |
| 	const rec_t*	rec;
 | |
| 	ulint		pos;
 | |
| 	ulint		base_pos;
 | |
| 	mtr_t		mtr;
 | |
| 	int		err = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_sys_virtual_fill_table");
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* deny access to user without PROCESS_ACL privilege */
 | |
| 	if (check_global_access(thd, PROCESS_ACL) || !dict_sys.sys_virtual) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	mtr.start();
 | |
| 	dict_sys.lock(SRW_LOCK_CALL);
 | |
| 
 | |
| 	rec = dict_startscan_system(&pcur, &mtr, dict_sys.sys_virtual);
 | |
| 
 | |
| 	while (rec) {
 | |
| 		const char*	err_msg;
 | |
| 		table_id_t	table_id;
 | |
| 
 | |
| 		/* populate a dict_col_t structure with information from
 | |
| 		a SYS_VIRTUAL row */
 | |
| 		err_msg = dict_process_sys_virtual_rec(rec,
 | |
| 						       &table_id, &pos,
 | |
| 						       &base_pos);
 | |
| 
 | |
| 		mtr.commit();
 | |
| 		dict_sys.unlock();
 | |
| 
 | |
| 		if (!err_msg) {
 | |
| 			err = i_s_dict_fill_sys_virtual(
 | |
| 				thd, table_id, pos, base_pos,
 | |
| 				tables->table);
 | |
| 			if (err) {
 | |
| 				err = i_s_sys_error_handling(err, thd);
 | |
| 				goto func_exit;
 | |
| 			}
 | |
| 		} else {
 | |
| 			push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
| 					    ER_CANT_FIND_SYSTEM_REC, "%s",
 | |
| 					    err_msg);
 | |
| 		}
 | |
| 
 | |
| 		/* Get the next record */
 | |
| 		mtr.start();
 | |
| 		dict_sys.lock(SRW_LOCK_CALL);
 | |
| 		rec = dict_getnext_system(&pcur, &mtr);
 | |
| 	}
 | |
| 
 | |
| 	mtr.commit();
 | |
| 	dict_sys.unlock();
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| func_exit:
 | |
| 	ut_free(pcur.old_rec_buf);
 | |
| 	DBUG_RETURN(err);
 | |
| }
 | |
| 
 | |
| /** Bind the dynamic table INFORMATION_SCHEMA.innodb_sys_virtual
 | |
| param[in,out]	p	table schema object
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_sys_virtual_init(
 | |
| 	void*	p)
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_sys_virtual_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_sys_virtual_fields_info;
 | |
| 	schema->fill_table = i_s_sys_virtual_fill_table;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_sys_virtual =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_SYS_VIRTUAL",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB SYS_VIRTUAL",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_sys_virtual_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| 
 | |
| namespace Show {
 | |
| /**  SYS_FIELDS  ***************************************************/
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_SYS_FIELDS */
 | |
| static ST_FIELD_INFO innodb_sys_fields_fields_info[]=
 | |
| {
 | |
| #define SYS_FIELD_INDEX_ID	0
 | |
|   Column("INDEX_ID", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_FIELD_NAME		1
 | |
|   Column("NAME", Varchar(NAME_CHAR_LEN), NOT_NULL),
 | |
| 
 | |
| #define SYS_FIELD_POS		2
 | |
|   Column("POS", ULong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /**********************************************************************//**
 | |
| Function to fill information_schema.innodb_sys_fields with information
 | |
| collected by scanning SYS_FIELDS table.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_dict_fill_sys_fields(
 | |
| /*=====================*/
 | |
| 	THD*		thd,		/*!< in: thread */
 | |
| 	index_id_t	index_id,	/*!< in: index id for the field */
 | |
| 	dict_field_t*	field,		/*!< in: table */
 | |
| 	ulint		pos,		/*!< in: Field position */
 | |
| 	TABLE*		table_to_fill)	/*!< in/out: fill this table */
 | |
| {
 | |
| 	Field**		fields;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_dict_fill_sys_fields");
 | |
| 
 | |
| 	fields = table_to_fill->field;
 | |
| 
 | |
| 	OK(fields[SYS_FIELD_INDEX_ID]->store(index_id, true));
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_FIELD_NAME], field->name));
 | |
| 
 | |
| 	OK(fields[SYS_FIELD_POS]->store(pos, true));
 | |
| 
 | |
| 	OK(schema_table_store_record(thd, table_to_fill));
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Function to go through each record in SYS_FIELDS table, and fill the
 | |
| information_schema.innodb_sys_fields table with related index field
 | |
| information
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_sys_fields_fill_table(
 | |
| /*======================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (not used) */
 | |
| {
 | |
| 	btr_pcur_t	pcur;
 | |
| 	const rec_t*	rec;
 | |
| 	mem_heap_t*	heap;
 | |
| 	index_id_t	last_id;
 | |
| 	mtr_t		mtr;
 | |
| 	int		err = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_sys_fields_fill_table");
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* deny access to user without PROCESS_ACL privilege */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	heap = mem_heap_create(1000);
 | |
| 	mtr.start();
 | |
| 
 | |
| 	/* will save last index id so that we know whether we move to
 | |
| 	the next index. This is used to calculate prefix length */
 | |
| 	last_id = 0;
 | |
| 
 | |
| 	dict_sys.lock(SRW_LOCK_CALL);
 | |
| 	rec = dict_startscan_system(&pcur, &mtr, dict_sys.sys_fields);
 | |
| 
 | |
| 	while (rec) {
 | |
| 		ulint		pos;
 | |
| 		const char*	err_msg;
 | |
| 		index_id_t	index_id;
 | |
| 		dict_field_t	field_rec;
 | |
| 
 | |
| 		/* Populate a dict_field_t structure with information from
 | |
| 		a SYS_FIELDS row */
 | |
| 		err_msg = dict_process_sys_fields_rec(heap, rec, &field_rec,
 | |
| 						      &pos, &index_id, last_id);
 | |
| 
 | |
| 		mtr.commit();
 | |
| 		dict_sys.unlock();
 | |
| 
 | |
| 		if (!err_msg) {
 | |
| 			err = i_s_dict_fill_sys_fields(
 | |
| 				thd, index_id, &field_rec,
 | |
| 				pos, tables->table);
 | |
| 			if (err) {
 | |
| 				err = i_s_sys_error_handling(err, thd);
 | |
| 				goto func_exit;
 | |
| 			}
 | |
| 			last_id = index_id;
 | |
| 		} else {
 | |
| 			push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
| 					    ER_CANT_FIND_SYSTEM_REC, "%s",
 | |
| 					    err_msg);
 | |
| 		}
 | |
| 
 | |
| 		mem_heap_empty(heap);
 | |
| 
 | |
| 		/* Get the next record */
 | |
| 		mtr.start();
 | |
| 		dict_sys.lock(SRW_LOCK_CALL);
 | |
| 		rec = dict_getnext_system(&pcur, &mtr);
 | |
| 	}
 | |
| 
 | |
| 	mtr.commit();
 | |
| 	dict_sys.unlock();
 | |
| func_exit:
 | |
| 	mem_heap_free(heap);
 | |
| 	ut_free(pcur.old_rec_buf);
 | |
| 
 | |
| 	DBUG_RETURN(err);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_sys_fields
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_sys_fields_init(
 | |
| /*===================*/
 | |
| 	void*   p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_sys_field_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_sys_fields_fields_info;
 | |
| 	schema->fill_table = i_s_sys_fields_fill_table;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_sys_fields =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_SYS_FIELDS",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB SYS_FIELDS",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_sys_fields_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /**  SYS_FOREIGN        ********************************************/
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_SYS_FOREIGN */
 | |
| static ST_FIELD_INFO innodb_sys_foreign_fields_info[]=
 | |
| {
 | |
| #define SYS_FOREIGN_ID		0
 | |
|   Column("ID", Varchar(NAME_LEN + 1), NOT_NULL),
 | |
| 
 | |
| #define SYS_FOREIGN_FOR_NAME	1
 | |
|   Column("FOR_NAME", Varchar(NAME_LEN + 1), NOT_NULL),
 | |
| 
 | |
| #define SYS_FOREIGN_REF_NAME	2
 | |
|   Column("REF_NAME", Varchar(NAME_LEN + 1), NOT_NULL),
 | |
| 
 | |
| #define SYS_FOREIGN_NUM_COL	3
 | |
|   Column("N_COLS", ULong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_FOREIGN_TYPE	4
 | |
|   Column("TYPE", ULong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /**********************************************************************//**
 | |
| Function to fill information_schema.innodb_sys_foreign with information
 | |
| collected by scanning SYS_FOREIGN table.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_dict_fill_sys_foreign(
 | |
| /*======================*/
 | |
| 	THD*		thd,		/*!< in: thread */
 | |
| 	dict_foreign_t*	foreign,	/*!< in: table */
 | |
| 	TABLE*		table_to_fill)	/*!< in/out: fill this table */
 | |
| {
 | |
| 	Field**		fields;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_dict_fill_sys_foreign");
 | |
| 
 | |
| 	fields = table_to_fill->field;
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_FOREIGN_ID], foreign->id));
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_FOREIGN_FOR_NAME],
 | |
| 			      foreign->foreign_table_name));
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_FOREIGN_REF_NAME],
 | |
| 			      foreign->referenced_table_name));
 | |
| 
 | |
| 	OK(fields[SYS_FOREIGN_NUM_COL]->store(foreign->n_fields));
 | |
| 
 | |
| 	OK(fields[SYS_FOREIGN_TYPE]->store(foreign->type));
 | |
| 
 | |
| 	OK(schema_table_store_record(thd, table_to_fill));
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Function to populate INFORMATION_SCHEMA.innodb_sys_foreign table. Loop
 | |
| through each record in SYS_FOREIGN, and extract the foreign key
 | |
| information.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_sys_foreign_fill_table(
 | |
| /*=======================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (not used) */
 | |
| {
 | |
| 	btr_pcur_t	pcur;
 | |
| 	const rec_t*	rec;
 | |
| 	mem_heap_t*	heap;
 | |
| 	mtr_t		mtr;
 | |
| 	int		err = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_sys_foreign_fill_table");
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* deny access to user without PROCESS_ACL privilege */
 | |
| 	if (check_global_access(thd, PROCESS_ACL) || !dict_sys.sys_foreign) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	heap = mem_heap_create(1000);
 | |
| 	mtr.start();
 | |
| 	dict_sys.lock(SRW_LOCK_CALL);
 | |
| 
 | |
| 	rec = dict_startscan_system(&pcur, &mtr, dict_sys.sys_foreign);
 | |
| 
 | |
| 	while (rec) {
 | |
| 		const char*	err_msg;
 | |
| 		dict_foreign_t	foreign_rec;
 | |
| 
 | |
| 		/* Populate a dict_foreign_t structure with information from
 | |
| 		a SYS_FOREIGN row */
 | |
| 		err_msg = dict_process_sys_foreign_rec(heap, rec, &foreign_rec);
 | |
| 
 | |
| 		mtr.commit();
 | |
| 		dict_sys.unlock();
 | |
| 
 | |
| 		if (!err_msg) {
 | |
| 			err = i_s_dict_fill_sys_foreign(
 | |
| 				thd, &foreign_rec, tables->table);
 | |
| 			if (err) {
 | |
| 				err = i_s_sys_error_handling(err, thd);
 | |
| 				goto func_exit;
 | |
| 			}
 | |
| 		} else {
 | |
| 			push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
| 					    ER_CANT_FIND_SYSTEM_REC, "%s",
 | |
| 					    err_msg);
 | |
| 		}
 | |
| 
 | |
| 		mem_heap_empty(heap);
 | |
| 
 | |
| 		/* Get the next record */
 | |
| 		mtr.start();
 | |
| 		dict_sys.lock(SRW_LOCK_CALL);
 | |
| 		rec = dict_getnext_system(&pcur, &mtr);
 | |
| 	}
 | |
| 
 | |
| 	mtr.commit();
 | |
| 	dict_sys.unlock();
 | |
| func_exit:
 | |
| 	mem_heap_free(heap);
 | |
| 	ut_free(pcur.old_rec_buf);
 | |
| 
 | |
| 	DBUG_RETURN(err);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_sys_foreign
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_sys_foreign_init(
 | |
| /*====================*/
 | |
| 	void*   p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_sys_foreign_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_sys_foreign_fields_info;
 | |
| 	schema->fill_table = i_s_sys_foreign_fill_table;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_sys_foreign =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_SYS_FOREIGN",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB SYS_FOREIGN",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_sys_foreign_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /**  SYS_FOREIGN_COLS   ********************************************/
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_SYS_FOREIGN_COLS */
 | |
| static ST_FIELD_INFO innodb_sys_foreign_cols_fields_info[]=
 | |
| {
 | |
| #define SYS_FOREIGN_COL_ID		0
 | |
|   Column("ID", Varchar(NAME_LEN + 1), NOT_NULL),
 | |
| 
 | |
| #define SYS_FOREIGN_COL_FOR_NAME	1
 | |
|   Column("FOR_COL_NAME", Varchar(NAME_CHAR_LEN), NOT_NULL),
 | |
| 
 | |
| #define SYS_FOREIGN_COL_REF_NAME	2
 | |
|   Column("REF_COL_NAME", Varchar(NAME_CHAR_LEN), NOT_NULL),
 | |
| 
 | |
| #define SYS_FOREIGN_COL_POS		3
 | |
|   Column("POS", ULong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /**********************************************************************//**
 | |
| Function to fill information_schema.innodb_sys_foreign_cols with information
 | |
| collected by scanning SYS_FOREIGN_COLS table.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_dict_fill_sys_foreign_cols(
 | |
| /*==========================*/
 | |
| 	THD*		thd,		/*!< in: thread */
 | |
| 	const char*	name,		/*!< in: foreign key constraint name */
 | |
| 	const char*	for_col_name,	/*!< in: referencing column name*/
 | |
| 	const char*	ref_col_name,	/*!< in: referenced column
 | |
| 					name */
 | |
| 	ulint		pos,		/*!< in: column position */
 | |
| 	TABLE*		table_to_fill)	/*!< in/out: fill this table */
 | |
| {
 | |
| 	Field**		fields;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_dict_fill_sys_foreign_cols");
 | |
| 
 | |
| 	fields = table_to_fill->field;
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_FOREIGN_COL_ID], name));
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_FOREIGN_COL_FOR_NAME], for_col_name));
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_FOREIGN_COL_REF_NAME], ref_col_name));
 | |
| 
 | |
| 	OK(fields[SYS_FOREIGN_COL_POS]->store(pos, true));
 | |
| 
 | |
| 	OK(schema_table_store_record(thd, table_to_fill));
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Function to populate INFORMATION_SCHEMA.innodb_sys_foreign_cols table. Loop
 | |
| through each record in SYS_FOREIGN_COLS, and extract the foreign key column
 | |
| information and fill the INFORMATION_SCHEMA.innodb_sys_foreign_cols table.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_sys_foreign_cols_fill_table(
 | |
| /*============================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (not used) */
 | |
| {
 | |
| 	btr_pcur_t	pcur;
 | |
| 	const rec_t*	rec;
 | |
| 	mem_heap_t*	heap;
 | |
| 	mtr_t		mtr;
 | |
| 	int		err = 0;
 | |
| 
 | |
| 	DBUG_ENTER("i_s_sys_foreign_cols_fill_table");
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* deny access to user without PROCESS_ACL privilege */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)
 | |
| 	    || !dict_sys.sys_foreign_cols) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	heap = mem_heap_create(1000);
 | |
| 	mtr.start();
 | |
| 	dict_sys.lock(SRW_LOCK_CALL);
 | |
| 
 | |
| 	rec = dict_startscan_system(&pcur, &mtr, dict_sys.sys_foreign_cols);
 | |
| 
 | |
| 	while (rec) {
 | |
| 		const char*	err_msg;
 | |
| 		const char*	name;
 | |
| 		const char*	for_col_name;
 | |
| 		const char*	ref_col_name;
 | |
| 		ulint		pos;
 | |
| 
 | |
| 		/* Extract necessary information from a SYS_FOREIGN_COLS row */
 | |
| 		err_msg = dict_process_sys_foreign_col_rec(
 | |
| 			heap, rec, &name, &for_col_name, &ref_col_name, &pos);
 | |
| 
 | |
| 		mtr.commit();
 | |
| 		dict_sys.unlock();
 | |
| 
 | |
| 		if (!err_msg) {
 | |
| 			err = i_s_dict_fill_sys_foreign_cols(
 | |
| 				thd, name, for_col_name,
 | |
| 				ref_col_name, pos, tables->table);
 | |
| 			if (err) {
 | |
| 				err = i_s_sys_error_handling(err, thd);
 | |
| 				goto func_exit;
 | |
| 			}
 | |
| 		} else {
 | |
| 			push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
| 					    ER_CANT_FIND_SYSTEM_REC, "%s",
 | |
| 					    err_msg);
 | |
| 		}
 | |
| 
 | |
| 		mem_heap_empty(heap);
 | |
| 
 | |
| 		/* Get the next record */
 | |
| 		mtr.start();
 | |
| 		dict_sys.lock(SRW_LOCK_CALL);
 | |
| 		rec = dict_getnext_system(&pcur, &mtr);
 | |
| 	}
 | |
| 
 | |
| 	mtr.commit();
 | |
| 	dict_sys.unlock();
 | |
| func_exit:
 | |
| 	mem_heap_free(heap);
 | |
| 	ut_free(pcur.old_rec_buf);
 | |
| 
 | |
| 	DBUG_RETURN(err);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.innodb_sys_foreign_cols
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_sys_foreign_cols_init(
 | |
| /*========================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_sys_foreign_cols_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_sys_foreign_cols_fields_info;
 | |
| 	schema->fill_table = i_s_sys_foreign_cols_fill_table;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_sys_foreign_cols =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_SYS_FOREIGN_COLS",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB SYS_FOREIGN_COLS",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_sys_foreign_cols_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /**  SYS_TABLESPACES    ********************************************/
 | |
| /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES */
 | |
| static ST_FIELD_INFO innodb_sys_tablespaces_fields_info[]=
 | |
| {
 | |
| #define SYS_TABLESPACES_SPACE		0
 | |
|   Column("SPACE", ULong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESPACES_NAME		1
 | |
|   Column("NAME", Varchar(MAX_FULL_NAME_LEN + 1), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESPACES_FLAGS		2
 | |
|   Column("FLAG", ULong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESPACES_ROW_FORMAT	3
 | |
|   Column("ROW_FORMAT", Varchar(22), NULLABLE),
 | |
| 
 | |
| #define SYS_TABLESPACES_PAGE_SIZE	4
 | |
|   Column("PAGE_SIZE", ULong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESPACES_FILENAME	5
 | |
|   Column("FILENAME", Varchar(FN_REFLEN), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESPACES_FS_BLOCK_SIZE	6
 | |
|   Column("FS_BLOCK_SIZE", ULong(),NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESPACES_FILE_SIZE	7
 | |
|   Column("FILE_SIZE", ULonglong(), NOT_NULL),
 | |
| 
 | |
| #define SYS_TABLESPACES_ALLOC_SIZE	8
 | |
|   Column("ALLOCATED_SIZE", ULonglong(), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| extern size_t os_file_get_fs_block_size(const char *path);
 | |
| 
 | |
| /** Produce one row of INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES.
 | |
| @param thd  connection
 | |
| @param s    tablespace
 | |
| @param t    output table
 | |
| @return 0 on success */
 | |
| static int i_s_sys_tablespaces_fill(THD *thd, const fil_space_t &s, TABLE *t)
 | |
| {
 | |
|   DBUG_ENTER("i_s_sys_tablespaces_fill");
 | |
|   const char *row_format;
 | |
| 
 | |
|   if (s.full_crc32() || is_system_tablespace(s.id))
 | |
|     row_format= nullptr;
 | |
|   else if (FSP_FLAGS_GET_ZIP_SSIZE(s.flags))
 | |
|     row_format= "Compressed";
 | |
|   else if (FSP_FLAGS_HAS_ATOMIC_BLOBS(s.flags))
 | |
|     row_format= "Dynamic";
 | |
|   else
 | |
|     row_format= "Compact or Redundant";
 | |
| 
 | |
|   Field **fields= t->field;
 | |
| 
 | |
|   OK(fields[SYS_TABLESPACES_SPACE]->store(s.id, true));
 | |
|   {
 | |
|     Field *f= fields[SYS_TABLESPACES_NAME];
 | |
|     const auto name= s.name();
 | |
|     if (name.data())
 | |
|     {
 | |
|       OK(f->store(name.data(), name.size(), system_charset_info));
 | |
|       f->set_notnull();
 | |
|     }
 | |
|     else if (srv_is_undo_tablespace(s.id))
 | |
|     {
 | |
|       char name[15];
 | |
|       snprintf(name, sizeof name, "innodb_undo%03u",
 | |
|                (s.id - srv_undo_space_id_start + 1));
 | |
|       OK(f->store(name, strlen(name), system_charset_info));
 | |
|     } else f->set_notnull();
 | |
|   }
 | |
| 
 | |
|   fields[SYS_TABLESPACES_NAME]->set_null();
 | |
|   OK(fields[SYS_TABLESPACES_FLAGS]->store(s.flags, true));
 | |
|   OK(field_store_string(fields[SYS_TABLESPACES_ROW_FORMAT], row_format));
 | |
|   const char *filepath= s.chain.start->name;
 | |
|   OK(field_store_string(fields[SYS_TABLESPACES_FILENAME], filepath));
 | |
| 
 | |
|   OK(fields[SYS_TABLESPACES_PAGE_SIZE]->store(s.physical_size(), true));
 | |
|   size_t fs_block_size;
 | |
|   os_file_size_t file= os_file_get_size(filepath);
 | |
|   if (file.m_total_size == os_offset_t(~0))
 | |
|   {
 | |
|     file.m_total_size= 0;
 | |
|     file.m_alloc_size= 0;
 | |
|     fs_block_size= 0;
 | |
|   }
 | |
|   else
 | |
|     fs_block_size= os_file_get_fs_block_size(filepath);
 | |
| 
 | |
|   OK(fields[SYS_TABLESPACES_FS_BLOCK_SIZE]->store(fs_block_size, true));
 | |
|   OK(fields[SYS_TABLESPACES_FILE_SIZE]->store(file.m_total_size, true));
 | |
|   OK(fields[SYS_TABLESPACES_ALLOC_SIZE]->store(file.m_alloc_size, true));
 | |
| 
 | |
|   OK(schema_table_store_record(thd, t));
 | |
| 
 | |
|   DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| /** Populate INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES.
 | |
| @param thd    connection
 | |
| @param tables table to fill
 | |
| @return 0 on success */
 | |
| static int i_s_sys_tablespaces_fill_table(THD *thd, TABLE_LIST *tables, Item*)
 | |
| {
 | |
|   DBUG_ENTER("i_s_sys_tablespaces_fill_table");
 | |
|   RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
|   if (check_global_access(thd, PROCESS_ACL))
 | |
|     DBUG_RETURN(0);
 | |
| 
 | |
|   int err= 0;
 | |
| 
 | |
|   mysql_mutex_lock(&fil_system.mutex);
 | |
|   fil_system.freeze_space_list++;
 | |
| 
 | |
|   for (fil_space_t &space : fil_system.space_list)
 | |
|   {
 | |
|     if (!space.is_temporary() && !space.is_being_imported() &&
 | |
|         !space.is_stopping() &&
 | |
|         space.chain.start)
 | |
|     {
 | |
|       space.reacquire();
 | |
|       mysql_mutex_unlock(&fil_system.mutex);
 | |
|       space.s_lock();
 | |
|       err= i_s_sys_tablespaces_fill(thd, space, tables->table);
 | |
|       space.s_unlock();
 | |
|       mysql_mutex_lock(&fil_system.mutex);
 | |
|       space.release();
 | |
|       if (err)
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   fil_system.freeze_space_list--;
 | |
|   mysql_mutex_unlock(&fil_system.mutex);
 | |
|   if (err == DB_SUCCESS)
 | |
|     err= i_s_sys_tablespaces_fill(thd, *fil_system.temp_space, tables->table);
 | |
|   else
 | |
|     err = i_s_sys_error_handling(err, thd);
 | |
|   DBUG_RETURN(err);
 | |
| }
 | |
| 
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_sys_tablespaces_init(
 | |
| /*========================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_sys_tablespaces_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_sys_tablespaces_fields_info;
 | |
| 	schema->fill_table = i_s_sys_tablespaces_fill_table;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_sys_tablespaces =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_SYS_TABLESPACES",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	plugin_author,
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB tablespaces",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_GPL,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_sys_tablespaces_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | |
| 
 | |
| namespace Show {
 | |
| /**  TABLESPACES_ENCRYPTION    ********************************************/
 | |
| /* Fields of the table INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION */
 | |
| static ST_FIELD_INFO innodb_tablespaces_encryption_fields_info[]=
 | |
| {
 | |
| #define TABLESPACES_ENCRYPTION_SPACE	0
 | |
|   Column("SPACE", ULong(), NOT_NULL),
 | |
| 
 | |
| #define TABLESPACES_ENCRYPTION_NAME		1
 | |
|   Column("NAME", Varchar(MAX_FULL_NAME_LEN + 1), NULLABLE),
 | |
| 
 | |
| #define TABLESPACES_ENCRYPTION_ENCRYPTION_SCHEME	2
 | |
|   Column("ENCRYPTION_SCHEME", ULong(), NOT_NULL),
 | |
| 
 | |
| #define TABLESPACES_ENCRYPTION_KEYSERVER_REQUESTS	3
 | |
|   Column("KEYSERVER_REQUESTS", ULong(), NOT_NULL),
 | |
| 
 | |
| #define TABLESPACES_ENCRYPTION_MIN_KEY_VERSION	4
 | |
|   Column("MIN_KEY_VERSION", ULong(), NOT_NULL),
 | |
| 
 | |
| #define TABLESPACES_ENCRYPTION_CURRENT_KEY_VERSION	5
 | |
|   Column("CURRENT_KEY_VERSION", ULong(), NOT_NULL),
 | |
| 
 | |
| #define TABLESPACES_ENCRYPTION_KEY_ROTATION_PAGE_NUMBER	6
 | |
|   Column("KEY_ROTATION_PAGE_NUMBER", ULonglong(), NULLABLE),
 | |
| 
 | |
| #define TABLESPACES_ENCRYPTION_KEY_ROTATION_MAX_PAGE_NUMBER 7
 | |
|   Column("KEY_ROTATION_MAX_PAGE_NUMBER", ULonglong(), NULLABLE),
 | |
| 
 | |
| #define TABLESPACES_ENCRYPTION_CURRENT_KEY_ID	8
 | |
|   Column("CURRENT_KEY_ID", ULong(), NOT_NULL),
 | |
| 
 | |
| #define TABLESPACES_ENCRYPTION_ROTATING_OR_FLUSHING 9
 | |
|   Column("ROTATING_OR_FLUSHING", SLong(1), NOT_NULL),
 | |
| 
 | |
|   CEnd()
 | |
| };
 | |
| } // namespace Show
 | |
| 
 | |
| /**********************************************************************//**
 | |
| Function to fill INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION.
 | |
| @param[in]	thd		thread handle
 | |
| @param[in]	space		Tablespace
 | |
| @param[in]	table_to_fill	I_S table to fill
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_dict_fill_tablespaces_encryption(
 | |
| 	THD*		thd,
 | |
| 	fil_space_t*	space,
 | |
| 	TABLE*		table_to_fill)
 | |
| {
 | |
| 	Field**	fields;
 | |
| 	struct fil_space_crypt_status_t status;
 | |
| 	DBUG_ENTER("i_s_dict_fill_tablespaces_encryption");
 | |
| 
 | |
| 	fields = table_to_fill->field;
 | |
| 
 | |
| 	fil_space_crypt_get_status(space, &status);
 | |
| 
 | |
| 	/* If tablespace id does not match, we did not find
 | |
| 	encryption information for this tablespace. */
 | |
| 	if (!space->crypt_data || space->id != status.space) {
 | |
| 		goto skip;
 | |
| 	}
 | |
| 
 | |
| 	OK(fields[TABLESPACES_ENCRYPTION_SPACE]->store(space->id, true));
 | |
| 
 | |
| 	{
 | |
| 		const auto name = space->name();
 | |
| 		if (name.data()) {
 | |
| 			OK(fields[TABLESPACES_ENCRYPTION_NAME]->store(
 | |
| 				   name.data(), name.size(),
 | |
| 				   system_charset_info));
 | |
| 			fields[TABLESPACES_ENCRYPTION_NAME]->set_notnull();
 | |
| 		} else if (srv_is_undo_tablespace(space->id)) {
 | |
| 			char undo_name[sizeof "innodb_undo000"];
 | |
| 			snprintf(undo_name, sizeof undo_name,
 | |
| 				 "innodb_undo%03" PRIu32, space->id);
 | |
| 			OK(fields[TABLESPACES_ENCRYPTION_NAME]->store(
 | |
| 				   undo_name, strlen(undo_name),
 | |
| 				   system_charset_info));
 | |
| 			fields[TABLESPACES_ENCRYPTION_NAME]->set_notnull();
 | |
| 		} else {
 | |
| 			fields[TABLESPACES_ENCRYPTION_NAME]->set_null();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	OK(fields[TABLESPACES_ENCRYPTION_ENCRYPTION_SCHEME]->store(
 | |
| 		   status.scheme, true));
 | |
| 	OK(fields[TABLESPACES_ENCRYPTION_KEYSERVER_REQUESTS]->store(
 | |
| 		   status.keyserver_requests, true));
 | |
| 	OK(fields[TABLESPACES_ENCRYPTION_MIN_KEY_VERSION]->store(
 | |
| 		   status.min_key_version, true));
 | |
| 	OK(fields[TABLESPACES_ENCRYPTION_CURRENT_KEY_VERSION]->store(
 | |
| 		   status.current_key_version, true));
 | |
| 	OK(fields[TABLESPACES_ENCRYPTION_CURRENT_KEY_ID]->store(
 | |
| 		   status.key_id, true));
 | |
| 	OK(fields[TABLESPACES_ENCRYPTION_ROTATING_OR_FLUSHING]->store(
 | |
| 			   status.rotating || status.flushing, true));
 | |
| 
 | |
| 	if (status.rotating) {
 | |
| 		fields[TABLESPACES_ENCRYPTION_KEY_ROTATION_PAGE_NUMBER]->set_notnull();
 | |
| 		OK(fields[TABLESPACES_ENCRYPTION_KEY_ROTATION_PAGE_NUMBER]->store(
 | |
| 			   status.rotate_next_page_number, true));
 | |
| 		fields[TABLESPACES_ENCRYPTION_KEY_ROTATION_MAX_PAGE_NUMBER]->set_notnull();
 | |
| 		OK(fields[TABLESPACES_ENCRYPTION_KEY_ROTATION_MAX_PAGE_NUMBER]->store(
 | |
| 			   status.rotate_max_page_number, true));
 | |
| 	} else {
 | |
| 		fields[TABLESPACES_ENCRYPTION_KEY_ROTATION_PAGE_NUMBER]
 | |
| 			->set_null();
 | |
| 		fields[TABLESPACES_ENCRYPTION_KEY_ROTATION_MAX_PAGE_NUMBER]
 | |
| 			->set_null();
 | |
| 	}
 | |
| 
 | |
| 	OK(schema_table_store_record(thd, table_to_fill));
 | |
| 
 | |
| skip:
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Function to populate INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION table.
 | |
| Loop through each record in TABLESPACES_ENCRYPTION, and extract the column
 | |
| information and fill the INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION table.
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| i_s_tablespaces_encryption_fill_table(
 | |
| /*===========================*/
 | |
| 	THD*		thd,	/*!< in: thread */
 | |
| 	TABLE_LIST*	tables,	/*!< in/out: tables to fill */
 | |
| 	Item*		)	/*!< in: condition (not used) */
 | |
| {
 | |
| 	DBUG_ENTER("i_s_tablespaces_encryption_fill_table");
 | |
| 	RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
 | |
| 
 | |
| 	/* deny access to user without PROCESS_ACL privilege */
 | |
| 	if (check_global_access(thd, PROCESS_ACL)) {
 | |
| 		DBUG_RETURN(0);
 | |
| 	}
 | |
| 
 | |
| 	int err = 0;
 | |
| 	mysql_mutex_lock(&fil_system.mutex);
 | |
| 	fil_system.freeze_space_list++;
 | |
| 
 | |
| 	for (fil_space_t& space : fil_system.space_list) {
 | |
| 		if (!space.is_temporary() && !space.is_being_imported()
 | |
| 		    && !space.is_stopping()) {
 | |
| 			space.reacquire();
 | |
| 			mysql_mutex_unlock(&fil_system.mutex);
 | |
| 			space.s_lock();
 | |
| 			err = i_s_dict_fill_tablespaces_encryption(
 | |
| 				thd, &space, tables->table);
 | |
| 			space.s_unlock();
 | |
| 			mysql_mutex_lock(&fil_system.mutex);
 | |
| 			space.release();
 | |
| 			if (err) {
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	fil_system.freeze_space_list--;
 | |
| 	mysql_mutex_unlock(&fil_system.mutex);
 | |
| 	DBUG_RETURN(err);
 | |
| }
 | |
| /*******************************************************************//**
 | |
| Bind the dynamic table INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION
 | |
| @return 0 on success */
 | |
| static
 | |
| int
 | |
| innodb_tablespaces_encryption_init(
 | |
| /*========================*/
 | |
| 	void*	p)	/*!< in/out: table schema object */
 | |
| {
 | |
| 	ST_SCHEMA_TABLE*	schema;
 | |
| 
 | |
| 	DBUG_ENTER("innodb_tablespaces_encryption_init");
 | |
| 
 | |
| 	schema = (ST_SCHEMA_TABLE*) p;
 | |
| 
 | |
| 	schema->fields_info = Show::innodb_tablespaces_encryption_fields_info;
 | |
| 	schema->fill_table = i_s_tablespaces_encryption_fill_table;
 | |
| 
 | |
| 	DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| struct st_maria_plugin	i_s_innodb_tablespaces_encryption =
 | |
| {
 | |
| 	/* the plugin type (a MYSQL_XXX_PLUGIN value) */
 | |
| 	/* int */
 | |
| 	MYSQL_INFORMATION_SCHEMA_PLUGIN,
 | |
| 
 | |
| 	/* pointer to type-specific plugin descriptor */
 | |
| 	/* void* */
 | |
| 	&i_s_info,
 | |
| 
 | |
| 	/* plugin name */
 | |
| 	/* const char* */
 | |
| 	"INNODB_TABLESPACES_ENCRYPTION",
 | |
| 
 | |
| 	/* plugin author (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"Google Inc",
 | |
| 
 | |
| 	/* general descriptive text (for SHOW PLUGINS) */
 | |
| 	/* const char* */
 | |
| 	"InnoDB TABLESPACES_ENCRYPTION",
 | |
| 
 | |
| 	/* the plugin license (PLUGIN_LICENSE_XXX) */
 | |
| 	/* int */
 | |
| 	PLUGIN_LICENSE_BSD,
 | |
| 
 | |
| 	/* the function to invoke when plugin is loaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	innodb_tablespaces_encryption_init,
 | |
| 
 | |
| 	/* the function to invoke when plugin is unloaded */
 | |
| 	/* int (*)(void*); */
 | |
| 	i_s_common_deinit,
 | |
| 
 | |
| 	i_s_version, nullptr, nullptr, PACKAGE_VERSION,
 | |
| 	MariaDB_PLUGIN_MATURITY_STABLE
 | |
| };
 | 
