mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
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.
This commit is contained in:
@ -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
|
|||||||
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;
|
||||||
|
34
mysql-test/suite/rpl/r/rename.result
Normal file
34
mysql-test/suite/rpl/r/rename.result
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
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
|
33
mysql-test/suite/rpl/t/rename.test
Normal file
33
mysql-test/suite/rpl/t/rename.test
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
--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;
|
|||||||
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;
|
|||||||
|
|
||||||
--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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
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,
|
|||||||
&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,
|
|||||||
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
|
||||||
|
Reference in New Issue
Block a user