From 5f13291f5e395eb45f792dbd769e0758cfd34e51 Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 11 Feb 2025 10:39:42 +0100 Subject: Update for mysql 8.0.33 --- CMakeLists.txt | 8 +- Makefile.am | 3 - configure.ac | 68 ----- m4/ac_mysql.m4 | 96 ------- src/Makefile.am | 8 - src/table_sizes.cc | 724 ----------------------------------------------------- table_sizes.cc | 597 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 603 insertions(+), 901 deletions(-) delete mode 100644 Makefile.am delete mode 100644 configure.ac delete mode 100644 m4/ac_mysql.m4 delete mode 100644 src/Makefile.am delete mode 100644 src/table_sizes.cc create mode 100644 table_sizes.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index e468c80..7392af9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,9 @@ INCLUDE_DIRECTORIES(SYSTEM ${BOOST_PATCHES_DIR} ${BOOST_INCLUDE_DIR}) -MYSQL_ADD_PLUGIN(table_sizes src/table_sizes.cc - MODULE_ONLY MODULE_OUTPUT_NAME "table_sizes") +MYSQL_ADD_PLUGIN(table_sizes + table_sizes.cc + MODULE_ONLY MODULE_OUTPUT_NAME "table_sizes" + LINK_LIBRARIES extra::rapidjson + ) +ADD_DEFINITIONS(-DMYSQL_SERVER) diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index 3cb8f22..0000000 --- a/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -AUTOMAKE_OPTIONS = foreign no-dependencies -ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 48857f8..0000000 --- a/configure.ac +++ /dev/null @@ -1,68 +0,0 @@ -# Process this file with autoconf to produce a configure script. - -AC_PREREQ(2.59) -AC_INIT(mysql-table-sizes-plugin, [dev]) -AC_CANONICAL_TARGET -AC_CONFIG_MACRO_DIR([m4]) - -AM_INIT_AUTOMAKE - -AC_CONFIG_SRCDIR([src/table_sizes.cc]) -AC_CONFIG_HEADER([src/config.h]) - -AC_DEFUN([CHECK_DEBUG], [ - AC_ARG_ENABLE([debug], - AS_HELP_STRING([--enable-debug], [turn on debugging, default: no])) - - AC_MSG_CHECKING(whether to enable debugging) - AS_IF([test "x$enable_debug" = "xyes"], - [ - CPPFLAGS="$CPPFLAGS -g -D_DEBUG" - AC_MSG_RESULT(yes) - ], - [ - CPPFLAGS="$CPPFLAGS -g -O2 -DDBUG_OFF" - AC_MSG_RESULT(no) - ] - ) -]) - - -dnl Run tests using C++ compiler -dnl AC_LANG(C++) - -CHECK_DEBUG - -#check for mysql src -MYSQL_SRC_TEST -MYSQL_PLUGIN_DIR_TEST -#MYSQL_LIB_SERVICES_TEST -AC_SUBST(MYSQL_INC) -AC_SUBST(MYSQL_PLUGIN_DIR) -#AC_SUBST(MYSQL_LIBSERVICES) - -#check for programs -AC_PROG_CC -AC_PROG_LIBTOOL -AC_PROG_CXX -AC_PROG_CPP - -#we can add the following flags for better error catching: -Werror -CPPFLAGS="$CPPFLAGS -Werror" -# From MySQL: Disable exceptions as they seams to create problems with gcc and threads. -CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-rtti" - -#make sure we have const -AC_C_CONST -AC_TYPE_SIZE_T - -#subst the relevant variables -AC_SUBST(CPPFLAGS) -AC_SUBST(CXXLAGS) -AC_SUBST(CFLAGS) - -AC_CONFIG_FILES([ - Makefile - src/Makefile -]) -AC_OUTPUT diff --git a/m4/ac_mysql.m4 b/m4/ac_mysql.m4 deleted file mode 100644 index b5a5c0c..0000000 --- a/m4/ac_mysql.m4 +++ /dev/null @@ -1,96 +0,0 @@ -dnl --------------------------------------------------------------------------- -dnl Macro: MYSQL_SRC_TEST -dnl --------------------------------------------------------------------------- - -dir_resolve() dnl {{{ -{ - pwd=`pwd` - cd "$1" 2>/dev/null || cd "${pwd}/${1}" 2>/dev/null - if test "$?" = "0" - then - echo `pwd -P` - else - echo "$1" - fi -} -dnl }}} - -AC_DEFUN([MYSQL_SRC_TEST], [ - AC_MSG_CHECKING(for mysql source code) - AC_ARG_WITH(mysql, - [AS_HELP_STRING([--with-mysql=PATH], [MySQL src directory required to build.])], - [ - ac_mysql_source_dir=`readlink -e "$withval"` - HEADERS="include/my_dir.h include/mysql/plugin.h include/mysql.h include/mysql_version.h include/config.h include/my_config.h" - for file in $HEADERS - do - if ! test -r "$ac_mysql_source_dir/$file" - then - AC_MSG_ERROR([Failed to find required header file $ac_mysql_source_dir/$file, check the path and make sure you've run './configure .... && cd include && make' in MySQL 5.1 sources dir or 'cmake . && make' in MySQL 5.5 sources dir.]) - fi - done - AC_DEFINE([MYSQL_SRC], [1], [Source directory for MySQL]) - MYSQL_INC="-I$ac_mysql_source_dir/sql -I$ac_mysql_source_dir/include -I$ac_mysql_source_dir/regex -I$ac_mysql_source_dir -I$ac_mysql_source_dir/libbinlogevents/export -I$ac_mysql_source_dir/libbinlogevents/include -I$ac_mysql_source_dir/sql/auth" - AC_MSG_RESULT(["$ac_mysql_source_dir"]) - ], - [ - AC_MSG_ERROR(["No mysql source provided. Please specify --with-mysql=!"]) - ] - ) -]) - -dnl --------------------------------------------------------------------------- -dnl Macro: MYSQL_PLUGIN_DIR_TEST -dnl --------------------------------------------------------------------------- - -AC_DEFUN([MYSQL_PLUGIN_DIR_TEST], [ - AC_MSG_CHECKING([for mysql plugin dir]) - ac_mysql_plugin_dir= - AC_ARG_WITH([mysql-plugindir], - [AS_HELP_STRING([--with-mysql-plugindir=PATH], [MySQL plugin directory where plugin is to be copied to])], - [ - ac_mysql_plugin_dir=`readlink -m "$withval"` - MYSQL_PLUGIN_DIR="$ac_mysql_plugin_dir" - ], - [ - ac_mysql_plugin_dir="/usr/lib/mysql/plugin" - MYSQL_PLUGIN_DIR="$ac_mysql_plugin_dir" - AC_MSG_RESULT([--with-mysql-plugindir was not set. Using $ac_mysql_plugin_dir]) - ] - ) -]) - -dnl --------------------------------------------------------------------------- -dnl Macro: MYSQL_LIB_SERVICES : 5.5 services lib to add to linker -dnl --------------------------------------------------------------------------- - -dnl AC_DEFUN([MYSQL_LIB_SERVICES_TEST], [ -dnl AC_MSG_CHECKING([for mysql libmysqlservices]) -dnl ac_mysql_libservices= -dnl AC_ARG_WITH([mysql-libservices], -dnl [AS_HELP_STRING([--with-mysql-libservices=PATH], [MySQL libmysqlservices.a location (relevant for 5.5 only)])], -dnl [ -dnl ac_mysql_libservices=`readlink -e "$withval"` -dnl if test -f "$ac_mysql_libservices" -dnl then -dnl MYSQL_LIBSERVICES="$ac_mysql_libservices" -dnl AC_MSG_RESULT([yes: Using $ac_mysql_libservices]) -dnl else -dnl AC_MSG_ERROR([invalid MySQL libmysqlservices : $ac_mysql_libservices]) -dnl fi -dnl ], -dnl [ -dnl if test -f "$ac_mysql_source_dir/VERSION" -dnl then -dnl source "$ac_mysql_source_dir/VERSION" -dnl if test "$MYSQL_VERSION_MAJOR.$MYSQL_VERSION_MINOR" = "5.5" -dnl then -dnl AC_MSG_ERROR([no mysql-libservices. Required for MySQL 5.5]) -dnl fi -dnl fi -dnl ac_mysql_libservices="" -dnl MYSQL_LIBSERVICES="$ac_mysql_libservices" -dnl AC_MSG_RESULT([--with-mysql-libservices was not set.]) -dnl ] -dnl ) -dnl ]) diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index 7965ee4..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -INCLUDES = $(MYSQL_INC) $(DEPS_CFLAGS) - -pkgplugindir = $(MYSQL_PLUGIN_DIR) - -pkgplugin_LTLIBRARIES = table_sizes.la -table_sizes_la_SOURCES = table_sizes.cc -table_sizes_la_LIBADD = $(DEPS_LIBS) -table_sizes_la_LDFLAGS = -module diff --git a/src/table_sizes.cc b/src/table_sizes.cc deleted file mode 100644 index 40d5090..0000000 --- a/src/table_sizes.cc +++ /dev/null @@ -1,724 +0,0 @@ -/* - 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 St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include -#include - -#include "sql/mysqld.h" -#include "sql/sql_base.h" -#include "sql/sql_show.h" -#include "sql/sql_class.h" // THD -#include "sql/sql_table.h" // filename_to_tablename -#include "sql/strfunc.h" // make_lex_string_root -#include "sql/sql_parse.h" // sql_command_flags -#include "sql/sql_lex.h" // LEX -#include "sql/item_func.h" // Item_Func -#include "sql/item_cmpfunc.h" // Item_cond -#include "sql/dd/cache/dictionary_client.h" // dd::cache::Dictionary_client -#include "sql/dd/dd_schema.h" // dd::Schema_MDL_locker -#include "sql/dd/string_type.h" // dd::String_type -#include "sql/dd/sdi_file.h" // dd::sdi_file -#include "sql/auth/auth_acls.h" // *_ACLS -#include "sql/auth/auth_common.h" // check_grant_db -#include "mf_wcomp.h" // wild_compare,wild_one,wild_many -#include "my_dir.h" // MY_DIR - -// InnoDB FTS -/** For storing table info when checking for orphaned tables. */ -struct fts_aux_table_t -{ - /** Parent table id */ - uint64_t parent_id; - - /** Table FT index id */ - uint64_t index_id; -}; - -/** Check if a table is an FTS auxiliary table name. -@param[out] table FTS table info -@param[in] name Table name -@param[in] len Length of table name -@return true if the name matches an auxiliary table name pattern */ -bool fts_is_aux_table_name(fts_aux_table_t *table, const char *name, - int len) -{ - /** FTS auxiliary table prefix that are common to all FT indexes.*/ - const char *FTS_PREFIX = "fts_"; - const char *FTS_PREFIX_5_7 = "FTS_"; - - /** FTS auxiliary table suffixes that are common to all FT indexes. */ - const char *fts_common_tables[] = {"being_deleted", "being_deleted_cache", - "config", "deleted", - "deleted_cache", NULL}; - const char *fts_common_tables_5_7[] = {"BEING_DELETED", "BEING_DELETED_CACHE", - "CONFIG", "DELETED", - "DELETED_CACHE", NULL}; - - /** FTS auxiliary INDEX split intervals. */ - const char *fts_index_selector[] = {"index_1", "index_2", "index_3", "index_4", - "index_5", "index_6", NULL}; - const char *fts_index_selector_5_7[] = {"INDEX_1", "INDEX_2", "INDEX_3", "INDEX_4", - "INDEX_5", "INDEX_6", NULL}; - - const char *ptr = name; - const char *end = name + len; - - /* All auxiliary tables are prefixed with "FTS_" and the name - length will be at the very least greater than 20 bytes. */ - if (ptr != NULL && len > 20 && - (strncmp(ptr, FTS_PREFIX, 4) == 0 || - strncmp(ptr, FTS_PREFIX_5_7, 4) == 0)) - { - /* Skip the prefix. */ - ptr += 4; - len -= 4; - - /* Try and read the table id. */ - if (sscanf(ptr, "%016" PRIx64, &table->parent_id) != 1) - return false; - - /* Skip the table id. */ - ptr = static_cast(memchr(ptr, '_', len)); - if (ptr == NULL) - return false; - - /* Skip the underscore. */ - ++ptr; - len = end - ptr; - - /* First search the common table suffix array. */ - for (int i = 0; fts_common_tables[i] != NULL; ++i) { - if (strncmp(ptr, fts_common_tables[i], len) == 0 || - strncmp(ptr, fts_common_tables_5_7[i], len) == 0) { - return true; - } - } - - /* Could be obsolete common tables. */ - if (native_strncasecmp(ptr, "ADDED", len) == 0 || - native_strncasecmp(ptr, "STOPWORDS", len) == 0) { - return true; - } - - /* Try and read the index id. */ - if (sscanf(ptr, "%016" PRIx64, &table->index_id) != 1) - return false; - - /* Skip the index id. */ - ptr = static_cast(memchr(ptr, '_', len)); - - if (ptr == NULL) - return false; - - /* Skip the underscore. */ - ++ptr; - len = end - ptr; - - /* Search the FT index specific array. */ - for (int i = 0; fts_index_selector[i] != NULL; ++i) { - if (strncmp(ptr, fts_index_selector[i], len) == 0 || - strncmp(ptr, fts_index_selector_5_7[i], len) == 0) { - return true; - } - } - - /* Other FT index specific table(s). */ - if (native_strncasecmp(ptr, "DOC_ID", len) == 0) - return true; - } - - return false; -} - -/*----------------------------------------------------------------------------*/ - -/** - Condition pushdown used for INFORMATION_SCHEMA / SHOW queries. - This structure is to implement an optimization when - accessing data dictionary data in the INFORMATION_SCHEMA - or SHOW commands. - When the query contain a TABLE_SCHEMA or TABLE_NAME clause, - narrow the search for data based on the constraints given. -*/ -struct LOOKUP_FIELD_VALUES { - /** - Value of a TABLE_SCHEMA clause. - Note that this value length may exceed @c NAME_LEN. - @sa wild_db_value - */ - boost::optional db_value; - /** - Value of a TABLE_NAME clause. - Note that this value length may exceed @c NAME_LEN. - @sa wild_table_value - */ - boost::optional table_value; - /** - True when @c db_value is a LIKE clause, - false when @c db_value is an '=' clause. - */ - bool wild_db_value = {false}; - /** - True when @c table_value is a LIKE clause, - false when @c table_value is an '=' clause. - */ - bool wild_table_value = {false}; -}; - -/** - @brief Get lookup value from the part of 'WHERE' condition - - @details This function gets lookup value from - the part of 'WHERE' condition if it's possible and - fill appropriate lookup_field_vals struct field - with this value. - - @param[in] thd thread handler - @param[in] item_func part of WHERE condition - @param[in] table I_S table - @param[in, out] lookup_field_vals Struct which holds lookup values - - @return - 0 success - 1 error, there can be no matching records for the condition -*/ - -static bool get_lookup_value(Item_func *item_func, TABLE_LIST *table, - LOOKUP_FIELD_VALUES *lookup_field_vals) -{ - ST_SCHEMA_TABLE *schema_table = table->schema_table; - ST_FIELD_INFO *field_info = schema_table->fields_info; - const char *field_name1 = - schema_table->idx_field1 >= 0 - ? field_info[schema_table->idx_field1].field_name - : ""; - const char *field_name2 = - schema_table->idx_field2 >= 0 - ? field_info[schema_table->idx_field2].field_name - : ""; - - if (item_func->functype() == Item_func::EQ_FUNC || - item_func->functype() == Item_func::EQUAL_FUNC) { - int idx_field, idx_val; - char tmp[MAX_FIELD_WIDTH]; - String *tmp_str, str_buff(tmp, sizeof(tmp), system_charset_info); - Item_field *item_field; - CHARSET_INFO *cs = system_charset_info; - - if (item_func->arguments()[0]->type() == Item::FIELD_ITEM && - item_func->arguments()[1]->const_item()) { - idx_field = 0; - idx_val = 1; - } else if (item_func->arguments()[1]->type() == Item::FIELD_ITEM && - item_func->arguments()[0]->const_item()) { - idx_field = 1; - idx_val = 0; - } else - return 0; - - item_field = (Item_field *)item_func->arguments()[idx_field]; - if (table->table != item_field->field->table) return 0; - tmp_str = item_func->arguments()[idx_val]->val_str(&str_buff); - - /* impossible value */ - if (!tmp_str) return 1; - - /* Lookup value is database name */ - if (!cs->coll->strnncollsp(cs, (uchar *)field_name1, strlen(field_name1), - (uchar *)item_field->field_name, - strlen(item_field->field_name))) { - lookup_field_vals->db_value.emplace(tmp_str->ptr(), tmp_str->length()); - } - /* Lookup value is table name */ - else if (!cs->coll->strnncollsp(cs, (uchar *)field_name2, - strlen(field_name2), - (uchar *)item_field->field_name, - strlen(item_field->field_name))) { - lookup_field_vals->table_value.emplace(tmp_str->ptr(), tmp_str->length()); - } - } - return 0; -} - -/** - @brief Calculates lookup values from 'WHERE' condition - - @details This function calculates lookup value(database name, table name) - from 'WHERE' condition if it's possible and - fill lookup_field_vals struct fields with these values. - - @param[in] thd thread handler - @param[in] cond WHERE condition - @param[in] table I_S table - @param[in, out] lookup_field_vals Struct which holds lookup values - - @return - 0 success - 1 error, there can be no matching records for the condition -*/ - -static bool calc_lookup_values_from_cond(Item *cond, - TABLE_LIST *table, LOOKUP_FIELD_VALUES *lookup_field_vals) -{ - if (!cond) return 0; - - if (cond->type() == Item::COND_ITEM) { - if (((Item_cond *)cond)->functype() == Item_func::COND_AND_FUNC) { - List_iterator li(*((Item_cond *)cond)->argument_list()); - Item *item; - while ((item = li++)) { - if (item->type() == Item::FUNC_ITEM) { - if (get_lookup_value((Item_func *)item, table, lookup_field_vals)) - return 1; - } else { - if (calc_lookup_values_from_cond(item, table, lookup_field_vals)) - return 1; - } - } - } - return 0; - } else if (cond->type() == Item::FUNC_ITEM && - get_lookup_value((Item_func *)cond, table, lookup_field_vals)) - return 1; - return 0; -} - -/** - @brief Calculate lookup values(database name, table name) - - @details This function calculates lookup values(database name, table name) - from 'WHERE' condition or wild values (for 'SHOW' commands only) - from LEX struct and fill lookup_field_vals struct field - with these values. - - @param[in] thd thread handler - @param[in] cond WHERE condition - @param[in] tables I_S table - @param[in, out] lookup_field_values Struct which holds lookup values - - @return - 0 success - 1 error, there can be no matching records for the condition -*/ - -static bool get_lookup_field_values(THD *thd, Item *cond, TABLE_LIST *tables, - LOOKUP_FIELD_VALUES *lookup_field_values) -{ - LEX *lex = thd->lex; - const char *wild = lex->wild ? lex->wild->ptr() : NullS; - bool rc = 0; - - switch (lex->sql_command) { - case SQLCOM_SHOW_DATABASES: - if (wild) { - lookup_field_values->db_value = wild; - lookup_field_values->wild_db_value = true; - } - break; - case SQLCOM_SHOW_TABLES: - case SQLCOM_SHOW_TABLE_STATUS: - case SQLCOM_SHOW_TRIGGERS: - case SQLCOM_SHOW_EVENTS: - lookup_field_values->db_value = lex->select_lex->db; - if (wild) { - lookup_field_values->table_value = wild; - lookup_field_values->wild_table_value = true; - } - break; - default: - /* - The "default" is for queries over I_S. - All previous cases handle SHOW commands. - */ - rc = calc_lookup_values_from_cond(cond, tables, lookup_field_values); - break; - } - - if (lower_case_table_names && !rc) { - /* - We can safely do in-place upgrades here since all of the above cases - are allocating a new memory buffer for these strings. - */ - if (lookup_field_values->db_value && !lookup_field_values->db_value->empty()) - casedn(system_charset_info, *lookup_field_values->db_value); - if (lookup_field_values->table_value && !lookup_field_values->table_value->empty()) - casedn(system_charset_info, *lookup_field_values->table_value); - } - - return rc; -} - -/*----------------------------------------------------------------------------*/ - -static bool is_special_db(const dd::String_type &db_name) -{ - /* I_S does not exist and we hide P_S */ - return is_infoschema_db(db_name.c_str(), db_name.size()) - || is_perfschema_db(db_name.c_str(), db_name.size()); -} - -static int -make_db_list(THD *thd, std::vector *db_names, - LOOKUP_FIELD_VALUES *lookup_field_vals) -{ - /* - If we have db lookup value we just add it to list and - exit from the function. - We don't do this for database names longer than the maximum - path length. - */ - if (lookup_field_vals->db_value - && lookup_field_vals->db_value->length() <= NAME_LEN) - { - const dd::String_type &db_name = lookup_field_vals->db_value.get(); - if (!is_special_db(db_name)) - db_names->push_back(db_name); - return 0; - } - - dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client()); - std::vector schemas; - if (thd->dd_client()->fetch_global_components(&schemas)) - return 1; - - for (const dd::Schema *schema_obj : schemas) - { - const dd::String_type &db_name = schema_obj->name(); - if (is_special_db(db_name)) - continue; - db_names->push_back(db_name); - } - return 0; -} - -/*----------------------------------------------------------------------------*/ - -/* - SYNOPSIS - thd thread handler - tables put found tables and their size in this list - db database name to set in TABLE_LIST structure - path path to database - wild filter for found files - - RETURN - FIND_FILES_OK success - FIND_FILES_OOM out of memory error - FIND_FILES_DIR no such directory, or directory can't be read -*/ -typedef std::map Table_sizes_map; -static find_files_result -find_tables(THD *thd, Table_sizes_map &tables, const char *db, - const char *path, boost::optional &wild, - MEM_ROOT *tmp_mem_root) -{ - MY_DIR *dirp; - MEM_ROOT **save_mem_root_ptr = THR_MALLOC; - uint col_access = thd->col_access; - DBUG_ENTER("find_files"); - - if (!(dirp = my_dir(path, MYF(MY_WANT_STAT)))) - { - if (my_errno() == ENOENT) - my_error(ER_BAD_DB_ERROR, MYF(0), db); - else - { - char errbuf[MYSYS_STRERROR_SIZE]; - my_error(ER_CANT_READ_DIR, MYF(0), path, - my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno())); - } - DBUG_RETURN(FIND_FILES_DIR); - } - - if (tmp_mem_root) - THR_MALLOC = &tmp_mem_root; - - char uname[NAME_LEN + 1]; /* Unencoded name */ - dd::String_type fts_schema_name, fts_table_name; /* FTS lookup */ - std::map fts_cache; - tables.clear(); - for (uint i = 0; i < dirp->number_off_files; i++) - { - - FILEINFO *file = dirp->dir_entry + i; - /* skip '.', '..' and temp files. */ - if ((file->name[0] == '.' && - (!file->name[1] || (file->name[1] == '.' && !file->name[2]))) - || is_prefix(file->name, tmp_file_prefix)) - continue; - - char *ext; - if ((ext = strrchr(file->name, '.'))) - { - // sdi files are small and their name is truncated - // lookup by id is possible but not worth the effort - if (!strcmp(ext, dd::sdi_file::EXT.c_str())) - continue; - *ext = 0; - ++ext; - } - - size_t file_name_len; - fts_aux_table_t fts_table; - if (ext && !strcmp(ext, "ibd") - && fts_is_aux_table_name(&fts_table, file->name, strlen(file->name))) - { - auto entry = fts_cache.find(fts_table.parent_id); - if (entry != fts_cache.end()) - fts_table_name = entry->second; - else if (thd->dd_client()->get_table_name_by_se_private_id("InnoDB", - fts_table.parent_id, &fts_schema_name, &fts_table_name)) - continue; - - file_name_len = (my_stpnmov(uname, fts_table_name.c_str(), sizeof(uname)) - uname); - } - else - file_name_len = filename_to_tablename(file->name, uname, sizeof(uname)); - - auto table_data = tables.find(uname); - if (table_data != tables.end()) - { - table_data->second += file->mystat->st_size; - continue; - } - - if (wild) - { - if (lower_case_table_names) - { - if (my_wildcmp(files_charset_info, - uname, uname + file_name_len, - wild->c_str(), wild->c_str() + wild->length(), - wild_prefix, wild_one, wild_many)) - continue; - } - else if (wild_compare(uname, file_name_len, wild->c_str(), wild->length(), 0)) - continue; - } - - /* Don't show tables where we don't have any privileges */ - if (db && !(col_access & TABLE_ACLS)) - { - TABLE_LIST table_list; - table_list.db = db; - table_list.db_length = strlen(db); - table_list.table_name = uname; - table_list.table_name_length = file_name_len; - table_list.grant.privilege = col_access; - if (check_grant(thd, TABLE_ACLS, &table_list, true, 1, true)) - continue; - } - - tables.emplace(uname, file->mystat->st_size); - } - - DBUG_PRINT("info", ("found: %ld files", tables.size())); - my_dirend(dirp); - - if (tmp_mem_root) - THR_MALLOC = save_mem_root_ptr; - - DBUG_RETURN(FIND_FILES_OK); -} - -static int -make_table_list(THD *thd, Table_sizes_map &table_names, LEX *lex, - LOOKUP_FIELD_VALUES *lookup_field_vals, const dd::String_type &db_name, - MEM_ROOT *tmp_mem_root) -{ - char path[FN_REFLEN + 1]; - build_table_filename(path, sizeof(path) - 1, db_name.c_str(), "", "", 0); - - const dd::Schema *sch_obj = NULL; - if (thd->dd_client()->acquire(db_name.c_str(), &sch_obj)) - return 1; - if (!sch_obj) - { - /* - Report missing database only if it is a 'SHOW' command. - Another thread may have dropped the database after we - got its name from the DD. - */ - if (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) - { - my_error(ER_BAD_DB_ERROR, MYF(0), db_name.c_str()); - return 1; - } - return 2; - } - - find_files_result res = find_tables(thd, table_names, db_name.c_str(), path, - lookup_field_vals->table_value, tmp_mem_root); - return (res == FIND_FILES_OK) ? 0 : 2; -} - -/*----------------------------------------------------------------------------*/ - -static struct st_mysql_information_schema info = -{ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; - -static ST_FIELD_INFO fields_info[] = -{ - {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, - {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", - SKIP_OPEN_TABLE}, - {"TABLE_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, - MY_I_S_UNSIGNED, "Table_size", SKIP_OPEN_TABLE}, - {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} -}; - -static int fill_table(THD *thd, TABLE_LIST *tables, Item *cond) -{ - LEX *lex = thd->lex; - TABLE *table = tables->table; - LOOKUP_FIELD_VALUES lookup_field_vals; - Security_context *sctx = thd->security_context(); - std::vector db_names; - int error = 1; - - DBUG_ENTER("fill_table"); - - MEM_ROOT tmp_mem_root; - init_sql_alloc(key_memory_get_all_tables, &tmp_mem_root, - TABLE_ALLOC_BLOCK_SIZE, 0); - - if (get_lookup_field_values(thd, cond, tables, &lookup_field_vals)) - { - error = 0; - goto err; - } - -#define STR_OR_NIL(S) ((S) ? (S->c_str()) : "") - DBUG_PRINT("INDEX VALUES", ("db_name='%s', table_name='%s'", - STR_OR_NIL(lookup_field_vals.db_value), - STR_OR_NIL(lookup_field_vals.table_value))); - - if (!lookup_field_vals.wild_db_value && !lookup_field_vals.wild_table_value) - { - /* - if lookup value is empty string then - it's impossible table name or db name - */ - if ((lookup_field_vals.db_value - && lookup_field_vals.db_value->empty()) - || (lookup_field_vals.table_value - && lookup_field_vals.table_value->empty())) - { - error = 0; - goto err; - } - } - - if (lookup_field_vals.db_value && !lookup_field_vals.db_value->empty() - && !lookup_field_vals.wild_db_value) - tables->has_db_lookup_value = true; - if (lookup_field_vals.table_value && !lookup_field_vals.table_value->empty() - && !lookup_field_vals.wild_table_value) - tables->has_table_lookup_value = true; - - if (lex->is_explain()) - { - /* EXPLAIN SELECT */ - error = 0; - goto err; - } - - if (make_db_list(thd, &db_names, &lookup_field_vals)) - goto err; - - for (auto &db_name : db_names) - { - bool have_db_privileges = false; - if (sctx->get_active_roles()->size() > 0) { - LEX_CSTRING const_db_name = { db_name.c_str(), db_name.size() }; - have_db_privileges = sctx->db_acl(const_db_name) > 0 ? true : false; - } - if (!(check_access(thd, SELECT_ACL, db_name.c_str(), &thd->col_access, NULL, 0, 1) - || (!thd->col_access && check_grant_db(thd, db_name.c_str()))) - || sctx->check_access(DB_ACLS | SHOW_DB_ACL, true) - || have_db_privileges - || acl_get(thd, sctx->host().str, sctx->ip().str, - sctx->priv_user().str, db_name.c_str(), 0)) - { - // We must make sure the schema is released and unlocked in the right - // order. Fail if we are unable to get a meta data lock on the schema - // name. - dd::Schema_MDL_locker mdl_handler(thd); - if (mdl_handler.ensure_locked(db_name.c_str())) - goto err; - - dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client()); - Table_sizes_map tables; - int res = make_table_list(thd, tables, lex, &lookup_field_vals, - db_name, &tmp_mem_root); - if (res) - { - if (res == 2) /* Not fatal error, continue */ - continue; - goto err; - } - - for (auto &table_data : tables) - { - table->field[0]->store(db_name.c_str(), db_name.size(), - system_charset_info); - table->field[1]->store(table_data.first.c_str(), table_data.first.size(), - system_charset_info); - table->field[2]->store(table_data.second, true); - if (schema_table_store_record(thd, table)) - goto err; - } - } - } - - error = 0; - -err: - free_root(&tmp_mem_root, MYF(0)); - DBUG_RETURN(error); - - return 0; -} - -static int init(void *ptr) -{ - ST_SCHEMA_TABLE *schema_table = (ST_SCHEMA_TABLE*)ptr; - - schema_table->fields_info = fields_info; - schema_table->fill_table = fill_table; - schema_table->idx_field1 = 0; - schema_table->idx_field2 = 1; - schema_table->i_s_requested_object = OPTIMIZE_I_S_TABLE; - return 0; -} - -mysql_declare_plugin(table_sizes) -{ - MYSQL_INFORMATION_SCHEMA_PLUGIN, - &info, /* type-specific descriptor */ - "TABLE_SIZES", /* table name */ - "Manuel Mausz", /* author */ - "Fast INFORMATION_SCHEMA table sizes", /* description */ - PLUGIN_LICENSE_GPL, /* license type */ - init, /* init function */ - NULL, - NULL, - 0x0800, /* version = 8.0 */ - NULL, /* no status variables */ - NULL, /* no system variables */ - NULL, /* no reserved information */ - 0 /* no flags */ -} -mysql_declare_plugin_end; diff --git a/table_sizes.cc b/table_sizes.cc new file mode 100644 index 0000000..ec8efc0 --- /dev/null +++ b/table_sizes.cc @@ -0,0 +1,597 @@ +/* + 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 St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include "sql/mysqld.h" +#include "sql/sql_base.h" +#include "sql/sql_class.h" // THD +#include "sql/sql_table.h" // filename_to_tablename +#include "sql/strfunc.h" // casedn +#include "sql/sql_parse.h" // sql_command_flags +#include "sql/sql_lex.h" // LEX +#include "sql/item_func.h" // Item_Func +#include "sql/item_cmpfunc.h" // Item_cond +#include "sql/dd/cache/dictionary_client.h" // dd::cache::Dictionary_client +#include "sql/dd/dd_schema.h" // dd::Schema_MDL_locker +#include "sql/dd/string_type.h" // dd::String_type +#include "sql/dd/sdi_file.h" // dd::sdi_file +#include "sql/auth/auth_acls.h" // *_ACLS +#include "sql/auth/auth_common.h" // check_grant_db +#include "mf_wcomp.h" // wild_compare,wild_one,wild_many +#include "my_dir.h" // MY_DIR + +// InnoDB FTS +/** For storing table info when checking for orphaned tables. */ +struct fts_aux_table_t +{ + /** Parent table id */ + uint64_t parent_id; + + /** Table FT index id */ + uint64_t index_id; +}; + +/** Check if a table is an FTS auxiliary table name. +@param[out] table FTS table info +@param[in] name Table name +@param[in] len Length of table name +@return true if the name matches an auxiliary table name pattern */ +bool fts_is_aux_table_name(fts_aux_table_t *table, const char *name, + int len) +{ + /** FTS auxiliary table prefix that are common to all FT indexes.*/ + const char *FTS_PREFIX = "fts_"; + const char *FTS_PREFIX_5_7 = "FTS_"; + + /** FTS auxiliary table suffixes that are common to all FT indexes. */ + const char *fts_common_tables[] = {"being_deleted", "being_deleted_cache", + "config", "deleted", + "deleted_cache", NULL}; + const char *fts_common_tables_5_7[] = {"BEING_DELETED", "BEING_DELETED_CACHE", + "CONFIG", "DELETED", + "DELETED_CACHE", NULL}; + + /** FTS auxiliary INDEX split intervals. */ + const char *fts_index_selector[] = {"index_1", "index_2", "index_3", "index_4", + "index_5", "index_6", NULL}; + const char *fts_index_selector_5_7[] = {"INDEX_1", "INDEX_2", "INDEX_3", "INDEX_4", + "INDEX_5", "INDEX_6", NULL}; + + const char *ptr = name; + const char *end = name + len; + + /* All auxiliary tables are prefixed with "FTS_" and the name + length will be at the very least greater than 20 bytes. */ + if (ptr != NULL && len > 20 && + (strncmp(ptr, FTS_PREFIX, 4) == 0 || + strncmp(ptr, FTS_PREFIX_5_7, 4) == 0)) + { + /* Skip the prefix. */ + ptr += 4; + len -= 4; + + /* Try and read the table id. */ + if (sscanf(ptr, "%016" PRIx64, &table->parent_id) != 1) + return false; + + /* Skip the table id. */ + ptr = static_cast(memchr(ptr, '_', len)); + if (ptr == NULL) + return false; + + /* Skip the underscore. */ + ++ptr; + len = end - ptr; + + /* First search the common table suffix array. */ + for (int i = 0; fts_common_tables[i] != NULL; ++i) { + if (strncmp(ptr, fts_common_tables[i], len) == 0 || + strncmp(ptr, fts_common_tables_5_7[i], len) == 0) { + return true; + } + } + + /* Could be obsolete common tables. */ + if (native_strncasecmp(ptr, "ADDED", len) == 0 || + native_strncasecmp(ptr, "STOPWORDS", len) == 0) { + return true; + } + + /* Try and read the index id. */ + if (sscanf(ptr, "%016" PRIx64, &table->index_id) != 1) + return false; + + /* Skip the index id. */ + ptr = static_cast(memchr(ptr, '_', len)); + + if (ptr == NULL) + return false; + + /* Skip the underscore. */ + ++ptr; + len = end - ptr; + + /* Search the FT index specific array. */ + for (int i = 0; fts_index_selector[i] != NULL; ++i) { + if (strncmp(ptr, fts_index_selector[i], len) == 0 || + strncmp(ptr, fts_index_selector_5_7[i], len) == 0) { + return true; + } + } + + /* Other FT index specific table(s). */ + if (native_strncasecmp(ptr, "DOC_ID", len) == 0) + return true; + } + + return false; +} + +/*----------------------------------------------------------------------------*/ + +/** + Condition pushdown used for INFORMATION_SCHEMA / SHOW queries. + This structure is to implement an optimization when + accessing data dictionary data in the INFORMATION_SCHEMA + or SHOW commands. + When the query contain a TABLE_SCHEMA or TABLE_NAME clause, + narrow the search for data based on the constraints given. +*/ +struct LOOKUP_FIELD_VALUES { + /** + Value of a TABLE_SCHEMA clause. + Note that this value length may exceed @c NAME_LEN. + */ + boost::optional db_value; + /** + Value of a TABLE_NAME clause. + Note that this value length may exceed @c NAME_LEN. + */ + boost::optional table_value; +}; + +/** + @brief Get lookup value from the part of 'WHERE' condition + + @details This function gets lookup value from + the part of 'WHERE' condition if it's possible and + fill appropriate lookup_field_vals struct field + with this value. + + @param[in] thd thread handler + @param[in] item_func part of WHERE condition + @param[in] table I_S table + @param[in, out] lookup_field_vals Struct which holds lookup values + + @return + 0 success + 1 error, there can be no matching records for the condition +*/ + +static bool get_lookup_value(Item_func *item_func, Table_ref *table, + LOOKUP_FIELD_VALUES *lookup_field_vals) +{ + ST_SCHEMA_TABLE *schema_table = table->schema_table; + ST_FIELD_INFO *field_info = schema_table->fields_info; + + if (item_func->functype() == Item_func::EQ_FUNC || + item_func->functype() == Item_func::EQUAL_FUNC) { + int idx_field, idx_val; + char tmp[MAX_FIELD_WIDTH]; + String *tmp_str, str_buff(tmp, sizeof(tmp), system_charset_info); + Item_field *item_field; + CHARSET_INFO *cs = system_charset_info; + + if (item_func->arguments()[0]->type() == Item::FIELD_ITEM && + item_func->arguments()[1]->const_item()) { + idx_field = 0; + idx_val = 1; + } else if (item_func->arguments()[1]->type() == Item::FIELD_ITEM && + item_func->arguments()[0]->const_item()) { + idx_field = 1; + idx_val = 0; + } else + return 0; + + item_field = (Item_field *)item_func->arguments()[idx_field]; + if (table->table != item_field->field->table) return 0; + tmp_str = item_func->arguments()[idx_val]->val_str(&str_buff); + + /* impossible value */ + if (!tmp_str) return 1; + + /* Lookup value is database name */ + if (!cs->coll->strnncollsp(cs, (const uchar *)field_info[0].field_name, + strlen(field_info[0].field_name), + (const uchar *)item_field->field_name, + strlen(item_field->field_name))) { + lookup_field_vals->db_value.emplace(tmp_str->ptr(), tmp_str->length()); + } + /* Lookup value is table name */ + else if (!cs->coll->strnncollsp(cs, (const uchar *)field_info[1].field_name, + strlen(field_info[1].field_name), + (const uchar *)item_field->field_name, + strlen(item_field->field_name))) { + lookup_field_vals->table_value.emplace(tmp_str->ptr(), tmp_str->length()); + } + } + return 0; +} + +/** + @brief Calculates lookup values from 'WHERE' condition + + @details This function calculates lookup value(database name, table name) + from 'WHERE' condition if it's possible and + fill lookup_field_vals struct fields with these values. + + @param[in] thd thread handler + @param[in] cond WHERE condition + @param[in] table I_S table + @param[in, out] lookup_field_vals Struct which holds lookup values + + @return + 0 success + 1 error, there can be no matching records for the condition +*/ + +static bool calc_lookup_values_from_cond(Item *cond, + Table_ref *table, LOOKUP_FIELD_VALUES *lookup_field_vals) +{ + if (!cond) return 0; + + if (cond->type() == Item::COND_ITEM) { + if (((Item_cond *)cond)->functype() == Item_func::COND_AND_FUNC) { + List_iterator li(*((Item_cond *)cond)->argument_list()); + Item *item; + while ((item = li++)) { + if (item->type() == Item::FUNC_ITEM) { + if (get_lookup_value((Item_func *)item, table, lookup_field_vals)) + return 1; + } else { + if (calc_lookup_values_from_cond(item, table, lookup_field_vals)) + return 1; + } + } + } + return 0; + } else if (cond->type() == Item::FUNC_ITEM && + get_lookup_value((Item_func *)cond, table, lookup_field_vals)) + return 1; + return 0; +} + +/** + @brief Calculate lookup values(database name, table name) + + @details This function calculates lookup values(database name, table name) + from 'WHERE' condition and fill lookup_field_vals struct field + with these values. + + @param[in] cond WHERE condition + @param[in] tables I_S table + @param[in, out] lookup_field_vals Struct which holds lookup values + + @return + 0 success + 1 error, there can be no matching records for the condition +*/ + +static bool get_lookup_field_values(Item *cond, Table_ref *tables, + LOOKUP_FIELD_VALUES *lookup_field_vals) +{ + bool rc = calc_lookup_values_from_cond(cond, tables, lookup_field_vals); + + if (lower_case_table_names && !rc) { + /* + We can safely do in-place upgrades here since all of the above cases + are allocating a new memory buffer for these strings. + */ + if (lookup_field_vals->db_value && !lookup_field_vals->db_value->empty()) + casedn(system_charset_info, *lookup_field_vals->db_value); + if (lookup_field_vals->table_value && !lookup_field_vals->table_value->empty()) + casedn(system_charset_info, *lookup_field_vals->table_value); + } + + return rc; +} + +/*----------------------------------------------------------------------------*/ + +static bool is_special_db(const dd::String_type &db_name) +{ + /* I_S does not exist and we hide P_S */ + return is_infoschema_db(db_name.c_str(), db_name.size()) + || is_perfschema_db(db_name.c_str(), db_name.size()); +} + +/** + Return either single or all schemas + + @param[in] thd thread handler + @param[out] db_names list of schemas + @param[in] lookup_field_vals Struct which holds lookup values + + @return true if error, false otherwise +*/ +static int make_db_list(THD *thd, std::vector *db_names, + LOOKUP_FIELD_VALUES *lookup_field_vals) +{ + /* + If we have db lookup value we just add it to list and + exit from the function. + We don't do this for database names longer than the maximum + path length. + */ + if (lookup_field_vals->db_value + && lookup_field_vals->db_value->length() <= NAME_LEN) + { + const dd::String_type &db_name = lookup_field_vals->db_value.get(); + if (!is_special_db(db_name)) + db_names->push_back(db_name); + return 0; + } + + dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client()); + std::vector schemas; + if (thd->dd_client()->fetch_global_components(&schemas)) + return 1; + + for (const dd::Schema *schema_obj : schemas) + { + const dd::String_type &db_name = schema_obj->name(); + if (is_special_db(db_name)) + continue; + db_names->push_back(db_name); + } + return 0; +} + +/*----------------------------------------------------------------------------*/ + +/** + Find files in a given directory. + + @param thd thread handler + @param tables put found files in this list + @param db_name filter for found files + @param wild filter for found files + + @return true if error, false otherwise +*/ +typedef std::map Table_sizes_map; +bool find_tables(THD *thd, Table_sizes_map &tables, + Table_ref &db_data, boost::optional &wild) +{ + MY_DIR *dirp; + DBUG_ENTER("find_files"); + + char path[FN_REFLEN + 1]; + build_table_filename(path, sizeof(path) - 1, db_data.db, "", "", 0); + + if (!(dirp = my_dir(path, MYF(MY_WANT_STAT)))) + { + if (my_errno() == ENOENT) + my_error(ER_BAD_DB_ERROR, MYF(0), db_data.db); + else + { + char errbuf[MYSYS_STRERROR_SIZE]; + my_error(ER_CANT_READ_DIR, MYF(0), path, + my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno())); + } + return true; + } + + char uname[NAME_LEN + 1]; /* Unencoded name */ + dd::String_type fts_schema_name, fts_table_name; /* FTS lookup */ + std::map fts_cache; + tables.clear(); + for (uint i = 0; i < dirp->number_off_files; i++) + { + FILEINFO *file = dirp->dir_entry + i; + /* skip '.', '..' and temp files. */ + if ((file->name[0] == '.' && + (!file->name[1] || (file->name[1] == '.' && !file->name[2]))) + || is_prefix(file->name, tmp_file_prefix)) + continue; + + char *ext; + if ((ext = strrchr(file->name, '.'))) + { + // sdi files are small and their name is truncated + // lookup by id is possible but not worth the effort + if (!strcmp(ext, dd::sdi_file::EXT.c_str())) + continue; + *ext = 0; + ++ext; + } + + size_t file_name_len; + fts_aux_table_t fts_table; + if (ext && !strcmp(ext, "ibd") + && fts_is_aux_table_name(&fts_table, file->name, strlen(file->name))) + { + auto entry = fts_cache.find(fts_table.parent_id); + if (entry != fts_cache.end()) + fts_table_name = entry->second; + else if (thd->dd_client()->get_table_name_by_se_private_id("InnoDB", + fts_table.parent_id, &fts_schema_name, &fts_table_name)) + continue; + + file_name_len = (my_stpnmov(uname, fts_table_name.c_str(), sizeof(uname)) - uname); + } + else + file_name_len = filename_to_tablename(file->name, uname, sizeof(uname)); + + auto table_data = tables.find(uname); + if (table_data != tables.end()) + { + table_data->second += file->mystat->st_size; + continue; + } + + if (wild) + { + if (lower_case_table_names) + { + if (my_wildcmp(files_charset_info, + uname, uname + file_name_len, + wild->c_str(), wild->c_str() + wild->length(), + wild_prefix, wild_one, wild_many)) + continue; + } + else if (wild_compare(uname, file_name_len, wild->c_str(), wild->length(), 0)) + continue; + } + + /* + Check_grant will grant access if there is any column privileges on + all of the tables thanks to the fourth parameter (bool show_table). + */ + db_data.table_name = uname; + db_data.table_name_length = file_name_len; + if (check_grant(thd, SELECT_ACL, &db_data, true, 1, true)) + continue; + + tables.emplace(uname, file->mystat->st_size); + } + + DBUG_PRINT("info", ("found: %ld files", tables.size())); + my_dirend(dirp); + + return false; +} + +static bool make_table_list(THD *thd, Table_sizes_map &table_names, + LOOKUP_FIELD_VALUES *lookup_field_vals, Table_ref &db_data) +{ + const dd::Schema *sch_obj = NULL; + if (thd->dd_client()->acquire(db_data.db, &sch_obj) || !sch_obj) + return 1; + + return find_tables(thd, table_names, db_data, lookup_field_vals->table_value); +} + +/*----------------------------------------------------------------------------*/ + +static ST_FIELD_INFO fields_info[] = +{ + {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0}, + {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", 0}, + {"TABLE_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + MY_I_S_UNSIGNED, "Table_size", 0}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} +}; + +static int fill_table(THD *thd, Table_ref *tables, Item *cond) +{ + TABLE *table = tables->table; + LOOKUP_FIELD_VALUES lookup_field_vals; + std::vector db_names; + + DBUG_ENTER("fill_table"); + + if (get_lookup_field_values(cond, tables, &lookup_field_vals)) + return 0; + +#define STR_OR_NIL(S) ((S) ? (S->c_str()) : "") + DBUG_PRINT("INDEX VALUES", ("db_name='%s', table_name='%s'", + STR_OR_NIL(lookup_field_vals.db_value), + STR_OR_NIL(lookup_field_vals.table_value))); + + /* EXPLAIN SELECT */ + if (thd->lex->is_explain()) + return 0; + + if (make_db_list(thd, &db_names, &lookup_field_vals)) + return 1; + + for (auto &db_name : db_names) + { + Table_ref db_data; + memset(reinterpret_cast(&db_data), 0, sizeof(db_data)); + db_data.db = db_name.c_str(); + db_data.db_length = db_name.length(); + db_data.grant.privilege = 0; + + // Get user's global and db-level privileges. + if (check_access(thd, SELECT_ACL, db_data.db, &db_data.grant.privilege, NULL, false, true)) + continue; + + // Now check, if user has access on global level or to any of database/ + // table/column/routine. + if (!(db_data.grant.privilege & DB_OP_ACLS) && check_grant_db(thd, db_data.db)) + continue; + + // We must make sure the schema is released and unlocked in the right + // order. Fail if we are unable to get a meta data lock on the schema + // name. + dd::Schema_MDL_locker mdl_handler(thd); + if (mdl_handler.ensure_locked(db_data.db)) + return 1; + + dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client()); + Table_sizes_map tables_data; + int res = make_table_list(thd, tables_data, &lookup_field_vals, db_data); + if (res) + continue; + + for (auto &table_data : tables_data) + { + table->field[0]->store(db_data.db, db_data.db_length, + system_charset_info); + table->field[1]->store(table_data.first.c_str(), table_data.first.size(), + system_charset_info); + table->field[2]->store(table_data.second, true); + if (schema_table_store_record(thd, table)) + return 1; + } + } + + return 0; +} + +static int init(void *ptr) +{ + ST_SCHEMA_TABLE *schema_table = (ST_SCHEMA_TABLE*)ptr; + + schema_table->fields_info = fields_info; + schema_table->fill_table = fill_table; + return 0; +} + +static struct st_mysql_information_schema info = +{ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; + +mysql_declare_plugin(table_sizes) +{ + MYSQL_INFORMATION_SCHEMA_PLUGIN, + &info, /* type-specific descriptor */ + "TABLE_SIZES", /* table name */ + "Manuel Mausz", /* author */ + "Fast INFORMATION_SCHEMA table sizes", /* description */ + PLUGIN_LICENSE_GPL, /* license type */ + init, /* init function */ + NULL, + NULL, + 0x0800, /* version = 8.0 */ + NULL, /* no status variables */ + NULL, /* no system variables */ + NULL, /* no reserved information */ + 0 /* no flags */ +} +mysql_declare_plugin_end; -- cgit v1.2.3