diff options
40 files changed, 822 insertions, 160 deletions
diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index 0845459f6b6..89bf9ed638e 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -2177,6 +2177,66 @@ t1 CREATE TABLE `t1` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8 DROP TABLE t1; # +# MDEV-15308 +# Assertion `ha_alter_info->alter_info->drop_list.elements > 0' failed +# in ha_innodb::prepare_inplace_alter_table +# +CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB; +ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP COLUMN b; +Warnings: +Note 1091 Can't DROP FOREIGN KEY `fk`; check that it exists +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB; +ALTER TABLE t1 DROP INDEX IF EXISTS fk, DROP COLUMN b; +Warnings: +Note 1091 Can't DROP INDEX `fk`; check that it exists +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 (a INT, b INT, c INT, KEY(c)) ENGINE=InnoDB; +ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP COLUMN c; +Warnings: +Note 1091 Can't DROP FOREIGN KEY `fk`; check that it exists +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 (a INT, b INT, c INT, KEY c1(c)) ENGINE=InnoDB; +ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP INDEX c1; +Warnings: +Note 1091 Can't DROP FOREIGN KEY `fk`; check that it exists +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB; +ALTER TABLE t1 DROP INDEX IF EXISTS fk, DROP COLUMN IF EXISTS c; +Warnings: +Note 1091 Can't DROP INDEX `fk`; check that it exists +Note 1091 Can't DROP COLUMN `c`; check that it exists +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +DROP TABLE t1; +# # End of 10.0 tests # # diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index 2ccc01d225a..d7e42f9b7aa 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -809,10 +809,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 2b8c925b309..ecb394e9e29 100644 --- a/mysql-test/r/rename.result +++ b/mysql-test/r/rename.result @@ -78,3 +78,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/r/statistics_close.result b/mysql-test/r/statistics_close.result new file mode 100644 index 00000000000..d7f76e304e4 --- /dev/null +++ b/mysql-test/r/statistics_close.result @@ -0,0 +1,11 @@ +CREATE TABLE t1 (i int); +connect con1,localhost,root,,test; +RENAME TABLE t1 TO t2; +connection default; +FLUSH TABLES; +connection con1; +disconnect con1; +connection default; +DROP TABLE IF EXISTS t1, t2; +Warnings: +Note 1051 Unknown table 'test.t1' diff --git a/mysql-test/suite/galera/r/galera_encrypt_tmp_files.result b/mysql-test/suite/galera/r/galera_encrypt_tmp_files.result new file mode 100644 index 00000000000..63f6c281af1 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_encrypt_tmp_files.result @@ -0,0 +1,35 @@ +SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment'; +VARIABLE_VALUE = 'Synced' +1 +SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; +VARIABLE_VALUE = 2 +1 +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB; +INSERT INTO t1 VALUES (1); +SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment'; +VARIABLE_VALUE = 'Synced' +1 +SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; +VARIABLE_VALUE = 2 +1 +SELECT COUNT(*) = 1 FROM t1; +COUNT(*) = 1 +1 +DROP TABLE t1; +CREATE TABLE `t1` ( +`col1` int(11) NOT NULL, +`col2` varchar(64) NOT NULL DEFAULT '', +`col3` varchar(32) NOT NULL DEFAULT '0', +`col4` varchar(64) NOT NULL DEFAULT '', +`col5` tinyint(4) NOT NULL DEFAULT '0', +`col6` int(11) NOT NULL DEFAULT '0', +`col7` varchar(64) NOT NULL DEFAULT '', +`col8` tinyint(4) NOT NULL DEFAULT '0', +`col9` tinyint(4) NOT NULL DEFAULT '0', +`col10` text NOT NULL, +`col11` varchar(255) NOT NULL DEFAULT '', +`col12` tinyint(4) NOT NULL DEFAULT '1' +) ; +create table t2 (test int); +insert into t2 values (1); +drop table t1,t2; diff --git a/mysql-test/suite/galera/t/galera_encrypt_tmp_files.cnf b/mysql-test/suite/galera/t/galera_encrypt_tmp_files.cnf new file mode 100644 index 00000000000..0f7f80b7d0b --- /dev/null +++ b/mysql-test/suite/galera/t/galera_encrypt_tmp_files.cnf @@ -0,0 +1,8 @@ +!include ../galera_2nodes.cnf +[mysqld] + +encrypt-tmp-files = 1 +plugin-load-add= @ENV.FILE_KEY_MANAGEMENT_SO +file-key-management +loose-file-key-management-filename= @ENV.MYSQL_TEST_DIR/std_data/keys.txt +log-bin diff --git a/mysql-test/suite/galera/t/galera_encrypt_tmp_files.test b/mysql-test/suite/galera/t/galera_encrypt_tmp_files.test new file mode 100644 index 00000000000..c42c3dbd98a --- /dev/null +++ b/mysql-test/suite/galera/t/galera_encrypt_tmp_files.test @@ -0,0 +1,57 @@ +# This file tests that mariadb cluster should not crash when encrypt_tmp_file +# is enabled + +--source include/galera_cluster.inc +--source include/have_innodb.inc + +SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment'; +SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; + +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) Engine=InnoDB; +INSERT INTO t1 VALUES (1); + +--connection node_2 +SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_local_state_comment'; +SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; + +SELECT COUNT(*) = 1 FROM t1; + +DROP TABLE t1; + +--connection node_1 + +CREATE TABLE `t1` ( + `col1` int(11) NOT NULL, + `col2` varchar(64) NOT NULL DEFAULT '', + `col3` varchar(32) NOT NULL DEFAULT '0', + `col4` varchar(64) NOT NULL DEFAULT '', + `col5` tinyint(4) NOT NULL DEFAULT '0', + `col6` int(11) NOT NULL DEFAULT '0', + `col7` varchar(64) NOT NULL DEFAULT '', + `col8` tinyint(4) NOT NULL DEFAULT '0', + `col9` tinyint(4) NOT NULL DEFAULT '0', + `col10` text NOT NULL, + `col11` varchar(255) NOT NULL DEFAULT '', + `col12` tinyint(4) NOT NULL DEFAULT '1' +) ; + +#Although we just need $counter >= 907 for IO_CACHE to use +#encrypted temp file. Just on safe side I am using $counter +# = 1100 +--disable_query_log +--let $counter=1100 +--let $query= (1,'test','test','test',0,0,'-1',0,0,'','',-1) +while($counter) +{ + --let $query= $query ,(1,'test','test','test',0,0,'-1',0,0,'','',-1) + --dec $counter +} +--let $query= INSERT INTO t1 values $query ; +--eval $query +--enable_query_log +#INSERT INTO `t1` VALUE + +create table t2 (test int); +insert into t2 values (1); + +drop table t1,t2; diff --git a/mysql-test/suite/galera/t/galera_var_dirty_reads.test b/mysql-test/suite/galera/t/galera_var_dirty_reads.test index 138b7c1c703..3e2108868af 100644 --- a/mysql-test/suite/galera/t/galera_var_dirty_reads.test +++ b/mysql-test/suite/galera/t/galera_var_dirty_reads.test @@ -4,6 +4,7 @@ --source include/galera_cluster.inc --source include/have_innodb.inc +--source include/have_perfschema.inc # Save original auto_increment_offset values. --let $node_1=node_1 diff --git a/mysql-test/suite/maria/lock.result b/mysql-test/suite/maria/lock.result index 90250568ef5..1c9d6b18100 100644 --- a/mysql-test/suite/maria/lock.result +++ b/mysql-test/suite/maria/lock.result @@ -99,3 +99,13 @@ f2 3 unlock tables; DROP TABLE t1,t2,tmp; +# +# MDEV-10378 Assertion `trn' failed in virtual int ha_maria::start_stmt +# +CREATE TABLE t1 (f1 VARCHAR(3), f2 INT, pk INT, PRIMARY KEY (pk)) ENGINE=Aria; +INSERT INTO t1 VALUES ('foo',10,1), ('foo',1,2); +LOCK TABLE t1 WRITE; +ALTER TABLE t1 ADD UNIQUE KEY (f1); +ERROR 23000: Duplicate entry 'foo' for key 'f1' +ALTER TABLE t1 ADD KEY (f2); +DROP TABLE t1; diff --git a/mysql-test/suite/maria/lock.test b/mysql-test/suite/maria/lock.test index 57447a18c55..4f3d4e8065e 100644 --- a/mysql-test/suite/maria/lock.test +++ b/mysql-test/suite/maria/lock.test @@ -105,3 +105,15 @@ INSERT INTO t2 (f2) SELECT f3 FROM tmp AS tmp_alias; select * from t2; unlock tables; DROP TABLE t1,t2,tmp; + +--echo # +--echo # MDEV-10378 Assertion `trn' failed in virtual int ha_maria::start_stmt +--echo # + +CREATE TABLE t1 (f1 VARCHAR(3), f2 INT, pk INT, PRIMARY KEY (pk)) ENGINE=Aria; +INSERT INTO t1 VALUES ('foo',10,1), ('foo',1,2); +LOCK TABLE t1 WRITE; +--error ER_DUP_ENTRY +ALTER TABLE t1 ADD UNIQUE KEY (f1); +ALTER TABLE t1 ADD KEY (f2); +DROP TABLE t1; diff --git a/mysql-test/suite/rpl/r/rename.result b/mysql-test/suite/rpl/r/rename.result new file mode 100644 index 00000000000..8220ae1f3b1 --- /dev/null +++ b/mysql-test/suite/rpl/r/rename.result @@ -0,0 +1,36 @@ +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; +connection slave; +connection master; +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/alter_table.test b/mysql-test/t/alter_table.test index 63d24c0740d..eddc9f704da 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -1808,6 +1808,37 @@ SHOW CREATE TABLE t1; DROP TABLE t1; --echo # +--echo # MDEV-15308 +--echo # Assertion `ha_alter_info->alter_info->drop_list.elements > 0' failed +--echo # in ha_innodb::prepare_inplace_alter_table +--echo # + +CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB; +ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP COLUMN b; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB; +ALTER TABLE t1 DROP INDEX IF EXISTS fk, DROP COLUMN b; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 (a INT, b INT, c INT, KEY(c)) ENGINE=InnoDB; +ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP COLUMN c; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 (a INT, b INT, c INT, KEY c1(c)) ENGINE=InnoDB; +ALTER TABLE t1 DROP FOREIGN KEY IF EXISTS fk, DROP INDEX c1; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB; +ALTER TABLE t1 DROP INDEX IF EXISTS fk, DROP COLUMN IF EXISTS c; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +--echo # --echo # End of 10.0 tests --echo # diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test index 5e8a130feea..cee5df089df 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/mysql-test/t/statistics_close.test b/mysql-test/t/statistics_close.test new file mode 100644 index 00000000000..de22a5a44fe --- /dev/null +++ b/mysql-test/t/statistics_close.test @@ -0,0 +1,18 @@ +# +# MDEV-16123 ASAN heap-use-after-free handler::ha_index_or_rnd_end +# MDEV-13828 Segmentation fault on RENAME TABLE +# + +CREATE TABLE t1 (i int); +--connect (con1,localhost,root,,test) +--send +RENAME TABLE t1 TO t2; +--connection default +FLUSH TABLES; +--connection con1 +--reap + +# Cleanup +--disconnect con1 +--connection default +DROP TABLE IF EXISTS t1, t2; diff --git a/sql/mf_iocache_encr.cc b/sql/mf_iocache_encr.cc index d078a183e31..879da12faa4 100644 --- a/sql/mf_iocache_encr.cc +++ b/sql/mf_iocache_encr.cc @@ -49,8 +49,8 @@ static int my_b_encr_read(IO_CACHE *info, uchar *Buffer, size_t Count) if (pos_in_file == info->end_of_file) { - info->read_pos= info->read_end= info->buffer; - info->pos_in_file= pos_in_file; + /* reading past EOF should not empty the cache */ + info->read_pos= info->read_end; info->error= 0; DBUG_RETURN(MY_TEST(Count)); } diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 3306575b845..49e14d6b013 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -426,7 +426,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt, const char *operator_name, thr_lock_type lock_type, - bool open_for_modify, + bool org_open_for_modify, bool repair_table_use_frm, uint extra_open_options, int (*prepare_func)(THD *, TABLE_LIST *, @@ -488,10 +488,11 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, for (table= tables; table; table= table->next_local) { char table_name[SAFE_NAME_LEN*2+2]; - char* db = table->db; + char *db= table->db; bool fatal_error=0; bool open_error; bool collect_eis= FALSE; + bool open_for_modify= org_open_for_modify; DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name)); strxmov(table_name, db, ".", table->table_name, NullS); @@ -1159,7 +1160,7 @@ send_result_message: } } /* Error path, a admin command failed. */ - if (thd->transaction_rollback_request) + if (thd->transaction_rollback_request || fatal_error) { /* Unlikely, but transaction rollback was requested by one of storage diff --git a/sql/sql_class.h b/sql/sql_class.h index 7302881ace2..a12015ecfe4 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4262,6 +4262,12 @@ public: /* Members related to temporary tables. */ public: + /* Opened table states. */ + enum Temporary_table_state { + TMP_TABLE_IN_USE, + TMP_TABLE_NOT_IN_USE, + TMP_TABLE_ANY + }; bool has_thd_temporary_tables(); TABLE *create_and_open_tmp_table(handlerton *hton, @@ -4271,8 +4277,10 @@ public: const char *table_name, bool open_in_engine); - TABLE *find_temporary_table(const char *db, const char *table_name); - TABLE *find_temporary_table(const TABLE_LIST *tl); + TABLE *find_temporary_table(const char *db, const char *table_name, + Temporary_table_state state= TMP_TABLE_IN_USE); + TABLE *find_temporary_table(const TABLE_LIST *tl, + Temporary_table_state state= TMP_TABLE_IN_USE); TMP_TABLE_SHARE *find_tmp_table_share_w_base_key(const char *key, uint key_length); @@ -4299,13 +4307,6 @@ private: /* Whether a lock has been acquired? */ bool m_tmp_tables_locked; - /* Opened table states. */ - enum Temporary_table_state { - TMP_TABLE_IN_USE, - TMP_TABLE_NOT_IN_USE, - TMP_TABLE_ANY - }; - bool has_temporary_tables(); uint create_tmp_table_def_key(char *key, const char *db, const char *table_name); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 4a27244f5b9..2a3454703f4 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -155,10 +155,11 @@ static void mysql_ha_close_table(SQL_HANDLER *handler) { THD *thd= handler->thd; TABLE *table= handler->table; + DBUG_ENTER("mysql_ha_close_table"); /* check if table was already closed */ if (!table) - return; + DBUG_VOID_RETURN; if (!table->s->tmp_table) { @@ -183,6 +184,7 @@ static void mysql_ha_close_table(SQL_HANDLER *handler) } my_free(handler->lock); handler->init(); + DBUG_VOID_RETURN; } /* diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f4038649745..1c36bd7bcdf 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -763,6 +763,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; @@ -776,7 +778,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; @@ -6525,6 +6526,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= thd->find_temporary_table(table, THD::TMP_TABLE_ANY); + DBUG_RETURN(res); +} + + static bool check_rename_table(THD *thd, TABLE_LIST *first_table, TABLE_LIST *all_tables) { @@ -6541,13 +6596,19 @@ static bool check_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 1588644f0e1..4e576602a1a 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -221,7 +221,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 (thd->find_temporary_table(new_table, THD::TMP_TABLE_ANY)) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); DBUG_RETURN(1); // This can't be skipped diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 903ebb91d01..0e4749dbe6e 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1519,7 +1519,8 @@ public: ~Stat_table_write_iter() { - cleanup(); + /* Ensure that cleanup has been run */ + DBUG_ASSERT(rowid_buf == 0); } }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b01629cb1e9..853535f3d51 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5888,10 +5888,28 @@ drop_create_field: List_iterator<Alter_drop> drop_it(alter_info->drop_list); Alter_drop *drop; bool remove_drop; + ulonglong left_flags= 0; while ((drop= drop_it++)) { + ulonglong cur_flag= 0; + switch (drop->type) { + case Alter_drop::COLUMN: + cur_flag= Alter_info::ALTER_DROP_COLUMN; + break; + case Alter_drop::FOREIGN_KEY: + cur_flag= Alter_info::DROP_FOREIGN_KEY; + break; + case Alter_drop::KEY: + cur_flag= Alter_info::ALTER_DROP_INDEX; + break; + default: + break; + } if (!drop->drop_if_exists) + { + left_flags|= cur_flag; continue; + } remove_drop= TRUE; if (drop->type == Alter_drop::COLUMN) { @@ -5980,12 +5998,15 @@ drop_create_field: ER_THD(thd, ER_CANT_DROP_FIELD_OR_KEY), drop->type_name(), drop->name); drop_it.remove(); - if (alter_info->drop_list.is_empty()) - alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN | - Alter_info::ALTER_DROP_INDEX | - Alter_info::DROP_FOREIGN_KEY); } + else + left_flags|= cur_flag; } + /* Reset state to what's left in drop list */ + alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN | + Alter_info::ALTER_DROP_INDEX | + Alter_info::DROP_FOREIGN_KEY); + alter_info->flags|= left_flags; } /* ALTER TABLE ADD KEY IF NOT EXISTS */ diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index 8077324d202..3201b306fad 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -99,12 +99,14 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton, @param db [IN] Database name @param table_name [IN] Table name + @param state [IN] State of temp table to open @return Success Pointer to first used table instance. Failure NULL */ TABLE *THD::find_temporary_table(const char *db, - const char *table_name) + const char *table_name, + Temporary_table_state state) { DBUG_ENTER("THD::find_temporary_table"); @@ -121,7 +123,7 @@ TABLE *THD::find_temporary_table(const char *db, key_length= create_tmp_table_def_key(key, db, table_name); locked= lock_temporary_tables(); - table = find_temporary_table(key, key_length, TMP_TABLE_IN_USE); + table= find_temporary_table(key, key_length, state); if (locked) { DBUG_ASSERT(m_tmp_tables_locked); @@ -140,16 +142,12 @@ TABLE *THD::find_temporary_table(const char *db, @return Success Pointer to first used table instance. Failure NULL */ -TABLE *THD::find_temporary_table(const TABLE_LIST *tl) +TABLE *THD::find_temporary_table(const TABLE_LIST *tl, + Temporary_table_state state) { DBUG_ENTER("THD::find_temporary_table"); - - if (!has_temporary_tables()) - { - DBUG_RETURN(NULL); - } - - TABLE *table= find_temporary_table(tl->get_db_name(), tl->get_table_name()); + TABLE *table= find_temporary_table(tl->get_db_name(), tl->get_table_name(), + state); DBUG_RETURN(table); } diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index b4ae46683e4..96e6d62189f 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -37,6 +37,7 @@ C_MODE_START #include "ma_checkpoint.h" #include "ma_recovery.h" C_MODE_END +#include "ma_trnman.h" //#include "sql_priv.h" #include "protocol.h" @@ -1393,7 +1394,8 @@ int ha_maria::check(THD * thd, HA_CHECK_OPT * check_opt) } /* Reset trn, that may have been set by repair */ - _ma_set_trn_for_table(file, old_trn); + if (old_trn && old_trn != file->trn) + _ma_set_trn_for_table(file, old_trn); thd_proc_info(thd, old_proc_info); thd_progress_end(thd); return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK; @@ -1527,7 +1529,8 @@ int ha_maria::zerofill(THD * thd, HA_CHECK_OPT *check_opt) error=maria_zerofill(param, file, share->open_file_name.str); /* Reset trn, that may have been set by repair */ - _ma_set_trn_for_table(file, old_trn); + if (old_trn && old_trn != file->trn) + _ma_set_trn_for_table(file, old_trn); if (!error) { @@ -1770,7 +1773,8 @@ int ha_maria::repair(THD *thd, HA_CHECK *param, bool do_optimize) maria_lock_database(file, F_UNLCK); /* Reset trn, that may have been set by repair */ - _ma_set_trn_for_table(file, old_trn); + if (old_trn && old_trn != file->trn) + _ma_set_trn_for_table(file, old_trn); error= error ? HA_ADMIN_FAILED : (optimize_done ? (write_log_record_for_repair(param, file) ? HA_ADMIN_FAILED : @@ -2581,9 +2585,12 @@ int ha_maria::extra(enum ha_extra_function operation) without calling commit/rollback in between. If file->trn is not set we can't remove file->share from the transaction list in the extra() call. - We also ensure that we set file->trn to 0 if THD_TRN is 0 as in - this case we have already freed the trn. This can happen when one - implicit_commit() is called as part of alter table. + In current code we don't have to do this for HA_EXTRA_PREPARE_FOR_RENAME + as this is only used the intermediate table used by ALTER TABLE which + is not part of the transaction (it's not in the TRN list). Better to + keep this for now, to not break anything in a stable release. + When HA_EXTRA_PREPARE_FOR_RENAME is not handled below, we can change + the warnings in _ma_remove_table_from_trnman() to asserts. table->in_use is not set in the case this is a done as part of closefrm() as part of drop table. @@ -2596,7 +2603,7 @@ int ha_maria::extra(enum ha_extra_function operation) { THD *thd= table->in_use; TRN *trn= THD_TRN; - _ma_set_trn_for_table(file, trn); + _ma_set_tmp_trn_for_table(file, trn); } DBUG_ASSERT(file->s->base.born_transactional || file->trn == 0 || file->trn == &dummy_transaction_object); @@ -2712,6 +2719,7 @@ int ha_maria::external_lock(THD *thd, int lock_type) if (file->trn) { /* This can only happen with tables created with clone() */ + DBUG_PRINT("info",("file->trn: %p", file->trn)); trnman_increment_locked_tables(file->trn); } @@ -2732,7 +2740,7 @@ int ha_maria::external_lock(THD *thd, int lock_type) } else { - TRN *trn= THD_TRN; + TRN *trn= (file->trn != &dummy_transaction_object ? file->trn : 0); /* End of transaction */ /* @@ -2747,8 +2755,7 @@ int ha_maria::external_lock(THD *thd, int lock_type) */ if (_ma_reenable_logging_for_table(file, TRUE)) DBUG_RETURN(1); - /** @todo zero file->trn also in commit and rollback */ - _ma_set_trn_for_table(file, NULL); // Safety + _ma_reset_trn_for_table(file); /* Ensure that file->state points to the current number of rows. This is needed if someone calls maria_info() without first doing an @@ -2805,13 +2812,6 @@ int ha_maria::start_stmt(THD *thd, thr_lock_type lock_type) DBUG_ASSERT(file->trn == trn); /* - If there was an implicit commit under this LOCK TABLES by a previous - statement (like a DDL), at least if that previous statement was about a - different ha_maria than 'this' then this->file->trn is a stale - pointer. We fix it: - */ - _ma_set_trn_for_table(file, trn); - /* As external_lock() was already called, don't increment locked_tables. Note that we call the function below possibly several times when statement starts (once per table). This is ok as long as that function @@ -2835,6 +2835,23 @@ int ha_maria::start_stmt(THD *thd, thr_lock_type lock_type) } +/* + Reset THD_TRN and all file->trn related to the transaction + This is needed as some calls, like extra() or external_lock() may access + it before next transaction is started +*/ + +static void reset_thd_trn(THD *thd, MARIA_HA *first_table) +{ + DBUG_ENTER("reset_thd_trn"); + THD_TRN= NULL; + for (MARIA_HA *table= first_table; table ; + table= table->trn_next) + _ma_reset_trn_for_table(table); + DBUG_VOID_RETURN; +} + + /** Performs an implicit commit of the Maria transaction and creates a new one. @@ -2858,10 +2875,10 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn) TRN *trn; int error; uint locked_tables; - DYNAMIC_ARRAY used_tables; extern my_bool plugins_are_initialized; - + MARIA_HA *used_tables, *trn_next; DBUG_ENTER("ha_maria::implicit_commit"); + if (!maria_hton || !plugins_are_initialized || !(trn= THD_TRN)) DBUG_RETURN(0); if (!new_trn && (thd->locked_tables_mode == LTM_LOCK_TABLES || @@ -2879,48 +2896,16 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn) locked_tables= trnman_has_locked_tables(trn); - if (new_trn && trn && trn->used_tables) - { - MARIA_USED_TABLES *tables; - /* - Save locked tables so that we can move them to another transaction - We are using a dynamic array as locked_tables in some cases can be - smaller than the used_tables list (for example when the server does - early unlock of tables. - */ - - my_init_dynamic_array2(&used_tables, sizeof(MARIA_SHARE*), (void*) 0, - locked_tables, 8, MYF(MY_THREAD_SPECIFIC)); - for (tables= (MARIA_USED_TABLES*) trn->used_tables; - tables; - tables= tables->next) - { - if (tables->share->base.born_transactional) - { - if (insert_dynamic(&used_tables, (uchar*) &tables->share)) - { - error= HA_ERR_OUT_OF_MEM; - goto end_and_free; - } - } - } - } - else - bzero(&used_tables, sizeof(used_tables)); - + used_tables= (MARIA_HA*) trn->used_instances; error= 0; if (unlikely(ma_commit(trn))) error= 1; if (!new_trn) { - /* - To be extra safe, we should also reset file->trn for all open - tables as some calls, like extra() may access it. We take care - of this in extra() by resetting file->trn if THD_TRN is 0. - */ - THD_TRN= NULL; + reset_thd_trn(thd, used_tables); goto end; } + /* We need to create a new transaction and put it in THD_TRN. Indeed, tables may be under LOCK TABLES, and so they will start the next @@ -2930,8 +2915,9 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn) THD_TRN= trn; if (unlikely(trn == NULL)) { + reset_thd_trn(thd, used_tables); error= HA_ERR_OUT_OF_MEM; - goto end_and_free; + goto end; } /* Move all locked tables to the new transaction @@ -2941,35 +2927,25 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn) in check table, we use the table without calling start_stmt(). */ - uint i; - for (i= 0 ; i < used_tables.elements ; i++) + for (MARIA_HA *handler= used_tables; handler ; + handler= trn_next) { - MARIA_SHARE *share; - LIST *handlers; + trn_next= handler->trn_next; + DBUG_ASSERT(handler->s->base.born_transactional); - share= *(dynamic_element(&used_tables, i, MARIA_SHARE**)); - /* Find table instances that was used in this transaction */ - for (handlers= share->open_list; handlers; handlers= handlers->next) + /* If handler uses versioning */ + if (handler->s->lock_key_trees) { - MARIA_HA *handler= (MARIA_HA*) handlers->data; - if (handler->external_ref && - ((TABLE*) handler->external_ref)->in_use == thd) - { - _ma_set_trn_for_table(handler, trn); - /* If handler uses versioning */ - if (handler->s->lock_key_trees) - { - if (_ma_setup_live_state(handler)) - error= HA_ERR_OUT_OF_MEM; - } - } + /* _ma_set_trn_for_table() will be called indirectly */ + if (_ma_setup_live_state(handler)) + error= HA_ERR_OUT_OF_MEM; } + else + _ma_set_trn_for_table(handler, trn); } /* This is just a commit, tables stay locked if they were: */ trnman_reset_locked_tables(trn, locked_tables); -end_and_free: - delete_dynamic(&used_tables); end: DBUG_RETURN(error); } @@ -3342,10 +3318,10 @@ static int maria_commit(handlerton *hton __attribute__ ((unused)), trnman_set_flags(trn, trnman_get_flags(trn) & ~TRN_STATE_INFO_LOGGED); /* statement or transaction ? */ - if ((thd->variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && !all) + if ((thd->variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && + !all) DBUG_RETURN(0); // end of statement - DBUG_PRINT("info", ("THD_TRN set to 0x0")); - THD_TRN= 0; + reset_thd_trn(thd, (MARIA_HA*) trn->used_instances); DBUG_RETURN(ma_commit(trn)); // end of transaction } @@ -3362,8 +3338,7 @@ static int maria_rollback(handlerton *hton __attribute__ ((unused)), trnman_rollback_statement(trn); DBUG_RETURN(0); // end of statement } - DBUG_PRINT("info", ("THD_TRN set to 0x0")); - THD_TRN= 0; + reset_thd_trn(thd, (MARIA_HA*) trn->used_instances); DBUG_RETURN(trnman_rollback_trn(trn) ? HA_ERR_OUT_OF_MEM : 0); // end of transaction } diff --git a/storage/maria/ha_maria.h b/storage/maria/ha_maria.h index 65fe5f545d1..f3548a38137 100644 --- a/storage/maria/ha_maria.h +++ b/storage/maria/ha_maria.h @@ -193,6 +193,7 @@ public: private: DsMrr_impl ds_mrr; friend ICP_RESULT index_cond_func_maria(void *arg); + friend void reset_thd_trn(THD *thd); }; #endif /* HA_MARIA_INCLUDED */ diff --git a/storage/maria/ma_blockrec.c b/storage/maria/ma_blockrec.c index c91ba8e23f6..d6bb769ed3c 100644 --- a/storage/maria/ma_blockrec.c +++ b/storage/maria/ma_blockrec.c @@ -271,6 +271,7 @@ #include "maria_def.h" #include "ma_blockrec.h" #include "trnman.h" +#include "ma_trnman.h" #include "ma_key_recover.h" #include "ma_recovery_util.h" #include <lf.h> @@ -7525,7 +7526,7 @@ void maria_ignore_trids(MARIA_HA *info) if (info->s->base.born_transactional) { if (!info->trn) - _ma_set_trn_for_table(info, &dummy_transaction_object); + _ma_set_tmp_trn_for_table(info, &dummy_transaction_object); /* Ignore transaction id when row is read */ info->trn->min_read_from= ~(TrID) 0; } diff --git a/storage/maria/ma_close.c b/storage/maria/ma_close.c index 882e9f585f1..6e85551c24f 100644 --- a/storage/maria/ma_close.c +++ b/storage/maria/ma_close.c @@ -37,6 +37,8 @@ int maria_close(register MARIA_HA *info) /* Check that we have unlocked key delete-links properly */ DBUG_ASSERT(info->key_del_used == 0); + /* Check that file is not part of any uncommited transactions */ + DBUG_ASSERT(info->trn == 0 || info->trn == &dummy_transaction_object); if (share->reopen == 1) { diff --git a/storage/maria/ma_commit.c b/storage/maria/ma_commit.c index 68435a45c0a..0ae3868dbf6 100644 --- a/storage/maria/ma_commit.c +++ b/storage/maria/ma_commit.c @@ -15,6 +15,7 @@ #include "maria_def.h" #include "trnman.h" +#include "ma_trnman.h" /** writes a COMMIT record to log and commits transaction in memory @@ -43,9 +44,9 @@ int ma_commit(TRN *trn) COMMIT record) and this is not an issue as * transaction's updates were not made visible to other transactions * "commit ok" was not sent to client - Alternatively, Recovery might commit trn (if MY_MIN(rec_lsn) is before COMMIT - record), which is ok too. All in all it means that "trn committed" is not - 100% equal to "COMMIT record written". + Alternatively, Recovery might commit trn (if MY_MIN(rec_lsn) is before + COMMIT record), which is ok too. All in all it means that "trn committed" + is not 100% equal to "COMMIT record written". - if COMMIT record is written after trnman_commit_trn(): if crash happens between the two, trn will be rolled back which is an issue (transaction's updates were made visible to other transactions). @@ -93,7 +94,12 @@ int ma_commit(TRN *trn) int maria_commit(MARIA_HA *info) { - return info->s->now_transactional ? ma_commit(info->trn) : 0; + TRN *trn; + if (!info->s->now_transactional) + return 0; + trn= info->trn; + info->trn= 0; /* checked in maria_close() */ + return ma_commit(trn); } @@ -120,10 +126,7 @@ int maria_begin(MARIA_HA *info) TRN *trn= trnman_new_trn(0); if (unlikely(!trn)) DBUG_RETURN(HA_ERR_OUT_OF_MEM); - - DBUG_PRINT("info", ("TRN set to %p", trn)); _ma_set_trn_for_table(info, trn); } DBUG_RETURN(0); } - diff --git a/storage/maria/ma_extra.c b/storage/maria/ma_extra.c index 41261b1422e..9feead42cf7 100644 --- a/storage/maria/ma_extra.c +++ b/storage/maria/ma_extra.c @@ -346,7 +346,7 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function, _ma_decrement_open_count(info, 0); if (info->trn) { - _ma_remove_table_from_trnman(share, info->trn); + _ma_remove_table_from_trnman(info); /* Ensure we don't point to the deleted data in trn */ info->state= info->state_start= &share->state.state; } @@ -409,7 +409,7 @@ int maria_extra(MARIA_HA *info, enum ha_extra_function function, if (info->trn) { mysql_mutex_lock(&share->intern_lock); - _ma_remove_table_from_trnman(share, info->trn); + _ma_remove_table_from_trnman(info); /* Ensure we don't point to the deleted data in trn */ info->state= info->state_start= &share->state.state; mysql_mutex_unlock(&share->intern_lock); diff --git a/storage/maria/ma_open.c b/storage/maria/ma_open.c index 8b3fa921cf1..d05e2313891 100644 --- a/storage/maria/ma_open.c +++ b/storage/maria/ma_open.c @@ -19,6 +19,8 @@ #include "ma_sp_defs.h" #include "ma_rt_index.h" #include "ma_blockrec.h" +#include "trnman.h" +#include "ma_trnman.h" #include <m_ctype.h> #include "ma_crypt.h" @@ -184,7 +186,7 @@ static MARIA_HA *maria_clone_internal(MARIA_SHARE *share, if (!share->base.born_transactional) /* For transactional ones ... */ { /* ... force crash if no trn given */ - _ma_set_trn_for_table(&info, &dummy_transaction_object); + _ma_set_tmp_trn_for_table(&info, &dummy_transaction_object); info.state= &share->state.state; /* Change global values by default */ } else diff --git a/storage/maria/ma_state.c b/storage/maria/ma_state.c index 71e1e8a67a6..2416502bd36 100644 --- a/storage/maria/ma_state.c +++ b/storage/maria/ma_state.c @@ -66,7 +66,7 @@ my_bool _ma_setup_live_state(MARIA_HA *info) DBUG_RETURN(1); trn= info->trn; - for (tables= (MARIA_USED_TABLES*) info->trn->used_tables; + for (tables= (MARIA_USED_TABLES*) trn->used_tables; tables; tables= tables->next) { @@ -551,6 +551,7 @@ my_bool _ma_trnman_end_trans_hook(TRN *trn, my_bool commit, my_free(tables); } trn->used_tables= 0; + trn->used_instances= 0; DBUG_RETURN(error); } @@ -565,18 +566,25 @@ my_bool _ma_trnman_end_trans_hook(TRN *trn, my_bool commit, share->internal_lock must be locked when function is called */ -void _ma_remove_table_from_trnman(MARIA_SHARE *share, TRN *trn) +void _ma_remove_table_from_trnman(MARIA_HA *info) { + MARIA_SHARE *share= info->s; + TRN *trn= info->trn; MARIA_USED_TABLES *tables, **prev; + MARIA_HA *handler, **prev_file; DBUG_ENTER("_ma_remove_table_from_trnman"); DBUG_PRINT("enter", ("trn: %p used_tables: %p share: %p in_trans: %d", trn, trn->used_tables, share, share->in_trans)); mysql_mutex_assert_owner(&share->intern_lock); + + if (trn == &dummy_transaction_object) + DBUG_VOID_RETURN; - for (prev= (MARIA_USED_TABLES**) (char*) &trn->used_tables, tables= *prev; - tables; - tables= *prev) + /* First remove share from used_tables */ + for (prev= (MARIA_USED_TABLES**) (char*) &trn->used_tables; + (tables= *prev); + prev= &tables->next) { if (tables->share == share) { @@ -585,8 +593,36 @@ void _ma_remove_table_from_trnman(MARIA_SHARE *share, TRN *trn) my_free(tables); break; } - prev= &tables->next; } + if (tables != 0) + { + /* + This can only happens in case of rename of intermediate table as + part of alter table + */ + DBUG_PRINT("warning", ("share: %p where not in used_tables_list", share)); + } + + /* unlink table from used_instances */ + for (prev_file= (MARIA_HA**) &trn->used_instances; + (handler= *prev_file); + prev_file= &handler->trn_next) + { + if (handler == info) + { + *prev_file= info->trn_next; + break; + } + } + if (handler != 0) + { + /* + This can only happens in case of rename of intermediate table as + part of alter table + */ + DBUG_PRINT("warning", ("table: %p where not in used_instances", info)); + } + info->trn= 0; /* Not part of trans anymore */ DBUG_VOID_RETURN; } diff --git a/storage/maria/ma_state.h b/storage/maria/ma_state.h index a86aada94fd..8728a2113e5 100644 --- a/storage/maria/ma_state.h +++ b/storage/maria/ma_state.h @@ -84,5 +84,5 @@ my_bool _ma_row_visible_non_transactional_table(MARIA_HA *info); my_bool _ma_row_visible_transactional_table(MARIA_HA *info); void _ma_remove_not_visible_states_with_lock(struct st_maria_share *share, my_bool all); -void _ma_remove_table_from_trnman(struct st_maria_share *share, TRN *trn); +void _ma_remove_table_from_trnman(MARIA_HA *info); void _ma_reset_history(struct st_maria_share *share); diff --git a/storage/maria/ma_trnman.h b/storage/maria/ma_trnman.h new file mode 100644 index 00000000000..9bfd1f0d047 --- /dev/null +++ b/storage/maria/ma_trnman.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2006-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _ma_trnman_h +#define _ma_trnman_h + +/** + Sets table's trn and prints debug information + Links table into used_instances if new_trn is not 0 + + @param tbl MARIA_HA of table + @param newtrn what to put into tbl->trn +*/ + +static inline void _ma_set_trn_for_table(MARIA_HA *tbl, TRN *newtrn) +{ + DBUG_PRINT("info",("table: %p trn: %p -> %p", + tbl, tbl->trn, newtrn)); + + /* check that we are not calling this twice in a row */ + DBUG_ASSERT(newtrn->used_instances != (void*) tbl); + + tbl->trn= newtrn; + /* Link into used list */ + tbl->trn_next= (MARIA_HA*) newtrn->used_instances; + newtrn->used_instances= tbl; +} + + +/* + Same as _ma_set_trn_for_table(), but don't link table into used_instance list + Used when we want to temporary set trn for a table in extra() +*/ + +static inline void _ma_set_tmp_trn_for_table(MARIA_HA *tbl, TRN *newtrn) +{ + DBUG_PRINT("info",("table: %p trn: %p -> %p", + tbl, tbl->trn, newtrn)); + tbl->trn= newtrn; +} + + +/* + Reset TRN in table +*/ + +static inline void _ma_reset_trn_for_table(MARIA_HA *tbl) +{ + DBUG_PRINT("info",("table: %p trn: %p -> NULL", tbl, tbl->trn)); + tbl->trn= 0; +} + +#endif /* _ma_trnman_h */ diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h index 03d71f95c11..2bd7ee9e427 100644 --- a/storage/maria/maria_def.h +++ b/storage/maria/maria_def.h @@ -601,6 +601,7 @@ struct st_maria_handler { MARIA_SHARE *s; /* Shared between open:s */ struct st_ma_transaction *trn; /* Pointer to active transaction */ + struct st_maria_handler *trn_next; MARIA_STATUS_INFO *state, state_save; MARIA_STATUS_INFO *state_start; /* State at start of transaction */ MARIA_USED_TABLES *used_tables; @@ -861,19 +862,6 @@ struct st_maria_handler #define get_pack_length(length) ((length) >= 255 ? 3 : 1) #define _ma_have_versioning(info) ((info)->row_flag & ROW_FLAG_TRANSID) -/** - Sets table's trn and prints debug information - @param tbl MARIA_HA of table - @param newtrn what to put into tbl->trn - @note cast of newtrn is because %p of NULL gives warning (NULL is int) -*/ -#define _ma_set_trn_for_table(tbl, newtrn) do { \ - DBUG_PRINT("info",("table: %p trn: %p -> %p", \ - (tbl), (tbl)->trn, (void *)(newtrn))); \ - (tbl)->trn= (newtrn); \ - } while (0) - - #define MARIA_MIN_BLOCK_LENGTH 20 /* Because of delete-link */ /* Don't use to small record-blocks */ #define MARIA_EXTEND_BLOCK_LENGTH 20 diff --git a/storage/maria/trnman.c b/storage/maria/trnman.c index bc48d39baaa..5b3c9f0287a 100644 --- a/storage/maria/trnman.c +++ b/storage/maria/trnman.c @@ -357,6 +357,7 @@ TRN *trnman_new_trn(WT_THD *wt) trn->commit_trid= MAX_TRID; trn->rec_lsn= trn->undo_lsn= trn->first_undo_lsn= 0; trn->used_tables= 0; + trn->used_instances= 0; trn->locked_tables= 0; trn->flags= 0; diff --git a/storage/maria/trnman.h b/storage/maria/trnman.h index 66139a31230..11c73797766 100644 --- a/storage/maria/trnman.h +++ b/storage/maria/trnman.h @@ -46,7 +46,8 @@ struct st_ma_transaction LF_PINS *pins; WT_THD *wt; mysql_mutex_t state_lock; - void *used_tables; /**< Tables used by transaction */ + void *used_tables; /**< Table shares used by transaction */ + void *used_instances; /* table files used by transaction */ TRN *next, *prev; TrID trid, min_read_from, commit_trid; LSN rec_lsn, undo_lsn; diff --git a/storage/xtradb/handler/i_s.cc b/storage/xtradb/handler/i_s.cc index 94cd6f29558..dc4fa99fc56 100644 --- a/storage/xtradb/handler/i_s.cc +++ b/storage/xtradb/handler/i_s.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2014, 2017, MariaDB Corporation. +Copyright (c) 2014, 2018, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -4922,9 +4922,11 @@ i_s_innodb_buffer_page_fill( mutex_enter(&dict_sys->mutex); - if (const dict_index_t* index = - dict_index_get_if_in_cache_low( - page_info->index_id)) { + const dict_index_t* index = + dict_index_get_if_in_cache_low( + page_info->index_id); + + if (index) { table_name_end = innobase_convert_name( table_name, sizeof(table_name), index->table_name, @@ -4947,7 +4949,10 @@ i_s_innodb_buffer_page_fill( OK(ret); - fields[IDX_BUFFER_PAGE_TABLE_NAME]->set_notnull(); + if (index) { + fields[IDX_BUFFER_PAGE_TABLE_NAME] + ->set_notnull(); + } } OK(fields[IDX_BUFFER_PAGE_NUM_RECS]->store( @@ -5621,9 +5626,11 @@ i_s_innodb_buf_page_lru_fill( mutex_enter(&dict_sys->mutex); - if (const dict_index_t* index = - dict_index_get_if_in_cache_low( - page_info->index_id)) { + const dict_index_t* index = + dict_index_get_if_in_cache_low( + page_info->index_id); + + if (index) { table_name_end = innobase_convert_name( table_name, sizeof(table_name), index->table_name, @@ -5646,7 +5653,10 @@ i_s_innodb_buf_page_lru_fill( OK(ret); - fields[IDX_BUF_LRU_PAGE_TABLE_NAME]->set_notnull(); + if (index) { + fields[IDX_BUF_LRU_PAGE_TABLE_NAME] + ->set_notnull(); + } } OK(fields[IDX_BUF_LRU_PAGE_NUM_RECS]->store( diff --git a/unittest/sql/mf_iocache-t.cc b/unittest/sql/mf_iocache-t.cc index 36860892508..fb5577a7ab9 100644 --- a/unittest/sql/mf_iocache-t.cc +++ b/unittest/sql/mf_iocache-t.cc @@ -187,10 +187,76 @@ void mdev9044() close_cached_file(&info); } +/* 2 Reads (with my_b_fill) in cache makes second read to fail */ +void mdev10259() +{ + int res; + uchar buf[200]; + memset(buf, FILL, sizeof(buf)); + + diag("MDEV-10259- mysqld crash with certain statement length and order with" + " Galera and encrypt-tmp-files=1"); + + init_io_cache_encryption(); + + res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0); + ok(res == 0, "open_cached_file" INFO_TAIL); + + res= my_b_write(&info, buf, sizeof(buf)); + ok(res == 0 && info.pos_in_file == 0, "200 write" INFO_TAIL); + + res= my_b_flush_io_cache(&info, 1); + ok(res == 0, "flush" INFO_TAIL); + + ulong saved_pos= my_b_tell(&info); + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + res= my_b_fill(&info); + ok(res == 200, "fill" INFO_TAIL); + + res= my_b_fill(&info); + ok(res == 0, "fill" INFO_TAIL); + + res= my_b_fill(&info); + ok(res == 0, "fill" INFO_TAIL); + + res= reinit_io_cache(&info, WRITE_CACHE, saved_pos, 0, 0); + ok(res == 0, "reinit WRITE_CACHE" INFO_TAIL); + + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + ok(200 == my_b_bytes_in_cache(&info),"my_b_bytes_in_cache == 200"); + + res= my_b_fill(&info); + ok(res == 0, "fill" INFO_TAIL); + + res= my_b_fill(&info); + ok(res == 0, "fill" INFO_TAIL); + + res= my_b_fill(&info); + ok(res == 0, "fill" INFO_TAIL); + + res= reinit_io_cache(&info, WRITE_CACHE, saved_pos, 0, 0); + ok(res == 0, "reinit WRITE_CACHE" INFO_TAIL); + + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + ok(200 == my_b_bytes_in_cache(&info),"my_b_bytes_in_cache == 200"); + + res= my_b_read(&info, buf, sizeof(buf)) || data_bad(buf, sizeof(buf)); + ok(res == 0 && info.pos_in_file == 0, "large read" INFO_TAIL); + + close_cached_file(&info); + +} + int main(int argc __attribute__((unused)),char *argv[]) { MY_INIT(argv[0]); - plan(29); + plan(46); /* temp files with and without encryption */ encrypt_tmp_files= 1; @@ -202,6 +268,10 @@ int main(int argc __attribute__((unused)),char *argv[]) /* regression tests */ mdev9044(); + encrypt_tmp_files= 1; + mdev10259(); + encrypt_tmp_files= 0; + my_end(0); return exit_status(); } |