diff options
Diffstat (limited to 'sql/sql_db.cc')
-rw-r--r-- | sql/sql_db.cc | 308 |
1 files changed, 159 insertions, 149 deletions
diff --git a/sql/sql_db.cc b/sql/sql_db.cc index c09ad51a824..71558e68067 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -18,7 +18,7 @@ /* create and drop of databases */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ +#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" #include "sql_db.h" @@ -32,11 +32,13 @@ #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 "sql_class.h" #include <mysys_err.h> #include "sp_head.h" #include "sp.h" #include "events.h" #include "sql_handler.h" +#include "sql_statistics.h" #include <my_dir.h> #include <m_ctype.h> #include "log.h" @@ -47,15 +49,12 @@ #define MAX_DROP_TABLE_Q_LEN 1024 -const char *del_exts[]= {".frm", ".BAK", ".TMD",".opt", NullS}; +const char *del_exts[]= {".BAK", ".opt", NullS}; static TYPELIB deletable_extentions= {array_elements(del_exts)-1,"del_exts", del_exts, NULL}; -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); +static bool find_db_tables_and_rm_known_files(THD *, MY_DIR *, char *, + const char *, TABLE_LIST **); 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); @@ -79,6 +78,29 @@ typedef struct my_dbopt_st } my_dbopt_t; +/** + Return TRUE if db1_name is equal to db2_name, FALSE otherwise. + + The function allows to compare database names according to the MariaDB + rules. The database names db1 and db2 are equal if: + - db1 is NULL and db2 is NULL; + or + - db1 is not-NULL, db2 is not-NULL, db1 is equal to db2 in + table_alias_charset + + This is the same rules as we use for filenames. +*/ + +static inline bool +cmp_db_names(const char *db1_name, + const char *db2_name) +{ + return ((!db1_name && !db2_name) || + (db1_name && db2_name && + my_strcasecmp(table_alias_charset, db1_name, db2_name) == 0)); +} + + /* Function we use in the creation of our hash to get key. */ @@ -160,8 +182,7 @@ bool my_dboptions_cache_init(void) if (!dboptions_init) { dboptions_init= 1; - error= my_hash_init(&dboptions, lower_case_table_names ? - &my_charset_bin : system_charset_info, + error= my_hash_init(&dboptions, table_alias_charset, 32, 0, 0, (my_hash_get_key) dboptions_get_key, free_dbopt,0); } @@ -193,8 +214,7 @@ void my_dbopt_cleanup(void) { mysql_rwlock_wrlock(&LOCK_dboptions); my_hash_free(&dboptions); - my_hash_init(&dboptions, lower_case_table_names ? - &my_charset_bin : system_charset_info, + my_hash_init(&dboptions, table_alias_charset, 32, 0, 0, (my_hash_get_key) dboptions_get_key, free_dbopt,0); mysql_rwlock_unlock(&LOCK_dboptions); @@ -559,7 +579,17 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, DBUG_RETURN(-1); } - if (lock_schema_name(thd, db)) + char db_tmp[SAFE_NAME_LEN], *dbnorm; + if (lower_case_table_names) + { + strmake_buf(db_tmp, db); + my_casedn_str(system_charset_info, db_tmp); + dbnorm= db_tmp; + } + else + dbnorm= db; + + if (lock_schema_name(thd, dbnorm)) DBUG_RETURN(-1); /* Check directory */ @@ -574,7 +604,7 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, error= -1; goto exit; } - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), db); error= 0; goto not_silent; @@ -754,23 +784,44 @@ exit: bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { ulong deleted_tables= 0; - bool error= true; + bool error= true, rm_mysql_schema; char path[FN_REFLEN + 16]; MY_DIR *dirp; uint length; - bool found_other_files= false; TABLE_LIST *tables= NULL; TABLE_LIST *table; Drop_table_error_handler err_handler; DBUG_ENTER("mysql_rm_db"); + char db_tmp[SAFE_NAME_LEN], *dbnorm; + if (lower_case_table_names) + { + strmake_buf(db_tmp, db); + my_casedn_str(system_charset_info, db_tmp); + dbnorm= db_tmp; + } + else + dbnorm= db; - if (lock_schema_name(thd, db)) + if (lock_schema_name(thd, dbnorm)) 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 del_dbopt(path); // Remove dboption hash entry + /* + Now remove the db.opt file. + The 'find_db_tables_and_rm_known_files' doesn't remove this file + if there exists a table with the name 'db', so let's just do it + separately. We know this file exists and needs to be deleted anyway. + */ + if (mysql_file_delete_with_symlink(key_file_misc, path, "", MYF(0)) && + my_errno != ENOENT) + { + my_error(EE_DELETE, MYF(0), path, my_errno); + DBUG_RETURN(true); + } + path[length]= '\0'; // Remove file name /* See if the directory exists */ @@ -783,55 +834,56 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) } else { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS), db); error= false; goto update_binlog; } } - if (find_db_tables_and_rm_known_files(thd, dirp, db, path, &tables, - &found_other_files)) + if (find_db_tables_and_rm_known_files(thd, dirp, dbnorm, path, &tables)) 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)) + if ((rm_mysql_schema= + (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"); + if (check_if_log_table(table, TRUE, "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)) + 0) || + lock_db_routines(thd, dbnorm)) goto exit; + if (!in_bootstrap && !rm_mysql_schema) + { + for (table= tables; table; table= table->next_local) + { + LEX_STRING db_name= { table->db, table->db_length }; + LEX_STRING table_name= { table->table_name, table->table_name_length }; + if (table->open_type == OT_BASE_ONLY || !find_temporary_table(thd, table)) + (void) delete_statistics_for_table(thd, &db_name, &table_name); + } + } + /* 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++; - } thd->push_internal_handler(&err_handler); if (!thd->killed && !(tables && - mysql_rm_table_no_locks(thd, tables, true, false, true, true))) + mysql_rm_table_no_locks(thd, tables, true, false, true, true, false))) { /* We temporarily disable the binary log while dropping the objects @@ -854,21 +906,21 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) ha_drop_database(path); tmp_disable_binlog(thd); - query_cache_invalidate1(thd, db); - (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */ + query_cache_invalidate1(thd, dbnorm); + if (!rm_mysql_schema) + { + (void) sp_drop_db_routines(thd, dbnorm); /* @todo Do not ignore errors */ #ifdef HAVE_EVENT_SCHEDULER - Events::drop_schema_events(thd, db); + Events::drop_schema_events(thd, dbnorm); #endif + } 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); + error= rm_dir_w_symlink(path, true); } thd->pop_internal_handler(); @@ -924,16 +976,10 @@ update_binlog: for (tbl= tables; tbl; tbl= tbl->next_local) { uint tbl_name_len; - bool exists; char quoted_name[FN_REFLEN+3]; // Only write drop table to the binlog for tables that no longer exist. - if (check_if_table_exists(thd, tbl, 0, &exists)) - { - error= true; - goto exit; - } - if (exists) + if (ha_table_exists(thd, tbl->db, tbl->table_name)) continue; my_snprintf(quoted_name, sizeof(quoted_name), "%`s", tbl->table_name); @@ -977,7 +1023,7 @@ 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) + if (thd->db && cmp_db_names(thd->db, db) && !error) mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); my_dirend(dirp); DBUG_RETURN(error); @@ -985,31 +1031,66 @@ exit: static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, - const char *db, + char *dbname, const char *path, - TABLE_LIST **tables, - bool *found_other_files) + TABLE_LIST **tables) { char filePath[FN_REFLEN]; + LEX_STRING db= { dbname, strlen(dbname) }; TABLE_LIST *tot_list=0, **tot_list_next_local, **tot_list_next_global; DBUG_ENTER("find_db_tables_and_rm_known_files"); DBUG_PRINT("enter",("path: %s", path)); + /* first, get the list of tables */ + Dynamic_array<LEX_STRING*> files(dirp->number_of_files); + Discovered_table_list tl(thd, &files); + if (ha_discover_table_names(thd, &db, dirp, &tl, true)) + DBUG_RETURN(1); + + /* Now put the tables in the list */ tot_list_next_local= tot_list_next_global= &tot_list; + for (size_t idx=0; idx < files.elements(); idx++) + { + LEX_STRING *table= files.at(idx); + + /* Drop the table nicely */ + TABLE_LIST *table_list=(TABLE_LIST*)thd->calloc(sizeof(*table_list)); + + if (!table_list) + DBUG_RETURN(true); + table_list->db= db.str; + table_list->db_length= db.length; + table_list->table_name= table->str; + table_list->table_name_length= table->length; + table_list->open_type= OT_BASE_ONLY; + + /* To be able to correctly look up the table in the table cache. */ + if (lower_case_table_names) + table_list->table_name_length= my_casedn_str(files_charset_info, + table_list->table_name); + + table_list->alias= table_list->table_name; // If lower_case_table_names=2 + table_list->mdl_request.init(MDL_key::TABLE, table_list->db, + 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; + } + *tables= tot_list; + + /* and at last delete all non-table files */ for (uint idx=0 ; - idx < (uint) dirp->number_off_files && !thd->killed ; + idx < (uint) dirp->number_of_files && !thd->killed ; idx++) { FILEINFO *file=dirp->dir_entry+idx; char *extension; DBUG_PRINT("info",("Examining: %s", file->name)); - /* skiping . and .. */ - if (file->name[0] == '.' && (!file->name[1] || - (file->name[1] == '.' && !file->name[2]))) - continue; - if (file->name[0] == 'a' && file->name[1] == 'r' && file->name[2] == 'c' && file->name[3] == '\0') { @@ -1026,59 +1107,12 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, DBUG_PRINT("my",("Archive subdir found: %s", newpath)); if ((mysql_rm_arc_files(thd, new_dirp, newpath)) < 0) DBUG_RETURN(true); - continue; } - *found_other_files= true; continue; } if (!(extension= strrchr(file->name, '.'))) extension= strend(file->name); - if (find_type(extension, &deletable_extentions, FIND_TYPE_NO_PREFIX) <= 0) - { - 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 */ - if (db && !my_strcasecmp(files_charset_info, - extension, reg_ext)) - { - /* Drop the table nicely */ - *extension= 0; // Remove extension - TABLE_LIST *table_list=(TABLE_LIST*) - thd->calloc(sizeof(*table_list) + - strlen(db) + 1 + - MYSQL50_TABLE_NAME_PREFIX_LENGTH + - strlen(file->name) + 1); - - if (!table_list) - 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; - table_list->table_name_length= filename_to_tablename(file->name, - table_list->table_name, - MYSQL50_TABLE_NAME_PREFIX_LENGTH + - strlen(file->name) + 1); - table_list->open_type= OT_BASE_ONLY; - - /* To be able to correctly look up the table in the table cache. */ - if (lower_case_table_names) - table_list->table_name_length= my_casedn_str(files_charset_info, - table_list->table_name); - - 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, - 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; - } - else + if (find_type(extension, &deletable_extentions, FIND_TYPE_NO_PREFIX) > 0) { strxmov(filePath, path, "/", file->name, NullS); /* @@ -1093,7 +1127,7 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, } } } - *tables= tot_list; + DBUG_RETURN(false); } @@ -1177,18 +1211,13 @@ long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path) DBUG_PRINT("enter", ("path: %s", org_path)); for (uint idx=0 ; - idx < (uint) dirp->number_off_files && !thd->killed ; + idx < (uint) dirp->number_of_files && !thd->killed ; idx++) { FILEINFO *file=dirp->dir_entry+idx; char *extension, *revision; DBUG_PRINT("info",("Examining: %s", file->name)); - /* skiping . and .. */ - if (file->name[0] == '.' && (!file->name[1] || - (file->name[1] == '.' && !file->name[2]))) - continue; - extension= fn_ext(file->name); if (extension[0] != '.' || extension[1] != 'f' || extension[2] != 'r' || @@ -1328,27 +1357,6 @@ static void backup_current_db_name(THD *thd, /** - Return TRUE if db1_name is equal to db2_name, FALSE otherwise. - - The function allows to compare database names according to the MySQL - rules. The database names db1 and db2 are equal if: - - db1 is NULL and db2 is NULL; - or - - db1 is not-NULL, db2 is not-NULL, db1 is equal (ignoring case) to - db2 in system character set (UTF8). -*/ - -static inline bool -cmp_db_names(const char *db1_name, - const char *db2_name) -{ - return ((!db1_name && !db2_name) || - (db1_name && db2_name && - my_strcasecmp(system_charset_info, db1_name, db2_name) == 0)); -} - - -/** @brief Change the current database and its attributes unconditionally. @param thd thread handle @@ -1492,14 +1500,18 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) DBUG_PRINT("info",("Use database: %s", new_db_file_name.str)); #ifndef NO_EMBEDDED_ACCESS_CHECKS - db_access= - test_all_bits(sctx->master_access, DB_ACLS) ? - DB_ACLS : - acl_get(sctx->host, - sctx->ip, - sctx->priv_user, - new_db_file_name.str, - FALSE) | sctx->master_access; + if (test_all_bits(sctx->master_access, DB_ACLS)) + db_access= DB_ACLS; + else + { + db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, + new_db_file_name.str, FALSE) | sctx->master_access; + if (sctx->priv_role[0]) + { + /* include a possible currently set role for access */ + db_access|= acl_get("", "", sctx->priv_role, new_db_file_name.str, FALSE); + } + } if (!force_switch && !(db_access & DB_ACLS) && @@ -1524,7 +1536,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) { /* Throw a warning and free new_db_file_name. */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR), new_db_file_name.str); @@ -1674,7 +1686,7 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db) /* Step2: Move tables to the new database */ if ((dirp = my_dir(path,MYF(MY_DONT_SORT)))) { - uint nfiles= (uint) dirp->number_off_files; + uint nfiles= (uint) dirp->number_of_files; for (uint idx=0 ; idx < nfiles && !thd->killed ; idx++) { FILEINFO *file= dirp->dir_entry + idx; @@ -1765,17 +1777,15 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db) if ((dirp = my_dir(path,MYF(MY_DONT_SORT)))) { - uint nfiles= (uint) dirp->number_off_files; + uint nfiles= (uint) dirp->number_of_files; for (uint idx=0 ; idx < nfiles ; idx++) { FILEINFO *file= dirp->dir_entry + idx; char oldname[FN_REFLEN + 1], newname[FN_REFLEN + 1]; DBUG_PRINT("info",("Examining: %s", file->name)); - /* skiping . and .. and MY_DB_OPT_FILE */ - if ((file->name[0] == '.' && - (!file->name[1] || (file->name[1] == '.' && !file->name[2]))) || - !my_strcasecmp(files_charset_info, file->name, MY_DB_OPT_FILE)) + /* skiping MY_DB_OPT_FILE */ + if (!my_strcasecmp(files_charset_info, file->name, MY_DB_OPT_FILE)) continue; /* pass empty file name, and file->name as extension to avoid encoding */ |