From 552a69076f8c93c3e3c57c0a26388faa162f9f46 Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 11 Feb 2025 10:41:08 +0100 Subject: Update for MySQL 8.4 --- CMakeLists.txt | 3 +- table_sizes.cc | 418 +++++++++++++++++++++++++++++---------------------------- 2 files changed, 211 insertions(+), 210 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7392af9..e195e8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,3 @@ -INCLUDE_DIRECTORIES(SYSTEM ${BOOST_PATCHES_DIR} ${BOOST_INCLUDE_DIR}) - MYSQL_ADD_PLUGIN(table_sizes table_sizes.cc MODULE_ONLY MODULE_OUTPUT_NAME "table_sizes" @@ -7,3 +5,4 @@ MYSQL_ADD_PLUGIN(table_sizes ) ADD_DEFINITIONS(-DMYSQL_SERVER) +ADD_DEFINITIONS(-DLOG_COMPONENT_TAG="TABLE_SIZES") diff --git a/table_sizes.cc b/table_sizes.cc index ec8efc0..ef12d19 100644 --- a/table_sizes.cc +++ b/table_sizes.cc @@ -13,8 +13,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include +#include "mysql/components/my_service.h" +#include "mysql/components/services/log_builtins.h" +#include "mysql/plugin.h" #include "sql/mysqld.h" #include "sql/sql_base.h" @@ -30,10 +31,19 @@ #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 "sql/auth/auth_common.h" // check_access + check_grant_db #include "mf_wcomp.h" // wild_compare,wild_one,wild_many #include "my_dir.h" // MY_DIR +// MySQL 8.0 logger service interface +static SERVICE_TYPE(registry) *reg_srv = nullptr; +SERVICE_TYPE(log_builtins) *log_bi = nullptr; +SERVICE_TYPE(log_builtins_string) *log_bs = nullptr; + +namespace table_sizes { + +static const char *IS_TABLE_NAME = "TABLE_SIZES"; + // InnoDB FTS /** For storing table info when checking for orphaned tables. */ struct fts_aux_table_t @@ -60,23 +70,23 @@ bool fts_is_aux_table_name(fts_aux_table_t *table, const char *name, /** 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}; + "deleted_cache", nullptr}; const char *fts_common_tables_5_7[] = {"BEING_DELETED", "BEING_DELETED_CACHE", "CONFIG", "DELETED", - "DELETED_CACHE", NULL}; + "DELETED_CACHE", nullptr}; /** FTS auxiliary INDEX split intervals. */ const char *fts_index_selector[] = {"index_1", "index_2", "index_3", "index_4", - "index_5", "index_6", NULL}; + "index_5", "index_6", nullptr}; const char *fts_index_selector_5_7[] = {"INDEX_1", "INDEX_2", "INDEX_3", "INDEX_4", - "INDEX_5", "INDEX_6", NULL}; + "INDEX_5", "INDEX_6", nullptr}; 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 && + if (ptr != nullptr && len > 20 && (strncmp(ptr, FTS_PREFIX, 4) == 0 || strncmp(ptr, FTS_PREFIX_5_7, 4) == 0)) { @@ -90,15 +100,19 @@ bool fts_is_aux_table_name(fts_aux_table_t *table, const char *name, /* Skip the table id. */ ptr = static_cast(memchr(ptr, '_', len)); - if (ptr == NULL) + if (ptr == nullptr) return false; /* Skip the underscore. */ ++ptr; len = end - ptr; + /* It's not enough to be a FTS auxiliary table name */ + if (len == 0) + return false; + /* First search the common table suffix array. */ - for (int i = 0; fts_common_tables[i] != NULL; ++i) { + for (int i = 0; fts_common_tables[i] != nullptr; ++i) { if (strncmp(ptr, fts_common_tables[i], len) == 0 || strncmp(ptr, fts_common_tables_5_7[i], len) == 0) { return true; @@ -118,15 +132,19 @@ bool fts_is_aux_table_name(fts_aux_table_t *table, const char *name, /* Skip the index id. */ ptr = static_cast(memchr(ptr, '_', len)); - if (ptr == NULL) + if (ptr == nullptr) return false; /* Skip the underscore. */ ++ptr; len = end - ptr; + /* It's not enough to be a FTS auxiliary table name */ + if (len == 0) + return false; + /* Search the FT index specific array. */ - for (int i = 0; fts_index_selector[i] != NULL; ++i) { + for (int i = 0; fts_index_selector[i] != nullptr; ++i) { if (strncmp(ptr, fts_index_selector[i], len) == 0 || strncmp(ptr, fts_index_selector_5_7[i], len) == 0) { return true; @@ -143,58 +161,27 @@ bool fts_is_aux_table_name(fts_aux_table_t *table, const char *name, /*----------------------------------------------------------------------------*/ -/** - 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; + std::map db_values; + std::map table_values; }; -/** - @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, +static bool get_lookup_value(THD *thd, 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; + String foo; + item_func->print(thd, &foo, QT_ORDINARY); + if (item_func->functype() == Item_func::EQ_FUNC || - item_func->functype() == Item_func::EQUAL_FUNC) { + item_func->functype() == Item_func::EQUAL_FUNC || + item_func->functype() == Item_func::LIKE_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; + String *tmp_str, str_buf(tmp, sizeof(tmp), system_charset_info); + bool is_wildcard = (item_func->functype() == Item_func::LIKE_FUNC); if (item_func->arguments()[0]->type() == Item::FIELD_ITEM && item_func->arguments()[1]->const_item()) { @@ -207,109 +194,93 @@ static bool get_lookup_value(Item_func *item_func, Table_ref *table, } 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); + Item_field *item_field = (Item_field *)item_func->arguments()[idx_field]; + if (table->table != item_field->field->table) + return 0; - /* impossible value */ - if (!tmp_str) return 1; + tmp_str = item_func->arguments()[idx_val]->val_str(&str_buf); + if (!tmp_str || !tmp_str->length()) + return 0; + if (lower_case_table_names && files_charset_info->casedn_multiply == 1) + my_casedn_str(files_charset_info, tmp_str->c_ptr_quick()); - /* 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()); + if (my_strcasecmp(system_charset_info, item_field->field_name, + field_info[0].field_name) == 0) { + /* Lookup value is database name */ + lookup_field_vals->db_values[dd::String_type(tmp_str->ptr(), tmp_str->length())] = is_wildcard; } - /* 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()); + else if (my_strcasecmp(system_charset_info, item_field->field_name, + field_info[1].field_name) == 0) { + /* Lookup value is table name */ + lookup_field_vals->table_values[dd::String_type(tmp_str->ptr(), tmp_str->length())] = is_wildcard; } } - return 0; -} + else if (item_func->functype() == Item_func::IN_FUNC) + { + Item_func_in *in_func = static_cast(item_func); + char tmp[MAX_FIELD_WIDTH]; + String *tmp_str, str_buf(tmp, sizeof(tmp), system_charset_info); + + if (in_func->arguments()[0]->type() != Item::FIELD_ITEM) + return 0; + + Item_field *item_field = (Item_field *)in_func->arguments()[0]; + if (table->table != item_field->field->table) + return 0; -/** - @brief Calculates lookup values from 'WHERE' condition + for (uint i = 1; i < in_func->arg_count; ++i) + { + if (in_func->arguments()[i] == nullptr) + continue; - @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. + tmp_str = in_func->arguments()[i]->val_str(&str_buf); + if (!tmp_str || !tmp_str->length()) + continue; + if (lower_case_table_names && files_charset_info->casedn_multiply == 1) + my_casedn_str(files_charset_info, tmp_str->c_ptr_quick()); - @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 + /* Lookup value is database name */ + if (my_strcasecmp(system_charset_info, item_field->field_name, + field_info[0].field_name) == 0) { + lookup_field_vals->db_values[dd::String_type(tmp_str->ptr(), tmp_str->length())] = false; + } + else if (my_strcasecmp(system_charset_info, item_field->field_name, + field_info[1].field_name) == 0) { + lookup_field_vals->table_values[dd::String_type(tmp_str->ptr(), tmp_str->length())] = false; + } + } + } - @return - 0 success - 1 error, there can be no matching records for the condition -*/ + return 0; +} -static bool calc_lookup_values_from_cond(Item *cond, +static bool calc_lookup_values_from_cond(THD *thd, 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) { + auto func_type = ((Item_cond *)cond)->functype(); + if (func_type == 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)) + if (get_lookup_value(thd, (Item_func *)item, table, lookup_field_vals)) return 1; } else { - if (calc_lookup_values_from_cond(item, table, lookup_field_vals)) + if (calc_lookup_values_from_cond(thd, 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)) + get_lookup_value(thd, (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) @@ -319,33 +290,9 @@ static bool is_special_db(const dd::String_type &db_name) || 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)) @@ -354,28 +301,34 @@ static int make_db_list(THD *thd, std::vector *db_names, for (const dd::Schema *schema_obj : schemas) { const dd::String_type &db_name = schema_obj->name(); - if (is_special_db(db_name)) + if (db_name.length() > NAME_LEN || is_special_db(db_name)) continue; - db_names->push_back(db_name); + bool match = true; + for (auto &db_value : lookup_field_vals->db_values) + { + dd::String_type wildx = db_value.first; + match = (db_value.second) + ? (lower_case_table_names) + ? !my_wildcmp(files_charset_info, + db_name.c_str(), db_name.c_str() + db_name.length(), + wildx.c_str(), wildx.c_str() + wildx.length(), + wild_prefix, wild_one, wild_many) + : !wild_compare(db_name.c_str(), db_name.length(), wildx.c_str(), wildx.length(), 0) + : (my_strcasecmp(files_charset_info, db_name.c_str(), wildx.c_str()) == 0); + if (match) + break; + } + if (match) + 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) +static bool find_tables(THD *thd, Table_sizes_map &tables, + Table_ref &db_data, std::map &wild) { MY_DIR *dirp; DBUG_ENTER("find_files"); @@ -396,6 +349,10 @@ bool find_tables(THD *thd, Table_sizes_map &tables, return true; } + /* check global privilege */ + bool has_global_show = (thd->security_context() + ->master_access(db_data.db) & (DB_OP_ACLS | SHOW_DB_ACL)); + char uname[NAME_LEN + 1]; /* Unencoded name */ dd::String_type fts_schema_name, fts_table_name; /* FTS lookup */ std::map fts_cache; @@ -444,19 +401,23 @@ bool find_tables(THD *thd, Table_sizes_map &tables, continue; } - if (wild) + bool match = true; + for (auto &wild_entry : 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; + dd::String_type wildx = wild_entry.first; + match = (wild_entry.second) + ? (lower_case_table_names) + ? !my_wildcmp(files_charset_info, + uname, uname + file_name_len, + wildx.c_str(), wildx.c_str() + wildx.length(), + wild_prefix, wild_one, wild_many) + : !wild_compare(uname, file_name_len, wildx.c_str(), wildx.length(), 0) + : (my_strcasecmp(files_charset_info, uname, wildx.c_str()) == 0); + if (match) + break; } + if (!match) + continue; /* Check_grant will grant access if there is any column privileges on @@ -464,7 +425,7 @@ bool find_tables(THD *thd, Table_sizes_map &tables, */ db_data.table_name = uname; db_data.table_name_length = file_name_len; - if (check_grant(thd, SELECT_ACL, &db_data, true, 1, true)) + if (!has_global_show && check_grant(thd, SELECT_ACL, &db_data, true, 1, true)) continue; tables.emplace(uname, file->mystat->st_size); @@ -479,11 +440,11 @@ bool find_tables(THD *thd, Table_sizes_map &tables, 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; + const dd::Schema *sch_obj = nullptr; 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); + return find_tables(thd, table_names, db_data, lookup_field_vals->table_values); } /*----------------------------------------------------------------------------*/ @@ -497,22 +458,39 @@ static ST_FIELD_INFO fields_info[] = {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} }; +static bool can_access_database(THD *thd, Table_ref &db_data) +{ + Security_context *sctx = thd->security_context(); + + /* check global privileges */ + if (sctx->master_access(db_data.db) & (DB_OP_ACLS | SHOW_DB_ACL)) + return true; + + /* check db privileges */ + Access_bitmask db_access = sctx->check_db_level_access(thd, db_data.db, db_data.db_length); + //if (check_access(thd, SELECT_ACL, db_data.db, &db_access, nullptr, false, true)) + // return false; + if (db_access) { + db_data.grant.privilege = db_access; + return true; + } + + /* check specific table privileges */ + if (!check_grant_db(thd, db_data.db)) + return true; + + return false; +} + static int fill_table(THD *thd, Table_ref *tables, Item *cond) { TABLE *table = tables->table; - LOOKUP_FIELD_VALUES lookup_field_vals; + LOOKUP_FIELD_VALUES lookup_field_vals = {}; std::vector db_names; - DBUG_ENTER("fill_table"); - - if (get_lookup_field_values(cond, tables, &lookup_field_vals)) + if (calc_lookup_values_from_cond(thd, 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; @@ -528,13 +506,7 @@ static int fill_table(THD *thd, Table_ref *tables, Item *cond) 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)) + if (!can_access_database(thd, db_data)) continue; // We must make sure the schema is released and unlocked in the right @@ -565,33 +537,63 @@ static int fill_table(THD *thd, Table_ref *tables, Item *cond) return 0; } -static int init(void *ptr) +static int view_init(void *ptr) { + if (init_logging_service_for_plugin(®_srv, &log_bi, &log_bs)) + return 1; + + LogErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, + "Plugin table_sizes initializing..."); + + if (reg_srv == nullptr) + { + LogPluginErrMsg(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "reg_srv is NULL in init"); + return 1; + } + ST_SCHEMA_TABLE *schema_table = (ST_SCHEMA_TABLE*)ptr; - schema_table->fields_info = fields_info; - schema_table->fill_table = fill_table; + schema_table->table_name = IS_TABLE_NAME; + schema_table->fields_info = fields_info; + schema_table->fill_table = fill_table; + schema_table->old_format = nullptr; + schema_table->process_table = nullptr; + return 0; +} + +static int view_deinit(void *) +{ + LogPluginErrMsg(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, + "Plugin table_sizes de-initializing..."); + + if (reg_srv == nullptr) + LogPluginErrMsg(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "reg_srv is NULL in deinit"); + else + deinit_logging_service_for_plugin(®_srv, &log_bi, &log_bs); + return 0; } static struct st_mysql_information_schema info = { MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; +} // namespace table_sizes + mysql_declare_plugin(table_sizes) { - MYSQL_INFORMATION_SCHEMA_PLUGIN, - &info, /* type-specific descriptor */ - "TABLE_SIZES", /* table name */ + MYSQL_INFORMATION_SCHEMA_PLUGIN, /* type */ + &table_sizes::info, /* descriptor */ + table_sizes::IS_TABLE_NAME, /* 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 */ + PLUGIN_LICENSE_GPL, /* plugin license */ + table_sizes::view_init, /* init function (when loaded) */ + nullptr, /* check uninstall function */ + table_sizes::view_deinit, /* deinit function (when unloaded) */ + 0x0840, /* version = 8.4 */ + nullptr, /* status variables */ + nullptr, /* system variables */ + nullptr, /* reserved information */ + 0 /* flags */ } mysql_declare_plugin_end; -- cgit v1.2.3