From bcff4f2e3520eaf170c282ba03d38ba6a4fa95cf Mon Sep 17 00:00:00 2001 From: manuel Date: Mon, 11 Feb 2013 14:23:16 +0100 Subject: initial import --- src/Makefile.am | 8 ++ src/mysql_inc.h | 13 ++ src/table_sizes.cc | 368 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 389 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/mysql_inc.h create mode 100644 src/table_sizes.cc (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..7965ee4 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,8 @@ +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/mysql_inc.h b/src/mysql_inc.h new file mode 100644 index 0000000..ca055ca --- /dev/null +++ b/src/mysql_inc.h @@ -0,0 +1,13 @@ +#ifndef MYSQL_INCL_H +#define MYSQL_INCL_H + +#ifndef HAVE_CONFIG_H +#define HAVE_CONFIG_H +#endif + +#define MYSQL_DYNAMIC_PLUGIN +#define MYSQL_SERVER 1 + +#include + +#endif diff --git a/src/table_sizes.cc b/src/table_sizes.cc new file mode 100644 index 0000000..b1fd8b0 --- /dev/null +++ b/src/table_sizes.cc @@ -0,0 +1,368 @@ +/* + 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, COND *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) +{ + /* + 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) != 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"); + + bzero((char *)&table_list, 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, COND *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"); + + bzero((char *)&lookup_field_vals, 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)) + goto err; + + LEX_STRING *db_name; + while (db_name = it++) + { + 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->host, sctx->ip, sctx->priv_user, db_name->str, 0)) + continue; + + 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: + 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, + 0x0010, /* version = 0.1 */ + NULL, /* no status variables */ + NULL, /* no system variables */ + NULL, /* no reserved information */ + 0 /* no flags */ +} +mysql_declare_plugin_end; -- cgit v1.2.3