mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	 2d77e21758
			
		
	
	
	2d77e21758
	
	
	
		
			
			Before MySQL 4.0.18, user-specified constraint names were ignored. Starting with MySQL 4.0.18, the specified constraint name was prepended with the schema name and '/'. Now we are transforming into a format where the constraint name is prepended with the dict_table_t::name and the impossible UTF-8 sequence 0xff. Generated constraint names will be ASCII decimal numbers. On upgrade, old FOREIGN KEY constraint names will be displayed without any schema name prefix. They will be updated to the new format on DDL operations. dict_foreign_t::sql_id(): Return the SQL constraint name without any schemaname/tablename\377 or schemaname/ prefix. row_rename_table_for_mysql(), dict_table_rename_in_cache(): Simplify the logic: Just rename constraints to the new format. dict_table_get_foreign_id(): Replaces dict_table_get_highest_foreign_id(). row_delete_constraint(): Try to drop all 3 constraint name variants.
		
			
				
	
	
		
			6519 lines
		
	
	
	
		
			168 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			6519 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 measurable 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);
 | |
| 	}
 | |
| 
 | |
| 	/* Acquire 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->sql_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;
 | |
| 	const char* id = strchr(name, '\377');
 | |
| 	if (!id) id = strchr(name, '/');
 | |
| 	id = id ? id + 1 : name;
 | |
| 
 | |
| 	OK(field_store_string(fields[SYS_FOREIGN_COL_ID], id));
 | |
| 
 | |
| 	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
 | |
| };
 |