commit 33d91d97055967344546fd5a6321382da256d831 Author: shimengchu.smc Date: Wed Oct 9 15:01:48 2024 +0800 [Bugifx] to#60113812 Incorrect assert if use semi consistent when table lock full Analysis ======== In function `row_search_mvcc`, if `use_semi_consistent` is true, call `sel_set_rec_lock` with `SELECT_SKIP_LOCKED` mode and `DB_SKIP_LOCKED` is excepted to be returned. However, `sel_set_rec_lock` may return `DB_LOCK_TABLE_FULL` if locks exhaust the BP so that less than 25 % of BP is available. So the following `switch` of row_search_mvcc will enter `default:`, assert !use_semi_consistent failed and crash. Fix ======== Correct the assert expression. diff --git a/mysql-test/r/bugfix_use_semi_consistent_when_table_lock_full.result b/mysql-test/r/bugfix_use_semi_consistent_when_table_lock_full.result new file mode 100644 index 00000000000..92c64b49bf9 --- /dev/null +++ b/mysql-test/r/bugfix_use_semi_consistent_when_table_lock_full.result @@ -0,0 +1,8 @@ +SET SESSION transaction_isolation="READ-COMMITTED"; +CREATE TABLE t1(id INT, name VARCHAR(200)); +INSERT INTO t1 VALUES(1, "before"); +SET GLOBAL debug="+d,simulate_lock_table_full"; +UPDATE t1 SET name="after" WHERE id=1; +ERROR HY000: The total number of locks exceeds the lock table size +DROP TABLE t1; +SET GLOBAL debug="-d,simulate_lock_table_full"; diff --git a/mysql-test/t/bugfix_use_semi_consistent_when_table_lock_full.test b/mysql-test/t/bugfix_use_semi_consistent_when_table_lock_full.test new file mode 100644 index 00000000000..d20dbc80d31 --- /dev/null +++ b/mysql-test/t/bugfix_use_semi_consistent_when_table_lock_full.test @@ -0,0 +1,24 @@ +--source include/have_debug.inc + +##### Prepare +# Try to use semi-consistent read: +# 1. transaction_isolation="READ-COMMITTED" +# 2. do update +# 3. mustn't use the unique search +SET SESSION transaction_isolation="READ-COMMITTED"; +CREATE TABLE t1(id INT, name VARCHAR(200)); +INSERT INTO t1 VALUES(1, "before"); + +##### Debug inject +# Simulate the situation of lock table full +SET GLOBAL debug="+d,simulate_lock_table_full"; + +##### Test +# Before this patch, a crash will occur here +# After this patch, this query will fail as expected +--error ER_LOCK_TABLE_FULL +UPDATE t1 SET name="after" WHERE id=1; + +##### Clear +DROP TABLE t1; +SET GLOBAL debug="-d,simulate_lock_table_full"; diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 280c0718e43..45c6880930b 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -1149,6 +1149,10 @@ static inline dberr_t sel_set_rec_lock(btr_pcur_t *pcur, const rec_t *rec, trx = thr_get_trx(thr); ut_ad(trx_can_be_handled_by_current_thread(trx)); + DBUG_EXECUTE_IF( + "simulate_lock_table_full", + if (sel_mode == SELECT_SKIP_LOCKED) return (DB_LOCK_TABLE_FULL);); + if (UT_LIST_GET_LEN(trx->lock.trx_locks) > 10000) { if (buf_LRU_buf_pool_running_out()) { return (DB_LOCK_TABLE_FULL); @@ -5287,7 +5291,7 @@ rec_loop: } default: - ut_a(!use_semi_consistent); + ut_a(!use_semi_consistent || err == DB_LOCK_TABLE_FULL); goto lock_wait_or_error; } if (err == DB_SUCCESS && !row_to_range_relation.row_can_be_in_range) {