/* 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 "mysql_inc.h" #include #include #include #include #define STR_OR_NIL(S) ((S) ? (S) : "") typedef struct st_lookup_field_values { LEX_STRING db_value, table_value; bool wild_db_value, wild_table_value; } LOOKUP_FIELD_VALUES; bool calc_lookup_values_from_cond(THD *thd, Item *cond, TABLE_LIST *table, LOOKUP_FIELD_VALUES *lookup_field_vals); /*----------------------------------------------------------------------------*/ static int make_db_list(THD *thd, List *db_names, LOOKUP_FIELD_VALUES *lookup_field_vals, MEM_ROOT *tmp_mem_root) { /* If we have db lookup vaule 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.str && lookup_field_vals->db_value.length < FN_REFLEN) { if (db_names->push_back(&lookup_field_vals->db_value)) return 1; return 0; } return (find_files(thd, db_names, NullS, mysql_data_home, NullS, TRUE, tmp_mem_root) != FIND_FILES_OK); } /*----------------------------------------------------------------------------*/ struct TABLE_DATA { LEX_STRING name; longlong size; }; static char *fn_remove_ext(char *name) { char *res = strrchr(name, '.'); if (res) return res; return name + strlen(name); } /* 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 */ static find_files_result find_tables(THD *thd, List *tables, const char *db, const char *path, const char *wild) { char *ext; MY_DIR *dirp; FILEINFO *file; uint file_name_len; char uname[NAME_LEN + 1]; /* Unencoded name */ #ifndef NO_EMBEDDED_ACCESS_CHECKS uint col_access = thd->col_access; #endif uint wild_length = 0; TABLE_LIST table_list; TABLE_DATA *table_data = NULL; DBUG_ENTER("find_files"); memset(&table_list, 0, sizeof(table_list)); if (!(dirp = my_dir(path, MYF(MY_WANT_STAT)))) { if (my_errno == ENOENT) my_error(ER_BAD_DB_ERROR, MYF(ME_BELL + ME_WAITTANG), db); else my_error(ER_CANT_READ_DIR, MYF(ME_BELL + ME_WAITTANG), path, my_errno); DBUG_RETURN(FIND_FILES_DIR); } for (uint i = 0; i < (uint)dirp->number_off_files; i++) { file = dirp->dir_entry + i; /* skip '.', '..', db.opt and temp files. */ if ((file->name[0] == '.' && (!file->name[1] || (file->name[1] == '.' && !file->name[2]))) || !my_strcasecmp(files_charset_info, file->name, MY_DB_OPT_FILE) || is_prefix(file->name, tmp_file_prefix)) continue; ext = fn_remove_ext(file->name); *ext = 0; file_name_len = filename_to_tablename(file->name, uname, sizeof(uname)); if (table_data != NULL && !my_strcasecmp(files_charset_info, uname, table_data->name.str)) { table_data->size += file->mystat->st_size; continue; } if (wild) { if (lower_case_table_names) { if (my_wildcmp(files_charset_info, uname, uname + file_name_len, wild, wild + wild_length, wild_prefix, wild_one, wild_many)) continue; } else if (wild_compare(uname, wild, 0)) continue; } #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Don't show tables where we don't have any privileges */ if (db && !(col_access & TABLE_ACLS)) { table_list.db = (char*)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; } #endif table_data = new TABLE_DATA(); table_data->size = file->mystat->st_size; if (!thd->make_lex_string(&table_data->name, uname, file_name_len, FALSE) || tables->push_back(table_data)) { my_dirend(dirp); DBUG_RETURN(FIND_FILES_OOM); } } DBUG_PRINT("info", ("found: %d files", tables->elements)); my_dirend(dirp); //(void)ha_find_files(thd, db, path, wild, dir, files); DBUG_RETURN(FIND_FILES_OK); } static int make_table_list(THD *thd, List *table_names, LEX *lex, LEX_STRING *db_name) { char path[FN_REFLEN + 1]; build_table_filename(path, sizeof(path) - 1, db_name->str, "", "", 0); find_files_result res = find_tables(thd, table_names, db_name->str, path, NullS); if (res != FIND_FILES_OK) { /* Downgrade errors about problems with database directory to warnings. Another thread may have dropped database, and we may still have a name for that directory. */ if (res == FIND_FILES_DIR) { if (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) return 1; thd->clear_error(); return 2; } return 1; } return 0; } /*----------------------------------------------------------------------------*/ 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_MAYBE_NULL | 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; #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *sctx = thd->security_ctx; #endif List db_names; List_iterator_fast it(db_names); int error = 1; DBUG_ENTER("fill_table"); MEM_ROOT tmp_mem_root; init_sql_alloc(&tmp_mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); memset(&lookup_field_vals, 0, sizeof(LOOKUP_FIELD_VALUES)); if (calc_lookup_values_from_cond(thd, cond, tables, &lookup_field_vals)) { error = 0; goto err; } if (lower_case_table_names) { /* We can safely do in-place upgrades here since we are allocating a new memory buffer for these strings. */ if (lookup_field_vals.db_value.str && lookup_field_vals.db_value.str[0]) my_casedn_str(system_charset_info, lookup_field_vals.db_value.str); if (lookup_field_vals.table_value.str && lookup_field_vals.table_value.str[0]) my_casedn_str(system_charset_info, lookup_field_vals.table_value.str); } DBUG_PRINT("INDEX VALUES", ("db_name='%s', table_name='%s'", STR_OR_NIL(lookup_field_vals.db_value.str), STR_OR_NIL(lookup_field_vals.table_value.str))); 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.str && !lookup_field_vals.db_value.str[0]) || (lookup_field_vals.table_value.str && !lookup_field_vals.table_value.str[0])) { error= 0; goto err; } } /* NOTE: doesn't change the output of EXPLAIN - maybe sometimes later */ if (lookup_field_vals.db_value.length && !lookup_field_vals.wild_db_value) tables->has_db_lookup_value = TRUE; if (lookup_field_vals.table_value.length && !lookup_field_vals.wild_table_value) tables->has_table_lookup_value = TRUE; if (lex->describe) { /* EXPLAIN SELECT */ error = 0; goto err; } if (make_db_list(thd, &db_names, &lookup_field_vals, &tmp_mem_root)) goto err; LEX_STRING *db_name; while ((db_name = it++)) { #ifndef NO_EMBEDDED_ACCESS_CHECKS if ((check_access(thd, SELECT_ACL, db_name->str, &thd->col_access, NULL, 0, 1) || (!thd->col_access && check_grant_db(thd, db_name->str))) && !sctx->master_access & (DB_ACLS | SHOW_DB_ACL) && !acl_get(sctx->get_host()->ptr(), sctx->get_ip()->ptr(), sctx->priv_user, db_name->str, 0)) continue; #endif List tables; int res = make_table_list(thd, &tables, lex, db_name); if (res) { tables.delete_elements(); if (res == 2) /* Not fatal error, continue */ continue; goto err; } TABLE_DATA *table_data; List_iterator_fast it_tables(tables); while ((table_data = it_tables++)) { table->field[0]->store(db_name->str, db_name->length, system_charset_info); table->field[1]->store(table_data->name.str, table_data->name.length, system_charset_info); table->field[2]->set_notnull(); table->field[2]->store((longlong) table_data->size, TRUE); if (schema_table_store_record(thd, table)) { tables.delete_elements(); goto err; } } tables.delete_elements(); } 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, 0x0506, /* version = 5.6 */ NULL, /* no status variables */ NULL, /* no system variables */ NULL, /* no reserved information */ 0 /* no flags */ } mysql_declare_plugin_end;