Commit da71c1ba authored by Monty's avatar Monty

MDEV-16229 Replication aborts with ER_VIEW_SELECT_TMPTABLE after half-failed RENAME

Problem was that detection of temporary tables was all wrong for
RENAME TABLE.
(Temporary tables where opened by top level call to
open_temporary_tables(), which can't detect if a temporary table
was renamed to something and then reused).

Fixed by adding proper parsing of rename list to check against
the current name of a table at each rename stage.
Also change do_rename_temporary() to check against the current
state of temporary tables, not according to the state of start
of RENAME TABLE.
parent 2f3779d3
...@@ -705,10 +705,9 @@ LOAD INDEX INTO CACHE t3; ...@@ -705,10 +705,9 @@ LOAD INDEX INTO CACHE t3;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
mysqltest_db1.t3 preload_keys status OK mysqltest_db1.t3 preload_keys status OK
# #
# RENAME (doesn't work for temporary tables, thus should fail). # RENAME should work for temporary tables
# #
RENAME TABLE t3 TO t3_1; RENAME TABLE t3 TO t3_1;
ERROR 42000: INSERT, CREATE command denied to user 'mysqltest_u1'@'localhost' for table 't3_1'
# #
# HANDLER OPEN/READ/CLOSE. # HANDLER OPEN/READ/CLOSE.
# #
......
...@@ -67,3 +67,69 @@ ERROR HY000: 'test.v1' is not BASE TABLE ...@@ -67,3 +67,69 @@ ERROR HY000: 'test.v1' is not BASE TABLE
drop view v1; drop view v1;
drop table t1; drop table t1;
End of 5.0 tests End of 5.0 tests
CREATE OR REPLACE TABLE t1 (a INT);
CREATE OR REPLACE TABLE t2 (a INT);
CREATE OR REPLACE TEMPORARY TABLE t1_tmp (b INT);
CREATE OR REPLACE TEMPORARY TABLE t2_tmp (b INT);
rename table t1 to t2;
ERROR 42S01: Table 't2' already exists
rename table t1 to tmp, tmp to t2;
ERROR 42S01: Table 't2' already exists
rename table t1_tmp to t2_tmp;
ERROR 42S01: Table 't2_tmp' already exists
rename table t1_tmp to tmp, tmp to t2_tmp;
ERROR 42S01: Table 't2_tmp' already exists
show create table t1_tmp;
Table Create Table
t1_tmp CREATE TEMPORARY TABLE `t1_tmp` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
show create table t2_tmp;
Table Create Table
t2_tmp CREATE TEMPORARY TABLE `t2_tmp` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
rename table t1 to t1_tmp;
rename table t2_tmp to t2;
rename table t2 to tmp, tmp to t2;
rename table t1_tmp to tmp, tmp to t1_tmp;
show tables;
Tables_in_test
t1_tmp
t2
SHOW CREATE TABLE t1_tmp;
Table Create Table
t1_tmp CREATE TEMPORARY TABLE `t1_tmp` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1_tmp;
SHOW CREATE TABLE t1_tmp;
Table Create Table
t1_tmp CREATE TABLE `t1_tmp` (
`a` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1_tmp;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TEMPORARY TABLE `t2` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t2;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t2;
CREATE TABLE t1 (a INT);
insert into t1 values (1);
CREATE TEMPORARY TABLE t1 (b INT);
insert into t1 values (2);
RENAME TABLE t1 TO tmp, t1 TO t2;
select * from tmp;
b
2
select * from t2;
a
1
drop table tmp,t2;
include/master-slave.inc
[connection master]
#
# MDEV-16229 Replication aborts with ER_VIEW_SELECT_TMPTABLE after
# half-failed RENAME
#
CREATE TABLE t1 (a INT);
CREATE TEMPORARY TABLE t1 (b INT);
RENAME TABLE t1 TO tmp, tmp TO t1;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TEMPORARY TABLE `t1` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
CREATE VIEW v AS SELECT * FROM t1;
ERROR HY000: View's SELECT refers to a temporary table 't1'
RENAME TABLE t1 TO tmp, t1 TO t2;
SHOW CREATE TABLE tmp;
Table Create Table
tmp CREATE TEMPORARY TABLE `tmp` (
`b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
CREATE VIEW v AS SELECT * FROM tmp;
ERROR HY000: View's SELECT refers to a temporary table 'tmp'
CREATE VIEW v AS SELECT * FROM t2;
DROP VIEW v;
DROP TABLE tmp;
DROP TABLE t2;
include/rpl_end.inc
--source include/have_binlog_format_mixed.inc
--source include/master-slave.inc
--echo #
--echo # MDEV-16229 Replication aborts with ER_VIEW_SELECT_TMPTABLE after
--echo # half-failed RENAME
--echo #
CREATE TABLE t1 (a INT);
CREATE TEMPORARY TABLE t1 (b INT);
RENAME TABLE t1 TO tmp, tmp TO t1;
SHOW CREATE TABLE t1;
--error ER_VIEW_SELECT_TMPTABLE
CREATE VIEW v AS SELECT * FROM t1;
RENAME TABLE t1 TO tmp, t1 TO t2;
SHOW CREATE TABLE tmp;
SHOW CREATE TABLE t2;
--error ER_VIEW_SELECT_TMPTABLE
CREATE VIEW v AS SELECT * FROM tmp;
CREATE VIEW v AS SELECT * FROM t2;
--sync_slave_with_master
# Cleanup
--connection master
DROP VIEW v;
DROP TABLE tmp;
DROP TABLE t2;
--source include/rpl_end.inc
...@@ -873,9 +873,8 @@ CACHE INDEX t3 IN keycache1; ...@@ -873,9 +873,8 @@ CACHE INDEX t3 IN keycache1;
LOAD INDEX INTO CACHE t3; LOAD INDEX INTO CACHE t3;
--echo # --echo #
--echo # RENAME (doesn't work for temporary tables, thus should fail). --echo # RENAME should work for temporary tables
--echo # --echo #
--error ER_TABLEACCESS_DENIED_ERROR
RENAME TABLE t3 TO t3_1; RENAME TABLE t3 TO t3_1;
--echo # --echo #
......
...@@ -95,3 +95,49 @@ drop table t1; ...@@ -95,3 +95,49 @@ drop table t1;
--source include/wait_until_count_sessions.inc --source include/wait_until_count_sessions.inc
#
# Test of rename with temporary tables
#
CREATE OR REPLACE TABLE t1 (a INT);
CREATE OR REPLACE TABLE t2 (a INT);
CREATE OR REPLACE TEMPORARY TABLE t1_tmp (b INT);
CREATE OR REPLACE TEMPORARY TABLE t2_tmp (b INT);
# Can't rename table over another one
--error ER_TABLE_EXISTS_ERROR
rename table t1 to t2;
--error ER_TABLE_EXISTS_ERROR
rename table t1 to tmp, tmp to t2;
--error ER_TABLE_EXISTS_ERROR
rename table t1_tmp to t2_tmp;
--error ER_TABLE_EXISTS_ERROR
rename table t1_tmp to tmp, tmp to t2_tmp;
show create table t1_tmp;
show create table t2_tmp;
# The following should work
rename table t1 to t1_tmp;
rename table t2_tmp to t2;
rename table t2 to tmp, tmp to t2;
rename table t1_tmp to tmp, tmp to t1_tmp;
show tables;
SHOW CREATE TABLE t1_tmp;
drop table t1_tmp;
SHOW CREATE TABLE t1_tmp;
drop table t1_tmp;
SHOW CREATE TABLE t2;
drop table t2;
SHOW CREATE TABLE t2;
drop table t2;
CREATE TABLE t1 (a INT);
insert into t1 values (1);
CREATE TEMPORARY TABLE t1 (b INT);
insert into t1 values (2);
RENAME TABLE t1 TO tmp, t1 TO t2;
select * from tmp;
select * from t2;
drop table tmp,t2;
...@@ -495,6 +495,8 @@ void init_update_queries(void) ...@@ -495,6 +495,8 @@ void init_update_queries(void)
There are other statements that deal with temporary tables and open There are other statements that deal with temporary tables and open
them, but which are not listed here. The thing is that the order of them, but which are not listed here. The thing is that the order of
pre-opening temporary tables for those statements is somewhat custom. pre-opening temporary tables for those statements is somewhat custom.
Note that SQLCOM_RENAME_TABLE should not be in this list!
*/ */
sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_DROP_TABLE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_DROP_TABLE]|= CF_PREOPEN_TMP_TABLES;
...@@ -508,7 +510,6 @@ void init_update_queries(void) ...@@ -508,7 +510,6 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_INSERT_SELECT]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_INSERT_SELECT]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_DELETE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_DELETE]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_DELETE_MULTI]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_DELETE_MULTI]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_RENAME_TABLE]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_REPLACE_SELECT]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_REPLACE_SELECT]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_SELECT]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_SELECT]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_SET_OPTION]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_SET_OPTION]|= CF_PREOPEN_TMP_TABLES;
...@@ -5333,6 +5334,60 @@ static bool execute_show_status(THD *thd, TABLE_LIST *all_tables) ...@@ -5333,6 +5334,60 @@ static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
} }
/*
Find out if a table is a temporary table
A table is a temporary table if it's a temporary table or
there has been before a temporary table that has been renamed
to the current name.
Some examples:
A->B B is a temporary table if and only if A is a temp.
A->B, B->C Second B is temp if A is temp
A->B, A->C Second A can't be temp as if A was temp then B is temp
and Second A can only be a normal table. C is also not temp
*/
static TABLE *find_temporary_table_for_rename(THD *thd,
TABLE_LIST *first_table,
TABLE_LIST *cur_table)
{
TABLE_LIST *table;
TABLE *res= 0;
bool found= 0;
DBUG_ENTER("find_temporary_table_for_rename");
/* Find last instance when cur_table is in TO part */
for (table= first_table;
table != cur_table;
table= table->next_local->next_local)
{
TABLE_LIST *next= table->next_local;
if (!strcmp(table->get_db_name(), cur_table->get_db_name()) &&
!strcmp(table->get_table_name(), cur_table->get_table_name()))
{
/* Table was moved away, can't be same as 'table' */
found= 1;
res= 0; // Table can't be a temporary table
}
if (!strcmp(next->get_db_name(), cur_table->get_db_name()) &&
!strcmp(next->get_table_name(), cur_table->get_table_name()))
{
/*
Table has matching name with new name of this table. cur_table should
have same temporary type as this table.
*/
found= 1;
res= table->table;
}
}
if (!found)
res= find_temporary_table(thd, table);
DBUG_RETURN(res);
}
static bool execute_rename_table(THD *thd, TABLE_LIST *first_table, static bool execute_rename_table(THD *thd, TABLE_LIST *first_table,
TABLE_LIST *all_tables) TABLE_LIST *all_tables)
{ {
...@@ -5349,13 +5404,19 @@ static bool execute_rename_table(THD *thd, TABLE_LIST *first_table, ...@@ -5349,13 +5404,19 @@ static bool execute_rename_table(THD *thd, TABLE_LIST *first_table,
&table->next_local->grant.m_internal, &table->next_local->grant.m_internal,
0, 0)) 0, 0))
return 1; return 1;
/* check if these are refering to temporary tables */
table->table= find_temporary_table_for_rename(thd, first_table, table);
table->next_local->table= table->table;
TABLE_LIST old_list, new_list; TABLE_LIST old_list, new_list;
/* /*
we do not need initialize old_list and new_list because we will we do not need initialize old_list and new_list because we will
come table[0] and table->next[0] there copy table[0] and table->next[0] there
*/ */
old_list= table[0]; old_list= table[0];
new_list= table->next_local[0]; new_list= table->next_local[0];
if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) || if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) ||
(!test_all_bits(table->next_local->grant.privilege, (!test_all_bits(table->next_local->grant.privilege,
INSERT_ACL | CREATE_ACL) && INSERT_ACL | CREATE_ACL) &&
......
...@@ -222,7 +222,7 @@ do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table, ...@@ -222,7 +222,7 @@ do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table,
new_alias= (lower_case_table_names == 2) ? new_table->alias : new_alias= (lower_case_table_names == 2) ? new_table->alias :
new_table->table_name; new_table->table_name;
if (is_temporary_table(new_table)) if (find_temporary_table(thd, new_table))
{ {
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
DBUG_RETURN(1); // This can't be skipped DBUG_RETURN(1); // This can't be skipped
......
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