summaryrefslogtreecommitdiff
path: root/sql/sql_db.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_db.cc')
-rw-r--r--sql/sql_db.cc288
1 files changed, 143 insertions, 145 deletions
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 8b92259573e..56dd266acb9 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB, 2008-2009 Sun Microsystems, Inc
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
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
@@ -28,6 +28,8 @@
#include "sql_acl.h" // SELECT_ACL, DB_ACLS,
// acl_get, check_grant_db
#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 <mysys_err.h>
#include "sp.h"
#include "events.h"
@@ -44,10 +46,12 @@ const char *del_exts[]= {".frm", ".BAK", ".TMD",".opt", NullS};
static TYPELIB deletable_extentions=
{array_elements(del_exts)-1,"del_exts", del_exts, NULL};
-static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
- const char *db, const char *path, uint level,
- TABLE_LIST **dropped_tables);
-
+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);
+
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);
static void mysql_change_db_impl(THD *thd,
@@ -736,36 +740,37 @@ exit:
}
-/*
- Drop all tables in a database and the database itself
-
- SYNOPSIS
- mysql_rm_db()
- thd Thread handle
- db Database name in the case given by user
- It's already validated and set to lower case
- (if needed) when we come here
- if_exists Don't give error if database doesn't exists
- silent Don't generate errors
-
- RETURN
- FALSE ok (Database dropped)
- ERROR Error
+/**
+ Drop all tables, routines and events in a database and the database itself.
+
+ @param thd Thread handle
+ @param db Database name in the case given by user
+ It's already validated and set to lower case
+ (if needed) when we come here
+ @param if_exists Don't give error if database doesn't exists
+ @param silent Don't write the statement to the binary log and don't
+ send ok packet to the client
+
+ @retval false OK (Database dropped)
+ @retval true Error
*/
bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
{
- long deleted=0;
- int error= 0;
+ ulong deleted_tables= 0;
+ bool error= true;
char path[FN_REFLEN+16];
MY_DIR *dirp;
uint length;
- TABLE_LIST* dropped_tables= 0;
+ bool found_other_files= false;
+ TABLE_LIST *tables= NULL;
+ TABLE_LIST *table;
+ Drop_table_error_handler err_handler;
DBUG_ENTER("mysql_rm_db");
if (lock_schema_name(thd, db))
- DBUG_RETURN(TRUE);
+ 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
@@ -777,20 +782,61 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
{
if (!if_exists)
{
- error= -1;
my_error(ER_DB_DROP_EXISTS, MYF(0), db);
- goto exit;
+ DBUG_RETURN(true);
}
else
+ {
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS), db);
+ error= false;
+ goto update_binlog;
+ }
}
- else
+
+ if (find_db_tables_and_rm_known_files(thd, dirp, db, path, &tables,
+ &found_other_files))
+ 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))
{
- Drop_table_error_handler err_handler;
- thd->push_internal_handler(&err_handler);
+ 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");
+ 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))
+ goto exit;
+
+ /* mysql_ha_rm_tables() requires a non-null TABLE_LIST. */
+ if (tables)
+ mysql_ha_rm_tables(thd, tables);
- error= -1;
+ 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)))
+ {
/*
We temporarily disable the binary log while dropping the objects
in the database. Since the DROP DATABASE statement is always
@@ -808,23 +854,30 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
ha_drop_database(), since NDB otherwise detects the binary log
as disabled and will not log the drop database statement on any
other connected server.
- */
- if ((deleted= mysql_rm_known_files(thd, dirp, db, path, 0,
- &dropped_tables)) >= 0)
- {
- ha_drop_database(path);
- tmp_disable_binlog(thd);
- query_cache_invalidate1(db);
- (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */
+ */
+
+ ha_drop_database(path);
+ tmp_disable_binlog(thd);
+ query_cache_invalidate1(db);
+ (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */
#ifdef HAVE_EVENT_SCHEDULER
- Events::drop_schema_events(thd, db);
+ Events::drop_schema_events(thd, db);
#endif
- error = 0;
- reenable_binlog(thd);
- }
- thd->pop_internal_handler();
+ 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);
}
- if (!silent && deleted>=0)
+ thd->pop_internal_handler();
+
+update_binlog:
+ if (!silent && !error)
{
const char *query;
ulong query_length;
@@ -859,16 +912,15 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
*/
if (mysql_bin_log.write(&qinfo))
{
- error= -1;
+ error= true;
goto exit;
}
}
thd->clear_error();
thd->server_status|= SERVER_STATUS_DB_DROPPED;
- my_ok(thd, (ulong) deleted);
- thd->server_status&= ~SERVER_STATUS_DB_DROPPED;
+ my_ok(thd, deleted_tables);
}
- else if (mysql_bin_log.is_open())
+ else if (mysql_bin_log.is_open() && !silent)
{
char *query, *query_pos, *query_end, *query_data_start;
TABLE_LIST *tbl;
@@ -880,9 +932,19 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
query_end= query + MAX_DROP_TABLE_Q_LEN;
db_len= strlen(db);
- for (tbl= dropped_tables; tbl; tbl= tbl->next_local)
+ for (tbl= tables; tbl; tbl= tbl->next_local)
{
uint tbl_name_len;
+ bool exists;
+
+ // Only write drop table to the binlog for tables that no longer exist.
+ if (check_if_table_exists(thd, tbl, &exists))
+ {
+ error= true;
+ goto exit;
+ }
+ if (exists)
+ continue;
/* 3 for the quotes and the comma*/
tbl_name_len= strlen(tbl->table_name) + 3;
@@ -894,7 +956,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
*/
if (write_to_binlog(thd, query, query_pos -1 - query, db, db_len))
{
- error= -1;
+ error= true;
goto exit;
}
query_pos= query_data_start;
@@ -914,7 +976,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
*/
if (write_to_binlog(thd, query, query_pos -1 - query, db, db_len))
{
- error= -1;
+ error= true;
goto exit;
}
}
@@ -927,28 +989,23 @@ 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 == 0)
+ if (thd->db && !strcmp(thd->db, db) && !error)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
+ my_dirend(dirp);
DBUG_RETURN(error);
}
-/*
- Removes files with known extensions plus all found subdirectories that
- are 2 hex digits (raid directories).
- thd MUST be set when calling this function!
-*/
-static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
- const char *org_path, uint level,
- TABLE_LIST **dropped_tables)
+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)
{
- long deleted=0;
- ulong found_other_files=0;
char filePath[FN_REFLEN];
TABLE_LIST *tot_list=0, **tot_list_next_local, **tot_list_next_global;
- List<String> raid_dirs;
- DBUG_ENTER("mysql_rm_known_files");
- DBUG_PRINT("enter",("path: %s", org_path));
+ DBUG_ENTER("find_db_tables_and_rm_known_files");
+ DBUG_PRINT("enter",("path: %s", path));
tot_list_next_local= tot_list_next_global= &tot_list;
@@ -965,36 +1022,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
(file->name[1] == '.' && !file->name[2])))
continue;
- /* Check if file is a raid directory */
- if ((my_isdigit(system_charset_info, file->name[0]) ||
- (file->name[0] >= 'a' && file->name[0] <= 'f')) &&
- (my_isdigit(system_charset_info, file->name[1]) ||
- (file->name[1] >= 'a' && file->name[1] <= 'f')) &&
- !file->name[2] && !level)
- {
- char newpath[FN_REFLEN], *copy_of_path;
- MY_DIR *new_dirp;
- String *dir;
- uint length;
-
- strxmov(newpath,org_path,"/",file->name,NullS);
- length= 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, NullS, newpath,1,0)) < 0)
- goto err;
- if (!(copy_of_path= (char*) thd->memdup(newpath, length+1)) ||
- !(dir= new (thd->mem_root) String(copy_of_path, length,
- &my_charset_bin)) ||
- raid_dirs.push_back(dir))
- goto err;
- continue;
- }
- found_other_files++;
- continue;
- }
- else if (file->name[0] == 'a' && file->name[1] == 'r' &&
+ if (file->name[0] == 'a' && file->name[1] == 'r' &&
file->name[2] == 'c' && file->name[3] == '\0')
{
/* .frm archive:
@@ -1003,24 +1031,24 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
*/
char newpath[FN_REFLEN];
MY_DIR *new_dirp;
- strxmov(newpath, org_path, "/", "arc", NullS);
+ strxmov(newpath, path, "/", "arc", NullS);
(void) 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;
+ DBUG_RETURN(true);
continue;
}
- found_other_files++;
+ *found_other_files= true;
continue;
}
if (!(extension= strrchr(file->name, '.')))
extension= strend(file->name);
- if (find_type(extension, &deletable_extentions,1+2) <= 0)
+ if (find_type(extension, &deletable_extentions, FIND_TYPE_NO_PREFIX) <= 0)
{
- if (find_type(extension, ha_known_exts(),1+2) <= 0)
- found_other_files++;
+ 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 */
@@ -1036,7 +1064,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
strlen(file->name) + 1);
if (!table_list)
- goto err;
+ 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;
@@ -1054,61 +1082,31 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
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);
+ 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;
- deleted++;
}
else
{
- strxmov(filePath, org_path, "/", file->name, NullS);
- if (mysql_file_delete_with_symlink(key_file_misc, filePath, MYF(MY_WME)))
+ strxmov(filePath, path, "/", file->name, NullS);
+ /*
+ We ignore ENOENT error in order to skip files that was deleted
+ by concurrently running statement like REAPIR TABLE ...
+ */
+ if (my_delete_with_symlink(filePath, MYF(0)) &&
+ my_errno != ENOENT)
{
- goto err;
+ my_error(EE_DELETE, MYF(0), filePath, my_errno);
+ DBUG_RETURN(true);
}
}
}
- if (thd->killed ||
- (tot_list && mysql_rm_table_part2(thd, tot_list, 1, 0, 1, 1)))
- goto err;
-
- /* Remove RAID directories */
- {
- List_iterator<String> it(raid_dirs);
- String *dir;
- while ((dir= it++))
- if (rmdir(dir->c_ptr()) < 0)
- found_other_files++;
- }
- my_dirend(dirp);
-
- if (dropped_tables)
- *dropped_tables= tot_list;
-
- /*
- 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), org_path, EEXIST);
- DBUG_RETURN(-1);
- }
- else
- {
- /* 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);
+ *tables= tot_list;
+ DBUG_RETURN(false);
}