From fa8cf408b907bde6c13e1470811f05d219c60df3 Mon Sep 17 00:00:00 2001 From: PranavKTiwari Date: Fri, 1 May 2026 08:56:54 +0530 Subject: [PATCH 1/3] MDEV-38839: Fix assertion in close_thread_tables on CREATE TABLE...SELECT FOR UPDATE with MyISAM temp table in MIXED binlog mode FOR UPDATE on a MyISAM table acquires TL_WRITE due to lack of row-level locking. In decide_logging_format(), this caused STMT_WRITES_TEMP_NON_TRANS_TABLE to be set for a temp table that was only being read, not written to. This incorrectly set MODIFIED_NON_TRANS_TABLE via mark_modified_non_trans_temp_table() in MYSQL_BIN_LOG::write(), which blocked binlog_truncate_trx_cache() in MIXED mode, leaving row events stranded in the cache and triggering the assertion: Fix: In decide_logging_format(), gate the write flag assignment on tbl->updating, which is false for FOR UPDATE (read-only access with write lock) and true for genuine writes (INSERT/UPDATE/DELETE). Sequences are handled separately via tbl->sequence since SELECT NEXT VALUE genuinely modifies the sequence table internally but has tbl->updating=false. Note: SELECT...FOR UPDATE locking is not replicated to the slave. In MIXED mode the statement is logged as row events representing the resulting data changes. The slave replays only those row events so FOR UPDATE locking semantics are irrelevant on the slave and are not affected by this fix. --- .../suite/binlog/r/binlog_bug38839.result | 11 +++++++ .../suite/binlog/t/binlog_bug38839.test | 17 ++++++++++ sql/sql_class.cc | 31 ++++++++++++++----- 3 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 mysql-test/suite/binlog/r/binlog_bug38839.result create mode 100644 mysql-test/suite/binlog/t/binlog_bug38839.test diff --git a/mysql-test/suite/binlog/r/binlog_bug38839.result b/mysql-test/suite/binlog/r/binlog_bug38839.result new file mode 100644 index 0000000000000..d68652f54bb93 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_bug38839.result @@ -0,0 +1,11 @@ +SET sql_mode=''; +CREATE TEMPORARY TABLE t ENGINE=MyISAM AS SELECT @a AS c; +INSERT INTO t VALUES (0xABB0); +SET autocommit=OFF; +CREATE TABLE t (c INT PRIMARY KEY) ENGINE=MyISAM SELECT * FROM t FOR UPDATE; +ERROR 23000: Duplicate entry '0' for key 'PRIMARY' +SET autocommit=ON; +DROP TEMPORARY TABLE IF EXISTS t; +DROP TABLE IF EXISTS t; +Warnings: +Note 1051 Unknown table 'test.t' diff --git a/mysql-test/suite/binlog/t/binlog_bug38839.test b/mysql-test/suite/binlog/t/binlog_bug38839.test new file mode 100644 index 0000000000000..7e5923c10d44d --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_bug38839.test @@ -0,0 +1,17 @@ +# Test case for MDEV-38839 +# Assertion `(thd->state_flags & Open_tables_state::BACKUPS_AVAIL) || +# !thd->has_pending_row_events()' failed in close_thread_tables +# on CREATE TABLE ... SELECT * FROM temp_table FOR UPDATE in MIXED binlog mode + +--source include/have_log_bin.inc +--source include/have_binlog_format_mixed.inc + +SET sql_mode=''; +CREATE TEMPORARY TABLE t ENGINE=MyISAM AS SELECT @a AS c; +INSERT INTO t VALUES (0xABB0); +SET autocommit=OFF; +--error ER_DUP_ENTRY +CREATE TABLE t (c INT PRIMARY KEY) ENGINE=MyISAM SELECT * FROM t FOR UPDATE; +SET autocommit=ON; +DROP TEMPORARY TABLE IF EXISTS t; +DROP TABLE IF EXISTS t; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 42723fb6caf62..23a69bf8b5bf9 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -7130,7 +7130,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) } } - if (tbl->lock_type >= TL_FIRST_WRITE) + if (tbl->updating || tbl->sequence) { bool trans; if (prev_write_table && prev_write_table->file->ht != @@ -7147,12 +7147,29 @@ int THD::decide_logging_format(TABLE_LIST *tables) trans= table->file->has_transactions(); - if (share->tmp_table) - lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TEMP_TRANS_TABLE : - LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE); - else - lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TRANS_TABLE : - LEX::STMT_WRITES_NON_TRANS_TABLE); + /* + For SELECT ... FOR UPDATE, tbl->updating is false because the + table is only locked for reading, not actually being modified. + MyISAM has no row-level locking so FOR UPDATE escalates to + TL_WRITE, but this does not mean the table is being written to. + Sequences are an exception: even though tbl->updating is false + for SELECT NEXT VALUE FOR s, the sequence table is genuinely + modified internally, which is indicated by tbl->sequence. + Only set the write flag when the table is actually being written + to (tbl->updating) or is a sequence (tbl->sequence), to avoid + incorrectly setting MODIFIED_NON_TRANS_TABLE which would block + binlog_truncate_trx_cache() in MIXED mode and cause an assertion + in close_thread_tables(). + */ + // if (tbl->updating || tbl->sequence) + // { + if (share->tmp_table) + lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TEMP_TRANS_TABLE : + LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE); + else + lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TRANS_TABLE : + LEX::STMT_WRITES_NON_TRANS_TABLE); + // } flags_write_all_set &= flags; flags_write_some_set |= flags; From 1660c345dd8d9df7b5ec7c0455f94f00114ffb2a Mon Sep 17 00:00:00 2001 From: PranavKTiwari Date: Fri, 8 May 2026 11:39:30 +0530 Subject: [PATCH 2/3] when the locking level is TL_READ updating flag is set to false. --- sql/sql_table.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 310d5e9784f16..6d7f6e19ef3c5 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -10913,6 +10913,7 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, if (online) { table_list->lock_type= TL_READ; + table_list->updating = false; } enum_tx_isolation iso_level_initial= thd->tx_isolation; From e6a40c7d5a92da7ea1fc391f1c3a2f851755b29e Mon Sep 17 00:00:00 2001 From: PranavKTiwari Date: Fri, 8 May 2026 12:40:29 +0530 Subject: [PATCH 3/3] If statment is for sequenc, set updating to true. --- sql/sql_class.cc | 2 +- sql/sql_parse.cc | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 23a69bf8b5bf9..cd126a31d1807 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -7130,7 +7130,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) } } - if (tbl->updating || tbl->sequence) + if (tbl->updating) { bool trans; if (prev_write_table && prev_write_table->file->ht != diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5dbd26c406e06..48a2cdfd9337d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -8185,6 +8185,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, if (!ptr->table_name.str) DBUG_RETURN(0); // EOM + if (ptr->sequence) + { + ptr->updating= true; + } + /* check that used name is unique. Sequences are ignored */ if (lock_type != TL_IGNORE && !ptr->sequence) {