summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty <monty@mariadb.org>2018-05-22 14:36:06 +0300
committerMonty <monty@mariadb.org>2018-05-22 23:05:48 +0300
commitda71c1bad79ee11009b3b28a2a745709e04020cf (patch)
tree7b0410b8d6a8dd724d604da1390d67bac09f1309
parent2f3779d31cbbabcb171c22237253c82146118446 (diff)
downloadmariadb-git-da71c1bad79ee11009b3b28a2a745709e04020cf.tar.gz
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.
-rw-r--r--mysql-test/r/grant2.result3
-rw-r--r--mysql-test/r/rename.result66
-rw-r--r--mysql-test/suite/rpl/r/rename.result34
-rw-r--r--mysql-test/suite/rpl/t/rename.test33
-rw-r--r--mysql-test/t/grant2.test3
-rw-r--r--mysql-test/t/rename.test46
-rw-r--r--sql/sql_parse.cc65
-rw-r--r--sql/sql_rename.cc2
8 files changed, 245 insertions, 7 deletions
diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result
index 9e9b3ffc4e5..70292240d84 100644
--- a/mysql-test/r/grant2.result
+++ b/mysql-test/r/grant2.result
@@ -705,10 +705,9 @@ LOAD INDEX INTO CACHE t3;
Table Op Msg_type Msg_text
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;
-ERROR 42000: INSERT, CREATE command denied to user 'mysqltest_u1'@'localhost' for table 't3_1'
#
# HANDLER OPEN/READ/CLOSE.
#
diff --git a/mysql-test/r/rename.result b/mysql-test/r/rename.result
index 74370ba74dd..c90aaf24e9b 100644
--- a/mysql-test/r/rename.result
+++ b/mysql-test/r/rename.result
@@ -67,3 +67,69 @@ ERROR HY000: 'test.v1' is not BASE TABLE
drop view v1;
drop table t1;
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;
diff --git a/mysql-test/suite/rpl/r/rename.result b/mysql-test/suite/rpl/r/rename.result
new file mode 100644
index 00000000000..5cedea51c86
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rename.result
@@ -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
diff --git a/mysql-test/suite/rpl/t/rename.test b/mysql-test/suite/rpl/t/rename.test
new file mode 100644
index 00000000000..ac499157918
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rename.test
@@ -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
diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test
index 8590dccd1d1..55eb21ed6f7 100644
--- a/mysql-test/t/grant2.test
+++ b/mysql-test/t/grant2.test
@@ -873,9 +873,8 @@ CACHE INDEX t3 IN keycache1;
LOAD INDEX INTO CACHE t3;
--echo #
---echo # RENAME (doesn't work for temporary tables, thus should fail).
+--echo # RENAME should work for temporary tables
--echo #
---error ER_TABLEACCESS_DENIED_ERROR
RENAME TABLE t3 TO t3_1;
--echo #
diff --git a/mysql-test/t/rename.test b/mysql-test/t/rename.test
index a55bc845acc..67732d5b5b9 100644
--- a/mysql-test/t/rename.test
+++ b/mysql-test/t/rename.test
@@ -95,3 +95,49 @@ drop table t1;
--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;
+
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 1162aa901d1..28d11ef2e5c 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -495,6 +495,8 @@ void init_update_queries(void)
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
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_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_DELETE]|= 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_SELECT]|= 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,
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,
0, 0))
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;
/*
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];
new_list= table->next_local[0];
+
if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) ||
(!test_all_bits(table->next_local->grant.privilege,
INSERT_ACL | CREATE_ACL) &&
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index 1a9cb842e6a..119f9063724 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -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_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);
DBUG_RETURN(1); // This can't be skipped