/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB 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; either version 2 of the License, or (at your option) any later version. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* create and drop of databases */ #include "mysql_priv.h" #include "sql_acl.h" #include #include #ifdef __WIN__ #include #endif static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path, uint level); /* db-name is already validated when we come here */ void mysql_create_db(THD *thd, char *db, uint create_options) { char path[FN_REFLEN+16]; MY_DIR *dirp; long result=1; DBUG_ENTER("mysql_create_db"); VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); VOID(pthread_mutex_lock(&LOCK_open)); // do not create database if another thread is holding read lock if (global_read_lock) { if (thd->global_read_lock) { net_printf(&thd->net, ER_CREATE_DB_WITH_READ_LOCK); VOID(pthread_mutex_unlock(&LOCK_open)); goto exit; } while (global_read_lock && ! thd->killed) { (void) pthread_cond_wait(&COND_refresh,&LOCK_open); } if (thd->killed) { net_printf(&thd->net, ER_SERVER_SHUTDOWN); VOID(pthread_mutex_unlock(&LOCK_open)); goto exit; } } VOID(pthread_mutex_unlock(&LOCK_open)); /* Check directory */ (void)sprintf(path,"%s/%s", mysql_data_home, db); unpack_dirname(path,path); // Convert if not unix if ((dirp = my_dir(path,MYF(MY_DONT_SORT)))) { my_dirend(dirp); if (!(create_options & HA_LEX_CREATE_IF_NOT_EXISTS)) { net_printf(&thd->net,ER_DB_CREATE_EXISTS,db); goto exit; } result = 0; } else { strend(path)[-1]=0; // Remove last '/' from path if (my_mkdir(path,0777,MYF(0)) < 0) { net_printf(&thd->net,ER_CANT_CREATE_DB,db,my_errno); goto exit; } } if (!thd->query) { thd->query = path; thd->query_length = (uint) (strxmov(path,"create database ", db, NullS)- path); } { mysql_update_log.write(thd,thd->query, thd->query_length); if (mysql_bin_log.is_open()) { Query_log_event qinfo(thd, thd->query); mysql_bin_log.write(&qinfo); } } if (thd->query == path) { thd->query = 0; // just in case thd->query_length = 0; } send_ok(&thd->net, result); exit: VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); DBUG_VOID_RETURN; } const char *del_exts[]= {".frm",".ISM",".ISD",".ISM",".HSH",".DAT",".MRG",".MYI",".MYD", ".db", ".BAK", NullS}; static TYPELIB deletable_extentions= {array_elements(del_exts)-1,"del_exts", del_exts}; /* db-name is already validated when we come here */ void mysql_rm_db(THD *thd,char *db,bool if_exists) { long deleted=0; char path[FN_REFLEN+16]; MY_DIR *dirp; DBUG_ENTER("mysql_rm_db"); VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); VOID(pthread_mutex_lock(&LOCK_open)); // do not drop database if another thread is holding read lock if (global_read_lock) { if (thd->global_read_lock) { net_printf(&thd->net, ER_DROP_DB_WITH_READ_LOCK); goto exit; } while (global_read_lock && ! thd->killed) { (void) pthread_cond_wait(&COND_refresh,&LOCK_open); } if (thd->killed) { net_printf(&thd->net, ER_SERVER_SHUTDOWN); goto exit; } } (void) sprintf(path,"%s/%s",mysql_data_home,db); unpack_dirname(path,path); // Convert if not unix /* See if the directory exists */ if (!(dirp = my_dir(path,MYF(MY_WME | MY_DONT_SORT)))) { if (!if_exists) net_printf(&thd->net,ER_DB_DROP_EXISTS,db); else send_ok(&thd->net,0); goto exit; } remove_db_from_cache(db); if ((deleted=mysql_rm_known_files(thd, dirp, path,0)) >= 0) { if (!thd->query) { thd->query = path; thd->query_length = (uint) (strxmov(path,"drop database ", db, NullS)- path); } mysql_update_log.write(thd, thd->query, thd->query_length); if (mysql_bin_log.is_open()) { Query_log_event qinfo(thd, thd->query); mysql_bin_log.write(&qinfo); } if (thd->query == path) { thd->query = 0; // just in case thd->query_length = 0; } send_ok(&thd->net,(ulong) deleted); } exit: VOID(pthread_mutex_unlock(&LOCK_open)); VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); /* If there are running queries on the tables, MySQL needs to get access to LOCK_open to end them. InnoDB on the other hand waits for the queries to end before dropping the database. That is why we must do the dropping outside of the mutexes above, otherwise the server always hangs if there are running queries. We only drop inside InnoDB if deleted got value >= 0 which means that it was successful. */ if (deleted >= 0) ha_drop_database(path); DBUG_VOID_RETURN; } /* Removes files with known extensions plus all found subdirectories that are 2 digits (raid directories). */ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, uint level) { long deleted=0; ulong found_other_files=0; char filePath[FN_REFLEN]; DBUG_ENTER("mysql_rm_known_files"); DBUG_PRINT("enter",("path: %s", org_path)); /* remove all files with known extensions */ for (uint idx=2 ; idx < (uint) dirp->number_off_files && !thd->killed ; idx++) { FILEINFO *file=dirp->dir_entry+idx; DBUG_PRINT("info",("Examining: %s", file->name)); /* Check if file is a raid directory */ if (isdigit(file->name[0]) && isdigit(file->name[1]) && !file->name[2] && !level) { char newpath[FN_REFLEN]; MY_DIR *new_dirp; strxmov(newpath,org_path,"/",file->name,NullS); 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,newpath,1)) < 0) { my_dirend(dirp); DBUG_RETURN(-1); } } continue; } if (find_type(fn_ext(file->name),&deletable_extentions,1+2) <= 0) { found_other_files++; continue; } strxmov(filePath,org_path,"/",file->name,NullS); unpack_filename(filePath,filePath); if (my_delete(filePath,MYF(MY_WME))) { net_printf(&thd->net,ER_DB_DROP_DELETE,filePath,my_error); my_dirend(dirp); DBUG_RETURN(-1); } deleted++; } my_dirend(dirp); if (thd->killed) { send_error(&thd->net,ER_SERVER_SHUTDOWN); DBUG_RETURN(-1); } /* If the directory is a symbolic link, remove the link first, then remove the directory the symbolic link pointed at */ if (!found_other_files) { char tmp_path[FN_REFLEN], *pos; char *path=unpack_filename(tmp_path,org_path); #ifdef HAVE_READLINK int error; /* Remove end FN_LIBCHAR as this causes problem on Linux in readlink */ pos=strend(path); if (pos > path && pos[-1] == FN_LIBCHAR) *--pos=0; if ((error=my_readlink(filePath, path, MYF(MY_WME))) < 0) DBUG_RETURN(-1); if (!error) { if (my_delete(path,MYF(!level ? MY_WME : 0))) { /* Don't give errors if we can't delete 'RAID' directory */ if (level) DBUG_RETURN(deleted); send_error(&thd->net); DBUG_RETURN(-1); } /* Delete directory symbolic link pointed at */ path= filePath; } #endif /* Remove last FN_LIBCHAR to not cause a probelm on OS/2 */ pos=strend(path); if (pos > path && pos[-1] == FN_LIBCHAR) *--pos=0; /* Don't give errors if we can't delete 'RAID' directory */ if (rmdir(path) < 0 && !level) { net_printf(&thd->net,ER_DB_DROP_RMDIR, path,errno); DBUG_RETURN(-1); } } DBUG_RETURN(deleted); } bool mysql_change_db(THD *thd,const char *name) { int length; char *dbname=my_strdup((char*) name,MYF(MY_WME)); char path[FN_REFLEN]; uint db_access; DBUG_ENTER("mysql_change_db"); if (!dbname || !(length=strip_sp(dbname))) { x_free(dbname); /* purecov: inspected */ send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } if ((length > NAME_LEN) || check_db_name(dbname)) { net_printf(&thd->net,ER_WRONG_DB_NAME, dbname); x_free(dbname); DBUG_RETURN(1); } DBUG_PRINT("general",("Use database: %s", dbname)); if (test_all_bits(thd->master_access,DB_ACLS)) db_access=DB_ACLS; else db_access= (acl_get(thd->host,thd->ip,(char*) &thd->remote.sin_addr, thd->priv_user,dbname) | thd->master_access); if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname))) { net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, thd->priv_user, thd->host ? thd->host : thd->ip ? thd->ip : "unknown", dbname); mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), thd->priv_user, thd->host ? thd->host : thd->ip ? thd->ip : "unknown", dbname); my_free(dbname,MYF(0)); DBUG_RETURN(1); } (void) sprintf(path,"%s/%s",mysql_data_home,dbname); length=unpack_dirname(path,path); // Convert if not unix if (length && path[length-1] == FN_LIBCHAR) path[length-1]=0; // remove ending '\' if (access(path,F_OK)) { net_printf(&thd->net,ER_BAD_DB_ERROR,dbname); my_free(dbname,MYF(0)); DBUG_RETURN(1); } send_ok(&thd->net); x_free(thd->db); thd->db=dbname; thd->db_access=db_access; DBUG_RETURN(0); }