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;
CREATE DATABASE db1 DEFAULT CHARACTER SET cp1251;
USE db1;
CREATE DATABASE db2;
SHOW CREATE DATABASE db1;
CREATE DATABASE mysqltest1 DEFAULT CHARACTER SET cp1251;
USE mysqltest1;
CREATE DATABASE mysqltest2;
SHOW CREATE DATABASE mysqltest1;
Database Create Database
db1 CREATE DATABASE `db1` /*!40100 DEFAULT CHARACTER SET cp1251 */
SHOW CREATE DATABASE db2;
mysqltest1 CREATE DATABASE `mysqltest1` /*!40100 DEFAULT CHARACTER SET cp1251 */
SHOW CREATE DATABASE mysqltest2;
Database Create Database
db2 CREATE DATABASE `db2` /*!40100 DEFAULT CHARACTER SET latin5 */
DROP DATABASE db2;
USE db1;
mysqltest2 CREATE DATABASE `mysqltest2` /*!40100 DEFAULT CHARACTER SET latin5 */
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=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));
SHOW CREATE TABLE t1;
Table Create Table
......@@ -32,4 +54,4 @@ t1 CREATE TABLE `t1` (
`a` char(10) collate latin1_german1_ci default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci
DROP TABLE t1;
DROP DATABASE db1;
DROP DATABASE mysqltest1;
......@@ -13,25 +13,48 @@
SET @@character_set_server=latin5;
CREATE DATABASE db1 DEFAULT CHARACTER SET cp1251;
USE db1;
CREATE DATABASE db2;
CREATE DATABASE mysqltest1 DEFAULT CHARACTER SET cp1251;
USE mysqltest1;
CREATE DATABASE mysqltest2;
#
# 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;
DROP DATABASE db2;
SHOW CREATE DATABASE mysqltest2;
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
#
USE db1;
USE mysqltest1;
CREATE TABLE t1 (a char(10));
SHOW CREATE TABLE t1;
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);
#define MY_DB_OPT_FILE "db.opt"
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
void my_dbopt_free(void);
/*
External variables
......@@ -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_slave_list, LOCK_active_mi, LOCK_manager,
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_attr_t connection_attrib;
extern I_List<THD> threads;
......
......@@ -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_global_system_variables,
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,
COND_slave_start;
pthread_cond_t COND_thread_cache,COND_flush_thread_cache;
......@@ -901,6 +902,7 @@ void clean_up(bool print_message)
bitmap_free(&slave_error_mask);
#endif
my_tz_free();
my_dbopt_free();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
acl_free(1);
grant_free();
......@@ -986,6 +988,7 @@ static void clean_up_mutexes()
(void) pthread_mutex_destroy(&LOCK_mysql_create_db);
(void) pthread_mutex_destroy(&LOCK_Acl);
(void) rwlock_destroy(&LOCK_grant);
(void) rwlock_destroy(&LOCK_dboptions);
(void) pthread_mutex_destroy(&LOCK_open);
(void) pthread_mutex_destroy(&LOCK_thread_count);
(void) pthread_mutex_destroy(&LOCK_mapped_file);
......@@ -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_slave, 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_refresh,NULL);
(void) pthread_cond_init(&COND_thread_cache,NULL);
......
......@@ -39,6 +39,184 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
const char *db, const char *path,
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:
......@@ -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
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)
{
ulong length;
CHARSET_INFO *cs= ((create && create->default_table_charset) ?
create->default_table_charset :
thd->variables.collation_server);
length= my_sprintf(buf,(buf,
"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 */
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)
bzero((char*) create,sizeof(*create));
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)
{
IO_CACHE cache;
......@@ -137,9 +325,16 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
}
}
}
error=0;
end_io_cache(&cache);
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);
}
......@@ -338,6 +533,8 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
int error = 0;
char path[FN_REFLEN+16], tmp_db[NAME_LEN+1];
MY_DIR *dirp;
uint length;
my_dbopt_t *dbopt;
DBUG_ENTER("mysql_rm_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)
}
(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 */
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,
if (lock_global_read_lock(thd))
return 1;
}
my_dbopt_free();
result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
}
if (options & REFRESH_HOSTS)
......
......@@ -1158,6 +1158,23 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
}
#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,
keys, tmp_table, db_options, file,
key_info_buffer, &key_count,
......
......@@ -1023,7 +1023,7 @@ create:
bzero((char*) &lex->create_info,sizeof(lex->create_info));
lex->create_info.options=$2 | $4;
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;
}
create2
......@@ -1815,7 +1815,7 @@ alter:
lex->select_lex.db=lex->name=0;
bzero((char*) &lex->create_info,sizeof(lex->create_info));
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->alter_info.reset();
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