Commit a7b01480 authored by Dmitry Lenev's avatar Dmitry Lenev

Fix for bug #44738 "fill_schema_table_from_frm() opens tables without

lowercasing table name".

In lower_case_table_names > 0 mode some queries to I_S left entries
with incorrect key in table definition cache. This wasted memory and
caused some of the further queries to I_S to produce stale results
in cases when table definition was changed by a DDL statement.
Also in combination with similar problem in CREATE TABLE (which also
has peeked into table definition cache using non-normalized key) this
issue led to to spurious ER_TABLE_EXISTS_ERROR errors when one tried
to create a table with the same name as a previously existing but
dropped table (assuming that table name contained characters in upper
case).

This problem occured due to fact that fill_schema_table_from_frm()
was not properly normalizing (lowercasing) database and table names
which it used for lookups in table definition cache.

This fix adds proper normalization to this function. It also solves
similar problem in CREATE TABLE's code by ensuring that it uses
properly normalized version of table name when it peeks into table
definition cache instead of non-normalized one.
parent 4863e5ce
...@@ -174,3 +174,74 @@ TABLE_SCHEMA TABLE_NAME ...@@ -174,3 +174,74 @@ TABLE_SCHEMA TABLE_NAME
mysqltest_LC2 myUC mysqltest_LC2 myUC
use test; use test;
drop database mysqltest_LC2; drop database mysqltest_LC2;
# End of 5.1 tests
#
# Test for bug #44738 "fill_schema_table_from_frm() opens tables without
# lowercasing table name". Due to not properly normalizing table names
# in lower_case_table_names modes in this function queries to I_S which
# were executed through it left entries with incorrect key in table
# definition cache. As result further queries to I_S that used this
# function produced stale results in cases when table definition was
# changed by a DDL statement. Also combination of this issue and a
# similar problem in CREATE TABLE (it also has peeked into table
# definition cache using non-normalized key) led to spurious
# ER_TABLE_EXISTS_ERROR errors when one tried to create table with the
# same name as a previously existing but dropped table.
#
drop database if exists mysqltest_UPPERCASE;
drop table if exists t_bug44738_UPPERCASE;
create database mysqltest_UPPERCASE;
use mysqltest_UPPERCASE;
create table t_bug44738_UPPERCASE (i int) comment='Old comment';
create table t_bug44738_lowercase (i int) comment='Old comment';
select table_schema, table_name, table_comment from information_schema.tables
where table_schema like 'mysqltest_%' and table_name like 't_bug44738_%'
order by table_name;
table_schema table_name table_comment
mysqltest_UPPERCASE t_bug44738_lowercase Old comment
mysqltest_UPPERCASE t_bug44738_UPPERCASE Old comment
alter table t_bug44738_UPPERCASE comment='New comment';
alter table t_bug44738_lowercase comment='New comment';
# There should be no stale entries in TDC for our tables after the
# above ALTER TABLE statements so new version of comments should be
# returned by the below query to I_S.
select table_schema, table_name, table_comment from information_schema.tables
where table_schema like 'mysqltest_%' and table_name like 't_bug44738_%'
order by table_name;
table_schema table_name table_comment
mysqltest_UPPERCASE t_bug44738_lowercase New comment
mysqltest_UPPERCASE t_bug44738_UPPERCASE New comment
drop database mysqltest_UPPERCASE;
use test;
# Let us check that the original test case which led to discovery
# of this problem also works.
create table t_bug44738_UPPERCASE (i int);
select table_schema, table_name, table_comment from information_schema.tables
where table_schema = 'test' and table_name like 't_bug44738_%';
table_schema table_name table_comment
test t_bug44738_UPPERCASE
drop table t_bug44738_UPPERCASE;
# After the above DROP TABLE there are no entries in TDC which correspond
# to our table and therefore the below statement should succeed.
create table t_bug44738_UPPERCASE (i int);
drop table t_bug44738_UPPERCASE;
# Finally, let us check that another issue which was exposed by
# the original test case is solved. I.e. that fuse in CREATE TABLE
# which ensures that table is not created if there is an entry for
# it in TDC even though it was removed from disk uses normalized
# version of the table name.
create table t_bug44738_UPPERCASE (i int) engine = myisam;
# Load table definition in TDC.
select table_schema, table_name, table_comment from information_schema.tables
where table_schema = 'test' and table_name like 't_bug44738_%';
table_schema table_name table_comment
test t_bug44738_UPPERCASE
# Simulate manual removal of the table.
# After manual removal of table still there should be an entry for table
# in TDC so attempt to create table with the same name should fail.
create table t_bug44738_UPPERCASE (i int);
ERROR 42S01: Table 't_bug44738_uppercase' already exists
# And should succeed after FLUSH TABLES.
flush tables;
create table t_bug44738_UPPERCASE (i int);
drop table t_bug44738_UPPERCASE;
...@@ -150,3 +150,75 @@ select TABLE_SCHEMA,TABLE_NAME FROM information_schema.TABLES ...@@ -150,3 +150,75 @@ select TABLE_SCHEMA,TABLE_NAME FROM information_schema.TABLES
where TABLE_SCHEMA ='mysqltest_LC2'; where TABLE_SCHEMA ='mysqltest_LC2';
use test; use test;
drop database mysqltest_LC2; drop database mysqltest_LC2;
--echo # End of 5.1 tests
--echo #
--echo # Test for bug #44738 "fill_schema_table_from_frm() opens tables without
--echo # lowercasing table name". Due to not properly normalizing table names
--echo # in lower_case_table_names modes in this function queries to I_S which
--echo # were executed through it left entries with incorrect key in table
--echo # definition cache. As result further queries to I_S that used this
--echo # function produced stale results in cases when table definition was
--echo # changed by a DDL statement. Also combination of this issue and a
--echo # similar problem in CREATE TABLE (it also has peeked into table
--echo # definition cache using non-normalized key) led to spurious
--echo # ER_TABLE_EXISTS_ERROR errors when one tried to create table with the
--echo # same name as a previously existing but dropped table.
--echo #
--disable_warnings
drop database if exists mysqltest_UPPERCASE;
drop table if exists t_bug44738_UPPERCASE;
--enable_warnings
create database mysqltest_UPPERCASE;
use mysqltest_UPPERCASE;
create table t_bug44738_UPPERCASE (i int) comment='Old comment';
create table t_bug44738_lowercase (i int) comment='Old comment';
select table_schema, table_name, table_comment from information_schema.tables
where table_schema like 'mysqltest_%' and table_name like 't_bug44738_%'
order by table_name;
alter table t_bug44738_UPPERCASE comment='New comment';
alter table t_bug44738_lowercase comment='New comment';
--echo # There should be no stale entries in TDC for our tables after the
--echo # above ALTER TABLE statements so new version of comments should be
--echo # returned by the below query to I_S.
select table_schema, table_name, table_comment from information_schema.tables
where table_schema like 'mysqltest_%' and table_name like 't_bug44738_%'
order by table_name;
drop database mysqltest_UPPERCASE;
use test;
--echo # Let us check that the original test case which led to discovery
--echo # of this problem also works.
create table t_bug44738_UPPERCASE (i int);
select table_schema, table_name, table_comment from information_schema.tables
where table_schema = 'test' and table_name like 't_bug44738_%';
drop table t_bug44738_UPPERCASE;
--echo # After the above DROP TABLE there are no entries in TDC which correspond
--echo # to our table and therefore the below statement should succeed.
create table t_bug44738_UPPERCASE (i int);
drop table t_bug44738_UPPERCASE;
--echo # Finally, let us check that another issue which was exposed by
--echo # the original test case is solved. I.e. that fuse in CREATE TABLE
--echo # which ensures that table is not created if there is an entry for
--echo # it in TDC even though it was removed from disk uses normalized
--echo # version of the table name.
create table t_bug44738_UPPERCASE (i int) engine = myisam;
--echo # Load table definition in TDC.
select table_schema, table_name, table_comment from information_schema.tables
where table_schema = 'test' and table_name like 't_bug44738_%';
--echo # Simulate manual removal of the table.
let $MYSQLD_DATADIR= `select @@datadir`;
--remove_file $MYSQLD_DATADIR/test/t_bug44738_UPPERCASE.frm
--remove_file $MYSQLD_DATADIR/test/t_bug44738_UPPERCASE.MYD
--remove_file $MYSQLD_DATADIR/test/t_bug44738_UPPERCASE.MYI
--echo # After manual removal of table still there should be an entry for table
--echo # in TDC so attempt to create table with the same name should fail.
--error ER_TABLE_EXISTS_ERROR
create table t_bug44738_UPPERCASE (i int);
--echo # And should succeed after FLUSH TABLES.
flush tables;
create table t_bug44738_UPPERCASE (i int);
drop table t_bug44738_UPPERCASE;
...@@ -3078,12 +3078,31 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, ...@@ -3078,12 +3078,31 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
int error; int error;
char key[MAX_DBKEY_LENGTH]; char key[MAX_DBKEY_LENGTH];
uint key_length; uint key_length;
char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
bzero((char*) &table_list, sizeof(TABLE_LIST)); bzero((char*) &table_list, sizeof(TABLE_LIST));
bzero((char*) &tbl, sizeof(TABLE)); bzero((char*) &tbl, sizeof(TABLE));
table_list.table_name= table_name->str; if (lower_case_table_names)
table_list.db= db_name->str; {
/*
In lower_case_table_names > 0 metadata locking and table definition
cache subsystems require normalized (lowercased) database and table
names as input.
*/
strmov(db_name_buff, db_name->str);
strmov(table_name_buff, table_name->str);
my_casedn_str(files_charset_info, db_name_buff);
my_casedn_str(files_charset_info, table_name_buff);
table_list.db= db_name_buff;
table_list.table_name= table_name_buff;
}
else
{
table_list.table_name= table_name->str;
table_list.db= db_name->str;
}
key_length= create_table_def_key(thd, key, &table_list, 0); key_length= create_table_def_key(thd, key, &table_list, 0);
pthread_mutex_lock(&LOCK_open); pthread_mutex_lock(&LOCK_open);
share= get_table_share(thd, &table_list, key, share= get_table_share(thd, &table_list, key,
......
...@@ -3844,7 +3844,7 @@ bool mysql_create_table_no_lock(THD *thd, ...@@ -3844,7 +3844,7 @@ bool mysql_create_table_no_lock(THD *thd,
Then she could create the table. This case is pretty obscure and Then she could create the table. This case is pretty obscure and
therefore we don't introduce a new error message only for it. therefore we don't introduce a new error message only for it.
*/ */
if (get_cached_table_share(db, alias)) if (get_cached_table_share(db, table_name))
{ {
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
goto unlock_and_end; goto unlock_and_end;
......
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