Skip to content

Commit 820c4fc

Browse files
[8.4] PS-9159 : crash due to SELECT GLOBAL_TEMPORARY_TABLES when there is running DDL
https://perconadev.atlassian.net/browse/PS-9159 Problem: -------- When there are temporary tables created by ALTER, a concurrent SELECT * FROM information_schema.global_temporary_tables causes a crash. The temporary table object from ALTER is created using an intermediate name (#sql-ib* or *#P#p0#tmp) Later I_S query tries to clone the handler object of the temporary table to avoid concurrent usage of handler object by two threads (THDs). Handler::clone()/ha_innobase::clone() opens the cloned object again using handler::open()/ha_innobase::open(). This time, it tries to open with the temp table name. The corresponding dd::Table object is valid. So it tries to load the object from DD and load to cache. A table object with different name (already exists in cache) but with same table_id. This causes the crash. Example data from core/rr: rr) p table->id $21 = 1075 (rr) p table->name $22 = {m_name = 0x7f575ec556c0 "test/#sql-3147d6_8#p#p0"} (rr) p table2->name $23 = {m_name = 0x7f575428f5d0 "test/b#p#p0#tmp"} (rr) p table2->id $24 = 1075 Fix: ---- Since commit 4480f97 (8.0.1), handler::clone()'s behaviour changed. It now assigns the TABLE* object to handler. This changes the behaviour of the handler::clone(). If TABLE* exists in the handler, it will do handler::open(). From the I_S query path, we can identify the tables that are never opened in SE from TABLE*->db_stat property. If we have a temp table, that is not opened in SE, do not try to clone the handler. Actually, handler::clone() is useless for temp tables from ALTERs anyway. We always return NULL index stats for tmp tables from DDLs (5.7 behaviour) anyway. (cherry picked from commit bc70691)
1 parent eab2f47 commit 820c4fc

File tree

5 files changed

+145
-3
lines changed

5 files changed

+145
-3
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--source include/count_sessions.inc
2+
3+
if (!$create_table_query) {
4+
--die "please set the variable $create_table_query to a valid query"
5+
}
6+
7+
if (!$alter_table_query) {
8+
--die "please set the variable $alter_table_query to a valid query"
9+
}
10+
11+
if (!$drop_table_query) {
12+
--die "please set the variable $alter_table_query to a valid query"
13+
}
14+
15+
connect (con1, localhost, root,,);
16+
--connection default
17+
--eval $create_table_query
18+
19+
20+
SET DEBUG_SYNC="alter_before_close_temp_tables SIGNAL enter_alter_table_before_close_temp_tables WAIT_FOR continue_commit_alter_ddl";
21+
--send_eval $alter_table_query
22+
23+
--connection con1
24+
SET DEBUG_SYNC="now WAIT_FOR enter_alter_table_before_close_temp_tables";
25+
--replace_column 1 SESSION_ID 3 TABLE_NAME 5 NAME
26+
SELECT * FROM information_schema.global_temporary_tables;
27+
SET DEBUG_SYNC="now SIGNAL continue_commit_alter_ddl";
28+
--connection default
29+
--reap
30+
--eval $drop_table_query
31+
--disconnect con1
32+
33+
--source include/wait_until_count_sessions.inc
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
create table b(id int primary key) PARTITION BY HASH(id) PARTITIONS 8;
2+
SET DEBUG_SYNC="alter_before_close_temp_tables SIGNAL enter_alter_table_before_close_temp_tables WAIT_FOR continue_commit_alter_ddl";
3+
alter table b rebuild partition p0,p1,p2,p3;
4+
SET DEBUG_SYNC="now WAIT_FOR enter_alter_table_before_close_temp_tables";
5+
SELECT * FROM information_schema.global_temporary_tables;
6+
SESSION_ID TABLE_SCHEMA TABLE_NAME ENGINE NAME TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH INDEX_LENGTH CREATE_TIME UPDATE_TIME
7+
SESSION_ID test TABLE_NAME InnoDB NAME 0 0 0 0 NULL NULL
8+
SET DEBUG_SYNC="now SIGNAL continue_commit_alter_ddl";
9+
drop table b;
10+
create table t1(id int primary key);
11+
SET DEBUG_SYNC="alter_before_close_temp_tables SIGNAL enter_alter_table_before_close_temp_tables WAIT_FOR continue_commit_alter_ddl";
12+
optimize table t1;
13+
SET DEBUG_SYNC="now WAIT_FOR enter_alter_table_before_close_temp_tables";
14+
SELECT * FROM information_schema.global_temporary_tables;
15+
SESSION_ID TABLE_SCHEMA TABLE_NAME ENGINE NAME TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH INDEX_LENGTH CREATE_TIME UPDATE_TIME
16+
SESSION_ID test TABLE_NAME InnoDB NAME 0 0 0 0 NULL NULL
17+
SET DEBUG_SYNC="now SIGNAL continue_commit_alter_ddl";
18+
Table Op Msg_type Msg_text
19+
test.t1 optimize note Table does not support optimize, doing recreate + analyze instead
20+
test.t1 optimize status OK
21+
drop table t1;
22+
create table t1(id int primary key,b INT);
23+
SET DEBUG_SYNC="alter_before_close_temp_tables SIGNAL enter_alter_table_before_close_temp_tables WAIT_FOR continue_commit_alter_ddl";
24+
alter table t1 add index k1(b);
25+
SET DEBUG_SYNC="now WAIT_FOR enter_alter_table_before_close_temp_tables";
26+
SELECT * FROM information_schema.global_temporary_tables;
27+
SESSION_ID TABLE_SCHEMA TABLE_NAME ENGINE NAME TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH INDEX_LENGTH CREATE_TIME UPDATE_TIME
28+
SESSION_ID test TABLE_NAME InnoDB NAME 0 0 0 0 NULL NULL
29+
SET DEBUG_SYNC="now SIGNAL continue_commit_alter_ddl";
30+
drop table t1;
31+
create table t1(id int primary key,b INT);
32+
SET DEBUG_SYNC="alter_before_close_temp_tables SIGNAL enter_alter_table_before_close_temp_tables WAIT_FOR continue_commit_alter_ddl";
33+
alter table t1 add column c INT;
34+
SET DEBUG_SYNC="now WAIT_FOR enter_alter_table_before_close_temp_tables";
35+
SELECT * FROM information_schema.global_temporary_tables;
36+
SESSION_ID TABLE_SCHEMA TABLE_NAME ENGINE NAME TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH INDEX_LENGTH CREATE_TIME UPDATE_TIME
37+
SESSION_ID test TABLE_NAME InnoDB NAME 0 0 0 0 NULL NULL
38+
SET DEBUG_SYNC="now SIGNAL continue_commit_alter_ddl";
39+
drop table t1;
40+
create table t1(partition_id tinyint NOT NULL , cal_date date NOT NULL, value CHAR(100), PRIMARY KEY(partition_id, cal_date)) ENGINE=InnoDB PARTITION BY LIST COLUMNS(partition_id, cal_date) (PARTITION p0 VALUES IN ((0,'2024-06-01'), (0, '2024-07-01')) ENGINE=InnoDB, PARTITION p1 VALUES IN ((1, '2024-08-01'),(1, '2024-09-01')) ENGINE=InnoDB);
41+
SET DEBUG_SYNC="alter_before_close_temp_tables SIGNAL enter_alter_table_before_close_temp_tables WAIT_FOR continue_commit_alter_ddl";
42+
ALTER TABLE t1 ADD PARTITION (PARTITION p3 VALUES IN ((3, '2024-10-01')));
43+
SET DEBUG_SYNC="now WAIT_FOR enter_alter_table_before_close_temp_tables";
44+
SELECT * FROM information_schema.global_temporary_tables;
45+
SESSION_ID TABLE_SCHEMA TABLE_NAME ENGINE NAME TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH INDEX_LENGTH CREATE_TIME UPDATE_TIME
46+
SESSION_ID test TABLE_NAME InnoDB NAME 0 0 0 0 NULL NULL
47+
SET DEBUG_SYNC="now SIGNAL continue_commit_alter_ddl";
48+
DROP TABLE t1;
49+
create table t1(partition_id tinyint NOT NULL , cal_date date NOT NULL, value CHAR(100), PRIMARY KEY(partition_id, cal_date)) ENGINE=InnoDB PARTITION BY LIST COLUMNS(partition_id, cal_date) (PARTITION p0 VALUES IN ((0,'2024-06-01'), (0, '2024-07-01')) ENGINE=InnoDB, PARTITION p1 VALUES IN ((1, '2024-08-01'),(1, '2024-09-01')) ENGINE=InnoDB);
50+
SET DEBUG_SYNC="alter_before_close_temp_tables SIGNAL enter_alter_table_before_close_temp_tables WAIT_FOR continue_commit_alter_ddl";
51+
ALTER TABLE t1 DROP PARTITION p0;
52+
SET DEBUG_SYNC="now WAIT_FOR enter_alter_table_before_close_temp_tables";
53+
SELECT * FROM information_schema.global_temporary_tables;
54+
SESSION_ID TABLE_SCHEMA TABLE_NAME ENGINE NAME TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH INDEX_LENGTH CREATE_TIME UPDATE_TIME
55+
SESSION_ID test TABLE_NAME InnoDB NAME 0 0 0 0 NULL NULL
56+
SET DEBUG_SYNC="now SIGNAL continue_commit_alter_ddl";
57+
DROP TABLE t1;
58+
CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255), body TEXT) ENGINE=InnoDB;
59+
SET DEBUG_SYNC="alter_before_close_temp_tables SIGNAL enter_alter_table_before_close_temp_tables WAIT_FOR continue_commit_alter_ddl";
60+
ALTER TABLE t1 ADD FULLTEXT(title, body);
61+
SET DEBUG_SYNC="now WAIT_FOR enter_alter_table_before_close_temp_tables";
62+
SELECT * FROM information_schema.global_temporary_tables;
63+
SESSION_ID TABLE_SCHEMA TABLE_NAME ENGINE NAME TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH INDEX_LENGTH CREATE_TIME UPDATE_TIME
64+
SESSION_ID test TABLE_NAME InnoDB NAME 0 0 0 0 NULL NULL
65+
SET DEBUG_SYNC="now SIGNAL continue_commit_alter_ddl";
66+
Warnings:
67+
Warning 124 InnoDB rebuilding table to add column FTS_DOC_ID
68+
DROP TABLE t1;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--source include/have_debug.inc
2+
--source include/have_debug_sync.inc
3+
4+
--let $create_table_query=create table b(id int primary key) PARTITION BY HASH(id) PARTITIONS 8
5+
--let $alter_table_query=alter table b rebuild partition p0,p1,p2,p3
6+
--let $drop_table_query=drop table b
7+
--source ../include/global_temp_table_alter.inc
8+
9+
--let $create_table_query=create table t1(id int primary key)
10+
--let $alter_table_query=optimize table t1
11+
--let $drop_table_query=drop table t1
12+
--source ../include/global_temp_table_alter.inc
13+
14+
--let $create_table_query=create table t1(id int primary key,b INT)
15+
--let $alter_table_query=alter table t1 add index k1(b)
16+
--let $drop_table_query=drop table t1
17+
--source ../include/global_temp_table_alter.inc
18+
19+
--let $create_table_query=create table t1(id int primary key,b INT)
20+
--let $alter_table_query=alter table t1 add column c INT
21+
--let $drop_table_query=drop table t1
22+
--source ../include/global_temp_table_alter.inc
23+
24+
--let $create_table_query=create table t1(partition_id tinyint NOT NULL , cal_date date NOT NULL, value CHAR(100), PRIMARY KEY(partition_id, cal_date)) ENGINE=InnoDB PARTITION BY LIST COLUMNS(partition_id, cal_date) (PARTITION p0 VALUES IN ((0,'2024-06-01'), (0, '2024-07-01')) ENGINE=InnoDB, PARTITION p1 VALUES IN ((1, '2024-08-01'),(1, '2024-09-01')) ENGINE=InnoDB)
25+
--let $alter_table_query= ALTER TABLE t1 ADD PARTITION (PARTITION p3 VALUES IN ((3, '2024-10-01')))
26+
--let $drop_table_query=DROP TABLE t1
27+
--source ../include/global_temp_table_alter.inc
28+
29+
--let $create_table_query=create table t1(partition_id tinyint NOT NULL , cal_date date NOT NULL, value CHAR(100), PRIMARY KEY(partition_id, cal_date)) ENGINE=InnoDB PARTITION BY LIST COLUMNS(partition_id, cal_date) (PARTITION p0 VALUES IN ((0,'2024-06-01'), (0, '2024-07-01')) ENGINE=InnoDB, PARTITION p1 VALUES IN ((1, '2024-08-01'),(1, '2024-09-01')) ENGINE=InnoDB)
30+
--let $alter_table_query= ALTER TABLE t1 DROP PARTITION p0
31+
--let $drop_table_query=DROP TABLE t1
32+
--source ../include/global_temp_table_alter.inc
33+
34+
--let $create_table_query=CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255), body TEXT) ENGINE=InnoDB
35+
--let $alter_table_query=ALTER TABLE t1 ADD FULLTEXT(title, body)
36+
--let $drop_table_query=DROP TABLE t1
37+
--source ../include/global_temp_table_alter.inc

sql/sql_show.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4474,12 +4474,13 @@ static int store_temporary_table_record(THD *thd, TABLE *table,
44744474

44754475
/* We have only one handler object for a temp table globally and it might
44764476
be in use by other thread. Do not trash it by invoking handler methods on
4477-
it but rather clone it. */
4478-
if (file) {
4477+
it but rather clone it. if db_stat is 0, the table is not opened in SE,
4478+
do not clone it */
4479+
if (file && tmp_table->db_stat != 0) {
44794480
file = file->clone(tmp_table->s->normalized_path.str, mem_root);
44804481
}
44814482

4482-
if (file) {
4483+
if (file && tmp_table->db_stat != 0) {
44834484
MYSQL_TIME time;
44844485

44854486
/**

sql/sql_table.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14075,6 +14075,9 @@ static bool mysql_inplace_alter_table(
1407514075
close_all_tables_for_name(thd, table->s, false, nullptr);
1407614076
table_list->table = table = nullptr;
1407714077
reopen_tables = true;
14078+
14079+
DEBUG_SYNC(thd, "alter_before_close_temp_tables");
14080+
1407814081
close_temporary_table(thd, altered_table, true, false);
1407914082
rollback_needs_dict_cache_reset = true;
1408014083

0 commit comments

Comments
 (0)