diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/sp.cc | 100 | ||||
-rw-r--r-- | sql/sp.h | 12 | ||||
-rw-r--r-- | sql/sql_db.cc | 259 |
3 files changed, 240 insertions, 131 deletions
diff --git a/sql/sp.cc b/sql/sp.cc index 4dea6843ef1..5d424564317 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1361,6 +1361,106 @@ err: /** + This internal handler is used to trap errors from opening mysql.proc. +*/ + +class Lock_db_routines_error_handler : public Internal_error_handler +{ +public: + bool handle_condition(THD *thd, + uint sql_errno, + const char* sqlstate, + MYSQL_ERROR::enum_warning_level level, + const char* msg, + MYSQL_ERROR ** cond_hdl) + { + if (sql_errno == ER_NO_SUCH_TABLE || + sql_errno == ER_COL_COUNT_DOESNT_MATCH_CORRUPTED) + return true; + return false; + } +}; + + +/** + Acquires exclusive metadata lock on all stored routines in the + given database. + + @note Will also return false (=success) if mysql.proc can't be opened + or is outdated. This allows DROP DATABASE to continue in these + cases. + */ + +bool lock_db_routines(THD *thd, char *db) +{ + TABLE *table; + uint key_len; + int nxtres= 0; + Open_tables_backup open_tables_state_backup; + MDL_request_list mdl_requests; + Lock_db_routines_error_handler err_handler; + DBUG_ENTER("lock_db_routines"); + + /* + mysql.proc will be re-opened during deletion, so we can ignore + errors when opening the table here. The error handler is + used to avoid getting the same warning twice. + */ + thd->push_internal_handler(&err_handler); + table= open_proc_table_for_read(thd, &open_tables_state_backup); + thd->pop_internal_handler(); + if (!table) + { + /* + DROP DATABASE should not fail even if mysql.proc does not exist + or is outdated. We therefore only abort mysql_rm_db() if we + have errors not handled by the error handler. + */ + DBUG_RETURN(thd->is_error() || thd->killed); + } + + table->field[MYSQL_PROC_FIELD_DB]->store(db, strlen(db), system_charset_info); + key_len= table->key_info->key_part[0].store_length; + table->file->ha_index_init(0, 1); + + if (! table->file->index_read_map(table->record[0], + table->field[MYSQL_PROC_FIELD_DB]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT)) + { + do + { + char *sp_name= get_field(thd->mem_root, + table->field[MYSQL_PROC_FIELD_NAME]); + longlong sp_type= table->field[MYSQL_PROC_MYSQL_TYPE]->val_int(); + MDL_request *mdl_request= new (thd->mem_root) MDL_request; + mdl_request->init(sp_type == TYPE_ENUM_FUNCTION ? + MDL_key::FUNCTION : MDL_key::PROCEDURE, + db, sp_name, MDL_EXCLUSIVE, MDL_TRANSACTION); + mdl_requests.push_front(mdl_request); + } while (! (nxtres= table->file->index_next_same(table->record[0], + table->field[MYSQL_PROC_FIELD_DB]->ptr, + key_len))); + } + table->file->ha_index_end(); + if (nxtres != 0 && nxtres != HA_ERR_END_OF_FILE) + { + table->file->print_error(nxtres, MYF(0)); + close_system_tables(thd, &open_tables_state_backup); + DBUG_RETURN(true); + } + close_system_tables(thd, &open_tables_state_backup); + + /* We should already hold a global IX lock and a schema X lock. */ + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "", + MDL_INTENTION_EXCLUSIVE) && + thd->mdl_context.is_lock_owner(MDL_key::SCHEMA, db, "", + MDL_EXCLUSIVE)); + DBUG_RETURN(thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)); +} + + +/** Drop all routines in database 'db' @note Close the thread tables, the calling code might want to @@ -84,6 +84,18 @@ enum int sp_drop_db_routines(THD *thd, char *db); +/** + Acquires exclusive metadata lock on all stored routines in the + given database. + + @param thd Thread handler + @param db Database name + + @retval false Success + @retval true Failure + */ +bool lock_db_routines(THD *thd, char *db); + sp_head * sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, bool cache_only); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 1391b2c4d72..292fbba2352 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -46,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, - 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, @@ -738,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 @@ -779,20 +782,72 @@ 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 + + thd->push_internal_handler(&err_handler); + + if (find_db_tables_and_rm_known_files(thd, dirp, db, path, &tables, + &found_other_files)) + { + thd->pop_internal_handler(); + 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)) + { + 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"); + thd->pop_internal_handler(); + 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)) { - Drop_table_error_handler err_handler; - thd->push_internal_handler(&err_handler); + thd->pop_internal_handler(); + goto exit; + } - error= -1; + /* mysql_ha_rm_tables() requires a non-null TABLE_LIST. */ + if (tables) + mysql_ha_rm_tables(thd, tables); + + for (table= tables; table; table= table->next_local) + { + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name, + false); + deleted_tables++; + } + + if (thd->killed || + (tables && mysql_rm_table_no_locks(thd, tables, true, false, true, true))) + { + tables= NULL; + } + else + { /* We temporarily disable the binary log while dropping the objects in the database. Since the DROP DATABASE statement is always @@ -810,23 +865,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, - &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; @@ -861,13 +923,13 @@ 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); + my_ok(thd, deleted_tables); } else if (mysql_bin_log.is_open()) { @@ -881,7 +943,7 @@ 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; @@ -895,7 +957,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; @@ -915,7 +977,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; } } @@ -928,27 +990,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. - 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, - 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; - TABLE_LIST *table; - 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; @@ -974,16 +1032,16 @@ 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, '.'))) @@ -991,7 +1049,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, if (find_type(extension, &deletable_extentions,1+2) <= 0) { if (find_type(extension, ha_known_exts(),1+2) <= 0) - found_other_files++; + *found_other_files= true; continue; } /* just for safety we use files_charset_info */ @@ -1007,7 +1065,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; @@ -1032,77 +1090,16 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, (*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); + strxmov(filePath, path, "/", file->name, NullS); if (mysql_file_delete_with_symlink(key_file_misc, filePath, MYF(MY_WME))) - { - goto err; - } - } - } - - /* - 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)) - { - for (table= tot_list; 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 err; - } + DBUG_RETURN(true); } } - - /* mysql_ha_rm_tables() requires a non-null TABLE_LIST. */ - if (tot_list) - mysql_ha_rm_tables(thd, tot_list); - - if (lock_table_names(thd, tot_list, NULL, thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_TEMPORARY)) - goto err; - - for (table= tot_list; table; table= table->next_local) - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name, - false); - - if (thd->killed || - (tot_list && mysql_rm_table_no_locks(thd, tot_list, true, - false, true, true))) - goto err; - - 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 - { - if (rm_dir_w_symlink(org_path, true)) - DBUG_RETURN(-1); - } - - DBUG_RETURN(deleted); - -err: - my_dirend(dirp); - DBUG_RETURN(-1); + *tables= tot_list; + DBUG_RETURN(false); } |