mariadb/mysql-test/main/global_temporary_table.test
Nikita Malyavin f2d831f502 MDEV-37381 SIGSEGV in mysql_ha_close_table after HANDLER OPEN of GTT
The crash happens because of HANDLER CLOSE happened after GTT was
deleted on commit, which, by the way, happened implicitly after
HANDLER OPEN.

The consequences of using open handler after commit, i.e. across
different transactions, are truly unclear for any table:
the handler should require at least calling ha_reset() to re-assign to a
new transaction handler. Probably, that's not the only questionable
consequence.

For Global temporary tables, we should either close the handlers
implicitly on commit, or fail committing if an open handler exists.

If to fail committing, then global_temporary_tp has to be transformed
into 2pc, which would potentially slow the transactions.

This patch favors implicitly closes the handlers sacrificing strictness
of the behavior in favor of leaving global_temporary_tp 1pc-capable.
2025-08-20 21:10:36 +02:00

614 lines
14 KiB
Text

--source include/have_partition.inc
--source include/have_innodb.inc
--source include/have_debug_sync.inc
--source include/not_embedded.inc
--echo # Safety
set lock_wait_timeout= 5;
--error ER_PARSE_ERROR
create table t1 (x int, t text) on commit preserve rows;
create global temporary table t1 (x int, t text) on commit preserve rows;
select TABLE_TYPE from information_schema.tables where table_name = 't1';
--error ER_BAD_TABLE_ERROR
drop temporary table t1;
show create table t1;
flush tables;
show create table t1;
insert into t1 values(1, 'one');
select TABLE_TYPE from information_schema.tables where table_name = 't1';
show table status where temporary='Y';
let $show = query_get_value('show create table t1', 'Create Table', 1);
truncate t1;
let $show1 = query_get_value('show create table t1', 'Create Table', 1);
--if($show != $show1) {
--die SHOW CREATE output mismatch
--}
insert into t1 values(1, 'one');
select * from t1;
--connect (con1,localhost,root,,)
select * from t1 join t1 as t2;
insert into t1 values(2, 'two');
select * from t1;
select * from t1 join t1 as t2;
--connection default
select * from t1;
select * from t1 join t1 as t2;
--error ER_LOCK_WAIT_TIMEOUT
alter table t1 add y int;
--error ER_LOCK_WAIT_TIMEOUT
drop table t1;
--connection con1
set debug_sync= 'thread_end signal closed';
--disconnect con1
--connection default
set debug_sync= 'now wait_for closed';
--error ER_BAD_TABLE_ERROR
drop temporary table t1;
select * from t1;
--error ER_LOCK_WAIT_TIMEOUT
alter table t1 add j int;
--error ER_LOCK_WAIT_TIMEOUT
drop table t1;
truncate table t1;
alter table t1 add j int;
show create table t1;
drop table t1;
eval $show;
show create table t1;
drop table t1;
--error ER_NO_SUCH_TABLE
show create table t1;
create global temporary table t1 (x int, t text) on commit preserve rows;
show create table t1;
drop table t1;
create global temporary table t (x int) on commit delete rows;
insert into t values (1);
select * from t;
begin;
insert into t values (1);
select * from t;
commit;
select * from t;
--connect (con1,localhost,root,,)
select * from t;
insert into t values (1);
select * from t;
begin;
insert into t values (1);
select * from t;
commit;
select * from t;
set debug_sync= 'thread_end signal closed';
--disconnect con1
--connection default
set debug_sync= 'now wait_for closed';
drop table t;
create global temporary table t (x int) on commit PRESERVE rows;
insert into t values (1);
select * from t;
begin;
insert into t values (1);
select * from t;
commit;
select * from t;
--connect (con1,localhost,root,,)
select * from t;
insert into t values (1);
select * from t;
begin;
insert into t values (1);
select * from t;
commit;
select * from t;
set debug_sync= 'thread_end signal closed';
--disconnect con1
--connection default
set debug_sync= 'now wait_for closed';
--error ER_LOCK_WAIT_TIMEOUT
drop table t;
truncate t;
drop table t;
create global temporary table t (x int) on commit PRESERVE rows;
--connect (con1,localhost,root,,)
insert t values(1);
--connection default
--error ER_LOCK_WAIT_TIMEOUT
alter table t add j int;
--error ER_LOCK_WAIT_TIMEOUT
set statement lock_wait_timeout=0 for drop table t;
--connection con1
set debug_sync= 'thread_end signal closed';
--disconnect con1
--connection default
set debug_sync= 'now wait_for closed';
drop table t;
--error ER_CANT_USE_WITH_GLOBAL_TEMPORARY_TABLE
create global temporary table t (x int) partition by hash(x);
--error ER_CANT_USE_WITH_GLOBAL_TEMPORARY_TABLE
create global temporary table t (x int) with system versioning;
--error ER_CANT_USE_WITH_GLOBAL_TEMPORARY_TABLE
create global temporary table t (x int, y int, foreign key (x) references t(y));
create global temporary table t (x int);
--error ER_ALTER_OPERATION_NOT_SUPPORTED
alter table t force, lock=none, algorithm=copy;
--error ER_ALTER_OPERATION_NOT_SUPPORTED
alter table t rename column x to y, lock=none, algorithm=inplace;
drop table t;
--echo ### VIEWS
create global temporary table t (x int);
--connect (con1,localhost,root,,)
begin;
insert into t values(1);
--connection default
create view v as select * from t;
begin;
insert into t values(2);
select * from v;
--connection con1
select * from v;
commit;
--echo # Now table was truncated
select * from v;
--connection default
commit;
drop view v;
drop table t;
--echo ### AS SELECT
create global temporary table t1(x int) on commit preserve rows
as select 1 as 'x';
select * from t1;
create global temporary table t2 on commit preserve rows
as values(5),(6),(7);
select * from t2;
--connection con1
select * from t1;
select `5` as 'empty' from t2;
truncate t1;
truncate t2;
--connection default
truncate t1;
truncate t2;
drop table t1;
drop table t2;
create global temporary table t(x int) on commit delete rows
as select 1 as 'x';
--echo # Implicit commit deletes data
select * from t;
drop table t;
--error ER_DUP_ENTRY
create global temporary table t2(`5` int primary key) as values(5),(5),(5);
select `5` as col from t2;
drop table t2;
--echo ### CREATE TABLE ... LIKE
create table t1(x int primary key);
create global temporary table t2 like t1;
show create table t2;
drop table t1;
create table t1 like t2;
show create table t1;
drop table t1;
create global temporary table t3 like t2;
show create table t3;
drop table t2;
drop table t3;
create global temporary table t2(x int primary key) on commit preserve rows;
create table t1 like t2;
show create table t1;
create global temporary table t3 like t2;
show create table t3;
create temporary table t4 like t2;
show create table t4;
truncate t2; # Unfortunately, we should do this
drop table t1;
drop table t2;
drop table t3;
drop table t4;
--echo ### RENAME
create global temporary table t2(x int primary key) on commit preserve rows;
--connection con1
insert t2 values (1);
--connection default
--error ER_LOCK_WAIT_TIMEOUT
rename table t2 NOWAIT to tx;
--connection con1
truncate t2;
--connection default
select * from t2;
--error ER_LOCK_WAIT_TIMEOUT
rename table t2 to tx;
truncate t2;
rename table t2 to tx;
--error ER_NO_SUCH_TABLE
show create table t2;
show create table tx;
drop table tx;
--echo ### FLUSH
create global temporary table t(x int primary key) on commit preserve rows;
flush table t;
insert t values (1);
flush table t;
select * from t;
truncate t;
flush table t;
drop table t;
--echo # Multi-table DML
create table t(x int, txt text);
create global temporary table gtt(x int) on commit preserve rows;
insert t values (1, 'one'), (2,'two'), (3, 'three'), (4, 'four');
insert gtt values (2),(3),(5);
--connection con1
insert gtt values (4),(6);
--connection default
update t, gtt set t.txt= CONCAT(t.txt, ' tables') where t.x = gtt.x;
--connection con1
update t, gtt set t.txt= CONCAT(t.txt, ' databases') where t.x = gtt.x;
truncate gtt;
--connection default
select * from t;
truncate gtt;
drop table t;
drop table gtt;
create table t(x int);
lock table t write;
create or replace table t(x int);
--error ER_CANT_CREATE_TABLE
create or replace global temporary table t(t text);
unlock table;
drop table t;
--echo ### PS Second execution
create global temporary table t(x int) on commit preserve rows;
insert into t values (1),(2),(3);
prepare stmt from 'update t set x = x + 1 where x > 2';
prepare ins_stmt from 'insert into t values (1),(2),(3)';
execute stmt;
execute stmt;
select * from t;
truncate table t;
insert into t values (1),(2),(3);
execute ins_stmt;
execute ins_stmt;
select * from t;
deallocate prepare stmt;
--connection con1
select * from t;
truncate t;
--connection default
truncate t;
drop table t;
create global temporary table t(x int) on commit preserve rows;
execute ins_stmt;
select * from t;
deallocate prepare ins_stmt;
prepare stmt from 'update t set x = x + 1 where x > 2';
prepare ins_stmt from 'insert into t values (1),(2),(3)';
insert into t values (1),(2),(3);
execute stmt;
execute stmt;
select * from t;
truncate table t;
execute ins_stmt;
execute ins_stmt;
select * from t;
--connection con1
select * from t;
truncate t;
--connection default
deallocate prepare stmt;
deallocate prepare ins_stmt;
truncate t;
drop table t;
--echo # Global temporary tables exist in the global tables namespace.
create global temporary table t(x int) on commit preserve rows;
insert t values (111);
create temporary table t(y int);
insert t values (222);
select * from t;
show create table t;
truncate t;
select * from t;
drop table t;
show create table t;
select * from t;
truncate table t;
drop table t;
create global temporary table t(x int) on commit preserve rows;
insert t values (1111);
create temporary table t(y int);
insert t values (222);
alter table t add z int;
select * from t;
rename table t to t1;
select * from t1;
drop table t1;
select * from t;
truncate t;
drop table t;
--echo # Invert the creation order: local temporary table is created first
create temporary table t(y int);
create global temporary table t(x int) on commit preserve rows select 1111 as x;
insert t values (222);
alter table t add z int;
select * from t;
rename table t to t1;
select * from t1;
select * from t;
drop table t1;
truncate t;
drop table t;
--echo # LOCK TABLES
create global temporary table t(x int) on commit preserve rows;
lock tables t write;
insert t values(1);
select * from t;
truncate t;
drop table t;
--error ER_NO_SUCH_TABLE
select * from t;
unlock tables;
create global temporary table t(x int) on commit preserve rows;
insert t values(1);
lock tables t write;
select * from t;
unlock tables;
truncate t;
drop table t;
--echo # Write lock works
create global temporary table t(x int) on commit preserve rows;
lock tables t write;
insert t values(1);
--connection con1
--error ER_LOCK_WAIT_TIMEOUT
set statement lock_wait_timeout= 0 for
select * from t nowait;
--error ER_LOCK_WAIT_TIMEOUT
lock tables t write nowait;
--connection default
truncate t;
drop table t;
--echo # Global descriptor is locked for read, but it allows inserting
--echo # into local copies
create global temporary table t(x int) on commit preserve rows;
lock tables t read;
insert t values(1);
--connection con1
select * from t;
--connection default
select * from t;
--connection con1
truncate t;
--connection default
truncate t;
unlock tables;
drop table t;
create global temporary table t(x int) on commit preserve rows;
lock tables t read;
--connection con1
lock tables t read nowait;
--connection default
insert t values(1);
--connection con1
select * from t;
--connection default
select * from t;
--connection con1
truncate t;
unlock tables;
--connection default
truncate t;
unlock tables;
drop table t;
--echo # mariabackup
create global temporary table t(x int) on commit preserve rows;
insert t values (1), (2), (3);
let $mysqldumpfile = $MYSQLTEST_VARDIR/tmp/mysqldumpfile.sql;
--exec $MYSQL_DUMP test t > $mysqldumpfile
truncate t;
drop table t;
--exec $MYSQL test < $mysqldumpfile
show create table t;
select * from t;
truncate t;
drop table t;
--echo # XA COMMIT/ROLLBACK
create global temporary table t(x int) on commit delete rows;
xa start "trx";
insert t values (1), (2), (3);
select * from t;
xa end "trx";
xa prepare "trx";
xa commit "trx";
select * from t;
xa start "trx";
insert t values (1), (2), (3);
select * from t;
xa end "trx";
xa rollback "trx";
select * from t;
drop table t;
--echo # MDEV-37369 SIGSEGV on NEXTVAL from Global temporary table
create global temporary table t(c int);
--error ER_NOT_SEQUENCE
select nextval(t);
drop table t;
--echo # MDEV-37383 crash in end_read_record after REPAIR of Global temporary table
create global temporary table t (c int) engine=innodb;
repair local table t;
delete from t;
truncate t;
drop table t;
create global temporary table t (c int) engine=innodb on commit preserve rows;
repair local table t;
delete from t;
truncate t;
drop table t;
create global temporary table t (c int) engine=innodb;
optimize table t;
analyze table t;
check table t;
delete from t;
drop table t;
create global temporary table t (c int) engine=innodb on commit preserve rows;
optimize table t;
analyze table t;
check table t;
delete from t;
truncate t;
drop table t;
--echo MDEV-37368 Assertion failed in close_thread_tables on UPDATE referring to bad field
create global temporary table t(c int);
--error ER_BAD_FIELD_ERROR
update t set foo= 1;
drop table t;
--echo MDEV-37378 SIGSEGV or Assertion failed on CREATE TRIGGER
create global temporary table t (c int);
--error ER_TRG_ON_VIEW_OR_TEMP_TABLE
create trigger tr after insert on t for each row insert into t values (1);
drop table t;
--echo # MDEV-37394 SIGSEGV in handler::ha_external_lock on CREATE GTT ... AS,
--echo # CREATE GTT ... ENGINE=INNODB SELECT, ASAN heap-use-after-free in unlock_external
create global temporary table t (c int) engine=innodb as select 1;
drop table t;
--echo # MDEV-37379 Assertion `index->is_readable()' failed on REPLACE DELAYED
--echo # DELAYED is transformed to a normal write when stmt logging is enabled.
set @save_binlog_format=@@global.binlog_format;
set global binlog_format=row;
create global temporary table t (c int) engine=innodb;
--error ER_DELAYED_NOT_SUPPORTED
replace delayed t values (0);
drop table t;
create global temporary table t (c int) engine=myisam;
--error ER_DELAYED_NOT_SUPPORTED
replace delayed t values (1);
drop table t;
set global binlog_format=@save_binlog_format;
--echo # MDEV-37381 SIGSEGV in mysql_ha_close_table after HANDLER OPEN of GTT
create global temporary table t (c int);
start transaction ;
handler t open as t;
insert t values(1),(2);
handler t read first;
commit;
--error ER_UNKNOWN_TABLE
handler t close;
drop table t;
create global temporary table t (c int);
start transaction ;
handler t open as t;
insert t values(1),(2);
handler t read first;
handler t close;
commit;
drop table t;
create global temporary table t (c int);
handler t open as t;
--error ER_TABLE_EXISTS_ERROR
create table t (c int);
--echo # The handler was closed on implicit commit after HANDLER OPEN
--error ER_UNKNOWN_TABLE
handler t close;
drop table t;
--disconnect con1