diff options
Diffstat (limited to 'table_sizes.cc')
| -rw-r--r-- | table_sizes.cc | 597 |
1 files changed, 597 insertions, 0 deletions
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 @@ | |||
| 1 | /* | ||
| 2 | This program is free software; you can redistribute it and/or modify | ||
| 3 | it under the terms of the GNU General Public License as published by | ||
| 4 | the Free Software Foundation; version 2 of the License. | ||
| 5 | |||
| 6 | This program is distributed in the hope that it will be useful, | ||
| 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 9 | GNU General Public License for more details. | ||
| 10 | |||
| 11 | You should have received a copy of the GNU General Public License | ||
| 12 | along with this program; if not, write to the Free Software | ||
| 13 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <mysql/plugin.h> | ||
| 17 | #include <boost/optional/optional.hpp> | ||
| 18 | |||
| 19 | #include "sql/mysqld.h" | ||
| 20 | #include "sql/sql_base.h" | ||
| 21 | #include "sql/sql_class.h" // THD | ||
| 22 | #include "sql/sql_table.h" // filename_to_tablename | ||
| 23 | #include "sql/strfunc.h" // casedn | ||
| 24 | #include "sql/sql_parse.h" // sql_command_flags | ||
| 25 | #include "sql/sql_lex.h" // LEX | ||
| 26 | #include "sql/item_func.h" // Item_Func | ||
| 27 | #include "sql/item_cmpfunc.h" // Item_cond | ||
| 28 | #include "sql/dd/cache/dictionary_client.h" // dd::cache::Dictionary_client | ||
| 29 | #include "sql/dd/dd_schema.h" // dd::Schema_MDL_locker | ||
| 30 | #include "sql/dd/string_type.h" // dd::String_type | ||
| 31 | #include "sql/dd/sdi_file.h" // dd::sdi_file | ||
| 32 | #include "sql/auth/auth_acls.h" // *_ACLS | ||
| 33 | #include "sql/auth/auth_common.h" // check_grant_db | ||
| 34 | #include "mf_wcomp.h" // wild_compare,wild_one,wild_many | ||
| 35 | #include "my_dir.h" // MY_DIR | ||
| 36 | |||
| 37 | // InnoDB FTS | ||
| 38 | /** For storing table info when checking for orphaned tables. */ | ||
| 39 | struct fts_aux_table_t | ||
| 40 | { | ||
| 41 | /** Parent table id */ | ||
| 42 | uint64_t parent_id; | ||
| 43 | |||
| 44 | /** Table FT index id */ | ||
| 45 | uint64_t index_id; | ||
| 46 | }; | ||
| 47 | |||
| 48 | /** Check if a table is an FTS auxiliary table name. | ||
| 49 | @param[out] table FTS table info | ||
| 50 | @param[in] name Table name | ||
| 51 | @param[in] len Length of table name | ||
| 52 | @return true if the name matches an auxiliary table name pattern */ | ||
| 53 | bool fts_is_aux_table_name(fts_aux_table_t *table, const char *name, | ||
| 54 | int len) | ||
| 55 | { | ||
| 56 | /** FTS auxiliary table prefix that are common to all FT indexes.*/ | ||
| 57 | const char *FTS_PREFIX = "fts_"; | ||
| 58 | const char *FTS_PREFIX_5_7 = "FTS_"; | ||
| 59 | |||
| 60 | /** FTS auxiliary table suffixes that are common to all FT indexes. */ | ||
| 61 | const char *fts_common_tables[] = {"being_deleted", "being_deleted_cache", | ||
| 62 | "config", "deleted", | ||
| 63 | "deleted_cache", NULL}; | ||
| 64 | const char *fts_common_tables_5_7[] = {"BEING_DELETED", "BEING_DELETED_CACHE", | ||
| 65 | "CONFIG", "DELETED", | ||
| 66 | "DELETED_CACHE", NULL}; | ||
| 67 | |||
| 68 | /** FTS auxiliary INDEX split intervals. */ | ||
| 69 | const char *fts_index_selector[] = {"index_1", "index_2", "index_3", "index_4", | ||
| 70 | "index_5", "index_6", NULL}; | ||
| 71 | const char *fts_index_selector_5_7[] = {"INDEX_1", "INDEX_2", "INDEX_3", "INDEX_4", | ||
| 72 | "INDEX_5", "INDEX_6", NULL}; | ||
| 73 | |||
| 74 | const char *ptr = name; | ||
| 75 | const char *end = name + len; | ||
| 76 | |||
| 77 | /* All auxiliary tables are prefixed with "FTS_" and the name | ||
| 78 | length will be at the very least greater than 20 bytes. */ | ||
| 79 | if (ptr != NULL && len > 20 && | ||
| 80 | (strncmp(ptr, FTS_PREFIX, 4) == 0 || | ||
| 81 | strncmp(ptr, FTS_PREFIX_5_7, 4) == 0)) | ||
| 82 | { | ||
| 83 | /* Skip the prefix. */ | ||
| 84 | ptr += 4; | ||
| 85 | len -= 4; | ||
| 86 | |||
| 87 | /* Try and read the table id. */ | ||
| 88 | if (sscanf(ptr, "%016" PRIx64, &table->parent_id) != 1) | ||
| 89 | return false; | ||
| 90 | |||
| 91 | /* Skip the table id. */ | ||
| 92 | ptr = static_cast<const char *>(memchr(ptr, '_', len)); | ||
| 93 | if (ptr == NULL) | ||
| 94 | return false; | ||
| 95 | |||
| 96 | /* Skip the underscore. */ | ||
| 97 | ++ptr; | ||
| 98 | len = end - ptr; | ||
| 99 | |||
| 100 | /* First search the common table suffix array. */ | ||
| 101 | for (int i = 0; fts_common_tables[i] != NULL; ++i) { | ||
| 102 | if (strncmp(ptr, fts_common_tables[i], len) == 0 || | ||
| 103 | strncmp(ptr, fts_common_tables_5_7[i], len) == 0) { | ||
| 104 | return true; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | /* Could be obsolete common tables. */ | ||
| 109 | if (native_strncasecmp(ptr, "ADDED", len) == 0 || | ||
| 110 | native_strncasecmp(ptr, "STOPWORDS", len) == 0) { | ||
| 111 | return true; | ||
| 112 | } | ||
| 113 | |||
| 114 | /* Try and read the index id. */ | ||
| 115 | if (sscanf(ptr, "%016" PRIx64, &table->index_id) != 1) | ||
| 116 | return false; | ||
| 117 | |||
| 118 | /* Skip the index id. */ | ||
| 119 | ptr = static_cast<const char *>(memchr(ptr, '_', len)); | ||
| 120 | |||
| 121 | if (ptr == NULL) | ||
| 122 | return false; | ||
| 123 | |||
| 124 | /* Skip the underscore. */ | ||
| 125 | ++ptr; | ||
| 126 | len = end - ptr; | ||
| 127 | |||
| 128 | /* Search the FT index specific array. */ | ||
| 129 | for (int i = 0; fts_index_selector[i] != NULL; ++i) { | ||
| 130 | if (strncmp(ptr, fts_index_selector[i], len) == 0 || | ||
| 131 | strncmp(ptr, fts_index_selector_5_7[i], len) == 0) { | ||
| 132 | return true; | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | /* Other FT index specific table(s). */ | ||
| 137 | if (native_strncasecmp(ptr, "DOC_ID", len) == 0) | ||
| 138 | return true; | ||
| 139 | } | ||
| 140 | |||
| 141 | return false; | ||
| 142 | } | ||
| 143 | |||
| 144 | /*----------------------------------------------------------------------------*/ | ||
| 145 | |||
| 146 | /** | ||
| 147 | Condition pushdown used for INFORMATION_SCHEMA / SHOW queries. | ||
| 148 | This structure is to implement an optimization when | ||
| 149 | accessing data dictionary data in the INFORMATION_SCHEMA | ||
| 150 | or SHOW commands. | ||
| 151 | When the query contain a TABLE_SCHEMA or TABLE_NAME clause, | ||
| 152 | narrow the search for data based on the constraints given. | ||
| 153 | */ | ||
| 154 | struct LOOKUP_FIELD_VALUES { | ||
| 155 | /** | ||
| 156 | Value of a TABLE_SCHEMA clause. | ||
| 157 | Note that this value length may exceed @c NAME_LEN. | ||
| 158 | */ | ||
| 159 | boost::optional<dd::String_type> db_value; | ||
| 160 | /** | ||
| 161 | Value of a TABLE_NAME clause. | ||
| 162 | Note that this value length may exceed @c NAME_LEN. | ||
| 163 | */ | ||
| 164 | boost::optional<dd::String_type> table_value; | ||
| 165 | }; | ||
| 166 | |||
| 167 | /** | ||
| 168 | @brief Get lookup value from the part of 'WHERE' condition | ||
| 169 | |||
| 170 | @details This function gets lookup value from | ||
| 171 | the part of 'WHERE' condition if it's possible and | ||
| 172 | fill appropriate lookup_field_vals struct field | ||
| 173 | with this value. | ||
| 174 | |||
| 175 | @param[in] thd thread handler | ||
| 176 | @param[in] item_func part of WHERE condition | ||
| 177 | @param[in] table I_S table | ||
| 178 | @param[in, out] lookup_field_vals Struct which holds lookup values | ||
| 179 | |||
| 180 | @return | ||
| 181 | 0 success | ||
| 182 | 1 error, there can be no matching records for the condition | ||
| 183 | */ | ||
| 184 | |||
| 185 | static bool get_lookup_value(Item_func *item_func, Table_ref *table, | ||
| 186 | LOOKUP_FIELD_VALUES *lookup_field_vals) | ||
| 187 | { | ||
| 188 | ST_SCHEMA_TABLE *schema_table = table->schema_table; | ||
| 189 | ST_FIELD_INFO *field_info = schema_table->fields_info; | ||
| 190 | |||
| 191 | if (item_func->functype() == Item_func::EQ_FUNC || | ||
| 192 | item_func->functype() == Item_func::EQUAL_FUNC) { | ||
| 193 | int idx_field, idx_val; | ||
| 194 | char tmp[MAX_FIELD_WIDTH]; | ||
| 195 | String *tmp_str, str_buff(tmp, sizeof(tmp), system_charset_info); | ||
| 196 | Item_field *item_field; | ||
| 197 | CHARSET_INFO *cs = system_charset_info; | ||
| 198 | |||
| 199 | if (item_func->arguments()[0]->type() == Item::FIELD_ITEM && | ||
| 200 | item_func->arguments()[1]->const_item()) { | ||
| 201 | idx_field = 0; | ||
| 202 | idx_val = 1; | ||
| 203 | } else if (item_func->arguments()[1]->type() == Item::FIELD_ITEM && | ||
| 204 | item_func->arguments()[0]->const_item()) { | ||
| 205 | idx_field = 1; | ||
| 206 | idx_val = 0; | ||
| 207 | } else | ||
| 208 | return 0; | ||
| 209 | |||
| 210 | item_field = (Item_field *)item_func->arguments()[idx_field]; | ||
| 211 | if (table->table != item_field->field->table) return 0; | ||
| 212 | tmp_str = item_func->arguments()[idx_val]->val_str(&str_buff); | ||
| 213 | |||
| 214 | /* impossible value */ | ||
| 215 | if (!tmp_str) return 1; | ||
| 216 | |||
| 217 | /* Lookup value is database name */ | ||
| 218 | if (!cs->coll->strnncollsp(cs, (const uchar *)field_info[0].field_name, | ||
| 219 | strlen(field_info[0].field_name), | ||
| 220 | (const uchar *)item_field->field_name, | ||
| 221 | strlen(item_field->field_name))) { | ||
| 222 | lookup_field_vals->db_value.emplace(tmp_str->ptr(), tmp_str->length()); | ||
| 223 | } | ||
| 224 | /* Lookup value is table name */ | ||
| 225 | else if (!cs->coll->strnncollsp(cs, (const uchar *)field_info[1].field_name, | ||
| 226 | strlen(field_info[1].field_name), | ||
| 227 | (const uchar *)item_field->field_name, | ||
| 228 | strlen(item_field->field_name))) { | ||
| 229 | lookup_field_vals->table_value.emplace(tmp_str->ptr(), tmp_str->length()); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | return 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | /** | ||
| 236 | @brief Calculates lookup values from 'WHERE' condition | ||
| 237 | |||
| 238 | @details This function calculates lookup value(database name, table name) | ||
| 239 | from 'WHERE' condition if it's possible and | ||
| 240 | fill lookup_field_vals struct fields with these values. | ||
| 241 | |||
| 242 | @param[in] thd thread handler | ||
| 243 | @param[in] cond WHERE condition | ||
| 244 | @param[in] table I_S table | ||
| 245 | @param[in, out] lookup_field_vals Struct which holds lookup values | ||
| 246 | |||
| 247 | @return | ||
| 248 | 0 success | ||
| 249 | 1 error, there can be no matching records for the condition | ||
| 250 | */ | ||
| 251 | |||
| 252 | static bool calc_lookup_values_from_cond(Item *cond, | ||
| 253 | Table_ref *table, LOOKUP_FIELD_VALUES *lookup_field_vals) | ||
| 254 | { | ||
| 255 | if (!cond) return 0; | ||
| 256 | |||
| 257 | if (cond->type() == Item::COND_ITEM) { | ||
| 258 | if (((Item_cond *)cond)->functype() == Item_func::COND_AND_FUNC) { | ||
| 259 | List_iterator<Item> li(*((Item_cond *)cond)->argument_list()); | ||
| 260 | Item *item; | ||
| 261 | while ((item = li++)) { | ||
| 262 | if (item->type() == Item::FUNC_ITEM) { | ||
| 263 | if (get_lookup_value((Item_func *)item, table, lookup_field_vals)) | ||
| 264 | return 1; | ||
| 265 | } else { | ||
| 266 | if (calc_lookup_values_from_cond(item, table, lookup_field_vals)) | ||
| 267 | return 1; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | } | ||
| 271 | return 0; | ||
| 272 | } else if (cond->type() == Item::FUNC_ITEM && | ||
| 273 | get_lookup_value((Item_func *)cond, table, lookup_field_vals)) | ||
| 274 | return 1; | ||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | /** | ||
| 279 | @brief Calculate lookup values(database name, table name) | ||
| 280 | |||
| 281 | @details This function calculates lookup values(database name, table name) | ||
| 282 | from 'WHERE' condition and fill lookup_field_vals struct field | ||
| 283 | with these values. | ||
| 284 | |||
| 285 | @param[in] cond WHERE condition | ||
| 286 | @param[in] tables I_S table | ||
| 287 | @param[in, out] lookup_field_vals Struct which holds lookup values | ||
| 288 | |||
| 289 | @return | ||
| 290 | 0 success | ||
| 291 | 1 error, there can be no matching records for the condition | ||
| 292 | */ | ||
| 293 | |||
| 294 | static bool get_lookup_field_values(Item *cond, Table_ref *tables, | ||
| 295 | LOOKUP_FIELD_VALUES *lookup_field_vals) | ||
| 296 | { | ||
| 297 | bool rc = calc_lookup_values_from_cond(cond, tables, lookup_field_vals); | ||
| 298 | |||
| 299 | if (lower_case_table_names && !rc) { | ||
| 300 | /* | ||
| 301 | We can safely do in-place upgrades here since all of the above cases | ||
| 302 | are allocating a new memory buffer for these strings. | ||
| 303 | */ | ||
| 304 | if (lookup_field_vals->db_value && !lookup_field_vals->db_value->empty()) | ||
| 305 | casedn(system_charset_info, *lookup_field_vals->db_value); | ||
| 306 | if (lookup_field_vals->table_value && !lookup_field_vals->table_value->empty()) | ||
| 307 | casedn(system_charset_info, *lookup_field_vals->table_value); | ||
| 308 | } | ||
| 309 | |||
| 310 | return rc; | ||
| 311 | } | ||
| 312 | |||
| 313 | /*----------------------------------------------------------------------------*/ | ||
| 314 | |||
| 315 | static bool is_special_db(const dd::String_type &db_name) | ||
| 316 | { | ||
| 317 | /* I_S does not exist and we hide P_S */ | ||
| 318 | return is_infoschema_db(db_name.c_str(), db_name.size()) | ||
| 319 | || is_perfschema_db(db_name.c_str(), db_name.size()); | ||
| 320 | } | ||
| 321 | |||
| 322 | /** | ||
| 323 | Return either single or all schemas | ||
| 324 | |||
| 325 | @param[in] thd thread handler | ||
| 326 | @param[out] db_names list of schemas | ||
| 327 | @param[in] lookup_field_vals Struct which holds lookup values | ||
| 328 | |||
| 329 | @return true if error, false otherwise | ||
| 330 | */ | ||
| 331 | static int make_db_list(THD *thd, std::vector<dd::String_type> *db_names, | ||
| 332 | LOOKUP_FIELD_VALUES *lookup_field_vals) | ||
| 333 | { | ||
| 334 | /* | ||
| 335 | If we have db lookup value we just add it to list and | ||
| 336 | exit from the function. | ||
| 337 | We don't do this for database names longer than the maximum | ||
| 338 | path length. | ||
| 339 | */ | ||
| 340 | if (lookup_field_vals->db_value | ||
| 341 | && lookup_field_vals->db_value->length() <= NAME_LEN) | ||
| 342 | { | ||
| 343 | const dd::String_type &db_name = lookup_field_vals->db_value.get(); | ||
| 344 | if (!is_special_db(db_name)) | ||
| 345 | db_names->push_back(db_name); | ||
| 346 | return 0; | ||
| 347 | } | ||
| 348 | |||
| 349 | dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client()); | ||
| 350 | std::vector<const dd::Schema *> schemas; | ||
| 351 | if (thd->dd_client()->fetch_global_components(&schemas)) | ||
| 352 | return 1; | ||
| 353 | |||
| 354 | for (const dd::Schema *schema_obj : schemas) | ||
| 355 | { | ||
| 356 | const dd::String_type &db_name = schema_obj->name(); | ||
| 357 | if (is_special_db(db_name)) | ||
| 358 | continue; | ||
| 359 | db_names->push_back(db_name); | ||
| 360 | } | ||
| 361 | return 0; | ||
| 362 | } | ||
| 363 | |||
| 364 | /*----------------------------------------------------------------------------*/ | ||
| 365 | |||
| 366 | /** | ||
| 367 | Find files in a given directory. | ||
| 368 | |||
| 369 | @param thd thread handler | ||
| 370 | @param tables put found files in this list | ||
| 371 | @param db_name filter for found files | ||
| 372 | @param wild filter for found files | ||
| 373 | |||
| 374 | @return true if error, false otherwise | ||
| 375 | */ | ||
| 376 | typedef std::map<dd::String_type, size_t> Table_sizes_map; | ||
| 377 | bool find_tables(THD *thd, Table_sizes_map &tables, | ||
| 378 | Table_ref &db_data, boost::optional<dd::String_type> &wild) | ||
| 379 | { | ||
| 380 | MY_DIR *dirp; | ||
| 381 | DBUG_ENTER("find_files"); | ||
| 382 | |||
| 383 | char path[FN_REFLEN + 1]; | ||
| 384 | build_table_filename(path, sizeof(path) - 1, db_data.db, "", "", 0); | ||
| 385 | |||
| 386 | if (!(dirp = my_dir(path, MYF(MY_WANT_STAT)))) | ||
| 387 | { | ||
| 388 | if (my_errno() == ENOENT) | ||
| 389 | my_error(ER_BAD_DB_ERROR, MYF(0), db_data.db); | ||
| 390 | else | ||
| 391 | { | ||
| 392 | char errbuf[MYSYS_STRERROR_SIZE]; | ||
| 393 | my_error(ER_CANT_READ_DIR, MYF(0), path, | ||
| 394 | my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno())); | ||
| 395 | } | ||
| 396 | return true; | ||
| 397 | } | ||
| 398 | |||
| 399 | char uname[NAME_LEN + 1]; /* Unencoded name */ | ||
| 400 | dd::String_type fts_schema_name, fts_table_name; /* FTS lookup */ | ||
| 401 | std::map<dd::Object_id, dd::String_type> fts_cache; | ||
| 402 | tables.clear(); | ||
| 403 | for (uint i = 0; i < dirp->number_off_files; i++) | ||
| 404 | { | ||
| 405 | FILEINFO *file = dirp->dir_entry + i; | ||
| 406 | /* skip '.', '..' and temp files. */ | ||
| 407 | if ((file->name[0] == '.' && | ||
| 408 | (!file->name[1] || (file->name[1] == '.' && !file->name[2]))) | ||
| 409 | || is_prefix(file->name, tmp_file_prefix)) | ||
| 410 | continue; | ||
| 411 | |||
| 412 | char *ext; | ||
| 413 | if ((ext = strrchr(file->name, '.'))) | ||
| 414 | { | ||
| 415 | // sdi files are small and their name is truncated | ||
| 416 | // lookup by id is possible but not worth the effort | ||
| 417 | if (!strcmp(ext, dd::sdi_file::EXT.c_str())) | ||
| 418 | continue; | ||
| 419 | *ext = 0; | ||
| 420 | ++ext; | ||
| 421 | } | ||
| 422 | |||
| 423 | size_t file_name_len; | ||
| 424 | fts_aux_table_t fts_table; | ||
| 425 | if (ext && !strcmp(ext, "ibd") | ||
| 426 | && fts_is_aux_table_name(&fts_table, file->name, strlen(file->name))) | ||
| 427 | { | ||
| 428 | auto entry = fts_cache.find(fts_table.parent_id); | ||
| 429 | if (entry != fts_cache.end()) | ||
| 430 | fts_table_name = entry->second; | ||
| 431 | else if (thd->dd_client()->get_table_name_by_se_private_id("InnoDB", | ||
| 432 | fts_table.parent_id, &fts_schema_name, &fts_table_name)) | ||
| 433 | continue; | ||
| 434 | |||
| 435 | file_name_len = (my_stpnmov(uname, fts_table_name.c_str(), sizeof(uname)) - uname); | ||
| 436 | } | ||
| 437 | else | ||
| 438 | file_name_len = filename_to_tablename(file->name, uname, sizeof(uname)); | ||
| 439 | |||
| 440 | auto table_data = tables.find(uname); | ||
| 441 | if (table_data != tables.end()) | ||
| 442 | { | ||
| 443 | table_data->second += file->mystat->st_size; | ||
| 444 | continue; | ||
| 445 | } | ||
| 446 | |||
| 447 | if (wild) | ||
| 448 | { | ||
| 449 | if (lower_case_table_names) | ||
| 450 | { | ||
| 451 | if (my_wildcmp(files_charset_info, | ||
| 452 | uname, uname + file_name_len, | ||
| 453 | wild->c_str(), wild->c_str() + wild->length(), | ||
| 454 | wild_prefix, wild_one, wild_many)) | ||
| 455 | continue; | ||
| 456 | } | ||
| 457 | else if (wild_compare(uname, file_name_len, wild->c_str(), wild->length(), 0)) | ||
| 458 | continue; | ||
| 459 | } | ||
| 460 | |||
| 461 | /* | ||
| 462 | Check_grant will grant access if there is any column privileges on | ||
| 463 | all of the tables thanks to the fourth parameter (bool show_table). | ||
| 464 | */ | ||
| 465 | db_data.table_name = uname; | ||
| 466 | db_data.table_name_length = file_name_len; | ||
| 467 | if (check_grant(thd, SELECT_ACL, &db_data, true, 1, true)) | ||
| 468 | continue; | ||
| 469 | |||
| 470 | tables.emplace(uname, file->mystat->st_size); | ||
| 471 | } | ||
| 472 | |||
| 473 | DBUG_PRINT("info", ("found: %ld files", tables.size())); | ||
| 474 | my_dirend(dirp); | ||
| 475 | |||
| 476 | return false; | ||
| 477 | } | ||
| 478 | |||
| 479 | static bool make_table_list(THD *thd, Table_sizes_map &table_names, | ||
| 480 | LOOKUP_FIELD_VALUES *lookup_field_vals, Table_ref &db_data) | ||
| 481 | { | ||
| 482 | const dd::Schema *sch_obj = NULL; | ||
| 483 | if (thd->dd_client()->acquire(db_data.db, &sch_obj) || !sch_obj) | ||
| 484 | return 1; | ||
| 485 | |||
| 486 | return find_tables(thd, table_names, db_data, lookup_field_vals->table_value); | ||
| 487 | } | ||
| 488 | |||
| 489 | /*----------------------------------------------------------------------------*/ | ||
| 490 | |||
| 491 | static ST_FIELD_INFO fields_info[] = | ||
| 492 | { | ||
| 493 | {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0}, | ||
| 494 | {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", 0}, | ||
| 495 | {"TABLE_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, | ||
| 496 | MY_I_S_UNSIGNED, "Table_size", 0}, | ||
| 497 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} | ||
| 498 | }; | ||
| 499 | |||
| 500 | static int fill_table(THD *thd, Table_ref *tables, Item *cond) | ||
| 501 | { | ||
| 502 | TABLE *table = tables->table; | ||
| 503 | LOOKUP_FIELD_VALUES lookup_field_vals; | ||
| 504 | std::vector<dd::String_type> db_names; | ||
| 505 | |||
| 506 | DBUG_ENTER("fill_table"); | ||
| 507 | |||
| 508 | if (get_lookup_field_values(cond, tables, &lookup_field_vals)) | ||
| 509 | return 0; | ||
| 510 | |||
| 511 | #define STR_OR_NIL(S) ((S) ? (S->c_str()) : "<nil>") | ||
| 512 | DBUG_PRINT("INDEX VALUES", ("db_name='%s', table_name='%s'", | ||
| 513 | STR_OR_NIL(lookup_field_vals.db_value), | ||
| 514 | STR_OR_NIL(lookup_field_vals.table_value))); | ||
| 515 | |||
| 516 | /* EXPLAIN SELECT */ | ||
| 517 | if (thd->lex->is_explain()) | ||
| 518 | return 0; | ||
| 519 | |||
| 520 | if (make_db_list(thd, &db_names, &lookup_field_vals)) | ||
| 521 | return 1; | ||
| 522 | |||
| 523 | for (auto &db_name : db_names) | ||
| 524 | { | ||
| 525 | Table_ref db_data; | ||
| 526 | memset(reinterpret_cast<char *>(&db_data), 0, sizeof(db_data)); | ||
| 527 | db_data.db = db_name.c_str(); | ||
| 528 | db_data.db_length = db_name.length(); | ||
| 529 | db_data.grant.privilege = 0; | ||
| 530 | |||
| 531 | // Get user's global and db-level privileges. | ||
| 532 | if (check_access(thd, SELECT_ACL, db_data.db, &db_data.grant.privilege, NULL, false, true)) | ||
| 533 | continue; | ||
| 534 | |||
| 535 | // Now check, if user has access on global level or to any of database/ | ||
| 536 | // table/column/routine. | ||
| 537 | if (!(db_data.grant.privilege & DB_OP_ACLS) && check_grant_db(thd, db_data.db)) | ||
| 538 | continue; | ||
| 539 | |||
| 540 | // We must make sure the schema is released and unlocked in the right | ||
| 541 | // order. Fail if we are unable to get a meta data lock on the schema | ||
| 542 | // name. | ||
| 543 | dd::Schema_MDL_locker mdl_handler(thd); | ||
| 544 | if (mdl_handler.ensure_locked(db_data.db)) | ||
| 545 | return 1; | ||
| 546 | |||
| 547 | dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client()); | ||
| 548 | Table_sizes_map tables_data; | ||
| 549 | int res = make_table_list(thd, tables_data, &lookup_field_vals, db_data); | ||
| 550 | if (res) | ||
| 551 | continue; | ||
| 552 | |||
| 553 | for (auto &table_data : tables_data) | ||
| 554 | { | ||
| 555 | table->field[0]->store(db_data.db, db_data.db_length, | ||
| 556 | system_charset_info); | ||
| 557 | table->field[1]->store(table_data.first.c_str(), table_data.first.size(), | ||
| 558 | system_charset_info); | ||
| 559 | table->field[2]->store(table_data.second, true); | ||
| 560 | if (schema_table_store_record(thd, table)) | ||
| 561 | return 1; | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | return 0; | ||
| 566 | } | ||
| 567 | |||
| 568 | static int init(void *ptr) | ||
| 569 | { | ||
| 570 | ST_SCHEMA_TABLE *schema_table = (ST_SCHEMA_TABLE*)ptr; | ||
| 571 | |||
| 572 | schema_table->fields_info = fields_info; | ||
| 573 | schema_table->fill_table = fill_table; | ||
| 574 | return 0; | ||
| 575 | } | ||
| 576 | |||
| 577 | static struct st_mysql_information_schema info = | ||
| 578 | { MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; | ||
| 579 | |||
| 580 | mysql_declare_plugin(table_sizes) | ||
| 581 | { | ||
| 582 | MYSQL_INFORMATION_SCHEMA_PLUGIN, | ||
| 583 | &info, /* type-specific descriptor */ | ||
| 584 | "TABLE_SIZES", /* table name */ | ||
| 585 | "Manuel Mausz", /* author */ | ||
| 586 | "Fast INFORMATION_SCHEMA table sizes", /* description */ | ||
| 587 | PLUGIN_LICENSE_GPL, /* license type */ | ||
| 588 | init, /* init function */ | ||
| 589 | NULL, | ||
| 590 | NULL, | ||
| 591 | 0x0800, /* version = 8.0 */ | ||
| 592 | NULL, /* no status variables */ | ||
| 593 | NULL, /* no system variables */ | ||
| 594 | NULL, /* no reserved information */ | ||
| 595 | 0 /* no flags */ | ||
| 596 | } | ||
| 597 | mysql_declare_plugin_end; | ||
