sql_db.cc 26.4 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2003 MySQL 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
   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"
21
#include <mysys_err.h>
unknown's avatar
unknown committed
22
#include "sql_acl.h"
23
#include "sp.h"
unknown's avatar
unknown committed
24 25 26 27 28 29
#include <my_dir.h>
#include <m_ctype.h>
#ifdef __WIN__
#include <direct.h>
#endif

30 31
const char *del_exts[]= {".frm", ".BAK", ".TMD",".opt", NullS};
static TYPELIB deletable_extentions=
32
{array_elements(del_exts)-1,"del_exts", del_exts, NULL};
33

34 35
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
				 const char *db, const char *path,
unknown's avatar
unknown committed
36
				 uint level);
37 38
static 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);
39 40 41
/* Database options hash */
static HASH dboptions;
static my_bool dboptions_init= 0;
unknown's avatar
unknown committed
42
static rw_lock_t LOCK_dboptions;
43 44 45 46 47 48 49 50 51

/* Structure for database options */
typedef struct my_dbopt_st
{
  char *name;			/* Database name                  */
  uint name_length;		/* Database length name           */
  CHARSET_INFO *charset;	/* Database default character set */
} my_dbopt_t;

unknown's avatar
unknown committed
52

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
/*
  Function we use in the creation of our hash to get key.
*/
static byte* dboptions_get_key(my_dbopt_t *opt, uint *length,
                               my_bool not_used __attribute__((unused)))
{
  *length= opt->name_length;
  return (byte*) opt->name;
}


/*
  Function to free dboptions hash element
*/

static void free_dbopt(void *dbopt)
{
  my_free((gptr) dbopt, MYF(0));
}


/* 
unknown's avatar
unknown committed
75 76 77 78 79 80 81 82 83 84 85
  Initialize database option hash

  SYNOPSIS
    my_dbopt_init()

  NOTES
    Must be called before any other database function is called.

  RETURN
    0	ok
    1	Fatal error
86 87
*/

unknown's avatar
unknown committed
88
bool my_dbopt_init(void)
89
{
unknown's avatar
unknown committed
90 91
  bool error= 0;
  (void) my_rwlock_init(&LOCK_dboptions, NULL);
92 93 94
  if (!dboptions_init)
  {
    dboptions_init= 1;
unknown's avatar
unknown committed
95 96 97 98
    error= hash_init(&dboptions, lower_case_table_names ? 
                     &my_charset_bin : system_charset_info,
                     32, 0, 0, (hash_get_key) dboptions_get_key,
                     free_dbopt,0);
99
  }
unknown's avatar
unknown committed
100
  return error;
101 102 103 104 105 106
}


/* 
  Free database option hash.
*/
unknown's avatar
unknown committed
107

108 109 110 111 112
void my_dbopt_free(void)
{
  if (dboptions_init)
  {
    dboptions_init= 0;
unknown's avatar
unknown committed
113 114
    hash_free(&dboptions);
    (void) rwlock_destroy(&LOCK_dboptions);
115
  }
unknown's avatar
unknown committed
116 117 118 119 120 121 122 123 124 125 126
}


void my_dbopt_cleanup(void)
{
  rw_wrlock(&LOCK_dboptions);
  hash_free(&dboptions);
  hash_init(&dboptions, lower_case_table_names ? 
            &my_charset_bin : system_charset_info,
            32, 0, 0, (hash_get_key) dboptions_get_key,
            free_dbopt,0);
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
  rw_unlock(&LOCK_dboptions);
}


/*
  Find database options in the hash.
  
  DESCRIPTION
    Search a database options in the hash, usings its path.
    Fills "create" on success.
  
  RETURN VALUES
    0 on success.
    1 on error.
*/

static my_bool get_dbopt(const char *dbname, HA_CREATE_INFO *create)
{
  my_dbopt_t *opt;
  uint length;
unknown's avatar
unknown committed
147
  my_bool error= 1;
148 149 150 151 152 153 154
  
  length= (uint) strlen(dbname);
  
  rw_rdlock(&LOCK_dboptions);
  if ((opt= (my_dbopt_t*) hash_search(&dboptions, (byte*) dbname, length)))
  {
    create->default_table_charset= opt->charset;
unknown's avatar
unknown committed
155
    error= 0;
156 157
  }
  rw_unlock(&LOCK_dboptions);
unknown's avatar
unknown committed
158
  return error;
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
}


/*
  Writes database options into the hash.
  
  DESCRIPTION
    Inserts database options into the hash, or updates
    options if they are already in the hash.
  
  RETURN VALUES
    0 on success.
    1 on error.
*/

static my_bool put_dbopt(const char *dbname, HA_CREATE_INFO *create)
{
  my_dbopt_t *opt;
  uint length;
unknown's avatar
unknown committed
178 179 180
  my_bool error= 0;
  DBUG_ENTER("put_dbopt");

181 182 183
  length= (uint) strlen(dbname);
  
  rw_wrlock(&LOCK_dboptions);
unknown's avatar
unknown committed
184
  if (!(opt= (my_dbopt_t*) hash_search(&dboptions, (byte*) dbname, length)))
185 186 187 188
  { 
    /* Options are not in the hash, insert them */
    char *tmp_name;
    if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
unknown's avatar
unknown committed
189 190
                         &opt, (uint) sizeof(*opt), &tmp_name, length+1,
                         NullS))
191
    {
unknown's avatar
unknown committed
192 193
      error= 1;
      goto end;
194 195 196 197
    }
    
    opt->name= tmp_name;
    strmov(opt->name, dbname);
unknown's avatar
unknown committed
198
    opt->name_length= length;
199
    
unknown's avatar
unknown committed
200 201
    if ((error= my_hash_insert(&dboptions, (byte*) opt)))
    {
202
      my_free((gptr) opt, MYF(0));
unknown's avatar
unknown committed
203 204
      goto end;
    }
205 206
  }

unknown's avatar
unknown committed
207 208 209 210
  /* Update / write options in hash */
  opt->charset= create->default_table_charset;

end:
211
  rw_unlock(&LOCK_dboptions);  
unknown's avatar
unknown committed
212
  DBUG_RETURN(error);
213 214 215 216 217 218 219 220 221 222 223
}


/*
  Deletes database options from the hash.
*/

void del_dbopt(const char *path)
{
  my_dbopt_t *opt;
  rw_wrlock(&LOCK_dboptions);
224 225
  if ((opt= (my_dbopt_t *)hash_search(&dboptions, (const byte*) path,
                                      strlen(path))))
226 227 228 229 230
    hash_delete(&dboptions, (byte*) opt);
  rw_unlock(&LOCK_dboptions);
}


231
/* 
232 233 234 235 236 237 238 239
  Create database options file:

  DESCRIPTION
    Currently database default charset is only stored there.

  RETURN VALUES
  0	ok
  1	Could not create file or write to it.  Error sent through my_error()
240 241
*/

242
static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
243 244
{
  register File file;
245 246
  char buf[256]; // Should be enough for one option
  bool error=1;
247

248 249 250 251 252 253
  if (!create->default_table_charset)
    create->default_table_charset= thd->variables.collation_server;
  
  if (put_dbopt(path, create))
    return 1;
  
254
  if ((file=my_create(path, CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0)
255
  {
256
    ulong length;
unknown's avatar
unknown committed
257 258 259 260 261
    length= (ulong) (strxnmov(buf, sizeof(buf), "default-character-set=",
                              create->default_table_charset->csname,
                              "\ndefault-collation=",
                              create->default_table_charset->name,
                              "\n", NullS) - buf);
262 263 264 265

    /* Error is written by my_write */
    if (!my_write(file,(byte*) buf, length, MYF(MY_NABP+MY_WME)))
      error=0;
266 267 268 269 270 271
    my_close(file,MYF(0));
  }
  return error;
}


272 273
/* 
  Load database options file
274

275 276 277 278 279 280
  load_db_opt()
  path		Path for option file
  create	Where to store the read options

  DESCRIPTION
    For now, only default-character-set is read.
281

282 283 284
  RETURN VALUES
  0	File found
  1	No database file or could not open it
285

286 287
*/

288
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
289 290 291 292 293 294 295 296
{
  File file;
  char buf[256];
  DBUG_ENTER("load_db_opt");
  bool error=1;
  uint nbytes;

  bzero((char*) create,sizeof(*create));
unknown's avatar
unknown committed
297
  create->default_table_charset= thd->variables.collation_server;
298 299 300 301 302 303
  
  /* Check if options for this database are already in the hash */
  if (!get_dbopt(path, create))
    DBUG_RETURN(0);	   
  
  /* Otherwise, load options from the .opt file */
304 305 306 307 308
  if ((file=my_open(path, O_RDONLY | O_SHARE, MYF(0))) >= 0)
  {
    IO_CACHE cache;
    init_io_cache(&cache, file, IO_SIZE, READ_CACHE, 0, 0, MYF(0));

309
    while ((int) (nbytes= my_b_gets(&cache, (char*) buf, sizeof(buf))) > 0)
310 311 312
    {
      char *pos= buf+nbytes-1;
      /* Remove end space and control characters */
unknown's avatar
unknown committed
313
      while (pos > buf && !my_isgraph(&my_charset_latin1, pos[-1]))
314 315 316
	pos--;
      *pos=0;
      if ((pos= strchr(buf, '=')))
317
      {
318
	if (!strncmp(buf,"default-character-set", (pos-buf)))
319
	{
320 321 322 323 324 325 326
          /*
             Try character set name, and if it fails 
             try collation name, probably it's an old
             4.1.0 db.opt file, which didn't have
             separate default-character-set and
             default-collation commands.
          */
unknown's avatar
unknown committed
327
	  if (!(create->default_table_charset=
328 329 330
		get_charset_by_csname(pos+1, MY_CS_PRIMARY, MYF(0))) &&
              !(create->default_table_charset=
                get_charset_by_name(pos+1, MYF(0))))
331
	  {
unknown's avatar
unknown committed
332
	    sql_print_error("Error while loading database options: '%s':",path);
333
	    sql_print_error(ER(ER_UNKNOWN_CHARACTER_SET),pos+1);
unknown's avatar
unknown committed
334
            create->default_table_charset= default_charset_info;
335 336 337 338
	  }
	}
	else if (!strncmp(buf,"default-collation", (pos-buf)))
	{
339 340
	  if (!(create->default_table_charset= get_charset_by_name(pos+1,
								   MYF(0))))
341
	  {
unknown's avatar
unknown committed
342
	    sql_print_error("Error while loading database options: '%s':",path);
343
	    sql_print_error(ER(ER_UNKNOWN_COLLATION),pos+1);
unknown's avatar
unknown committed
344
            create->default_table_charset= default_charset_info;
345 346 347 348
	  }
	}
      }
    }
349
    end_io_cache(&cache);
350
    my_close(file,MYF(0));
351 352 353 354 355 356 357 358
    /*
      Put the loaded value into the hash.
      Note that another thread could've added the same
      entry to the hash after we called get_dbopt(),
      but it's not an error, as put_dbopt() takes this
      possibility into account.
    */
    error= put_dbopt(path, create);
359
  }
360
  DBUG_RETURN(error);
361 362
}

363

364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
/*
  Create a database

  SYNOPSIS
  mysql_create_db()
  thd		Thread handler
  db		Name of database to create
		Function assumes that this is already validated.
  create_info	Database create options (like character set)
  silent	Used by replication when internally creating a database.
		In this case the entry should not be logged.

  RETURN VALUES
  0	ok
  -1	Error
379

380 381 382 383
*/

int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
		    bool silent)
unknown's avatar
unknown committed
384 385
{
  char	 path[FN_REFLEN+16];
unknown's avatar
unknown committed
386 387
  long result= 1;
  int error= 0;
388
  MY_STAT stat_info;
unknown's avatar
unknown committed
389
  uint create_options= create_info ? create_info->options : 0;
390
  uint path_len;
unknown's avatar
unknown committed
391
  DBUG_ENTER("mysql_create_db");
392
  
unknown's avatar
unknown committed
393
  VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
394 395

  // do not create database if another thread is holding read lock
396
  if (wait_if_global_read_lock(thd, 0, 1))
397
  {
unknown's avatar
unknown committed
398 399
    error= -1;
    goto exit2;
400
  }
unknown's avatar
unknown committed
401 402

  /* Check directory */
403
  strxmov(path, mysql_data_home, "/", db, NullS);
404 405
  path_len= unpack_dirname(path,path);    // Convert if not unix
  path[path_len-1]= 0;                    // Remove last '/' from path
406

407
  if (my_stat(path,&stat_info,MYF(0)))
unknown's avatar
unknown committed
408
  {
409
   if (!(create_options & HA_LEX_CREATE_IF_NOT_EXISTS))
unknown's avatar
unknown committed
410
    {
unknown's avatar
unknown committed
411
      my_error(ER_DB_CREATE_EXISTS,MYF(0),db);
unknown's avatar
unknown committed
412
      error= -1;
unknown's avatar
unknown committed
413 414
      goto exit;
    }
unknown's avatar
unknown committed
415
    result= 0;
unknown's avatar
unknown committed
416 417 418
  }
  else
  {
419 420 421 422 423
    if (my_errno != ENOENT)
    {
      my_error(EE_STAT, MYF(0),path,my_errno);
      goto exit;
    }
unknown's avatar
unknown committed
424 425
    if (my_mkdir(path,0777,MYF(0)) < 0)
    {
unknown's avatar
unknown committed
426
      my_error(ER_CANT_CREATE_DB,MYF(0),db,my_errno);
unknown's avatar
unknown committed
427
      error= -1;
unknown's avatar
unknown committed
428 429 430
      goto exit;
    }
  }
431

432 433
  path[path_len-1]= FN_LIBCHAR;
  strmake(path+path_len, MY_DB_OPT_FILE, sizeof(path)-path_len-1);
434
  if (write_db_opt(thd, path, create_info))
435 436 437 438 439
  {
    /*
      Could not create options file.
      Restore things to beginning.
    */
unknown's avatar
unknown committed
440
    path[path_len]= 0;
441 442 443 444 445 446 447 448 449 450 451
    if (rmdir(path) >= 0)
    {
      error= -1;
      goto exit;
    }
    /*
      We come here when we managed to create the database, but not the option
      file.  In this case it's best to just continue as if nothing has
      happened.  (This is a very unlikely senario)
    */
  }
452
  
unknown's avatar
unknown committed
453
  if (!silent)
unknown's avatar
unknown committed
454
  {
455 456 457 458
    char *query;
    uint query_length;

    if (!thd->query)				// Only in replication
459
    {
460
      query= 	     path;
unknown's avatar
unknown committed
461
      query_length= (uint) (strxmov(path,"create database `", db, "`", NullS) -
462
			    path);
463
    }
464
    else
465
    {
466 467
      query= 	    thd->query;
      query_length= thd->query_length;
468
    }
469
    if (mysql_bin_log.is_open())
470
    {
unknown's avatar
unknown committed
471
      Query_log_event qinfo(thd, query, query_length, 0);
472
      mysql_bin_log.write(&qinfo);
473
    }
474
    send_ok(thd, result);
unknown's avatar
unknown committed
475
  }
unknown's avatar
unknown committed
476

unknown's avatar
unknown committed
477
exit:
unknown's avatar
unknown committed
478 479
  start_waiting_global_read_lock(thd);
exit2:
unknown's avatar
unknown committed
480
  VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
481
  DBUG_RETURN(error);
unknown's avatar
unknown committed
482 483 484
}


485 486
/* db-name is already validated when we come here */

487
int mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
488
{
489
  char path[FN_REFLEN+16];
490
  long result=1;
unknown's avatar
unknown committed
491
  int error= 0;
492
  DBUG_ENTER("mysql_alter_db");
493 494 495 496

  VOID(pthread_mutex_lock(&LOCK_mysql_create_db));

  // do not alter database if another thread is holding read lock
unknown's avatar
unknown committed
497
  if ((error=wait_if_global_read_lock(thd,0,1)))
498
    goto exit2;
499

500
  /* Check directory */
unknown's avatar
unknown committed
501
  strxmov(path, mysql_data_home, "/", db, "/", MY_DB_OPT_FILE, NullS);
502
  fn_format(path, path, "", "", MYF(MY_UNPACK_FILENAME));
503
  if ((error=write_db_opt(thd, path, create_info)))
504 505
    goto exit;

506
  /* 
507 508
     Change options if current database is being altered
     TODO: Delete this code
509 510 511
  */
  if (thd->db && !strcmp(thd->db,db))
  {
512 513
    thd->db_charset= (create_info && create_info->default_table_charset) ?
		     create_info->default_table_charset : 
unknown's avatar
unknown committed
514
		     thd->variables.collation_server;
515
    thd->variables.collation_database= thd->db_charset;
516 517
  }

518
  if (mysql_bin_log.is_open())
unknown's avatar
unknown committed
519
  {
unknown's avatar
unknown committed
520
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
unknown's avatar
unknown committed
521
    thd->clear_error();
522
    mysql_bin_log.write(&qinfo);
unknown's avatar
unknown committed
523
  }
524
  send_ok(thd, result);
unknown's avatar
unknown committed
525

unknown's avatar
unknown committed
526
exit:
unknown's avatar
unknown committed
527 528
  start_waiting_global_read_lock(thd);
exit2:
unknown's avatar
unknown committed
529
  VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
530
  DBUG_RETURN(error ? -1 : 0); /* -1 to delegate send_error() */
unknown's avatar
unknown committed
531 532 533
}


534
/*
unknown's avatar
unknown committed
535
  Drop all tables in a database and the database itself
536

unknown's avatar
unknown committed
537 538 539 540 541 542 543 544 545 546 547
  SYNOPSIS
    mysql_rm_db()
    thd			Thread handle
    db			Database name in the case given by user
		        It's already validated when we come here
    if_exists		Don't give error if database doesn't exists
    silent		Don't generate errors

  RETURN
    0   ok (Database dropped)
    -1	Error generated
548
*/
549

unknown's avatar
unknown committed
550
int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
unknown's avatar
unknown committed
551 552
{
  long deleted=0;
unknown's avatar
unknown committed
553
  int error= 0;
unknown's avatar
unknown committed
554
  char	path[FN_REFLEN+16], tmp_db[NAME_LEN+1];
unknown's avatar
unknown committed
555
  MY_DIR *dirp;
556
  uint length;
unknown's avatar
unknown committed
557 558 559 560
  DBUG_ENTER("mysql_rm_db");

  VOID(pthread_mutex_lock(&LOCK_mysql_create_db));

561
  // do not drop database if another thread is holding read lock
562
  if (wait_if_global_read_lock(thd, 0, 1))
unknown's avatar
unknown committed
563 564 565 566
  {
    error= -1;
    goto exit2;
  }
567

unknown's avatar
unknown committed
568
  (void) sprintf(path,"%s/%s",mysql_data_home,db);
569 570 571 572 573
  length= unpack_dirname(path,path);		// Convert if not unix
  strmov(path+length, MY_DB_OPT_FILE);		// Append db option file name
  del_dbopt(path);				// Remove dboption hash entry
  path[length]= '\0';				// Remove file name

unknown's avatar
unknown committed
574
  /* See if the directory exists */
unknown's avatar
unknown committed
575
  if (!(dirp= my_dir(path,MYF(MY_DONT_SORT))))
unknown's avatar
unknown committed
576
  {
unknown's avatar
unknown committed
577
    if (!if_exists)
578
    {
unknown's avatar
unknown committed
579 580
      error= -1;
      my_error(ER_DB_DROP_EXISTS,MYF(0),db);
581
      goto exit;
582
    }
583
    else
584 585
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			  ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS), db);
586 587 588 589 590 591 592 593 594 595 596 597 598
  }
  else
  {
    pthread_mutex_lock(&LOCK_open);
    remove_db_from_cache(db);
    pthread_mutex_unlock(&LOCK_open);
    
    error= -1;
    if ((deleted= mysql_rm_known_files(thd, dirp, db, path, 0)) >= 0)
    {
      ha_drop_database(path);
      query_cache_invalidate1(db);  
      error = 0;
599
    }
unknown's avatar
unknown committed
600
  }
unknown's avatar
unknown committed
601 602 603 604
  if (lower_case_table_names)
  {
    /* Convert database to lower case */
    strmov(tmp_db, db);
605
    my_casedn_str(files_charset_info, tmp_db);
unknown's avatar
unknown committed
606 607
    db= tmp_db;
  }
608
  if (!silent && deleted>=0 && thd)
unknown's avatar
unknown committed
609
  {
610 611 612
    const char *query;
    ulong query_length;
    if (!thd->query)
613
    {
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
      /* The client used the old obsolete mysql_drop_db() call */
      query= path;
      query_length= (uint) (strxmov(path, "drop database `", db, "`",
                                     NullS) - path);
    }
    else
    {
      query =thd->query;
      query_length= thd->query_length;
    }
    if (mysql_bin_log.is_open())
    {
      Query_log_event qinfo(thd, query, query_length, 0);
      thd->clear_error();
      mysql_bin_log.write(&qinfo);
unknown's avatar
unknown committed
629
    }
630
    send_ok(thd, (ulong) deleted);
unknown's avatar
unknown committed
631 632 633
  }

exit:
634
  (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now  */
635
  start_waiting_global_read_lock(thd);
636 637
  /*
    If this database was the client's selected database, we silently change the
unknown's avatar
unknown committed
638 639
    client's selected database to nothing (to have an empty SELECT DATABASE()
    in the future). For this we free() thd->db and set it to 0. But we don't do
640 641 642
    free() for the slave thread. Indeed, doing a x_free() on it leads to nasty
    problems (i.e. long painful debugging) because in this thread, thd->db is
    the same as data_buf and db of the Query_log_event which is dropping the
unknown's avatar
unknown committed
643 644 645 646 647
    database. So if you free() thd->db, you're freeing data_buf. You set
    thd->db to 0 but not data_buf (thd->db and data_buf are two distinct
    pointers which point to the same place). Then in ~Query_log_event(), we
    have 'if (data_buf) free(data_buf)' data_buf is !=0 so this makes a
    DOUBLE free().
648 649 650 651
    Side effects of this double free() are, randomly (depends on the machine),
    when the slave is replicating a DROP DATABASE: 
    - garbage characters in the error message:
    "Error 'Can't drop database 'test2'; database doesn't exist' on query
652
    'h4zI'"
653 654 655 656
    - segfault
    - hang in "free(vio)" (yes!) in the I/O or SQL slave threads (so slave
    server hangs at shutdown etc).
  */
657
  if (thd->db && !strcmp(thd->db, db))
658
  {
659 660
    if (!(thd->slave_thread)) /* a slave thread will free it itself */
      x_free(thd->db);
661 662
    thd->db= 0;
    thd->db_length= 0;
663
  }
unknown's avatar
unknown committed
664 665
exit2:
  VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
666

667
  DBUG_RETURN(error);
unknown's avatar
unknown committed
668 669 670 671
}

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

676 677
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
678 679 680 681
{
  long deleted=0;
  ulong found_other_files=0;
  char filePath[FN_REFLEN];
682
  TABLE_LIST *tot_list=0, **tot_list_next;
683
  List<String> raid_dirs;
unknown's avatar
unknown committed
684
  DBUG_ENTER("mysql_rm_known_files");
685
  DBUG_PRINT("enter",("path: %s", org_path));
686 687

  tot_list_next= &tot_list;
unknown's avatar
unknown committed
688

689
  for (uint idx=0 ;
690
       idx < (uint) dirp->number_off_files && !thd->killed ;
unknown's avatar
unknown committed
691 692 693
       idx++)
  {
    FILEINFO *file=dirp->dir_entry+idx;
694
    char *extension;
unknown's avatar
unknown committed
695 696
    DBUG_PRINT("info",("Examining: %s", file->name));

697 698 699 700 701
    /* skiping . and .. */
    if (file->name[0] == '.' && (!file->name[1] ||
       (file->name[1] == '.' &&  !file->name[2])))
      continue;

unknown's avatar
unknown committed
702
    /* Check if file is a raid directory */
703
    if ((my_isdigit(system_charset_info, file->name[0]) ||
704
	 (file->name[0] >= 'a' && file->name[0] <= 'f')) &&
705
	(my_isdigit(system_charset_info, file->name[1]) ||
706
	 (file->name[1] >= 'a' && file->name[1] <= 'f')) &&
unknown's avatar
unknown committed
707 708
	!file->name[2] && !level)
    {
709
      char newpath[FN_REFLEN], *copy_of_path;
unknown's avatar
unknown committed
710
      MY_DIR *new_dirp;
711
      String *dir;
712
      uint length;
713

714
      strxmov(newpath,org_path,"/",file->name,NullS);
715
      length= unpack_filename(newpath,newpath);
unknown's avatar
unknown committed
716 717 718
      if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT))))
      {
	DBUG_PRINT("my",("New subdir found: %s", newpath));
719
	if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1)) < 0)
720 721
	  goto err;
	if (!(copy_of_path= thd->memdup(newpath, length+1)) ||
unknown's avatar
unknown committed
722
	    !(dir= new (thd->mem_root) String(copy_of_path, length,
unknown's avatar
unknown committed
723
					       &my_charset_bin)) ||
724 725
	    raid_dirs.push_back(dir))
	  goto err;
726
	continue;
unknown's avatar
unknown committed
727
      }
728
      found_other_files++;
unknown's avatar
unknown committed
729 730
      continue;
    }
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
    else if (file->name[0] == 'a' && file->name[1] == 'r' &&
             file->name[2] == 'c' && file->name[3] == '\0')
    {
      /* .frm archive */
      char newpath[FN_REFLEN], *copy_of_path;
      MY_DIR *new_dirp;
      uint length;
      strxmov(newpath, org_path, "/", "arc", NullS);
      length= 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;
	continue;
      }
      found_other_files++;
      continue;
    }
750 751
    extension= fn_ext(file->name);
    if (find_type(extension, &deletable_extentions,1+2) <= 0)
unknown's avatar
unknown committed
752
    {
753
      if (find_type(extension, ha_known_exts(),1+2) <= 0)
unknown's avatar
unknown committed
754
	found_other_files++;
unknown's avatar
unknown committed
755 756
      continue;
    }
757 758
    // just for safety we use files_charset_info
    if (db && !my_strcasecmp(files_charset_info,
unknown's avatar
unknown committed
759
                             extension, reg_ext))
unknown's avatar
unknown committed
760
    {
761
      /* Drop the table nicely */
762
      *extension= 0;			// Remove extension
763
      TABLE_LIST *table_list=(TABLE_LIST*)
764
	thd->calloc(sizeof(*table_list)+ strlen(db)+strlen(file->name)+2);
765
      if (!table_list)
766
	goto err;
767
      table_list->db= (char*) (table_list+1);
768 769
      strmov(table_list->real_name= strmov(table_list->db,db)+1, file->name);
      table_list->alias= table_list->real_name;	// If lower_case_table_names=2
770 771
      /* Link into list */
      (*tot_list_next)= table_list;
unknown's avatar
VIEW  
unknown committed
772
      tot_list_next= &table_list->next_local;
773
      deleted++;
unknown's avatar
unknown committed
774
    }
775 776
    else
    {
777
      strxmov(filePath, org_path, "/", file->name, NullS);
778 779
      if (my_delete_with_symlink(filePath,MYF(MY_WME)))
      {
780
	goto err;
781 782 783
      }
    }
  }
unknown's avatar
unknown committed
784
  if (thd->killed ||
785
      (tot_list && mysql_rm_table_part2_with_lock(thd, tot_list, 1, 0, 1)))
786 787 788
    goto err;

  /* Remove RAID directories */
789
  {
790 791
    List_iterator<String> it(raid_dirs);
    String *dir;
unknown's avatar
unknown committed
792
    while ((dir= it++))
793 794
      if (rmdir(dir->c_ptr()) < 0)
	found_other_files++;
unknown's avatar
unknown committed
795
  }
796 797
  my_dirend(dirp);  
  
unknown's avatar
unknown committed
798 799 800 801
  /*
    If the directory is a symbolic link, remove the link first, then
    remove the directory the symbolic link pointed at
  */
802 803 804 805 806 807
  if (found_other_files)
  {
    my_error(ER_DB_DROP_RMDIR, MYF(0), org_path, EEXIST);
    DBUG_RETURN(-1);
  }
  else
unknown's avatar
unknown committed
808
  {
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
    /* Don't give errors if we can't delete 'RAID' directory */
    if (rm_dir_w_symlink(org_path, level == 0))
      DBUG_RETURN(-1);
  }

  DBUG_RETURN(deleted);

err:
  my_dirend(dirp);
  DBUG_RETURN(-1);
}


/*
  Remove directory with symlink

  SYNOPSIS
    rm_dir_w_symlink()
    org_path    path of derictory
    send_error  send errors
  RETURN
    0 OK
    1 ERROR
*/

static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error)
{
  char tmp_path[FN_REFLEN], tmp2_path[FN_REFLEN], *pos;
  char *path= tmp_path;
  DBUG_ENTER("rm_dir_w_symlink");
  unpack_filename(tmp_path, org_path);
840
#ifdef HAVE_READLINK
841
  int error;
842

843 844 845 846 847 848 849 850 851 852
  /* 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(tmp2_path, path, MYF(MY_WME))) < 0)
    DBUG_RETURN(1);
  if (!error)
  {
    if (my_delete(path, MYF(send_error ? MY_WME : 0)))
unknown's avatar
unknown committed
853
    {
854
      DBUG_RETURN(send_error);
unknown's avatar
unknown committed
855
    }
856 857 858
    /* Delete directory symbolic link pointed at */
    path= tmp2_path;
  }
unknown's avatar
unknown committed
859
#endif
860 861
  /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */
  pos= strend(path);
unknown's avatar
unknown committed
862

863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912
  if (pos > path && pos[-1] == FN_LIBCHAR)
    *--pos=0;
  if (rmdir(path) < 0 && send_error)
  {
    my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno);
    DBUG_RETURN(-1);
  }
  DBUG_RETURN(0);
}


/*
  Remove .frm archives from directory

  SYNOPSIS
    thd       thread handler
    dirp      list of files in archive directory
    db        data base name
    org_path  path of archive directory

  RETURN
    > 0 number of removed files
    -1  error
*/
static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp,
				 const char *org_path)
{
  long deleted= 0;
  ulong found_other_files= 0;
  char filePath[FN_REFLEN];
  DBUG_ENTER("mysql_rm_arc_files");
  DBUG_PRINT("enter", ("path: %s", org_path));

  for (uint idx=0 ;
       idx < (uint) dirp->number_off_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' ||
        extension[3] != 'm' || extension[4] != '-')
unknown's avatar
unknown committed
913
    {
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
      found_other_files++;
      continue;
    }
    revision= extension+5;
    while (*revision && my_isdigit(system_charset_info, *revision))
      revision++;
    if (*revision)
    {
      found_other_files++;
      continue;
    }
    strxmov(filePath, org_path, "/", file->name, NullS);
    if (my_delete_with_symlink(filePath,MYF(MY_WME)))
    {
      goto err;
unknown's avatar
unknown committed
929 930
    }
  }
931 932 933 934 935 936 937 938 939 940 941 942
  if (thd->killed)
    goto err;

  my_dirend(dirp);

  /*
    If the directory is a symbolic link, remove the link first, then
    remove the directory the symbolic link pointed at
  */
  if (!found_other_files &&
      rm_dir_w_symlink(org_path, 0))
    DBUG_RETURN(-1);
unknown's avatar
unknown committed
943
  DBUG_RETURN(deleted);
944 945 946 947

err:
  my_dirend(dirp);
  DBUG_RETURN(-1);
unknown's avatar
unknown committed
948 949 950
}


951 952 953 954 955 956 957 958 959 960 961 962 963
/*
  Change default database.

  SYNOPSIS
    mysql_change_db()
    thd		Thread handler
    name	Databasename

  DESCRIPTION
    Becasue the database name may have been given directly from the
    communication packet (in case of 'connect' or 'COM_INIT_DB')
    we have to do end space removal in this function.

964 965 966 967
  NOTES
    Do as little as possible in this function, as it is not called for the
    replication slave SQL thread (for that thread, setting of thd->db is done
    in ::exec_event() methods of log_event.cc).
unknown's avatar
unknown committed
968

969 970 971 972 973
  RETURN VALUES
    0	ok
    1	error
*/

974
bool mysql_change_db(THD *thd, const char *name)
unknown's avatar
unknown committed
975
{
unknown's avatar
unknown committed
976
  int length, db_length;
unknown's avatar
unknown committed
977 978
  char *dbname=my_strdup((char*) name,MYF(MY_WME));
  char	path[FN_REFLEN];
979
  HA_CREATE_INFO create;
980 981 982
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  ulong db_access;
#endif
unknown's avatar
unknown committed
983 984
  DBUG_ENTER("mysql_change_db");

unknown's avatar
unknown committed
985
  if (!dbname || !(db_length= strlen(dbname)))
unknown's avatar
unknown committed
986 987
  {
    x_free(dbname);				/* purecov: inspected */
988
    send_error(thd,ER_NO_DB_ERROR);	/* purecov: inspected */
unknown's avatar
unknown committed
989 990
    DBUG_RETURN(1);				/* purecov: inspected */
  }
unknown's avatar
unknown committed
991
  if (check_db_name(dbname))
unknown's avatar
unknown committed
992
  {
993 994 995
    net_printf(thd, ER_WRONG_DB_NAME, dbname);
    x_free(dbname);
    DBUG_RETURN(1);
unknown's avatar
unknown committed
996
  }
unknown's avatar
unknown committed
997
  DBUG_PRINT("info",("Use database: %s", dbname));
998
#ifndef NO_EMBEDDED_ACCESS_CHECKS
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
  if (test_all_bits(thd->master_access,DB_ACLS))
    db_access=DB_ACLS;
  else
    db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
		thd->master_access);
  if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname)))
  {
    net_printf(thd,ER_DBACCESS_DENIED_ERROR,
	       thd->priv_user,
	       thd->priv_host,
	       dbname);
    mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
		    thd->priv_user,
		    thd->priv_host,
		    dbname);
    my_free(dbname,MYF(0));
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
1017
#endif
1018 1019 1020 1021 1022 1023 1024 1025 1026
  (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,ER_BAD_DB_ERROR,dbname);
    my_free(dbname,MYF(0));
    DBUG_RETURN(1);
unknown's avatar
unknown committed
1027
  }
1028
  send_ok(thd);
unknown's avatar
unknown committed
1029
  x_free(thd->db);
1030
  thd->db=dbname;				// THD::~THD will free this
unknown's avatar
unknown committed
1031
  thd->db_length=db_length;
unknown's avatar
unknown committed
1032
#ifndef NO_EMBEDDED_ACCESS_CHECKS
1033
  thd->db_access=db_access;
unknown's avatar
unknown committed
1034
#endif
1035 1036 1037 1038 1039 1040
  strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE);
  load_db_opt(thd, path, &create);
  thd->db_charset= create.default_table_charset ?
		   create.default_table_charset :
		   thd->variables.collation_server;
  thd->variables.collation_database= thd->db_charset;
unknown's avatar
unknown committed
1041 1042
  DBUG_RETURN(0);
}