diff options
author | unknown <bar@mysql.com> | 2006-02-13 11:49:28 +0400 |
---|---|---|
committer | unknown <bar@mysql.com> | 2006-02-13 11:49:28 +0400 |
commit | bc5dc9e08582e42c695fbc337c42d7be4a8f80a2 (patch) | |
tree | 7556874a6f6490a8cd49f55fe0c7ed08d7f14167 | |
parent | 3a2b70f8414a1ee02f8a210f22a2707f66bf0ce5 (diff) | |
download | mariadb-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.result | 29 | ||||
-rw-r--r-- | mysql-test/t/renamedb.test | 18 | ||||
-rw-r--r-- | sql/mysql_priv.h | 13 | ||||
-rw-r--r-- | sql/mysqld.cc | 6 | ||||
-rw-r--r-- | sql/sql_db.cc | 433 | ||||
-rw-r--r-- | sql/sql_lex.h | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 44 | ||||
-rw-r--r-- | sql/sql_rename.cc | 4 | ||||
-rw-r--r-- | sql/sql_table.cc | 60 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 20 |
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 { |