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;"
This commit is contained in:
parent
206e642588
commit
a5025f4ca6
@ -1,15 +1,37 @@
|
||||
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);
|
||||
|
213
sql/sql_db.cc
213
sql/sql_db.cc
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user