Commit 004f3990 authored by lars@mysql.com's avatar lars@mysql.com

Merge lthalmann@bk-internal.mysql.com:/home/bk/mysql-5.1-new

into  mysql.com:/users/lthalmann/bkroot/mysql-5.1-new
parents 2d3fcf26 0b30190c
......@@ -49,6 +49,8 @@ mysqlbinlog_SOURCES = mysqlbinlog.cc $(top_srcdir)/mysys/mf_tempdir.c \
$(top_srcdir)/mysys/base64.c
mysqlbinlog_LDADD = $(LDADD) $(CXXLDFLAGS)
mysqlslap_LDADD = $(LDADD) $(CXXLDFLAGS) $(CLIENT_THREAD_LIBS)
mysqlimport_LDADD = $(LDADD) $(CXXLDFLAGS) $(CLIENT_THREAD_LIBS) \
$(top_builddir)/mysys/libmysys.a
mysqltestmanager_pwgen_SOURCES = mysqlmanager-pwgen.c
mysqltestmanagerc_SOURCES= mysqlmanagerc.c $(yassl_dummy_link_fix)
mysqlcheck_SOURCES= mysqlcheck.c $(yassl_dummy_link_fix)
......@@ -57,7 +59,8 @@ mysqlslap_SOURCES= mysqlslap.c $(top_srcdir)/mysys/my_lock.c \
$(top_srcdir)/mysys/my_alarm.c \
$(yassl_dummy_link_fix)
mysqldump_SOURCES= mysqldump.c my_user.c $(yassl_dummy_link_fix)
mysqlimport_SOURCES= mysqlimport.c $(yassl_dummy_link_fix)
mysqlimport_SOURCES= mysqlimport.c \
$(yassl_dummy_link_fix)
sql_src=log_event.h mysql_priv.h log_event.cc my_decimal.h my_decimal.cc
strings_src=decimal.c
......
......@@ -54,6 +54,7 @@ enum options_client
OPT_MYSQL_LOCK_DIRECTORY,
OPT_MYSQL_SLAP_SLAVE,
OPT_USE_THREADS,
OPT_IMPORT_USE_THREADS,
OPT_MYSQL_NUMBER_OF_QUERY,
OPT_MYSQL_PRESERVE_SCHEMA,
OPT_IGNORE_TABLE,OPT_INSERT_IGNORE,OPT_SHOW_WARNINGS,OPT_DROP_DATABASE,
......
......@@ -29,6 +29,12 @@
#include "client_priv.h"
#include "mysql_version.h"
#include <my_pthread.h>
/* Global Thread counter */
int counter= 0;
pthread_mutex_t counter_mutex;
static void db_error_with_table(MYSQL *mysql, char *table);
static void db_error(MYSQL *mysql);
......@@ -39,6 +45,7 @@ static char *add_load_option(char *ptr,const char *object,
static my_bool verbose=0,lock_tables=0,ignore_errors=0,opt_delete=0,
replace=0,silent=0,ignore=0,opt_compress=0,
opt_low_priority= 0, tty_password= 0;
static my_bool opt_use_threads= 0;
static uint opt_local_file=0;
static MYSQL mysql_connection;
static char *opt_password=0, *current_user=0,
......@@ -108,8 +115,9 @@ static struct my_option my_long_options[] =
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"local", 'L', "Read all files through the client.", (gptr*) &opt_local_file,
(gptr*) &opt_local_file, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"lock-tables", 'l', "Lock all tables for write.", (gptr*) &lock_tables,
(gptr*) &lock_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"lock-tables", 'l', "Lock all tables for write (this disables threads).",
(gptr*) &lock_tables, (gptr*) &lock_tables, 0, GET_BOOL, NO_ARG,
0, 0, 0, 0, 0, 0},
{"low-priority", OPT_LOW_PRIORITY,
"Use LOW_PRIORITY when updating the table.", (gptr*) &opt_low_priority,
(gptr*) &opt_low_priority, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
......@@ -138,6 +146,11 @@ static struct my_option my_long_options[] =
(gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#include <sslopt-longopts.h>
{"use-threads", OPT_USE_THREADS,
"Parrelize the loading of files. Requires an arguement for the number \
threads to use for loading of data.",
(gptr*) &opt_use_threads, (gptr*) &opt_use_threads, 0,
GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifndef DONT_ALLOW_USER_CHANGE
{"user", 'u', "User for login if not current user.", (gptr*) &current_user,
(gptr*) &current_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
......@@ -287,7 +300,7 @@ static int write_to_table(char *filename, MYSQL *sock)
{
if (verbose)
fprintf(stdout, "Deleting the old data from table %s\n", tablename);
sprintf(sql_statement, "DELETE FROM %s", tablename);
snprintf(sql_statement, FN_REFLEN*16+256, "DELETE FROM %s", tablename);
if (mysql_query(sock, sql_statement))
{
db_error_with_table(sock, tablename);
......@@ -497,12 +510,41 @@ static char *field_escape(char *to,const char *from,uint length)
}
int
worker_thread(char *raw_table_name)
{
MYSQL *sock= 0;
if (!(sock= db_connect(current_host,current_db,current_user,opt_password)))
{
goto error;
}
if (mysql_query(sock, "set @@character_set_database=binary;"))
{
db_error(sock); /* We shall countinue here, if --force was given */
goto error;
}
/*
We should do something about the error
*/
write_to_table(raw_table_name, sock);
error:
if (sock)
db_disconnect(current_host, sock);
pthread_mutex_lock(&counter_mutex);
counter--;
pthread_mutex_unlock(&counter_mutex);
return 0;
}
int main(int argc, char **argv)
{
int exitcode=0, error=0;
char **argv_to_free;
MYSQL *sock=0;
MY_INIT(argv[0]);
load_defaults("my",load_default_groups,&argc,&argv);
......@@ -513,25 +555,79 @@ int main(int argc, char **argv)
free_defaults(argv_to_free);
return(1);
}
if (!(sock= db_connect(current_host,current_db,current_user,opt_password)))
{
free_defaults(argv_to_free);
return(1); /* purecov: deadcode */
}
if (mysql_query(sock, "set @@character_set_database=binary;"))
if (opt_use_threads && !lock_tables)
{
db_error(sock); /* We shall countinue here, if --force was given */
return(1);
pthread_t mainthread; /* Thread descriptor */
pthread_attr_t attr; /* Thread attributes */
VOID(pthread_mutex_init(&counter_mutex, NULL));
for (; *argv != NULL; argv++) // Loop through tables
{
/*
If we hit thread count limit we loop until some threads exit.
We sleep for a second, so that we don't chew up a lot of
CPU in the loop.
*/
sanity_label:
if (counter == opt_use_threads)
{
sleep(1);
goto sanity_label;
}
pthread_mutex_lock(&counter_mutex);
counter++;
pthread_mutex_unlock(&counter_mutex);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,
PTHREAD_CREATE_DETACHED);
/* now create the thread */
if (pthread_create(&mainthread, &attr, (void *)worker_thread,
(void *)*argv) != 0)
{
pthread_mutex_lock(&counter_mutex);
counter--;
pthread_mutex_unlock(&counter_mutex);
fprintf(stderr,"%s: Could not create thread\n",
my_progname);
}
}
/*
We loop until we know that all children have cleaned up.
*/
loop_label:
if (counter)
{
sleep(1);
goto loop_label;
}
VOID(pthread_mutex_destroy(&counter_mutex));
}
else
{
MYSQL *sock= 0;
if (!(sock= db_connect(current_host,current_db,current_user,opt_password)))
{
free_defaults(argv_to_free);
return(1); /* purecov: deadcode */
}
if (lock_tables)
lock_table(sock, argc, argv);
for (; *argv != NULL; argv++)
if ((error=write_to_table(*argv, sock)))
if (exitcode == 0)
exitcode = error;
db_disconnect(current_host, sock);
if (mysql_query(sock, "set @@character_set_database=binary;"))
{
db_error(sock); /* We shall countinue here, if --force was given */
return(1);
}
if (lock_tables)
lock_table(sock, argc, argv);
for (; *argv != NULL; argv++)
if ((error=write_to_table(*argv, sock)))
if (exitcode == 0)
exitcode = error;
db_disconnect(current_host, sock);
}
my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR));
#ifdef HAVE_SMEM
my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR));
......
......@@ -2650,3 +2650,35 @@ DELIMITER ;
DROP TRIGGER tr1;
DROP TABLE t1;
create table t1 (a text , b text);
create table t2 (a text , b text);
insert t1 values ("Duck, Duck", "goose");
insert t1 values ("Duck, Duck", "pidgeon");
insert t2 values ("We the people", "in order to perform");
insert t2 values ("a more perfect", "union");
select * from t1;
a b
Duck, Duck goose
Duck, Duck pidgeon
select * from t2;
a b
We the people in order to perform
a more perfect union
test.t1: Records: 2 Deleted: 0 Skipped: 0 Warnings: 0
test.t2: Records: 2 Deleted: 0 Skipped: 0 Warnings: 0
test.t1: Records: 2 Deleted: 0 Skipped: 0 Warnings: 0
test.t2: Records: 2 Deleted: 0 Skipped: 0 Warnings: 0
select * from t1;
a b
Duck, Duck goose
Duck, Duck pidgeon
Duck, Duck goose
Duck, Duck pidgeon
select * from t2;
a b
We the people in order to perform
a more perfect union
We the people in order to perform
a more perfect union
drop table t1;
drop table t2;
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;
......@@ -10,6 +10,7 @@
#
##############################################################################
archive : Change between azio and gzio is causing an issue
events : Test case instability - infinite locking. To be fixed.
func_group : Bug#15448
func_math : Bug#15448
......
......@@ -1048,3 +1048,27 @@ SET SQL_MODE = @old_sql_mode;
DROP TRIGGER tr1;
DROP TABLE t1;
#
# Added for use-thread option
#
create table t1 (a text , b text);
create table t2 (a text , b text);
insert t1 values ("Duck, Duck", "goose");
insert t1 values ("Duck, Duck", "pidgeon");
insert t2 values ("We the people", "in order to perform");
insert t2 values ("a more perfect", "union");
select * from t1;
select * from t2;
--exec $MYSQL_DUMP --tab=$MYSQLTEST_VARDIR/tmp/ test
--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/t1.sql
--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/t2.sql
# The first load tests the pausing code
--exec $MYSQL_IMPORT --use-threads=1 test $MYSQLTEST_VARDIR/tmp/t1.txt $MYSQLTEST_VARDIR/tmp/t2.txt
# Now we test with multiple threads!
--exec $MYSQL_IMPORT --use-threads=5 test $MYSQLTEST_VARDIR/tmp/t1.txt $MYSQLTEST_VARDIR/tmp/t2.txt
select * from t1;
select * from t2;
drop table t1;
drop table t2;
--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;
......@@ -405,12 +405,14 @@ int ha_archive::write_meta_file(File meta_file, ha_rows rows,
See ha_example.cc for a longer description.
*/
ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
ARCHIVE_SHARE *ha_archive::get_share(const char *table_name,
TABLE *table, int *rc)
{
ARCHIVE_SHARE *share;
char meta_file_name[FN_REFLEN];
uint length;
char *tmp_name;
DBUG_ENTER("ha_archive::get_share");
pthread_mutex_lock(&archive_mutex);
length=(uint) strlen(table_name);
......@@ -425,7 +427,8 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
NullS))
{
pthread_mutex_unlock(&archive_mutex);
return NULL;
*rc= HA_ERR_OUT_OF_MEM;
DBUG_RETURN(NULL);
}
share->use_count= 0;
......@@ -468,9 +471,14 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
thr_lock_init(&share->lock);
}
share->use_count++;
DBUG_PRINT("info", ("archive table %.*s has %d open handles now",
share->table_name_length, share->table_name,
share->use_count));
if (share->crashed)
*rc= HA_ERR_CRASHED_ON_USAGE;
pthread_mutex_unlock(&archive_mutex);
return share;
DBUG_RETURN(share);
}
......@@ -481,14 +489,23 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
int ha_archive::free_share(ARCHIVE_SHARE *share)
{
int rc= 0;
DBUG_ENTER("ha_archive::free_share");
DBUG_PRINT("info", ("archive table %.*s has %d open handles on entrance",
share->table_name_length, share->table_name,
share->use_count));
pthread_mutex_lock(&archive_mutex);
if (!--share->use_count)
{
hash_delete(&archive_open_tables, (byte*) share);
thr_lock_delete(&share->lock);
VOID(pthread_mutex_destroy(&share->mutex));
(void)write_meta_file(share->meta_file, share->rows_recorded,
share->auto_increment_value, FALSE);
if (share->crashed)
(void)write_meta_file(share->meta_file, share->rows_recorded,
share->auto_increment_value, TRUE);
else
(void)write_meta_file(share->meta_file, share->rows_recorded,
share->auto_increment_value, FALSE);
if (azclose(&(share->archive_write)))
rc= 1;
if (my_close(share->meta_file, MYF(0)))
......@@ -497,7 +514,7 @@ int ha_archive::free_share(ARCHIVE_SHARE *share)
}
pthread_mutex_unlock(&archive_mutex);
return rc;
DBUG_RETURN(rc);
}
......@@ -524,10 +541,23 @@ const char **ha_archive::bas_ext() const
*/
int ha_archive::open(const char *name, int mode, uint open_options)
{
int rc= 0;
DBUG_ENTER("ha_archive::open");
if (!(share= get_share(name, table)))
DBUG_RETURN(HA_ERR_OUT_OF_MEM); // Not handled well by calling code!
DBUG_PRINT("info", ("archive table was opened for crash %s",
(open_options & HA_OPEN_FOR_REPAIR) ? "yes" : "no"));
share= get_share(name, table, &rc);
if (rc == HA_ERR_CRASHED_ON_USAGE && !(open_options & HA_OPEN_FOR_REPAIR))
{
free_share(share);
DBUG_RETURN(rc);
}
else if (rc == HA_ERR_OUT_OF_MEM)
{
DBUG_RETURN(rc);
}
thr_lock_data_init(&share->lock,&lock,NULL);
if (!(azopen(&archive, share->data_file_name, O_RDONLY|O_BINARY)))
......@@ -537,10 +567,14 @@ int ha_archive::open(const char *name, int mode, uint open_options)
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
}
if (open_options & HA_OPEN_FOR_REPAIR)
DBUG_PRINT("info", ("archive table was crashed %s",
rc == HA_ERR_CRASHED_ON_USAGE ? "yes" : "no"));
if (rc == HA_ERR_CRASHED_ON_USAGE && open_options & HA_OPEN_FOR_REPAIR)
{
DBUG_RETURN(0);
DBUG_RETURN(share->crashed ? HA_ERR_CRASHED_ON_USAGE : 0);
}
else
DBUG_RETURN(rc);
}
......@@ -926,6 +960,7 @@ int ha_archive::rnd_init(bool scan)
if (scan)
{
scan_rows= share->rows_recorded;
DBUG_PRINT("info", ("archive will retrieve %llu rows", scan_rows));
records= 0;
/*
......@@ -937,6 +972,7 @@ int ha_archive::rnd_init(bool scan)
pthread_mutex_lock(&share->mutex);
if (share->dirty == TRUE)
{
DBUG_PRINT("info", ("archive flushing out rows for scan"));
azflush(&(share->archive_write), Z_SYNC_FLUSH);
share->dirty= FALSE;
}
......@@ -1176,6 +1212,7 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
share->rows_recorded++;
}
}
DBUG_PRINT("info", ("recovered %llu archive rows", share->rows_recorded));
my_free((char*)buf, MYF(0));
if (rc && rc != HA_ERR_END_OF_FILE)
......@@ -1200,10 +1237,24 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
azwrite(&writer, block, read);
}
azclose(&writer);
azflush(&writer, Z_SYNC_FLUSH);
share->dirty= FALSE;
azclose(&(share->archive_write));
share->archive_write= writer;
my_rename(writer_filename,share->data_file_name,MYF(0));
/*
Now we need to reopen our read descriptor since it has changed.
*/
azclose(&archive);
if (!(azopen(&archive, share->data_file_name, O_RDONLY|O_BINARY)))
{
rc= HA_ERR_CRASHED_ON_USAGE;
goto error;
}
DBUG_RETURN(0);
error:
......
......@@ -101,7 +101,7 @@ public:
int read_meta_file(File meta_file, ha_rows *rows, ulonglong *auto_increment);
int write_meta_file(File meta_file, ha_rows rows,
ulonglong auto_increment, bool dirty);
ARCHIVE_SHARE *get_share(const char *table_name, TABLE *table);
ARCHIVE_SHARE *get_share(const char *table_name, TABLE *table, int *rc);
int free_share(ARCHIVE_SHARE *share);
bool auto_repair() const { return 1; } // For the moment we just do this
int read_data_header(azio_stream *file_to_read);
......
......@@ -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
......@@ -1333,7 +1336,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,
......@@ -1364,7 +1367,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[];
......
......@@ -1170,7 +1170,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();
......@@ -1283,6 +1283,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);
......@@ -2838,7 +2839,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;
/*
......@@ -2892,6 +2893,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);
......@@ -5232,8 +5234,8 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite,
*/
{"log-bin-trust-function-creators", OPT_LOG_BIN_TRUST_FUNCTION_CREATORS,
"If equal to 0 (the default), then when --log-bin is used, creation of "
"a function is allowed only to users having the SUPER privilege and only "
"if this function may not break binary logging."
"a function (a trigger) is allowed only to users having the SUPER privilege "
"and only if this function (trigger) may not break binary logging."
#ifdef HAVE_ROW_BASED_REPLICATION
" If using --binlog-format=row, the security issues do not exist and the "
"binary logging cannot break so this option is automatically set to 1."
......
......@@ -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);
......@@ -1219,3 +1331,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);
}
......@@ -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,
......@@ -818,6 +819,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;
......
......@@ -3073,7 +3073,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;
}
......@@ -3663,6 +3663,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;
......
......@@ -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())
{
......
......@@ -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
**/
......
......@@ -195,10 +195,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
/*
There is no DETERMINISTIC clause for triggers, so can't check it.
But a trigger can in theory be used to do nasty things (if it supported
DROP for example) so we do the check for privileges. For now there is
already a stronger test right above; but when this stronger test will
be removed, the test below will hold. Because triggers have the same
nature as functions regarding binlogging: their body is implicitely
DROP for example) so we do the check for privileges. Triggers have the
same nature as functions regarding binlogging: their body is implicitely
binlogged, so they share the same danger, so trust_function_creators
applies to them too.
*/
......
......@@ -863,7 +863,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
......@@ -5485,6 +5485,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;
......@@ -5520,6 +5527,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
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment