sql_db.cc 9.57 KB
Newer Older
unknown's avatar
unknown committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/* 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 <my_dir.h>
#include <m_ctype.h>
#ifdef __WIN__
#include <direct.h>
#endif

28 29
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
				 const char *db, const char *path,
unknown's avatar
unknown committed
30 31
				 uint level);

32 33
/* db-name is already validated when we come here */

unknown's avatar
unknown committed
34
int mysql_create_db(THD *thd, char *db, uint create_options, bool silent)
unknown's avatar
unknown committed
35 36 37 38
{
  char	 path[FN_REFLEN+16];
  MY_DIR *dirp;
  long result=1;
39
  int error = 0;
unknown's avatar
unknown committed
40 41 42
  DBUG_ENTER("mysql_create_db");
  
  VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
43 44

  // do not create database if another thread is holding read lock
unknown's avatar
unknown committed
45
  if (wait_if_global_read_lock(thd,0))
46
  {
unknown's avatar
unknown committed
47 48
    error= -1;
    goto exit2;
49
  }
unknown's avatar
unknown committed
50 51 52 53 54 55 56 57 58

  /* 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))
    {
unknown's avatar
unknown committed
59 60
      my_error(ER_DB_CREATE_EXISTS,MYF(0),db);
      error = -1;
unknown's avatar
unknown committed
61 62 63 64 65 66 67 68 69
      goto exit;
    }
    result = 0;
  }
  else
  {
    strend(path)[-1]=0;				// Remove last '/' from path
    if (my_mkdir(path,0777,MYF(0)) < 0)
    {
unknown's avatar
unknown committed
70 71
      my_error(ER_CANT_CREATE_DB,MYF(0),db,my_errno);
      error = -1;
unknown's avatar
unknown committed
72 73 74
      goto exit;
    }
  }
75

unknown's avatar
unknown committed
76
  if (!silent)
unknown's avatar
unknown committed
77
  {
78
    if (!thd->query)
79
    {
80 81 82
      thd->query = path;
      thd->query_length = (uint) (strxmov(path,"create database ", db, NullS)-
				  path);
83
    }
84 85 86 87 88 89 90 91 92 93 94 95 96 97
    {
      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);
unknown's avatar
unknown committed
98
  }
unknown's avatar
unknown committed
99

unknown's avatar
unknown committed
100
exit:
unknown's avatar
unknown committed
101 102
  start_waiting_global_read_lock(thd);
exit2:
unknown's avatar
unknown committed
103
  VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
104
  DBUG_RETURN(error);
unknown's avatar
unknown committed
105 106
}

107
const char *del_exts[]= {".frm", ".BAK", NullS};
unknown's avatar
unknown committed
108 109 110
static TYPELIB deletable_extentions=
{array_elements(del_exts)-1,"del_exts", del_exts};

unknown's avatar
unknown committed
111 112 113 114
const char *known_exts[]=
{".ISM",".ISD",".ISM",".MRG",".MYI",".MYD", ".db", NullS};
static TYPELIB known_extentions=
{array_elements(del_exts)-1,"del_exts", known_exts};
unknown's avatar
unknown committed
115

116 117 118 119 120 121
/*
  Drop all tables in a database.

  db-name is already validated when we come here
  If thd == 0, do not write any messages; This is useful in replication
  when we want to remove a stale database before replacing it with the new one
122
*/
123 124


unknown's avatar
unknown committed
125
int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
unknown's avatar
unknown committed
126 127
{
  long deleted=0;
128
  int error = 0;
unknown's avatar
unknown committed
129 130 131 132 133 134
  char	path[FN_REFLEN+16];
  MY_DIR *dirp;
  DBUG_ENTER("mysql_rm_db");

  VOID(pthread_mutex_lock(&LOCK_mysql_create_db));

135
  // do not drop database if another thread is holding read lock
136
  if (wait_if_global_read_lock(thd,0))
unknown's avatar
unknown committed
137 138 139 140
  {
    error= -1;
    goto exit2;
  }
141

unknown's avatar
unknown committed
142 143 144
  (void) sprintf(path,"%s/%s",mysql_data_home,db);
  unpack_dirname(path,path);			// Convert if not unix
  /* See if the directory exists */
unknown's avatar
unknown committed
145
  if (!(dirp = my_dir(path,MYF(MY_DONT_SORT))))
unknown's avatar
unknown committed
146
  {
unknown's avatar
unknown committed
147
    if (!if_exists)
148
    {
unknown's avatar
unknown committed
149 150
      error= -1;
      my_error(ER_DB_DROP_EXISTS,MYF(0),db);
151
    }
unknown's avatar
unknown committed
152 153
    else if (!silent)
      send_ok(&thd->net,0);
unknown's avatar
unknown committed
154 155 156 157
    goto exit;
  }
  remove_db_from_cache(db);

158 159
  error = -1;
  if ((deleted=mysql_rm_known_files(thd, dirp, db, path,0)) >= 0 && thd)
unknown's avatar
unknown committed
160
  {
unknown's avatar
unknown committed
161
    if (!silent)
162
    {
unknown's avatar
unknown committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
      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);
unknown's avatar
unknown committed
181
    }
unknown's avatar
unknown committed
182
    error = 0;
unknown's avatar
unknown committed
183 184 185
  }

exit:
186
  start_waiting_global_read_lock(thd);
unknown's avatar
unknown committed
187 188
exit2:
  VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
189

190
  DBUG_RETURN(error);
unknown's avatar
unknown committed
191 192 193 194 195
}

/*
  Removes files with known extensions plus all found subdirectories that
  are 2 digits (raid directories).
196
  thd MUST be set when calling this function!
unknown's avatar
unknown committed
197 198
*/

199 200
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
				 const char *org_path, uint level)
unknown's avatar
unknown committed
201 202 203 204
{
  long deleted=0;
  ulong found_other_files=0;
  char filePath[FN_REFLEN];
205
  TABLE_LIST *tot_list=0, **tot_list_next;
unknown's avatar
unknown committed
206
  DBUG_ENTER("mysql_rm_known_files");
207
  DBUG_PRINT("enter",("path: %s", org_path));
208 209

  tot_list_next= &tot_list;
unknown's avatar
unknown committed
210 211

  for (uint idx=2 ;
212
       idx < (uint) dirp->number_off_files && !thd->killed ;
unknown's avatar
unknown committed
213 214 215 216 217 218 219 220 221 222 223
       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;
224
      strxmov(newpath,org_path,"/",file->name,NullS);
225
      unpack_filename(newpath,newpath);
unknown's avatar
unknown committed
226 227 228
      if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT))))
      {
	DBUG_PRINT("my",("New subdir found: %s", newpath));
229
	if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1)) < 0)
unknown's avatar
unknown committed
230 231 232 233 234 235 236
	{
	  my_dirend(dirp);
	  DBUG_RETURN(-1);
	}
      }
      continue;
    }
237
    if (find_type(fn_ext(file->name),&deletable_extentions,1+2) <= 0)
unknown's avatar
unknown committed
238
    {
unknown's avatar
unknown committed
239 240
      if (find_type(fn_ext(file->name),&known_extentions,1+2) <= 0)
	found_other_files++;
unknown's avatar
unknown committed
241 242
      continue;
    }
243
    strxmov(filePath,org_path,"/",file->name,NullS);
244
    if (db && !strcasecmp(fn_ext(file->name), reg_ext))
unknown's avatar
unknown committed
245
    {
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
      /* Drop the table nicely */
      *fn_ext(file->name)=0;			// Remove extension
      TABLE_LIST *table_list=(TABLE_LIST*)
	thd->calloc(sizeof(*table_list)+ strlen(db)+strlen(file->name)+1);
      if (!table_list)
      {
	my_dirend(dirp);
	DBUG_RETURN(-1);
      }
      table_list->db= (char*) (table_list+1);
      strmov(table_list->real_name=strmov(table_list->db,db)+1,
	     file->name);
      /* Link into list */
      (*tot_list_next)= table_list;
      tot_list_next= &table_list->next;
unknown's avatar
unknown committed
261
    }
262 263
    else
    {
unknown's avatar
unknown committed
264

265 266 267 268 269 270 271 272
      if (my_delete_with_symlink(filePath,MYF(MY_WME)))
      {
	my_dirend(dirp);
	DBUG_RETURN(-1);
      }
      deleted++;
    }
  }
unknown's avatar
unknown committed
273 274
  my_dirend(dirp);

275
  if (thd->killed || (tot_list && mysql_rm_table_part2(thd, tot_list, 1, 1)))
unknown's avatar
unknown committed
276 277 278 279 280 281 282 283
    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)
  {
284
    char tmp_path[FN_REFLEN];
285 286
    char *path=unpack_filename(tmp_path,org_path);
#ifdef HAVE_READLINK
unknown's avatar
unknown committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300
    int linkcount = readlink(path,filePath,sizeof(filePath)-1);
    if (linkcount > 0)			// If the path was a symbolic link
    {
      *(filePath + linkcount) = '\0';
      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);
	DBUG_RETURN(-1);
      }
      path=filePath;
    }
#endif
301
    /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */
302 303 304
    char *pos=strend(path);
    if (pos > path && pos[-1] == FN_LIBCHAR)
      *--pos=0;
unknown's avatar
unknown committed
305 306 307
    /* Don't give errors if we can't delete 'RAID' directory */
    if (rmdir(path) < 0 && !level)
    {
308
      my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno);
unknown's avatar
unknown committed
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
      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=stripp_sp(dbname)))
  {
    x_free(dbname);				/* purecov: inspected */
    send_error(&thd->net,ER_NO_DB_ERROR);	/* purecov: inspected */
    DBUG_RETURN(1);				/* purecov: inspected */
  }
unknown's avatar
unknown committed
330
  if ((length > NAME_LEN) || check_db_name(dbname))
unknown's avatar
unknown committed
331 332
  {
    net_printf(&thd->net,ER_WRONG_DB_NAME, dbname);
unknown's avatar
unknown committed
333
    x_free(dbname);
unknown's avatar
unknown committed
334 335 336 337 338 339 340 341 342 343 344 345 346
    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,
347
	       thd->host_or_ip,
unknown's avatar
unknown committed
348
	       dbname);
349
    mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
unknown's avatar
unknown committed
350
		    thd->priv_user,
351
		    thd->host_or_ip,
unknown's avatar
unknown committed
352 353 354 355 356 357
		    dbname);
    my_free(dbname,MYF(0));
    DBUG_RETURN(1);
  }

  (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
unknown's avatar
unknown committed
358 359 360
  length=unpack_dirname(path,path);		// Convert if not unix
  if (length && path[length-1] == FN_LIBCHAR)
    path[length-1]=0;				// remove ending '\'
unknown's avatar
unknown committed
361 362 363 364 365 366 367 368 369 370 371 372
  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);
}