summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Vojtovich <svoj@mariadb.org>2018-09-12 16:36:45 +0400
committerSergey Vojtovich <svoj@mariadb.org>2018-10-02 13:42:44 +0400
commitbad2f1569da57c4a81cc84ec2f4a79924df9c8d6 (patch)
tree9ec5b4596b163d275051f7de74736183a6c191c3
parentb9a5ff364466d2d1495352dd6c932d877923a614 (diff)
downloadmariadb-git-bad2f1569da57c4a81cc84ec2f4a79924df9c8d6.tar.gz
MDEV-17167 - InnoDB: Failing assertion: table->get_ref_count() == 0 uponmariadb-10.3.10
truncating a temporary table TRUNCATE expects only one TABLE instance (which is used by TRUNCATE itself) to be open. However this requirement wasn't enforced after "MDEV-5535: Cannot reopen temporary table". Fixed by closing unused table instances before performing TRUNCATE.
-rw-r--r--mysql-test/main/temp_table.result24
-rw-r--r--mysql-test/main/temp_table.test26
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_plist.h7
-rw-r--r--sql/sql_truncate.cc2
-rw-r--r--sql/temporary_tables.cc30
6 files changed, 89 insertions, 1 deletions
diff --git a/mysql-test/main/temp_table.result b/mysql-test/main/temp_table.result
index d1bec4a2af5..5de86e47fb1 100644
--- a/mysql-test/main/temp_table.result
+++ b/mysql-test/main/temp_table.result
@@ -548,3 +548,27 @@ DROP TABLE nonexisting_table, t1;
ERROR 42S02: Unknown table 'temp_db.nonexisting_table'
# Cleanup
DROP DATABASE temp_db;
+USE test;
+#
+# MDEV-17167 - InnoDB: Failing assertion: table->get_ref_count() == 0
+# upon truncating a temporary table
+#
+CREATE TEMPORARY TABLE t1(a INT) ENGINE=InnoDB;
+SELECT * FROM t1 AS t1a1, t1 AS t2a2;
+a a
+TRUNCATE TABLE t1;
+LOCK TABLES t1 WRITE;
+TRUNCATE TABLE t1;
+SELECT * FROM t1;
+a
+UNLOCK TABLES;
+LOCK TABLES t1 AS t1a1 WRITE, t1 AS t1a2 WRITE;
+TRUNCATE TABLE t1;
+SELECT * FROM t1 AS t1a1, t1 AS t1a2;
+a a
+UNLOCK TABLES;
+CREATE TABLE t2(a INT) ENGINE=InnoDB;
+LOCK TABLES t2 WRITE;
+TRUNCATE TABLE t1;
+UNLOCK TABLES;
+DROP TABLE t1, t2;
diff --git a/mysql-test/main/temp_table.test b/mysql-test/main/temp_table.test
index fb44362b537..a528e27b890 100644
--- a/mysql-test/main/temp_table.test
+++ b/mysql-test/main/temp_table.test
@@ -594,4 +594,30 @@ DROP TABLE nonexisting_table, t1;
--echo # Cleanup
DROP DATABASE temp_db;
+USE test;
+
+--echo #
+--echo # MDEV-17167 - InnoDB: Failing assertion: table->get_ref_count() == 0
+--echo # upon truncating a temporary table
+--echo #
+CREATE TEMPORARY TABLE t1(a INT) ENGINE=InnoDB;
+SELECT * FROM t1 AS t1a1, t1 AS t2a2;
+TRUNCATE TABLE t1;
+
+LOCK TABLES t1 WRITE;
+TRUNCATE TABLE t1;
+SELECT * FROM t1;
+UNLOCK TABLES;
+
+LOCK TABLES t1 AS t1a1 WRITE, t1 AS t1a2 WRITE;
+TRUNCATE TABLE t1;
+SELECT * FROM t1 AS t1a1, t1 AS t1a2;
+UNLOCK TABLES;
+
+CREATE TABLE t2(a INT) ENGINE=InnoDB;
+LOCK TABLES t2 WRITE;
+TRUNCATE TABLE t1;
+UNLOCK TABLES;
+
+DROP TABLE t1, t2;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 97c5bfa3c12..acd48b07900 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -4628,6 +4628,7 @@ public:
TMP_TABLE_SHARE* save_tmp_table_share(TABLE *table);
void restore_tmp_table_share(TMP_TABLE_SHARE *share);
+ void close_unused_temporary_table_instances(const TABLE_LIST *tl);
private:
/* Whether a lock has been acquired? */
diff --git a/sql/sql_plist.h b/sql/sql_plist.h
index bb9889cc534..4d279af7a0d 100644
--- a/sql/sql_plist.h
+++ b/sql/sql_plist.h
@@ -184,7 +184,12 @@ public:
list= &a;
current= a.m_first;
}
- /* Operator for it++ */
+ /**
+ Operator for it++
+
+ @note since we save next element pointer, caller may remove current element.
+ Such modification doesn't invalidate iterator.
+ */
inline T* operator++(int)
{
T *result= current;
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
index 5a6417880b3..bab9bb5e9ac 100644
--- a/sql/sql_truncate.cc
+++ b/sql/sql_truncate.cc
@@ -401,6 +401,8 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
/* In RBR, the statement is not binlogged if the table is temporary. */
binlog_stmt= !thd->is_current_stmt_binlog_format_row();
+ thd->close_unused_temporary_table_instances(table_ref);
+
error= handler_truncate(thd, table_ref, TRUE);
/*
diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
index f23ec7a1acc..1c8af5eaf66 100644
--- a/sql/temporary_tables.cc
+++ b/sql/temporary_tables.cc
@@ -1540,3 +1540,33 @@ void THD::unlock_temporary_tables()
DBUG_VOID_RETURN;
}
+
+/**
+ Close unused TABLE instances for given temporary table.
+
+ @param tl [IN] TABLE_LIST
+
+ Initial use case was TRUNCATE, which expects only one instance (which is used
+ by TRUNCATE itself) to be open. Most probably some ALTER TABLE variants and
+ REPAIR may have similar expectations.
+*/
+
+void THD::close_unused_temporary_table_instances(const TABLE_LIST *tl)
+{
+ TMP_TABLE_SHARE *share= find_tmp_table_share(tl);
+
+ if (share)
+ {
+ All_share_tables_list::Iterator tables_it(share->all_tmp_tables);
+
+ while (TABLE *table= tables_it++)
+ {
+ if (table->query_id == 0)
+ {
+ /* Note: removing current list element doesn't invalidate iterator. */
+ share->all_tmp_tables.remove(table);
+ free_temporary_table(table);
+ }
+ }
+ }
+}