summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <bar@mysql.com>2006-02-13 11:49:28 +0400
committerunknown <bar@mysql.com>2006-02-13 11:49:28 +0400
commitbc5dc9e08582e42c695fbc337c42d7be4a8f80a2 (patch)
tree7556874a6f6490a8cd49f55fe0c7ed08d7f14167
parent3a2b70f8414a1ee02f8a210f22a2707f66bf0ce5 (diff)
downloadmariadb-git-bc5dc9e08582e42c695fbc337c42d7be4a8f80a2.tar.gz
renamedb.test, renamedb.result:
new file Many files: WL#757 RENAME DATABASE sql/mysql_priv.h: WL#757 RENAME DATABASE sql/mysqld.cc: WL#757 RENAME DATABASE sql/sql_db.cc: WL#757 RENAME DATABASE sql/sql_lex.h: WL#757 RENAME DATABASE sql/sql_parse.cc: WL#757 RENAME DATABASE sql/sql_rename.cc: WL#757 RENAME DATABASE sql/sql_table.cc: WL#757 RENAME DATABASE sql/sql_yacc.yy: WL#757 RENAME DATABASE
-rw-r--r--mysql-test/r/renamedb.result29
-rw-r--r--mysql-test/t/renamedb.test18
-rw-r--r--sql/mysql_priv.h13
-rw-r--r--sql/mysqld.cc6
-rw-r--r--sql/sql_db.cc433
-rw-r--r--sql/sql_lex.h2
-rw-r--r--sql/sql_parse.cc44
-rw-r--r--sql/sql_rename.cc4
-rw-r--r--sql/sql_table.cc60
-rw-r--r--sql/sql_yacc.yy20
10 files changed, 605 insertions, 24 deletions
diff --git a/mysql-test/r/renamedb.result b/mysql-test/r/renamedb.result
new file mode 100644
index 00000000000..26ae42e72aa
--- /dev/null
+++ b/mysql-test/r/renamedb.result
@@ -0,0 +1,29 @@
+drop database if exists testdb1;
+create database testdb1 default character set latin2;
+use testdb1;
+create table t1 (a int);
+insert into t1 values (1),(2),(3);
+show create database testdb1;
+Database Create Database
+testdb1 CREATE DATABASE `testdb1` /*!40100 DEFAULT CHARACTER SET latin2 */
+show tables;
+Tables_in_testdb1
+t1
+rename database testdb1 to testdb2;
+show create database testdb1;
+ERROR 42000: Unknown database 'testdb1'
+show create database testdb2;
+Database Create Database
+testdb2 CREATE DATABASE `testdb2` /*!40100 DEFAULT CHARACTER SET latin2 */
+select database();
+database()
+testdb2
+show tables;
+Tables_in_testdb2
+t1
+select a from t1 order by a;
+a
+1
+2
+3
+drop database testdb2;
diff --git a/mysql-test/t/renamedb.test b/mysql-test/t/renamedb.test
new file mode 100644
index 00000000000..5cfb2ce0c12
--- /dev/null
+++ b/mysql-test/t/renamedb.test
@@ -0,0 +1,18 @@
+--disable_warnings
+drop database if exists testdb1;
+--enable_warnings
+
+create database testdb1 default character set latin2;
+use testdb1;
+create table t1 (a int);
+insert into t1 values (1),(2),(3);
+show create database testdb1;
+show tables;
+rename database testdb1 to testdb2;
+--error 1049
+show create database testdb1;
+show create database testdb2;
+select database();
+show tables;
+select a from t1 order by a;
+drop database testdb2;
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 589ca1349c1..1d7664f6e4f 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -611,6 +611,7 @@ void write_bin_log(THD *thd, bool clear_error,
bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent);
bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create);
bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent);
+bool mysql_rename_db(THD *thd, LEX_STRING *old_db, LEX_STRING *new_db);
void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
void mysql_client_binlog_statement(THD *thd);
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
@@ -623,7 +624,7 @@ int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
bool quick_rm_table(handlerton *base,const char *db,
const char *table_name);
void close_cached_table(THD *thd, TABLE *table);
-bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
+bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent);
bool mysql_change_db(THD *thd,const char *name,bool no_access_check);
void mysql_parse(THD *thd,char *inBuf,uint length);
bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
@@ -1228,10 +1229,12 @@ uint check_word(TYPELIB *lib, const char *val, const char *end,
bool is_keyword(const char *name, uint len);
#define MY_DB_OPT_FILE "db.opt"
+bool my_database_names_init(void);
+void my_database_names_free(void);
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
-bool my_dbopt_init(void);
void my_dbopt_cleanup(void);
-void my_dbopt_free(void);
+extern int creating_database; // How many database locks are made
+extern int creating_table; // How many mysql_create_table() are running
/*
External variables
@@ -1334,7 +1337,7 @@ extern FILE *bootstrap_file;
extern int bootstrap_error;
extern FILE *stderror_file;
extern pthread_key(MEM_ROOT**,THR_MALLOC);
-extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open,
+extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_lock_db,
LOCK_thread_count,LOCK_mapped_file,LOCK_user_locks, LOCK_status,
LOCK_error_log, LOCK_delayed_insert, LOCK_uuid_generator,
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
@@ -1365,7 +1368,7 @@ extern const char *opt_date_time_formats[];
extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
extern String null_string;
-extern HASH open_cache;
+extern HASH open_cache, lock_db_cache;
extern TABLE *unused_tables;
extern const char* any_db;
extern struct my_option my_long_options[];
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index f5b93e6a5e5..4513c2816cb 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -1158,7 +1158,7 @@ void clean_up(bool print_message)
bitmap_free(&slave_error_mask);
#endif
my_tz_free();
- my_dbopt_free();
+ my_database_names_free();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
acl_free(1);
grant_free();
@@ -1274,6 +1274,7 @@ static void wait_for_signal_thread_to_end()
static void clean_up_mutexes()
{
(void) pthread_mutex_destroy(&LOCK_mysql_create_db);
+ (void) pthread_mutex_destroy(&LOCK_lock_db);
(void) pthread_mutex_destroy(&LOCK_Acl);
(void) rwlock_destroy(&LOCK_grant);
(void) pthread_mutex_destroy(&LOCK_open);
@@ -2829,7 +2830,7 @@ static int init_common_variables(const char *conf_file_name, int argc,
if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1))
return 1;
- if (my_dbopt_init())
+ if (my_database_names_init())
return 1;
/*
@@ -2883,6 +2884,7 @@ You should consider changing lower_case_table_names to 1 or 2",
static int init_thread_environment()
{
(void) pthread_mutex_init(&LOCK_mysql_create_db,MY_MUTEX_INIT_SLOW);
+ (void) pthread_mutex_init(&LOCK_lock_db,MY_MUTEX_INIT_SLOW);
(void) pthread_mutex_init(&LOCK_Acl,MY_MUTEX_INIT_SLOW);
(void) pthread_mutex_init(&LOCK_open, NULL);
(void) pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST);
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 5ffa4fd76ed..cc6b859b459 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -38,6 +38,107 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
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);
+
+
+/* Database lock hash */
+HASH lock_db_cache;
+pthread_mutex_t LOCK_lock_db;
+int creating_database= 0; // how many database locks are made
+
+
+/* Structure for database lock */
+typedef struct my_dblock_st
+{
+ char *name; /* Database name */
+ uint name_length; /* Database length name */
+} my_dblock_t;
+
+
+/*
+ lock_db key.
+*/
+
+static byte* lock_db_get_key(my_dblock_t *ptr, uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= ptr->name_length;
+ return (byte*) ptr->name;
+}
+
+
+/*
+ Free lock_db hash element.
+*/
+
+static void lock_db_free_element(void *ptr)
+{
+ my_free((gptr) ptr, MYF(0));
+}
+
+
+/*
+ Put a database lock entry into the hash.
+
+ DESCRIPTION
+ Insert a database lock entry into hash.
+ LOCK_db_lock must be previously locked.
+
+ RETURN VALUES
+ 0 on success.
+ 1 on error.
+*/
+
+static my_bool lock_db_insert(const char *dbname, uint length)
+{
+ my_dblock_t *opt;
+ my_bool error= 0;
+ DBUG_ENTER("lock_db_insert");
+
+ safe_mutex_assert_owner(&LOCK_lock_db);
+
+ if (!(opt= (my_dblock_t*) hash_search(&lock_db_cache,
+ (byte*) dbname, length)))
+ {
+ /* Db is not in the hash, insert it */
+ char *tmp_name;
+ if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ &opt, (uint) sizeof(*opt), &tmp_name, length+1,
+ NullS))
+ {
+ error= 1;
+ goto end;
+ }
+
+ opt->name= tmp_name;
+ strmov(opt->name, dbname);
+ opt->name_length= length;
+
+ if ((error= my_hash_insert(&lock_db_cache, (byte*) opt)))
+ {
+ my_free((gptr) opt, MYF(0));
+ goto end;
+ }
+ }
+
+end:
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Delete a database lock entry from hash.
+*/
+
+void lock_db_delete(const char *name, uint length)
+{
+ my_dblock_t *opt;
+ safe_mutex_assert_owner(&LOCK_lock_db);
+ opt= (my_dblock_t *)hash_search(&lock_db_cache, (const byte*) name, length);
+ DBUG_ASSERT(opt != NULL);
+ hash_delete(&lock_db_cache, (byte*) opt);
+}
+
+
/* Database options hash */
static HASH dboptions;
static my_bool dboptions_init= 0;
@@ -90,10 +191,10 @@ static void free_dbopt(void *dbopt)
/*
- Initialize database option hash
+ Initialize database option hash and locked database hash.
SYNOPSIS
- my_dbopt_init()
+ my_database_names()
NOTES
Must be called before any other database function is called.
@@ -103,7 +204,7 @@ static void free_dbopt(void *dbopt)
1 Fatal error
*/
-bool my_dbopt_init(void)
+bool my_database_names_init(void)
{
bool error= 0;
(void) my_rwlock_init(&LOCK_dboptions, NULL);
@@ -113,27 +214,38 @@ bool my_dbopt_init(void)
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);
+ free_dbopt,0) ||
+ hash_init(&lock_db_cache, lower_case_table_names ?
+ &my_charset_bin : system_charset_info,
+ 32, 0, 0, (hash_get_key) lock_db_get_key,
+ lock_db_free_element,0);
+
}
return error;
}
+
/*
- Free database option hash.
+ Free database option hash and locked databases hash.
*/
-void my_dbopt_free(void)
+void my_database_names_free(void)
{
if (dboptions_init)
{
dboptions_init= 0;
hash_free(&dboptions);
(void) rwlock_destroy(&LOCK_dboptions);
+ hash_free(&lock_db_cache);
}
}
+/*
+ Cleanup cached options
+*/
+
void my_dbopt_cleanup(void)
{
rw_wrlock(&LOCK_dboptions);
@@ -1209,3 +1321,312 @@ end:
}
DBUG_RETURN(0);
}
+
+
+static int
+lock_databases(THD *thd, const char *db1, uint length1,
+ const char *db2, uint length2)
+{
+ pthread_mutex_lock(&LOCK_lock_db);
+ while (!thd->killed &&
+ (hash_search(&lock_db_cache,(byte*) db1, length1) ||
+ hash_search(&lock_db_cache,(byte*) db2, length2)))
+ {
+ wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
+ pthread_mutex_lock(&LOCK_lock_db);
+ }
+
+ if (thd->killed)
+ {
+ pthread_mutex_unlock(&LOCK_lock_db);
+ return 1;
+ }
+
+ lock_db_insert(db1, length1);
+ lock_db_insert(db2, length2);
+ creating_database++;
+
+ /*
+ Wait if a concurent thread is creating a table at the same time.
+ The assumption here is that it will not take too long until
+ there is a point in time when a table is not created.
+ */
+
+ while (!thd->killed && creating_table)
+ {
+ wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
+ pthread_mutex_lock(&LOCK_lock_db);
+ }
+
+ if (thd->killed)
+ {
+ lock_db_delete(db1, length1);
+ lock_db_delete(db2, length2);
+ creating_database--;
+ pthread_mutex_unlock(&LOCK_lock_db);
+ pthread_cond_signal(&COND_refresh);
+ return(1);
+ }
+
+ /*
+ We can unlock now as the hash will protect against anyone creating a table
+ in the databases we are using
+ */
+ pthread_mutex_unlock(&LOCK_lock_db);
+ return 0;
+}
+
+
+/*
+ Rename database.
+
+ SYNOPSIS
+ mysql_rename_db()
+ thd Thread handler
+ olddb Old database name
+ newdb New database name
+
+ DESCRIPTION
+ This function is invoked whenever a RENAME DATABASE query is executed:
+
+ RENAME DATABASE 'olddb' TO 'newdb'.
+
+ NOTES
+
+ If we have managed to rename (move) tables to the new database
+ but something failed on a later step, then we store the
+ RENAME DATABASE event in the log. mysql_rename_db() is atomic in
+ the sense that it will rename all or none of the tables.
+
+ TODO:
+ - Better trigger, stored procedure, event, grant handling,
+ see the comments below.
+ NOTE: It's probably a good idea to call wait_if_global_read_lock()
+ once in mysql_rename_db(), instead of locking inside all
+ the required functions for renaming triggerts, SP, events, grants, etc.
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+
+
+bool mysql_rename_db(THD *thd, LEX_STRING *old_db, LEX_STRING *new_db)
+{
+ int error= 0, change_to_newdb= 0;
+ char path[FN_REFLEN+16];
+ uint length;
+ HA_CREATE_INFO create_info;
+ MY_DIR *dirp;
+ TABLE_LIST *table_list;
+ SELECT_LEX *sl= thd->lex->current_select;
+ DBUG_ENTER("mysql_rename_db");
+
+ if (lock_databases(thd, old_db->str, old_db->length,
+ new_db->str, new_db->length))
+ return 1;
+
+ /*
+ Let's remember if we should do "USE newdb" afterwards.
+ thd->db will be cleared in mysql_rename_db()
+ */
+ if (thd->db && !strcmp(thd->db, old_db->str))
+ change_to_newdb= 1;
+
+ build_table_filename(path, sizeof(path)-1, old_db->str, "", MY_DB_OPT_FILE);
+ if ((load_db_opt(thd, path, &create_info)))
+ create_info.default_table_charset= thd->variables.collation_server;
+
+ length= build_table_filename(path, sizeof(path)-1, old_db->str, "", "");
+ if (length && path[length-1] == FN_LIBCHAR)
+ path[length-1]=0; // remove ending '\'
+ if ((error= my_access(path,F_OK)))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0), old_db->str);
+ goto exit;
+ }
+
+ /* Step1: Create the new database */
+ if ((error= mysql_create_db(thd, new_db->str, &create_info, 1)))
+ goto exit;
+
+ /* Step2: Move tables to the new database */
+ if ((dirp = my_dir(path,MYF(MY_DONT_SORT))))
+ {
+ uint nfiles= (uint) dirp->number_off_files;
+ for (uint idx=0 ; idx < nfiles && !thd->killed ; idx++)
+ {
+ FILEINFO *file= dirp->dir_entry + idx;
+ char *extension, tname[FN_REFLEN];
+ LEX_STRING table_str;
+ DBUG_PRINT("info",("Examining: %s", file->name));
+
+ /* skiping non-FRM files */
+ if (my_strcasecmp(files_charset_info,
+ (extension= fn_rext(file->name)), reg_ext))
+ continue;
+
+ /* A frm file found, add the table info rename list */
+ *extension= '\0';
+
+ table_str.length= filename_to_tablename(file->name,
+ tname, sizeof(tname)-1);
+ table_str.str= sql_memdup(tname, table_str.length + 1);
+ Table_ident *old_ident= new Table_ident(thd, *old_db, table_str, 0);
+ Table_ident *new_ident= new Table_ident(thd, *new_db, table_str, 0);
+ if (!old_ident || !new_ident ||
+ !sl->add_table_to_list(thd, old_ident, NULL,
+ TL_OPTION_UPDATING, TL_IGNORE) ||
+ !sl->add_table_to_list(thd, new_ident, NULL,
+ TL_OPTION_UPDATING, TL_IGNORE))
+ {
+ error= 1;
+ my_dirend(dirp);
+ goto exit;
+ }
+ }
+ my_dirend(dirp);
+ }
+
+ if ((table_list= thd->lex->query_tables) &&
+ (error= mysql_rename_tables(thd, table_list, 1)))
+ {
+ /*
+ Failed to move all tables from the old database to the new one.
+ In the best case mysql_rename_tables() moved all tables back to the old
+ database. In the worst case mysql_rename_tables() moved some tables
+ to the new database, then failed, then started to move the tables back, and
+ then failed again. In this situation we have some tables in the
+ old database and some tables in the new database.
+ Let's delete the option file, and then the new database directory.
+ If some tables were left in the new directory, rmdir() will fail.
+ It garantees we never loose any tables.
+ */
+ build_table_filename(path, sizeof(path)-1, new_db->str,"",MY_DB_OPT_FILE);
+ my_delete(path, MYF(MY_WME));
+ length= build_table_filename(path, sizeof(path)-1, new_db->str, "", "");
+ if (length && path[length-1] == FN_LIBCHAR)
+ path[length-1]=0; // remove ending '\'
+ rmdir(path);
+ goto exit;
+ }
+
+
+ /*
+ Step3: move all remaining files to the new db's directory.
+ Skip db opt file: it's been created by mysql_create_db() in
+ the new directory, and will be dropped by mysql_rm_db() in the old one.
+ Trigger TRN and TRG files are be moved as regular files at the moment,
+ without any special treatment.
+
+ Triggers without explicit database qualifiers in table names work fine:
+ use d1;
+ create trigger trg1 before insert on t2 for each row set @a:=1
+ rename database d1 to d2;
+
+ TODO: Triggers, having the renamed database explicitely written
+ in the table qualifiers.
+ 1. when the same database is renamed:
+ create trigger d1.trg1 before insert on d1.t1 for each row set @a:=1;
+ rename database d1 to d2;
+ Problem: After database renaming, the trigger's body
+ still points to the old database d1.
+ 2. when another database is renamed:
+ create trigger d3.trg1 before insert on d3.t1 for each row
+ insert into d1.t1 values (...);
+ rename database d1 to d2;
+ Problem: After renaming d1 to d2, the trigger's body
+ in the database d3 still points to database d1.
+ */
+
+ if ((dirp = my_dir(path,MYF(MY_DONT_SORT))))
+ {
+ uint nfiles= (uint) dirp->number_off_files;
+ for (uint idx=0 ; idx < nfiles ; idx++)
+ {
+ FILEINFO *file= dirp->dir_entry + idx;
+ char oldname[FN_REFLEN], newname[FN_REFLEN];
+ DBUG_PRINT("info",("Examining: %s", file->name));
+
+ /* skiping . and .. and MY_DB_OPT_FILE */
+ if ((file->name[0] == '.' &&
+ (!file->name[1] || (file->name[1] == '.' && !file->name[2]))) ||
+ !my_strcasecmp(files_charset_info, file->name, MY_DB_OPT_FILE))
+ continue;
+
+ /* pass empty file name, and file->name as extension to avoid encoding */
+ build_table_filename(oldname, sizeof(oldname)-1,
+ old_db->str, "", file->name);
+ build_table_filename(newname, sizeof(newname)-1,
+ new_db->str, "", file->name);
+ my_rename(oldname, newname, MYF(MY_WME));
+ }
+ my_dirend(dirp);
+ }
+
+ /*
+ Step4: TODO: moving stored procedures in the 'proc' system table
+ We need a new function: sp_move_db_routines(thd, olddb, newdb)
+ Which will basically have the same effect with:
+ UPDATE proc SET db='newdb' WHERE db='olddb'
+ Note, for 5.0 to 5.1 upgrade purposes we don't really need it.
+
+ The biggest problem here is that we can't have a lock on LOCK_open() while
+ calling open_table() for 'proc'.
+
+ Two solutions:
+ - Start by opening the 'event' and 'proc' (and other) tables for write
+ even before creating the 'to' database. (This will have the nice
+ effect of blocking another 'rename database' while the lock is active).
+ - Use the solution "Disable create of new tables during lock table"
+
+ For an example of how to read through all rows, see:
+ sql_help.cc::search_topics()
+ */
+
+ /*
+ Step5: TODO: moving events in the 'event' system table
+ We need a new function evex_move_db_events(thd, olddb, newdb)
+ Which will have the same effect with:
+ UPDATE event SET db='newdb' WHERE db='olddb'
+ Note, for 5.0 to 5.1 upgrade purposes we don't really need it.
+ */
+
+ /*
+ Step6: TODO: moving grants in the 'db', 'tables_priv', 'columns_priv'.
+ Update each grant table, doing the same with:
+ UPDATE system_table SET db='newdb' WHERE db='olddb'
+ */
+
+ /*
+ Step7: drop the old database.
+ remove_db_from_cache(olddb) and query_cache_invalidate(olddb)
+ are done inside mysql_rm_db(), no needs to execute them again.
+ mysql_rm_db() also "unuses" if we drop the current database.
+ */
+ error= mysql_rm_db(thd, old_db->str, 0, 1);
+
+ /* Step8: logging */
+ if (mysql_bin_log.is_open())
+ {
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, TRUE);
+ thd->clear_error();
+ mysql_bin_log.write(&qinfo);
+ }
+
+ /* Step9: Let's do "use newdb" if we renamed the current database */
+ if (change_to_newdb)
+ error|= mysql_change_db(thd, new_db->str, 0);
+
+exit:
+ pthread_mutex_lock(&LOCK_lock_db);
+ /* Remove the databases from db lock cache */
+ lock_db_delete(old_db->str, old_db->length);
+ lock_db_delete(new_db->str, new_db->length);
+ creating_database--;
+ /* Signal waiting CREATE TABLE's to continue */
+ pthread_cond_signal(&COND_refresh);
+ pthread_mutex_unlock(&LOCK_lock_db);
+
+ DBUG_RETURN(error);
+}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 07a42f7af2c..ae50bfa8ddc 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -67,6 +67,7 @@ enum enum_sql_command {
SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES,
SQLCOM_GRANT,
SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB,
+ SQLCOM_RENAME_DB,
SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT,
SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION,
SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK,
@@ -817,6 +818,7 @@ typedef struct st_lex
required a local context, the parser pops the top-most context.
*/
List<Name_resolution_context> context_stack;
+ List<LEX_STRING> db_list;
SQL_LIST proc_list, auxilliary_table_list, save_list;
create_field *last_field;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ed7e7dfb684..652359288ca 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3065,7 +3065,7 @@ end_with_restore_list:
}
}
query_cache_invalidate3(thd, first_table, 0);
- if (end_active_trans(thd) || mysql_rename_tables(thd, first_table))
+ if (end_active_trans(thd) || mysql_rename_tables(thd, first_table, 0))
goto error;
break;
}
@@ -3643,6 +3643,48 @@ end_with_restore_list:
res= mysql_rm_db(thd, lex->name, lex->drop_if_exists, 0);
break;
}
+ case SQLCOM_RENAME_DB:
+ {
+ LEX_STRING *olddb, *newdb;
+ List_iterator <LEX_STRING> db_list(lex->db_list);
+ olddb= db_list++;
+ newdb= db_list++;
+ if (end_active_trans(thd))
+ {
+ res= 1;
+ break;
+ }
+#ifdef HAVE_REPLICATION
+ if (thd->slave_thread &&
+ (!rpl_filter->db_ok(olddb->str) ||
+ !rpl_filter->db_ok(newdb->str) ||
+ !rpl_filter->db_ok_with_wild_table(olddb->str) ||
+ !rpl_filter->db_ok_with_wild_table(newdb->str)))
+ {
+ res= 1;
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ break;
+ }
+#endif
+ if (check_access(thd,ALTER_ACL,olddb->str,0,1,0,is_schema_db(olddb->str)) ||
+ check_access(thd,DROP_ACL,olddb->str,0,1,0,is_schema_db(olddb->str)) ||
+ check_access(thd,CREATE_ACL,newdb->str,0,1,0,is_schema_db(newdb->str)))
+ {
+ res= 1;
+ break;
+ }
+ if (thd->locked_tables || thd->active_transaction())
+ {
+ res= 1;
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
+ goto error;
+ }
+ res= mysql_rename_db(thd, olddb, newdb);
+ if (!res)
+ send_ok(thd);
+ break;
+ }
case SQLCOM_ALTER_DB:
{
char *db= lex->name ? lex->name : thd->db;
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index 150c1dba1c9..5f20442cc84 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -31,7 +31,7 @@ static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list);
second entry is the new name.
*/
-bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
+bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
{
bool error= 1;
TABLE_LIST *ren_table= 0;
@@ -79,7 +79,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
}
/* Lets hope this doesn't fail as the result will be messy */
- if (!error)
+ if (!silent && !error)
{
if (mysql_bin_log.is_open())
{
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 2938f27c58d..e5f4366afd8 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -28,6 +28,8 @@
#include <io.h>
#endif
+int creating_table= 0; // How many mysql_create_table are running
+
const char *primary_key_name="PRIMARY";
static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
@@ -1973,7 +1975,7 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field)
Create a table
SYNOPSIS
- mysql_create_table()
+ mysql_create_table_internal()
thd Thread object
db Database
table_name Table name
@@ -1996,11 +1998,12 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field)
TRUE error
*/
-bool mysql_create_table(THD *thd,const char *db, const char *table_name,
- HA_CREATE_INFO *create_info,
- List<create_field> &fields,
- List<Key> &keys,bool internal_tmp_table,
- uint select_field_count)
+bool mysql_create_table_internal(THD *thd,
+ const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
+ List<create_field> &fields,
+ List<Key> &keys,bool internal_tmp_table,
+ uint select_field_count)
{
char path[FN_REFLEN];
uint path_length;
@@ -2009,7 +2012,7 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name,
KEY *key_info_buffer;
handler *file;
bool error= TRUE;
- DBUG_ENTER("mysql_create_table");
+ DBUG_ENTER("mysql_create_table_internal");
/* Check for duplicate fields and check type of table to create */
if (!fields.elements)
@@ -2289,6 +2292,49 @@ warn:
goto unlock_and_end;
}
+
+/*
+ Database locking aware wrapper for mysql_create_table_internal(),
+*/
+
+bool mysql_create_table(THD *thd, const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
+ List<create_field> &fields,
+ List<Key> &keys,bool internal_tmp_table,
+ uint select_field_count)
+{
+ bool result;
+ DBUG_ENTER("mysql_create_table");
+
+ /* Wait for any database locks */
+ pthread_mutex_lock(&LOCK_lock_db);
+ while (!thd->killed &&
+ hash_search(&lock_db_cache,(byte*) db, strlen(db)))
+ {
+ wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
+ pthread_mutex_lock(&LOCK_lock_db);
+ }
+
+ if (thd->killed)
+ {
+ pthread_mutex_unlock(&LOCK_lock_db);
+ DBUG_RETURN(TRUE);
+ }
+ creating_table++;
+ pthread_mutex_unlock(&LOCK_lock_db);
+
+ result= mysql_create_table_internal(thd, db, table_name, create_info,
+ fields, keys, internal_tmp_table,
+ select_field_count);
+
+ pthread_mutex_lock(&LOCK_lock_db);
+ if (!--creating_table && creating_database)
+ pthread_cond_signal(&COND_refresh);
+ pthread_mutex_unlock(&LOCK_lock_db);
+ DBUG_RETURN(result);
+}
+
+
/*
** Give the key name after the first field with an optional '_#' after
**/
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index a86eccb493f..01eadadadfb 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -868,7 +868,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
clear_privileges flush_options flush_option
equal optional_braces opt_key_definition key_usage_list2
opt_mi_check_type opt_to mi_check_types normal_join
- table_to_table_list table_to_table opt_table_list opt_as
+ db_to_db table_to_table_list table_to_table opt_table_list opt_as
handler_rkey_function handler_read_or_scan
single_multi table_wild_list table_wild_one opt_wild
union_clause union_list
@@ -5483,6 +5483,13 @@ rename:
}
table_to_table_list
{}
+ | RENAME DATABASE
+ {
+ Lex->db_list.empty();
+ Lex->sql_command= SQLCOM_RENAME_DB;
+ }
+ db_to_db
+ {}
| RENAME USER clear_privileges rename_list
{
Lex->sql_command = SQLCOM_RENAME_USER;
@@ -5518,6 +5525,17 @@ table_to_table:
YYABORT;
};
+db_to_db:
+ ident TO_SYM ident
+ {
+ LEX *lex=Lex;
+ if (Lex->db_list.push_back((LEX_STRING*)
+ sql_memdup(&$1, sizeof(LEX_STRING))) ||
+ Lex->db_list.push_back((LEX_STRING*)
+ sql_memdup(&$3, sizeof(LEX_STRING))))
+ YYABORT;
+ };
+
keycache:
CACHE_SYM INDEX_SYM keycache_list IN_SYM key_cache_name
{