sql_db.cc 9.84 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4 5 6
   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.
unknown's avatar
unknown committed
7

unknown's avatar
unknown committed
8 9 10 11
   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.
unknown's avatar
unknown committed
12

unknown's avatar
unknown committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   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
  DBUG_ENTER("mysql_create_db");
unknown's avatar
unknown committed
41

unknown's avatar
unknown committed
42
  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", ".TMD", 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
const char *known_exts[]=
112
{".ISM",".ISD",".ISM",".MRG",".MYI",".MYD",".db",NullS};
unknown's avatar
unknown committed
113
static TYPELIB known_extentions=
114
{array_elements(known_exts)-1,"known_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
merge  
unknown committed
161
    ha_drop_database(path);
unknown's avatar
unknown committed
162
    query_cache_invalidate1(db);  
unknown's avatar
unknown committed
163
    if (!silent)
164
    {
unknown's avatar
unknown committed
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
      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
183
    }
unknown's avatar
unknown committed
184
    error = 0;
unknown's avatar
unknown committed
185 186 187
  }

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

192
  DBUG_RETURN(error);
unknown's avatar
unknown committed
193 194 195 196 197
}

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

201 202
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
203 204 205 206
{
  long deleted=0;
  ulong found_other_files=0;
  char filePath[FN_REFLEN];
207
  TABLE_LIST *tot_list=0, **tot_list_next;
unknown's avatar
unknown committed
208
  DBUG_ENTER("mysql_rm_known_files");
209
  DBUG_PRINT("enter",("path: %s", org_path));
210 211

  tot_list_next= &tot_list;
unknown's avatar
unknown committed
212 213

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

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

277
  if (thd->killed || (tot_list && mysql_rm_table_part2(thd, tot_list, 1, 1)))
unknown's avatar
unknown committed
278 279 280 281 282 283 284 285
    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)
  {
286
    char tmp_path[FN_REFLEN], *pos;
287 288
    char *path=unpack_filename(tmp_path,org_path);
#ifdef HAVE_READLINK
289 290 291 292 293 294 295 296 297 298
    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)
unknown's avatar
unknown committed
299 300 301 302 303 304 305 306
    {
      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);
      }
307 308
      /* Delete directory symbolic link pointed at */
      path= filePath;
unknown's avatar
unknown committed
309 310
    }
#endif
311
    /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */
312
    pos=strend(path);
313 314
    if (pos > path && pos[-1] == FN_LIBCHAR)
      *--pos=0;
unknown's avatar
unknown committed
315 316 317
    /* Don't give errors if we can't delete 'RAID' directory */
    if (rmdir(path) < 0 && !level)
    {
318
      my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno);
unknown's avatar
unknown committed
319 320 321 322 323 324 325 326 327
      DBUG_RETURN(-1);
    }
  }
  DBUG_RETURN(deleted);
}


bool mysql_change_db(THD *thd,const char *name)
{
unknown's avatar
unknown committed
328
  int length, db_length;
unknown's avatar
unknown committed
329 330 331 332 333
  char *dbname=my_strdup((char*) name,MYF(MY_WME));
  char	path[FN_REFLEN];
  uint db_access;
  DBUG_ENTER("mysql_change_db");

unknown's avatar
unknown committed
334
  if (!dbname || !(db_length=stripp_sp(dbname)))
unknown's avatar
unknown committed
335 336 337 338 339
  {
    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
340
  if ((db_length > NAME_LEN) || check_db_name(dbname))
unknown's avatar
unknown committed
341 342
  {
    net_printf(&thd->net,ER_WRONG_DB_NAME, dbname);
unknown's avatar
unknown committed
343
    x_free(dbname);
unknown's avatar
unknown committed
344 345
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
346
  DBUG_PRINT("info",("Use database: %s", dbname));
unknown's avatar
unknown committed
347 348 349 350 351 352 353 354 355 356
  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,
357
	       thd->host_or_ip,
unknown's avatar
unknown committed
358
	       dbname);
359
    mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
unknown's avatar
unknown committed
360
		    thd->priv_user,
361
		    thd->host_or_ip,
unknown's avatar
unknown committed
362 363 364 365 366 367
		    dbname);
    my_free(dbname,MYF(0));
    DBUG_RETURN(1);
  }

  (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
unknown's avatar
unknown committed
368 369 370
  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
371 372 373 374 375 376 377 378 379
  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;
unknown's avatar
unknown committed
380
  thd->db_length=db_length;
unknown's avatar
unknown committed
381 382 383
  thd->db_access=db_access;
  DBUG_RETURN(0);
}