diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 8 | ||||
| -rw-r--r-- | src/mysql_inc.h | 13 | ||||
| -rw-r--r-- | src/table_sizes.cc | 368 |
3 files changed, 389 insertions, 0 deletions
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 @@ | |||
| 1 | INCLUDES = $(MYSQL_INC) $(DEPS_CFLAGS) | ||
| 2 | |||
| 3 | pkgplugindir = $(MYSQL_PLUGIN_DIR) | ||
| 4 | |||
| 5 | pkgplugin_LTLIBRARIES = table_sizes.la | ||
| 6 | table_sizes_la_SOURCES = table_sizes.cc | ||
| 7 | table_sizes_la_LIBADD = $(DEPS_LIBS) | ||
| 8 | 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 @@ | |||
| 1 | #ifndef MYSQL_INCL_H | ||
| 2 | #define MYSQL_INCL_H | ||
| 3 | |||
| 4 | #ifndef HAVE_CONFIG_H | ||
| 5 | #define HAVE_CONFIG_H | ||
| 6 | #endif | ||
| 7 | |||
| 8 | #define MYSQL_DYNAMIC_PLUGIN | ||
| 9 | #define MYSQL_SERVER 1 | ||
| 10 | |||
| 11 | #include <mysql_version.h> | ||
| 12 | |||
| 13 | #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 @@ | |||
| 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 | #include "mysql_inc.h" | ||
| 16 | #include <sql/sql_show.h> | ||
| 17 | #include <sql/sql_table.h> | ||
| 18 | #include <sql/sql_parse.h> | ||
| 19 | #include <sql/sql_db.h> | ||
| 20 | |||
| 21 | #define STR_OR_NIL(S) ((S) ? (S) : "<nil>") | ||
| 22 | |||
| 23 | typedef struct st_lookup_field_values | ||
| 24 | { | ||
| 25 | LEX_STRING db_value, table_value; | ||
| 26 | bool wild_db_value, wild_table_value; | ||
| 27 | } LOOKUP_FIELD_VALUES; | ||
| 28 | |||
| 29 | bool calc_lookup_values_from_cond(THD *thd, COND *cond, TABLE_LIST *table, | ||
| 30 | LOOKUP_FIELD_VALUES *lookup_field_vals); | ||
| 31 | |||
| 32 | /*----------------------------------------------------------------------------*/ | ||
| 33 | |||
| 34 | static int | ||
| 35 | make_db_list(THD *thd, List<LEX_STRING> *db_names, | ||
| 36 | LOOKUP_FIELD_VALUES *lookup_field_vals) | ||
| 37 | { | ||
| 38 | /* | ||
| 39 | If we have db lookup vaule we just add it to list and | ||
| 40 | exit from the function. | ||
| 41 | We don't do this for database names longer than the maximum | ||
| 42 | path length. | ||
| 43 | */ | ||
| 44 | if (lookup_field_vals->db_value.str | ||
| 45 | && lookup_field_vals->db_value.length < FN_REFLEN) | ||
| 46 | { | ||
| 47 | if (db_names->push_back(&lookup_field_vals->db_value)) | ||
| 48 | return 1; | ||
| 49 | return 0; | ||
| 50 | } | ||
| 51 | |||
| 52 | return (find_files(thd, db_names, NullS, | ||
| 53 | mysql_data_home, NullS, TRUE) != FIND_FILES_OK); | ||
| 54 | } | ||
| 55 | |||
| 56 | /*----------------------------------------------------------------------------*/ | ||
| 57 | |||
| 58 | struct TABLE_DATA | ||
| 59 | { | ||
| 60 | LEX_STRING name; | ||
| 61 | longlong size; | ||
| 62 | }; | ||
| 63 | |||
| 64 | static char *fn_remove_ext(char *name) | ||
| 65 | { | ||
| 66 | char *res = strrchr(name, '.'); | ||
| 67 | if (res) | ||
| 68 | return res; | ||
| 69 | return name + strlen(name); | ||
| 70 | } | ||
| 71 | |||
| 72 | /* | ||
| 73 | SYNOPSIS | ||
| 74 | thd thread handler | ||
| 75 | tables put found tables and their size in this list | ||
| 76 | db database name to set in TABLE_LIST structure | ||
| 77 | path path to database | ||
| 78 | wild filter for found files | ||
| 79 | |||
| 80 | RETURN | ||
| 81 | FIND_FILES_OK success | ||
| 82 | FIND_FILES_OOM out of memory error | ||
| 83 | FIND_FILES_DIR no such directory, or directory can't be read | ||
| 84 | */ | ||
| 85 | static find_files_result | ||
| 86 | find_tables(THD *thd, List<TABLE_DATA> *tables, | ||
| 87 | const char *db, const char *path, const char *wild) | ||
| 88 | { | ||
| 89 | char *ext; | ||
| 90 | MY_DIR *dirp; | ||
| 91 | FILEINFO *file; | ||
| 92 | uint file_name_len; | ||
| 93 | char uname[NAME_LEN + 1]; /* Unencoded name */ | ||
| 94 | #ifndef NO_EMBEDDED_ACCESS_CHECKS | ||
| 95 | uint col_access = thd->col_access; | ||
| 96 | #endif | ||
| 97 | uint wild_length = 0; | ||
| 98 | TABLE_LIST table_list; | ||
| 99 | TABLE_DATA *table_data = NULL; | ||
| 100 | DBUG_ENTER("find_files"); | ||
| 101 | |||
| 102 | bzero((char *)&table_list, sizeof(table_list)); | ||
| 103 | |||
| 104 | if (!(dirp = my_dir(path, MYF(MY_WANT_STAT)))) | ||
| 105 | { | ||
| 106 | if (my_errno == ENOENT) | ||
| 107 | my_error(ER_BAD_DB_ERROR, MYF(ME_BELL + ME_WAITTANG), db); | ||
| 108 | else | ||
| 109 | my_error(ER_CANT_READ_DIR, MYF(ME_BELL + ME_WAITTANG), path, my_errno); | ||
| 110 | DBUG_RETURN(FIND_FILES_DIR); | ||
| 111 | } | ||
| 112 | |||
| 113 | for (uint i = 0; i < (uint)dirp->number_off_files; i++) | ||
| 114 | { | ||
| 115 | file = dirp->dir_entry + i; | ||
| 116 | /* skip '.', '..', db.opt and temp files. */ | ||
| 117 | if ((file->name[0] == '.' && | ||
| 118 | (!file->name[1] || (file->name[1] == '.' && !file->name[2]))) | ||
| 119 | || !my_strcasecmp(files_charset_info, file->name, MY_DB_OPT_FILE) | ||
| 120 | || is_prefix(file->name, tmp_file_prefix)) | ||
| 121 | continue; | ||
| 122 | |||
| 123 | ext = fn_remove_ext(file->name); | ||
| 124 | *ext = 0; | ||
| 125 | file_name_len = filename_to_tablename(file->name, uname, sizeof(uname)); | ||
| 126 | |||
| 127 | if (table_data != NULL | ||
| 128 | && !my_strcasecmp(files_charset_info, uname, table_data->name.str)) | ||
| 129 | { | ||
| 130 | table_data->size += file->mystat->st_size; | ||
| 131 | continue; | ||
| 132 | } | ||
| 133 | |||
| 134 | if (wild) | ||
| 135 | { | ||
| 136 | if (lower_case_table_names) | ||
| 137 | { | ||
| 138 | if (my_wildcmp(files_charset_info, | ||
| 139 | uname, uname + file_name_len, | ||
| 140 | wild, wild + wild_length, | ||
| 141 | wild_prefix, wild_one, wild_many)) | ||
| 142 | continue; | ||
| 143 | } | ||
| 144 | else if (wild_compare(uname, wild, 0)) | ||
| 145 | continue; | ||
| 146 | } | ||
| 147 | |||
| 148 | #ifndef NO_EMBEDDED_ACCESS_CHECKS | ||
| 149 | /* Don't show tables where we don't have any privileges */ | ||
| 150 | if (db && !(col_access & TABLE_ACLS)) | ||
| 151 | { | ||
| 152 | table_list.db = (char*)db; | ||
| 153 | table_list.db_length = strlen(db); | ||
| 154 | table_list.table_name = uname; | ||
| 155 | table_list.table_name_length = file_name_len; | ||
| 156 | table_list.grant.privilege = col_access; | ||
| 157 | if (check_grant(thd, TABLE_ACLS, &table_list, TRUE, 1, TRUE)) | ||
| 158 | continue; | ||
| 159 | } | ||
| 160 | #endif | ||
| 161 | |||
| 162 | table_data = new TABLE_DATA(); | ||
| 163 | table_data->size = file->mystat->st_size; | ||
| 164 | if (!thd->make_lex_string(&table_data->name, uname, file_name_len, FALSE) | ||
| 165 | || tables->push_back(table_data)) | ||
| 166 | { | ||
| 167 | my_dirend(dirp); | ||
| 168 | DBUG_RETURN(FIND_FILES_OOM); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | DBUG_PRINT("info", ("found: %d files", tables->elements)); | ||
| 173 | my_dirend(dirp); | ||
| 174 | |||
| 175 | //(void)ha_find_files(thd, db, path, wild, dir, files); | ||
| 176 | |||
| 177 | DBUG_RETURN(FIND_FILES_OK); | ||
| 178 | } | ||
| 179 | |||
| 180 | static int | ||
| 181 | make_table_list(THD *thd, List<TABLE_DATA> *table_names, LEX *lex, | ||
| 182 | LEX_STRING *db_name) | ||
| 183 | { | ||
| 184 | char path[FN_REFLEN + 1]; | ||
| 185 | build_table_filename(path, sizeof(path) - 1, db_name->str, "", "", 0); | ||
| 186 | |||
| 187 | find_files_result res = find_tables(thd, table_names, db_name->str, path, | ||
| 188 | NullS); | ||
| 189 | if (res != FIND_FILES_OK) | ||
| 190 | { | ||
| 191 | /* | ||
| 192 | Downgrade errors about problems with database directory to warnings. | ||
| 193 | Another thread may have dropped database, and we may still have a name | ||
| 194 | for that directory. | ||
| 195 | */ | ||
| 196 | if (res == FIND_FILES_DIR) | ||
| 197 | { | ||
| 198 | if (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) | ||
| 199 | return 1; | ||
| 200 | thd->clear_error(); | ||
| 201 | return 2; | ||
| 202 | } | ||
| 203 | return 1; | ||
| 204 | } | ||
| 205 | return 0; | ||
| 206 | } | ||
| 207 | |||
| 208 | /*----------------------------------------------------------------------------*/ | ||
| 209 | |||
| 210 | static struct st_mysql_information_schema info = | ||
| 211 | { MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; | ||
| 212 | |||
| 213 | static ST_FIELD_INFO fields_info[] = | ||
| 214 | { | ||
| 215 | {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, | ||
| 216 | {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", | ||
| 217 | SKIP_OPEN_TABLE}, | ||
| 218 | {"TABLE_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, | ||
| 219 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Table_size", SKIP_OPEN_TABLE}, | ||
| 220 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} | ||
| 221 | }; | ||
| 222 | |||
| 223 | static int fill_table(THD *thd, TABLE_LIST *tables, COND *cond) | ||
| 224 | { | ||
| 225 | LEX *lex = thd->lex; | ||
| 226 | TABLE *table = tables->table; | ||
| 227 | LOOKUP_FIELD_VALUES lookup_field_vals; | ||
| 228 | #ifndef NO_EMBEDDED_ACCESS_CHECKS | ||
| 229 | Security_context *sctx = thd->security_ctx; | ||
| 230 | #endif | ||
| 231 | List<LEX_STRING> db_names; | ||
| 232 | List_iterator_fast<LEX_STRING> it(db_names); | ||
| 233 | int error = 1; | ||
| 234 | |||
| 235 | DBUG_ENTER("fill_table"); | ||
| 236 | |||
| 237 | bzero((char *)&lookup_field_vals, sizeof(LOOKUP_FIELD_VALUES)); | ||
| 238 | if (calc_lookup_values_from_cond(thd, cond, tables, &lookup_field_vals)) | ||
| 239 | { | ||
| 240 | error = 0; | ||
| 241 | goto err; | ||
| 242 | } | ||
| 243 | |||
| 244 | if (lower_case_table_names) | ||
| 245 | { | ||
| 246 | /* | ||
| 247 | We can safely do in-place upgrades here since we are | ||
| 248 | allocating a new memory buffer for these strings. | ||
| 249 | */ | ||
| 250 | if (lookup_field_vals.db_value.str && lookup_field_vals.db_value.str[0]) | ||
| 251 | my_casedn_str(system_charset_info, lookup_field_vals.db_value.str); | ||
| 252 | if (lookup_field_vals.table_value.str && lookup_field_vals.table_value.str[0]) | ||
| 253 | my_casedn_str(system_charset_info, lookup_field_vals.table_value.str); | ||
| 254 | } | ||
| 255 | |||
| 256 | DBUG_PRINT("INDEX VALUES", ("db_name='%s', table_name='%s'", | ||
| 257 | STR_OR_NIL(lookup_field_vals.db_value.str), | ||
| 258 | STR_OR_NIL(lookup_field_vals.table_value.str))); | ||
| 259 | |||
| 260 | if (!lookup_field_vals.wild_db_value && !lookup_field_vals.wild_table_value) | ||
| 261 | { | ||
| 262 | /* | ||
| 263 | if lookup value is empty string then | ||
| 264 | it's impossible table name or db name | ||
| 265 | */ | ||
| 266 | if ((lookup_field_vals.db_value.str | ||
| 267 | && !lookup_field_vals.db_value.str[0]) | ||
| 268 | || (lookup_field_vals.table_value.str | ||
| 269 | && !lookup_field_vals.table_value.str[0])) | ||
| 270 | { | ||
| 271 | error= 0; | ||
| 272 | goto err; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | /* NOTE: doesn't change the output of EXPLAIN - maybe sometimes later */ | ||
| 277 | if (lookup_field_vals.db_value.length | ||
| 278 | && !lookup_field_vals.wild_db_value) | ||
| 279 | tables->has_db_lookup_value = TRUE; | ||
| 280 | if (lookup_field_vals.table_value.length | ||
| 281 | && !lookup_field_vals.wild_table_value) | ||
| 282 | tables->has_table_lookup_value = TRUE; | ||
| 283 | |||
| 284 | if (lex->describe) | ||
| 285 | { | ||
| 286 | /* EXPLAIN SELECT */ | ||
| 287 | error = 0; | ||
| 288 | goto err; | ||
| 289 | } | ||
| 290 | |||
| 291 | if (make_db_list(thd, &db_names, &lookup_field_vals)) | ||
| 292 | goto err; | ||
| 293 | |||
| 294 | LEX_STRING *db_name; | ||
| 295 | while (db_name = it++) | ||
| 296 | { | ||
| 297 | if ((check_access(thd, SELECT_ACL, db_name->str, &thd->col_access, NULL, 0, 1) | ||
| 298 | || (!thd->col_access && check_grant_db(thd, db_name->str))) | ||
| 299 | && !sctx->master_access & (DB_ACLS | SHOW_DB_ACL) | ||
| 300 | && !acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0)) | ||
| 301 | continue; | ||
| 302 | |||
| 303 | List<TABLE_DATA> tables; | ||
| 304 | int res = make_table_list(thd, &tables, lex, db_name); | ||
| 305 | if (res) | ||
| 306 | { | ||
| 307 | tables.delete_elements(); | ||
| 308 | if (res == 2) /* Not fatal error, continue */ | ||
| 309 | continue; | ||
| 310 | goto err; | ||
| 311 | } | ||
| 312 | |||
| 313 | TABLE_DATA *table_data; | ||
| 314 | List_iterator_fast<TABLE_DATA> it_tables(tables); | ||
| 315 | while (table_data = it_tables++) | ||
| 316 | { | ||
| 317 | table->field[0]->store(db_name->str, db_name->length, | ||
| 318 | system_charset_info); | ||
| 319 | table->field[1]->store(table_data->name.str, table_data->name.length, | ||
| 320 | system_charset_info); | ||
| 321 | table->field[2]->set_notnull(); | ||
| 322 | table->field[2]->store((longlong) table_data->size, TRUE); | ||
| 323 | if (schema_table_store_record(thd, table)) | ||
| 324 | { | ||
| 325 | tables.delete_elements(); | ||
| 326 | goto err; | ||
| 327 | } | ||
| 328 | } | ||
| 329 | tables.delete_elements(); | ||
| 330 | } | ||
| 331 | |||
| 332 | error = 0; | ||
| 333 | |||
| 334 | err: | ||
| 335 | DBUG_RETURN(error); | ||
| 336 | |||
| 337 | return 0; | ||
| 338 | } | ||
| 339 | |||
| 340 | static int init(void *ptr) | ||
| 341 | { | ||
| 342 | ST_SCHEMA_TABLE *schema_table = (ST_SCHEMA_TABLE*)ptr; | ||
| 343 | |||
| 344 | schema_table->fields_info = fields_info; | ||
| 345 | schema_table->fill_table = fill_table; | ||
| 346 | schema_table->idx_field1 = 0; | ||
| 347 | schema_table->idx_field2 = 1; | ||
| 348 | schema_table->i_s_requested_object = OPTIMIZE_I_S_TABLE; | ||
| 349 | return 0; | ||
| 350 | } | ||
| 351 | |||
| 352 | mysql_declare_plugin(table_sizes) | ||
| 353 | { | ||
| 354 | MYSQL_INFORMATION_SCHEMA_PLUGIN, | ||
| 355 | &info, /* type-specific descriptor */ | ||
| 356 | "TABLE_SIZES", /* table name */ | ||
| 357 | "Manuel Mausz", /* author */ | ||
| 358 | "Fast INFORMATION_SCHEMA table sizes", /* description */ | ||
| 359 | PLUGIN_LICENSE_GPL, /* license type */ | ||
| 360 | init, /* init function */ | ||
| 361 | NULL, | ||
| 362 | 0x0010, /* version = 0.1 */ | ||
| 363 | NULL, /* no status variables */ | ||
| 364 | NULL, /* no system variables */ | ||
| 365 | NULL, /* no reserved information */ | ||
| 366 | 0 /* no flags */ | ||
| 367 | } | ||
| 368 | mysql_declare_plugin_end; | ||
