diff options
Diffstat (limited to 'sql/sql_db.cc')
-rw-r--r-- | sql/sql_db.cc | 288 |
1 files changed, 143 insertions, 145 deletions
diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 8b92259573e..56dd266acb9 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB, 2008-2009 Sun Microsystems, Inc +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 @@ -28,6 +28,8 @@ #include "sql_acl.h" // SELECT_ACL, DB_ACLS, // acl_get, check_grant_db #include "log_event.h" // Query_log_event +#include "sql_base.h" // lock_table_names, tdc_remove_table +#include "sql_handler.h" // mysql_ha_rm_tables #include <mysys_err.h> #include "sp.h" #include "events.h" @@ -44,10 +46,12 @@ const char *del_exts[]= {".frm", ".BAK", ".TMD",".opt", NullS}; static TYPELIB deletable_extentions= {array_elements(del_exts)-1,"del_exts", del_exts, NULL}; -static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, - const char *db, const char *path, uint level, - TABLE_LIST **dropped_tables); - +static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, + const char *db, + const char *path, + TABLE_LIST **tables, + bool *found_other_files); + long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path); static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error); static void mysql_change_db_impl(THD *thd, @@ -736,36 +740,37 @@ exit: } -/* - Drop all tables in a database and the database itself - - SYNOPSIS - mysql_rm_db() - thd Thread handle - db Database name in the case given by user - It's already validated and set to lower case - (if needed) when we come here - if_exists Don't give error if database doesn't exists - silent Don't generate errors - - RETURN - FALSE ok (Database dropped) - ERROR Error +/** + Drop all tables, routines and events in a database and the database itself. + + @param thd Thread handle + @param db Database name in the case given by user + It's already validated and set to lower case + (if needed) when we come here + @param if_exists Don't give error if database doesn't exists + @param silent Don't write the statement to the binary log and don't + send ok packet to the client + + @retval false OK (Database dropped) + @retval true Error */ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { - long deleted=0; - int error= 0; + ulong deleted_tables= 0; + bool error= true; char path[FN_REFLEN+16]; MY_DIR *dirp; uint length; - TABLE_LIST* dropped_tables= 0; + bool found_other_files= false; + TABLE_LIST *tables= NULL; + TABLE_LIST *table; + Drop_table_error_handler err_handler; DBUG_ENTER("mysql_rm_db"); if (lock_schema_name(thd, db)) - DBUG_RETURN(TRUE); + DBUG_RETURN(true); length= build_table_filename(path, sizeof(path) - 1, db, "", "", 0); strmov(path+length, MY_DB_OPT_FILE); // Append db option file name @@ -777,20 +782,61 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { if (!if_exists) { - error= -1; my_error(ER_DB_DROP_EXISTS, MYF(0), db); - goto exit; + DBUG_RETURN(true); } else + { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS), db); + error= false; + goto update_binlog; + } } - else + + if (find_db_tables_and_rm_known_files(thd, dirp, db, path, &tables, + &found_other_files)) + goto exit; + + /* + Disable drop of enabled log tables, must be done before name locking. + This check is only needed if we are dropping the "mysql" database. + */ + if ((my_strcasecmp(system_charset_info, MYSQL_SCHEMA_NAME.str, db) == 0)) { - Drop_table_error_handler err_handler; - thd->push_internal_handler(&err_handler); + for (table= tables; table; table= table->next_local) + { + if (check_if_log_table(table->db_length, table->db, + table->table_name_length, table->table_name, true)) + { + my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP"); + goto exit; + } + } + } + + /* Lock all tables and stored routines about to be dropped. */ + if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout, + MYSQL_OPEN_SKIP_TEMPORARY) || + lock_db_routines(thd, db)) + goto exit; + + /* mysql_ha_rm_tables() requires a non-null TABLE_LIST. */ + if (tables) + mysql_ha_rm_tables(thd, tables); - error= -1; + for (table= tables; table; table= table->next_local) + { + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name, + false); + deleted_tables++; + } + + thd->push_internal_handler(&err_handler); + if (!thd->killed && + !(tables && + mysql_rm_table_no_locks(thd, tables, true, false, true, true))) + { /* We temporarily disable the binary log while dropping the objects in the database. Since the DROP DATABASE statement is always @@ -808,23 +854,30 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) ha_drop_database(), since NDB otherwise detects the binary log as disabled and will not log the drop database statement on any other connected server. - */ - if ((deleted= mysql_rm_known_files(thd, dirp, db, path, 0, - &dropped_tables)) >= 0) - { - ha_drop_database(path); - tmp_disable_binlog(thd); - query_cache_invalidate1(db); - (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */ + */ + + ha_drop_database(path); + tmp_disable_binlog(thd); + query_cache_invalidate1(db); + (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */ #ifdef HAVE_EVENT_SCHEDULER - Events::drop_schema_events(thd, db); + Events::drop_schema_events(thd, db); #endif - error = 0; - reenable_binlog(thd); - } - thd->pop_internal_handler(); + reenable_binlog(thd); + + /* + If the directory is a symbolic link, remove the link first, then + remove the directory the symbolic link pointed at + */ + if (found_other_files) + my_error(ER_DB_DROP_RMDIR, MYF(0), path, EEXIST); + else + error= rm_dir_w_symlink(path, true); } - if (!silent && deleted>=0) + thd->pop_internal_handler(); + +update_binlog: + if (!silent && !error) { const char *query; ulong query_length; @@ -859,16 +912,15 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) */ if (mysql_bin_log.write(&qinfo)) { - error= -1; + error= true; goto exit; } } thd->clear_error(); thd->server_status|= SERVER_STATUS_DB_DROPPED; - my_ok(thd, (ulong) deleted); - thd->server_status&= ~SERVER_STATUS_DB_DROPPED; + my_ok(thd, deleted_tables); } - else if (mysql_bin_log.is_open()) + else if (mysql_bin_log.is_open() && !silent) { char *query, *query_pos, *query_end, *query_data_start; TABLE_LIST *tbl; @@ -880,9 +932,19 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) query_end= query + MAX_DROP_TABLE_Q_LEN; db_len= strlen(db); - for (tbl= dropped_tables; tbl; tbl= tbl->next_local) + for (tbl= tables; tbl; tbl= tbl->next_local) { uint tbl_name_len; + bool exists; + + // Only write drop table to the binlog for tables that no longer exist. + if (check_if_table_exists(thd, tbl, &exists)) + { + error= true; + goto exit; + } + if (exists) + continue; /* 3 for the quotes and the comma*/ tbl_name_len= strlen(tbl->table_name) + 3; @@ -894,7 +956,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) */ if (write_to_binlog(thd, query, query_pos -1 - query, db, db_len)) { - error= -1; + error= true; goto exit; } query_pos= query_data_start; @@ -914,7 +976,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) */ if (write_to_binlog(thd, query, query_pos -1 - query, db, db_len)) { - error= -1; + error= true; goto exit; } } @@ -927,28 +989,23 @@ exit: SELECT DATABASE() in the future). For this we free() thd->db and set it to 0. */ - if (thd->db && !strcmp(thd->db, db) && error == 0) + if (thd->db && !strcmp(thd->db, db) && !error) mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); + my_dirend(dirp); DBUG_RETURN(error); } -/* - Removes files with known extensions plus all found subdirectories that - are 2 hex digits (raid directories). - thd MUST be set when calling this function! -*/ -static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, - const char *org_path, uint level, - TABLE_LIST **dropped_tables) +static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, + const char *db, + const char *path, + TABLE_LIST **tables, + bool *found_other_files) { - long deleted=0; - ulong found_other_files=0; char filePath[FN_REFLEN]; TABLE_LIST *tot_list=0, **tot_list_next_local, **tot_list_next_global; - List<String> raid_dirs; - DBUG_ENTER("mysql_rm_known_files"); - DBUG_PRINT("enter",("path: %s", org_path)); + DBUG_ENTER("find_db_tables_and_rm_known_files"); + DBUG_PRINT("enter",("path: %s", path)); tot_list_next_local= tot_list_next_global= &tot_list; @@ -965,36 +1022,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, (file->name[1] == '.' && !file->name[2]))) continue; - /* Check if file is a raid directory */ - if ((my_isdigit(system_charset_info, file->name[0]) || - (file->name[0] >= 'a' && file->name[0] <= 'f')) && - (my_isdigit(system_charset_info, file->name[1]) || - (file->name[1] >= 'a' && file->name[1] <= 'f')) && - !file->name[2] && !level) - { - char newpath[FN_REFLEN], *copy_of_path; - MY_DIR *new_dirp; - String *dir; - uint length; - - strxmov(newpath,org_path,"/",file->name,NullS); - length= unpack_filename(newpath,newpath); - if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT)))) - { - DBUG_PRINT("my",("New subdir found: %s", newpath)); - if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1,0)) < 0) - goto err; - if (!(copy_of_path= (char*) thd->memdup(newpath, length+1)) || - !(dir= new (thd->mem_root) String(copy_of_path, length, - &my_charset_bin)) || - raid_dirs.push_back(dir)) - goto err; - continue; - } - found_other_files++; - continue; - } - else if (file->name[0] == 'a' && file->name[1] == 'r' && + if (file->name[0] == 'a' && file->name[1] == 'r' && file->name[2] == 'c' && file->name[3] == '\0') { /* .frm archive: @@ -1003,24 +1031,24 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, */ char newpath[FN_REFLEN]; MY_DIR *new_dirp; - strxmov(newpath, org_path, "/", "arc", NullS); + strxmov(newpath, path, "/", "arc", NullS); (void) unpack_filename(newpath, newpath); if ((new_dirp = my_dir(newpath, MYF(MY_DONT_SORT)))) { DBUG_PRINT("my",("Archive subdir found: %s", newpath)); if ((mysql_rm_arc_files(thd, new_dirp, newpath)) < 0) - goto err; + DBUG_RETURN(true); continue; } - found_other_files++; + *found_other_files= true; continue; } if (!(extension= strrchr(file->name, '.'))) extension= strend(file->name); - if (find_type(extension, &deletable_extentions,1+2) <= 0) + if (find_type(extension, &deletable_extentions, FIND_TYPE_NO_PREFIX) <= 0) { - if (find_type(extension, ha_known_exts(),1+2) <= 0) - found_other_files++; + if (find_type(extension, ha_known_exts(), FIND_TYPE_NO_PREFIX) <= 0) + *found_other_files= true; continue; } /* just for safety we use files_charset_info */ @@ -1036,7 +1064,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, strlen(file->name) + 1); if (!table_list) - goto err; + DBUG_RETURN(true); table_list->db= (char*) (table_list+1); table_list->db_length= strmov(table_list->db, db) - table_list->db; table_list->table_name= table_list->db + table_list->db_length + 1; @@ -1054,61 +1082,31 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, table_list->alias= table_list->table_name; // If lower_case_table_names=2 table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix); table_list->mdl_request.init(MDL_key::TABLE, table_list->db, - table_list->table_name, MDL_EXCLUSIVE); + table_list->table_name, MDL_EXCLUSIVE, + MDL_TRANSACTION); /* Link into list */ (*tot_list_next_local)= table_list; (*tot_list_next_global)= table_list; tot_list_next_local= &table_list->next_local; tot_list_next_global= &table_list->next_global; - deleted++; } else { - strxmov(filePath, org_path, "/", file->name, NullS); - if (mysql_file_delete_with_symlink(key_file_misc, filePath, MYF(MY_WME))) + strxmov(filePath, path, "/", file->name, NullS); + /* + We ignore ENOENT error in order to skip files that was deleted + by concurrently running statement like REAPIR TABLE ... + */ + if (my_delete_with_symlink(filePath, MYF(0)) && + my_errno != ENOENT) { - goto err; + my_error(EE_DELETE, MYF(0), filePath, my_errno); + DBUG_RETURN(true); } } } - if (thd->killed || - (tot_list && mysql_rm_table_part2(thd, tot_list, 1, 0, 1, 1))) - goto err; - - /* Remove RAID directories */ - { - List_iterator<String> it(raid_dirs); - String *dir; - while ((dir= it++)) - if (rmdir(dir->c_ptr()) < 0) - found_other_files++; - } - my_dirend(dirp); - - if (dropped_tables) - *dropped_tables= tot_list; - - /* - If the directory is a symbolic link, remove the link first, then - remove the directory the symbolic link pointed at - */ - if (found_other_files) - { - my_error(ER_DB_DROP_RMDIR, MYF(0), org_path, EEXIST); - DBUG_RETURN(-1); - } - else - { - /* Don't give errors if we can't delete 'RAID' directory */ - if (rm_dir_w_symlink(org_path, level == 0)) - DBUG_RETURN(-1); - } - - DBUG_RETURN(deleted); - -err: - my_dirend(dirp); - DBUG_RETURN(-1); + *tables= tot_list; + DBUG_RETURN(false); } |