Commit a5025f4c authored by bar@mysql.com's avatar bar@mysql.com

WL#807 Optimize loading database options in load_db_opt

also known as
BUG#2326 Charset of table is determined by charset of db only if "USE db;"
parent 206e6425
SET @@character_set_server=latin5; SET @@character_set_server=latin5;
CREATE DATABASE db1 DEFAULT CHARACTER SET cp1251; CREATE DATABASE mysqltest1 DEFAULT CHARACTER SET cp1251;
USE db1; USE mysqltest1;
CREATE DATABASE db2; CREATE DATABASE mysqltest2;
SHOW CREATE DATABASE db1; SHOW CREATE DATABASE mysqltest1;
Database Create Database Database Create Database
db1 CREATE DATABASE `db1` /*!40100 DEFAULT CHARACTER SET cp1251 */ mysqltest1 CREATE DATABASE `mysqltest1` /*!40100 DEFAULT CHARACTER SET cp1251 */
SHOW CREATE DATABASE db2; SHOW CREATE DATABASE mysqltest2;
Database Create Database Database Create Database
db2 CREATE DATABASE `db2` /*!40100 DEFAULT CHARACTER SET latin5 */ mysqltest2 CREATE DATABASE `mysqltest2` /*!40100 DEFAULT CHARACTER SET latin5 */
DROP DATABASE db2; CREATE TABLE mysqltest2.t1 (a char(10));
USE db1; SHOW CREATE TABLE mysqltest2.t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` char(10) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin5
DROP TABLE mysqltest2.t1;
ALTER DATABASE mysqltest2 DEFAULT CHARACTER SET latin7;
CREATE TABLE mysqltest2.t1 (a char(10));
SHOW CREATE TABLE mysqltest2.t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` char(10) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin7
DROP DATABASE mysqltest2;
CREATE DATABASE mysqltest2 CHARACTER SET latin2;
CREATE TABLE mysqltest2.t1 (a char(10));
SHOW CREATE TABLE mysqltest2.t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` char(10) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin2
DROP DATABASE mysqltest2;
USE mysqltest1;
CREATE TABLE t1 (a char(10)); CREATE TABLE t1 (a char(10));
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
Table Create Table Table Create Table
...@@ -32,4 +54,4 @@ t1 CREATE TABLE `t1` ( ...@@ -32,4 +54,4 @@ t1 CREATE TABLE `t1` (
`a` char(10) collate latin1_german1_ci default NULL `a` char(10) collate latin1_german1_ci default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci
DROP TABLE t1; DROP TABLE t1;
DROP DATABASE db1; DROP DATABASE mysqltest1;
...@@ -13,25 +13,48 @@ ...@@ -13,25 +13,48 @@
SET @@character_set_server=latin5; SET @@character_set_server=latin5;
CREATE DATABASE db1 DEFAULT CHARACTER SET cp1251; CREATE DATABASE mysqltest1 DEFAULT CHARACTER SET cp1251;
USE db1; USE mysqltest1;
CREATE DATABASE db2; CREATE DATABASE mysqltest2;
# #
# This should be cp1251 # This should be cp1251
# #
SHOW CREATE DATABASE db1; SHOW CREATE DATABASE mysqltest1;
# #
# This should take the default latin5 value from server level. # Database "mysqltest2" should take the default latin5 value from
# the server level.
# Afterwards, table "d2.t1" should inherit the default latin5 value from
# the database "mysqltest2", using database option hash.
# #
SHOW CREATE DATABASE db2; SHOW CREATE DATABASE mysqltest2;
DROP DATABASE db2; CREATE TABLE mysqltest2.t1 (a char(10));
SHOW CREATE TABLE mysqltest2.t1;
DROP TABLE mysqltest2.t1;
#
# Now we check if the database charset is updated in
# the database options hash when we ALTER DATABASE.
#
ALTER DATABASE mysqltest2 DEFAULT CHARACTER SET latin7;
CREATE TABLE mysqltest2.t1 (a char(10));
SHOW CREATE TABLE mysqltest2.t1;
DROP DATABASE mysqltest2;
#
# Now we check if the database charset is removed from
# the database option hash when we DROP DATABASE.
#
CREATE DATABASE mysqltest2 CHARACTER SET latin2;
CREATE TABLE mysqltest2.t1 (a char(10));
SHOW CREATE TABLE mysqltest2.t1;
DROP DATABASE mysqltest2;
# #
# Check that table value uses database level by default # Check that table value uses database level by default
# #
USE db1; USE mysqltest1;
CREATE TABLE t1 (a char(10)); CREATE TABLE t1 (a char(10));
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
DROP TABLE t1; DROP TABLE t1;
...@@ -50,4 +73,4 @@ DROP TABLE t1; ...@@ -50,4 +73,4 @@ DROP TABLE t1;
# #
# #
# #
DROP DATABASE db1; DROP DATABASE mysqltest1;
...@@ -801,6 +801,7 @@ bool is_keyword(const char *name, uint len); ...@@ -801,6 +801,7 @@ bool is_keyword(const char *name, uint len);
#define MY_DB_OPT_FILE "db.opt" #define MY_DB_OPT_FILE "db.opt"
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create); bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
void my_dbopt_free(void);
/* /*
External variables External variables
...@@ -894,7 +895,8 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, ...@@ -894,7 +895,8 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open,
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_slave_list, LOCK_active_mi, LOCK_manager,
LOCK_global_system_variables, LOCK_user_conn; LOCK_global_system_variables, LOCK_user_conn;
extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave; extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave,
LOCK_dboptions;
extern pthread_cond_t COND_refresh, COND_thread_count, COND_manager; extern pthread_cond_t COND_refresh, COND_thread_count, COND_manager;
extern pthread_attr_t connection_attrib; extern pthread_attr_t connection_attrib;
extern I_List<THD> threads; extern I_List<THD> threads;
......
...@@ -382,7 +382,8 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, ...@@ -382,7 +382,8 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count,
LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received, LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received,
LOCK_global_system_variables, LOCK_global_system_variables,
LOCK_user_conn, LOCK_slave_list, LOCK_active_mi; LOCK_user_conn, LOCK_slave_list, LOCK_active_mi;
rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave; rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave,
LOCK_dboptions;
pthread_cond_t COND_refresh,COND_thread_count, COND_slave_stopped, pthread_cond_t COND_refresh,COND_thread_count, COND_slave_stopped,
COND_slave_start; COND_slave_start;
pthread_cond_t COND_thread_cache,COND_flush_thread_cache; pthread_cond_t COND_thread_cache,COND_flush_thread_cache;
...@@ -901,6 +902,7 @@ void clean_up(bool print_message) ...@@ -901,6 +902,7 @@ void clean_up(bool print_message)
bitmap_free(&slave_error_mask); bitmap_free(&slave_error_mask);
#endif #endif
my_tz_free(); my_tz_free();
my_dbopt_free();
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
acl_free(1); acl_free(1);
grant_free(); grant_free();
...@@ -986,6 +988,7 @@ static void clean_up_mutexes() ...@@ -986,6 +988,7 @@ static void clean_up_mutexes()
(void) pthread_mutex_destroy(&LOCK_mysql_create_db); (void) pthread_mutex_destroy(&LOCK_mysql_create_db);
(void) pthread_mutex_destroy(&LOCK_Acl); (void) pthread_mutex_destroy(&LOCK_Acl);
(void) rwlock_destroy(&LOCK_grant); (void) rwlock_destroy(&LOCK_grant);
(void) rwlock_destroy(&LOCK_dboptions);
(void) pthread_mutex_destroy(&LOCK_open); (void) pthread_mutex_destroy(&LOCK_open);
(void) pthread_mutex_destroy(&LOCK_thread_count); (void) pthread_mutex_destroy(&LOCK_thread_count);
(void) pthread_mutex_destroy(&LOCK_mapped_file); (void) pthread_mutex_destroy(&LOCK_mapped_file);
...@@ -2408,6 +2411,7 @@ static int init_thread_environment() ...@@ -2408,6 +2411,7 @@ static int init_thread_environment()
(void) my_rwlock_init(&LOCK_sys_init_connect, NULL); (void) my_rwlock_init(&LOCK_sys_init_connect, NULL);
(void) my_rwlock_init(&LOCK_sys_init_slave, NULL); (void) my_rwlock_init(&LOCK_sys_init_slave, NULL);
(void) my_rwlock_init(&LOCK_grant, NULL); (void) my_rwlock_init(&LOCK_grant, NULL);
(void) my_rwlock_init(&LOCK_dboptions, NULL);
(void) pthread_cond_init(&COND_thread_count,NULL); (void) pthread_cond_init(&COND_thread_count,NULL);
(void) pthread_cond_init(&COND_refresh,NULL); (void) pthread_cond_init(&COND_refresh,NULL);
(void) pthread_cond_init(&COND_thread_cache,NULL); (void) pthread_cond_init(&COND_thread_cache,NULL);
......
...@@ -39,6 +39,184 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, ...@@ -39,6 +39,184 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
const char *db, const char *path, const char *db, const char *path,
uint level); uint level);
/* Database options hash */
static HASH dboptions;
static my_bool dboptions_init= 0;
/* Structure for database options */
typedef struct my_dbopt_st
{
char *name; /* Database name */
uint name_length; /* Database length name */
CHARSET_INFO *charset; /* Database default character set */
} my_dbopt_t;
/*
Function we use in the creation of our hash to get key.
*/
static byte* dboptions_get_key(my_dbopt_t *opt, uint *length,
my_bool not_used __attribute__((unused)))
{
*length= opt->name_length;
return (byte*) opt->name;
}
/*
Function to free dboptions hash element
*/
static void free_dbopt(void *dbopt)
{
my_free((gptr) dbopt, MYF(0));
}
/*
Initialize database option hash.
*/
static my_bool my_dbopt_init(void)
{
my_bool rc;
rw_wrlock(&LOCK_dboptions);
if (!dboptions_init)
{
dboptions_init= 1;
rc= 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);
}
else
rc= 0;
rw_unlock(&LOCK_dboptions);
return rc;
}
/*
Free database option hash.
*/
void my_dbopt_free(void)
{
rw_wrlock(&LOCK_dboptions);
if (dboptions_init)
{
hash_free(&dboptions);
dboptions_init= 0;
}
rw_unlock(&LOCK_dboptions);
}
/*
Find database options in the hash.
DESCRIPTION
Search a database options in the hash, usings its path.
Fills "create" on success.
RETURN VALUES
0 on success.
1 on error.
*/
static my_bool get_dbopt(const char *dbname, HA_CREATE_INFO *create)
{
my_dbopt_t *opt;
uint length;
my_bool rc;
if (my_dbopt_init())
return 1;
length= (uint) strlen(dbname);
rw_rdlock(&LOCK_dboptions);
if ((opt= (my_dbopt_t*) hash_search(&dboptions, (byte*) dbname, length)))
{
create->default_table_charset= opt->charset;
rc= 0;
}
else
rc= 1;
rw_unlock(&LOCK_dboptions);
return rc;
}
/*
Writes database options into the hash.
DESCRIPTION
Inserts database options into the hash, or updates
options if they are already in the hash.
RETURN VALUES
0 on success.
1 on error.
*/
static my_bool put_dbopt(const char *dbname, HA_CREATE_INFO *create)
{
my_dbopt_t *opt;
uint length;
my_bool rc;
if (my_dbopt_init())
return 1;
length= (uint) strlen(dbname);
rw_wrlock(&LOCK_dboptions);
if ((opt= (my_dbopt_t*) hash_search(&dboptions, (byte*) dbname, length)))
{
/* Options are already in hash, update them */
opt->charset= create->default_table_charset;
rc= 0;
}
else
{
/* Options are not in the hash, insert them */
char *tmp_name;
if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
&opt, sizeof(*opt), &tmp_name, length+1, NullS))
{
rc= 1;
goto ret;
}
opt->name= tmp_name;
opt->name_length= length;
opt->charset= create->default_table_charset;
strmov(opt->name, dbname);
if ((rc= my_hash_insert(&dboptions, (byte*) opt)))
my_free((gptr) opt, MYF(0));
}
ret:
rw_unlock(&LOCK_dboptions);
return rc;
}
/*
Deletes database options from the hash.
*/
void del_dbopt(const char *path)
{
my_dbopt_t *opt;
rw_wrlock(&LOCK_dboptions);
if ((opt= (my_dbopt_t *)hash_search(&dboptions, path, strlen(path))))
hash_delete(&dboptions, (byte*) opt);
rw_unlock(&LOCK_dboptions);
}
/* /*
Create database options file: Create database options file:
...@@ -56,15 +234,19 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) ...@@ -56,15 +234,19 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
char buf[256]; // Should be enough for one option char buf[256]; // Should be enough for one option
bool error=1; bool error=1;
if (!create->default_table_charset)
create->default_table_charset= thd->variables.collation_server;
if (put_dbopt(path, create))
return 1;
if ((file=my_create(path, CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0) if ((file=my_create(path, CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0)
{ {
ulong length; ulong length;
CHARSET_INFO *cs= ((create && create->default_table_charset) ?
create->default_table_charset :
thd->variables.collation_server);
length= my_sprintf(buf,(buf, length= my_sprintf(buf,(buf,
"default-character-set=%s\ndefault-collation=%s\n", "default-character-set=%s\ndefault-collation=%s\n",
cs->csname,cs->name)); create->default_table_charset->csname,
create->default_table_charset->name));
/* Error is written by my_write */ /* Error is written by my_write */
if (!my_write(file,(byte*) buf, length, MYF(MY_NABP+MY_WME))) if (!my_write(file,(byte*) buf, length, MYF(MY_NABP+MY_WME)))
...@@ -101,6 +283,12 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) ...@@ -101,6 +283,12 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
bzero((char*) create,sizeof(*create)); bzero((char*) create,sizeof(*create));
create->default_table_charset= thd->variables.collation_server; create->default_table_charset= thd->variables.collation_server;
/* Check if options for this database are already in the hash */
if (!get_dbopt(path, create))
DBUG_RETURN(0);
/* Otherwise, load options from the .opt file */
if ((file=my_open(path, O_RDONLY | O_SHARE, MYF(0))) >= 0) if ((file=my_open(path, O_RDONLY | O_SHARE, MYF(0))) >= 0)
{ {
IO_CACHE cache; IO_CACHE cache;
...@@ -137,9 +325,16 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) ...@@ -137,9 +325,16 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
} }
} }
} }
error=0;
end_io_cache(&cache); end_io_cache(&cache);
my_close(file,MYF(0)); my_close(file,MYF(0));
/*
Put the loaded value into the hash.
Note that another thread could've added the same
entry to the hash after we called get_dbopt(),
but it's not an error, as put_dbopt() takes this
possibility into account.
*/
error= put_dbopt(path, create);
} }
DBUG_RETURN(error); DBUG_RETURN(error);
} }
...@@ -338,6 +533,8 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) ...@@ -338,6 +533,8 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
int error = 0; int error = 0;
char path[FN_REFLEN+16], tmp_db[NAME_LEN+1]; char path[FN_REFLEN+16], tmp_db[NAME_LEN+1];
MY_DIR *dirp; MY_DIR *dirp;
uint length;
my_dbopt_t *dbopt;
DBUG_ENTER("mysql_rm_db"); DBUG_ENTER("mysql_rm_db");
VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
...@@ -350,7 +547,11 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) ...@@ -350,7 +547,11 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
} }
(void) sprintf(path,"%s/%s",mysql_data_home,db); (void) sprintf(path,"%s/%s",mysql_data_home,db);
unpack_dirname(path,path); // Convert if not unix length= unpack_dirname(path,path); // Convert if not unix
strmov(path+length, MY_DB_OPT_FILE); // Append db option file name
del_dbopt(path); // Remove dboption hash entry
path[length]= '\0'; // Remove file name
/* See if the directory exists */ /* See if the directory exists */
if (!(dirp = my_dir(path,MYF(MY_DONT_SORT)))) if (!(dirp = my_dir(path,MYF(MY_DONT_SORT))))
{ {
......
...@@ -4805,6 +4805,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, ...@@ -4805,6 +4805,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
if (lock_global_read_lock(thd)) if (lock_global_read_lock(thd))
return 1; return 1;
} }
my_dbopt_free();
result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables); result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
} }
if (options & REFRESH_HOSTS) if (options & REFRESH_HOSTS)
......
...@@ -1158,6 +1158,23 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, ...@@ -1158,6 +1158,23 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
} }
#endif #endif
/*
If the table character set was not given explicitely,
let's fetch the database default character set and
apply it to the table.
*/
if (!create_info->default_table_charset)
{
HA_CREATE_INFO db_info;
uint length;
char path[FN_REFLEN];
(void) sprintf(path,"%s/%s", mysql_data_home, db);
length= unpack_dirname(path,path); // Convert if not unix
strmov(path+length, MY_DB_OPT_FILE);
load_db_opt(thd, path, &db_info);
create_info->default_table_charset= db_info.default_table_charset;
}
if (mysql_prepare_table(thd, create_info, fields, if (mysql_prepare_table(thd, create_info, fields,
keys, tmp_table, db_options, file, keys, tmp_table, db_options, file,
key_info_buffer, &key_count, key_info_buffer, &key_count,
......
...@@ -1023,7 +1023,7 @@ create: ...@@ -1023,7 +1023,7 @@ create:
bzero((char*) &lex->create_info,sizeof(lex->create_info)); bzero((char*) &lex->create_info,sizeof(lex->create_info));
lex->create_info.options=$2 | $4; lex->create_info.options=$2 | $4;
lex->create_info.db_type= (enum db_type) lex->thd->variables.table_type; lex->create_info.db_type= (enum db_type) lex->thd->variables.table_type;
lex->create_info.default_table_charset= thd->variables.collation_database; lex->create_info.default_table_charset= NULL;
lex->name=0; lex->name=0;
} }
create2 create2
...@@ -1815,7 +1815,7 @@ alter: ...@@ -1815,7 +1815,7 @@ alter:
lex->select_lex.db=lex->name=0; lex->select_lex.db=lex->name=0;
bzero((char*) &lex->create_info,sizeof(lex->create_info)); bzero((char*) &lex->create_info,sizeof(lex->create_info));
lex->create_info.db_type= DB_TYPE_DEFAULT; lex->create_info.db_type= DB_TYPE_DEFAULT;
lex->create_info.default_table_charset= thd->variables.collation_database; lex->create_info.default_table_charset= NULL;
lex->create_info.row_type= ROW_TYPE_NOT_USED; lex->create_info.row_type= ROW_TYPE_NOT_USED;
lex->alter_info.reset(); lex->alter_info.reset();
lex->alter_info.is_simple= 1; lex->alter_info.is_simple= 1;
......
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