From 6bccdde3660e35eb211c280de499f48eb4166bf0 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Thu, 12 Mar 2026 11:56:10 +1100 Subject: [PATCH 1/2] MDEV-24813 Signal full scan to storage engines. When starting to do a full table/index scan without a WHERE or JOIN condition, tell the storage engine so and the corresponding ulong-truncated LIMIT. Include an innodb implementation: added an innodb switch table_lock_on_full_scan, so that when the switch is on, on receiving the full scan signal from the sql layer, if the truncated LIMIT is ULONG_MAX (likely no LIMIT), attempt to acquire a table lock. Updated tests that have different results with the switch on. (Comment and code edited by Sergei Petrunia) --- include/my_base.h | 7 +- ...nnodb_table_lock_on_full_scan.combinations | 6 + .../innodb_table_lock_on_full_scan.inc | 4 + mysql-test/include/innodb_trx_weight.inc | 17 +- mysql-test/main/innodb_full_scan-master.opt | 2 + mysql-test/main/innodb_full_scan.result | 232 ++++++++++++++++++ mysql-test/main/innodb_full_scan.test | 211 ++++++++++++++++ .../innodb/r/autoinc_debug,table_lock.rdiff | 20 ++ ...oid_deadlock_with_blocked,table_lock.rdiff | 70 ++++++ .../r/avoid_deadlock_with_blocked.result | 3 + .../deadlock_on_lock_upgrade,table_lock.rdiff | 12 + .../r/deadlock_victim_race,table_lock.rdiff | 37 +++ .../innodb/r/deadlock_victim_race.result | 3 +- .../deadlock_wait_lock_race,table_lock.rdiff | 19 ++ .../innodb/r/deadlock_wait_lock_race.result | 1 + .../r/deadlock_wait_thr_race,table_lock.rdiff | 26 ++ .../innodb/r/deadlock_wait_thr_race.result | 1 + .../innodb_i_s_innodb_locks,table_lock.rdiff | 53 ++++ .../innodb/r/innodb_i_s_innodb_locks.result | Bin 5451 -> 5451 bytes .../r/innodb_i_s_innodb_trx,table_lock.rdiff | 11 + ...innodb_information_schema,table_lock.rdiff | 62 +++++ .../r/innodb_lock_wait_timeout_1.result | 3 + .../r/insert_into_empty,32k,table_lock.rdiff | 29 +++ .../r/insert_into_empty,4k,table_lock.rdiff | 54 ++++ .../r/insert_into_empty,64k,table_lock.rdiff | 29 +++ .../r/insert_into_empty,8k,table_lock.rdiff | 54 ++++ .../r/insert_into_empty,table_lock.rdiff | 16 ++ .../r/lock_delete_updated,table_lock.rdiff | 17 ++ .../innodb/r/lock_isolation,table_lock.rdiff | 11 + .../r/lock_memory_debug,table_lock.rdiff | 12 + .../innodb/r/mdev-14846,table_lock.rdiff | 12 + .../suite/innodb/r/row_lock,table_lock.rdiff | 12 + mysql-test/suite/innodb/t/autoinc_debug.test | 11 +- .../innodb/t/avoid_deadlock_with_blocked.test | 62 +++++ .../innodb/t/deadlock_on_lock_upgrade.test | 7 + .../suite/innodb/t/deadlock_victim_race.test | 37 ++- .../innodb/t/deadlock_wait_lock_race.test | 22 +- .../innodb/t/deadlock_wait_thr_race.test | 28 ++- .../innodb/t/innodb_i_s_innodb_locks.test | 2 + .../suite/innodb/t/innodb_i_s_innodb_trx.test | 1 + .../innodb/t/innodb_information_schema.test | 1 + .../innodb/t/innodb_lock_wait_timeout_1.test | 14 +- .../suite/innodb/t/innodb_trx_weight.test | 6 + .../suite/innodb/t/insert_into_empty.test | 12 +- .../suite/innodb/t/lock_delete_updated.test | 10 +- mysql-test/suite/innodb/t/lock_isolation.test | 1 + .../suite/innodb/t/lock_memory_debug.test | 13 +- mysql-test/suite/innodb/t/mdev-14846.test | 18 +- mysql-test/suite/innodb/t/row_lock.test | 18 +- .../suite/sys_vars/r/sysvars_innodb.result | 12 + sql/ha_partition.cc | 4 + sql/handler.cc | 7 + sql/handler.h | 1 + sql/sql_delete.cc | 1 + sql/sql_select.cc | 13 +- sql/sql_update.cc | 3 + storage/innobase/handler/ha_innodb.cc | 31 +++ storage/innobase/handler/ha_innodb.h | 2 + storage/innobase/include/row0mysql.h | 5 + storage/innobase/row/row0sel.cc | 6 +- storage/mroonga/ha_mroonga.cpp | 3 + 61 files changed, 1353 insertions(+), 44 deletions(-) create mode 100644 mysql-test/include/innodb_table_lock_on_full_scan.combinations create mode 100644 mysql-test/include/innodb_table_lock_on_full_scan.inc create mode 100644 mysql-test/main/innodb_full_scan-master.opt create mode 100644 mysql-test/main/innodb_full_scan.result create mode 100644 mysql-test/main/innodb_full_scan.test create mode 100644 mysql-test/suite/innodb/r/autoinc_debug,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/avoid_deadlock_with_blocked,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/deadlock_on_lock_upgrade,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/deadlock_victim_race,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/deadlock_wait_lock_race,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/deadlock_wait_thr_race,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/innodb_i_s_innodb_locks,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/innodb_i_s_innodb_trx,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/innodb_information_schema,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/insert_into_empty,32k,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/insert_into_empty,4k,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/insert_into_empty,64k,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/insert_into_empty,8k,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/insert_into_empty,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/lock_delete_updated,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/lock_isolation,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/lock_memory_debug,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/mdev-14846,table_lock.rdiff create mode 100644 mysql-test/suite/innodb/r/row_lock,table_lock.rdiff diff --git a/include/my_base.h b/include/my_base.h index 758f11ed21214..248b1df59c004 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -231,7 +231,12 @@ enum ha_extra_function { /** Finish HA_EXTRA_BEGIN_COPY or HA_EXTRA_BEGIN_ALTER_IGNORE_COPY */ HA_EXTRA_END_COPY, /** Abort HA_EXTRA_BEGIN_COPY or HA_EXTRA_BEGIN_ALTER_IGNORE_COPY */ - HA_EXTRA_ABORT_COPY + HA_EXTRA_ABORT_COPY, + /* + Advise the engine that the next operation will be a full table or + full index scan + */ + HA_EXTRA_FULL_SCAN }; /* Compatible option, to be deleted in 6.0 */ diff --git a/mysql-test/include/innodb_table_lock_on_full_scan.combinations b/mysql-test/include/innodb_table_lock_on_full_scan.combinations new file mode 100644 index 0000000000000..88a3880dcac20 --- /dev/null +++ b/mysql-test/include/innodb_table_lock_on_full_scan.combinations @@ -0,0 +1,6 @@ +[row_lock] +--disable_innodb_table_lock_on_full_scan + +[table_lock] +--innodb_table_lock_on_full_scan + diff --git a/mysql-test/include/innodb_table_lock_on_full_scan.inc b/mysql-test/include/innodb_table_lock_on_full_scan.inc new file mode 100644 index 0000000000000..418d1036d6f2c --- /dev/null +++ b/mysql-test/include/innodb_table_lock_on_full_scan.inc @@ -0,0 +1,4 @@ +# The goal of including this file is to enable innodb_table_lock_on_full_scan combinations +# (see include/innodb_table_lock_on_full_scan.combinations) + +--source include/have_innodb.inc diff --git a/mysql-test/include/innodb_trx_weight.inc b/mysql-test/include/innodb_trx_weight.inc index a1e6427651139..91131ce7deaf5 100644 --- a/mysql-test/include/innodb_trx_weight.inc +++ b/mysql-test/include/innodb_trx_weight.inc @@ -25,12 +25,23 @@ SELECT * FROM t2 FOR UPDATE; INSERT INTO t2 VALUES (0); -- connection con2 - INSERT INTO t1 VALUES (0); + if (!$insert_deadlock) { + INSERT INTO t1 VALUES (0); + } + if ($insert_deadlock) { + --error ER_LOCK_DEADLOCK + INSERT INTO t1 VALUES (0); + } ROLLBACK; -- connection con1 - -- error ER_LOCK_DEADLOCK - -- reap + if (!$insert_deadlock) { + -- error ER_LOCK_DEADLOCK + -- reap + } + if ($insert_deadlock) { + -- reap + } -- } # else -- if (!$con1_should_be_rolledback) { diff --git a/mysql-test/main/innodb_full_scan-master.opt b/mysql-test/main/innodb_full_scan-master.opt new file mode 100644 index 0000000000000..dfce39955c89d --- /dev/null +++ b/mysql-test/main/innodb_full_scan-master.opt @@ -0,0 +1,2 @@ +--innodb_monitor_enable=lock_rec_lock_created + diff --git a/mysql-test/main/innodb_full_scan.result b/mysql-test/main/innodb_full_scan.result new file mode 100644 index 0000000000000..5c2bfedaa5481 --- /dev/null +++ b/mysql-test/main/innodb_full_scan.result @@ -0,0 +1,232 @@ +# +# MDEV-24813 Locking full table scan fails to use table-level locking +# +set @old_innodb_table_lock_on_full_scan=@@innodb_table_lock_on_full_scan; +set innodb_table_lock_on_full_scan=1; +CREATE TABLE t (a INT PRIMARY KEY) ENGINE=InnoDB; +insert into t values (42); +# SELECT +set @init=(SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'); +BEGIN; +SELECT * FROM t LOCK IN SHARE MODE; +a +42 +COMMIT; +## +0, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +0 +BEGIN; +SELECT * FROM t for update; +a +42 +COMMIT; +## +0, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +0 +## SELECT without explicit transaction +SELECT * FROM t; +a +42 +### +0, was +0 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +0 +# SELECT with a condition +BEGIN; +select * from t where a > 10 lock in share mode; +a +42 +COMMIT; +### +1, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +1 +BEGIN; +SELECT * FROM t where a > 10 for update; +a +42 +COMMIT; +## +1, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +2 +## SELECT with a condition without explicit transaction +SELECT * FROM t where a > 10; +a +42 +### +0, was +0 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +2 +DROP TABLE t; +# INSERT INTO ... SELECT with UNION +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 select seq, seq from seq_1_to_100; +create table t2 (a int, b int) engine=innodb; +insert into t2 +select * from t1 where t1.a = 10 +UNION +select * from t1; +## +1, was +3 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +3 +insert into t2 +select * from t1 +UNION +select * from t1 where t1.a = 10; +## +1, was +3 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +4 +insert into t2 +select * from t1 where t1.a > 10 +UNION +select * from t1; +## +1, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +5 +insert into t2 +select * from t1 +UNION +select * from t1 where t1.a > 10; +## +0, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +5 +drop table t1, t2; +# CREATE ... SELECT with UNION +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 select seq, seq from seq_1_to_100; +create table t2 (a int, b int) engine=innodb +select * from t1 where t1.a = 10 +UNION +select * from t1; +## +1, was +3 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +6 +create or replace table t2 (a int, b int) engine=innodb +select * from t1 +UNION +select * from t1 where t1.a = 10; +## +1, was +3 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +7 +create or replace table t2 (a int, b int) engine=innodb +select * from t1 where t1.a > 10 +UNION +select * from t1; +## +1, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +8 +create or replace table t2 (a int, b int) engine=innodb +select * from t1 +UNION +select * from t1 where t1.a > 10; +## +0, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +8 +drop table t1, t2; +# UPDATE +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 select seq, seq from seq_1_to_100; +UPDATE t1 SET b = b + 3; +## +0, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +8 +UPDATE t1 SET b = b + 3 WHERE a = 10; +## +1, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +9 +UPDATE t1 SET b = b + 3 WHERE a > 10; +## +1, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +10 +# DELETE +DELETE FROM t1; +## +0, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +10 +DELETE FROM t1 WHERE a = 10; +## +1, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +11 +DELETE FROM t1 WHERE a > 10; +## +1, was +1 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +12 +drop table t1; +# JOIN +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 select seq, seq from seq_1_to_100; +create table t2 (a int primary key, b int) engine=innodb; +insert into t2 select seq, seq from seq_1_to_100; +begin; +select * from t1 join t2 on t1.a = t2.a LOCK IN SHARE MODE; +commit; +## +1, was +2 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +13 +begin; +select * from t1 join t2 on t1.a = t2.b LOCK IN SHARE MODE; +commit; +## +2, was +2 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +15 +begin; +select * from t1 join t2 on t1.b = t2.b LOCK IN SHARE MODE; +commit; +## +1, was +2 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +16 +drop table t1, t2; +# Full index scan +create table t10 (a int, b varchar(100), index(a)) engine=innodb; +insert into t10 select seq, uuid() from seq_1_to_10000; +create table t11(a int) engine=innodb; +insert into t11 select a from t10; +## +0, was +11 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +16 +drop table t10, t11; +create table t10 (a int, b varchar(100), index(a)) engine=innodb; +insert into t10 select seq, uuid() from seq_1_to_10000; +create table t11(a int) engine=innodb; +insert into t11 select a from t10 order by a desc; +## +0, was +11 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +16 +drop table t10, t11; +# Range access +create table t10 (a int, b varchar(100), index(a)) engine=innodb; +insert into t10 select seq, uuid() from seq_1_to_10000; +create table t11(a int) engine=innodb; +explain +insert into t11 select a from t10 where a > 30; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t10 range a a 5 NULL # Using where; Using index +insert into t11 select a from t10 where a > 30; +## +11, was +11 +SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +COUNT +27 +drop table t10, t11; +set innodb_table_lock_on_full_scan=@old_innodb_table_lock_on_full_scan; diff --git a/mysql-test/main/innodb_full_scan.test b/mysql-test/main/innodb_full_scan.test new file mode 100644 index 0000000000000..ceff613a6eec0 --- /dev/null +++ b/mysql-test/main/innodb_full_scan.test @@ -0,0 +1,211 @@ +--source include/have_sequence.inc +--source include/have_innodb.inc +--source include/no_view_protocol.inc +--echo # +--echo # MDEV-24813 Locking full table scan fails to use table-level locking +--echo # + +set @old_innodb_table_lock_on_full_scan=@@innodb_table_lock_on_full_scan; +set innodb_table_lock_on_full_scan=1; + +CREATE TABLE t (a INT PRIMARY KEY) ENGINE=InnoDB; +insert into t values (42); + +--echo # SELECT +# The .opt file sets innodb_monitor_enable=lock_rec_lock_created +set @init=(SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'); +let print_lock_rec_lock_created=SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; + +BEGIN; +SELECT * FROM t LOCK IN SHARE MODE; +COMMIT; +--echo ## +0, was +1 +eval $print_lock_rec_lock_created; + +BEGIN; +SELECT * FROM t for update; +COMMIT; +--echo ## +0, was +1 +eval $print_lock_rec_lock_created; + +--echo ## SELECT without explicit transaction +SELECT * FROM t; +--echo ### +0, was +0 +eval $print_lock_rec_lock_created; + +--echo # SELECT with a condition +BEGIN; +select * from t where a > 10 lock in share mode; +COMMIT; +--echo ### +1, was +1 +eval $print_lock_rec_lock_created; + +BEGIN; +SELECT * FROM t where a > 10 for update; +COMMIT; +--echo ## +1, was +1 +eval $print_lock_rec_lock_created; + +--echo ## SELECT with a condition without explicit transaction +SELECT * FROM t where a > 10; +--echo ### +0, was +0 +eval $print_lock_rec_lock_created; + +DROP TABLE t; + +--echo # INSERT INTO ... SELECT with UNION +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 select seq, seq from seq_1_to_100; +create table t2 (a int, b int) engine=innodb; +insert into t2 + select * from t1 where t1.a = 10 + UNION + select * from t1; +--echo ## +1, was +3 +eval $print_lock_rec_lock_created; + +insert into t2 + select * from t1 + UNION + select * from t1 where t1.a = 10; +--echo ## +1, was +3 +eval $print_lock_rec_lock_created; + +insert into t2 + select * from t1 where t1.a > 10 + UNION + select * from t1; +--echo ## +1, was +1 +eval $print_lock_rec_lock_created; + +insert into t2 + select * from t1 + UNION + select * from t1 where t1.a > 10; +--echo ## +0, was +1 +eval $print_lock_rec_lock_created; + +drop table t1, t2; + +--echo # CREATE ... SELECT with UNION +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 select seq, seq from seq_1_to_100; +create table t2 (a int, b int) engine=innodb + select * from t1 where t1.a = 10 + UNION + select * from t1; +--echo ## +1, was +3 +eval $print_lock_rec_lock_created; + +create or replace table t2 (a int, b int) engine=innodb + select * from t1 + UNION + select * from t1 where t1.a = 10; +--echo ## +1, was +3 +eval $print_lock_rec_lock_created; + +create or replace table t2 (a int, b int) engine=innodb + select * from t1 where t1.a > 10 + UNION + select * from t1; +--echo ## +1, was +1 +eval $print_lock_rec_lock_created; + +create or replace table t2 (a int, b int) engine=innodb + select * from t1 + UNION + select * from t1 where t1.a > 10; +--echo ## +0, was +1 +eval $print_lock_rec_lock_created; + +drop table t1, t2; + +--echo # UPDATE +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 select seq, seq from seq_1_to_100; + +UPDATE t1 SET b = b + 3; +--echo ## +0, was +1 +eval $print_lock_rec_lock_created; +UPDATE t1 SET b = b + 3 WHERE a = 10; +--echo ## +1, was +1 +eval $print_lock_rec_lock_created; +UPDATE t1 SET b = b + 3 WHERE a > 10; +--echo ## +1, was +1 +eval $print_lock_rec_lock_created; + +--echo # DELETE +DELETE FROM t1; +--echo ## +0, was +1 +eval $print_lock_rec_lock_created; +DELETE FROM t1 WHERE a = 10; +--echo ## +1, was +1 +eval $print_lock_rec_lock_created; +DELETE FROM t1 WHERE a > 10; +--echo ## +1, was +1 +eval $print_lock_rec_lock_created; + +drop table t1; + +--echo # JOIN +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 select seq, seq from seq_1_to_100; +create table t2 (a int primary key, b int) engine=innodb; +insert into t2 select seq, seq from seq_1_to_100; + +--disable_result_log +begin; +select * from t1 join t2 on t1.a = t2.a LOCK IN SHARE MODE; +commit; +--enable_result_log +--echo ## +1, was +2 +eval $print_lock_rec_lock_created; + +--disable_result_log +begin; +select * from t1 join t2 on t1.a = t2.b LOCK IN SHARE MODE; +commit; +--enable_result_log +--echo ## +2, was +2 +eval $print_lock_rec_lock_created; + +--disable_result_log +begin; +select * from t1 join t2 on t1.b = t2.b LOCK IN SHARE MODE; +commit; +--enable_result_log +--echo ## +1, was +2 +eval $print_lock_rec_lock_created; + +drop table t1, t2; + +--echo # Full index scan +create table t10 (a int, b varchar(100), index(a)) engine=innodb; +insert into t10 select seq, uuid() from seq_1_to_10000; +create table t11(a int) engine=innodb; +insert into t11 select a from t10; +--echo ## +0, was +11 +eval $print_lock_rec_lock_created; +drop table t10, t11; + +create table t10 (a int, b varchar(100), index(a)) engine=innodb; +insert into t10 select seq, uuid() from seq_1_to_10000; +create table t11(a int) engine=innodb; +insert into t11 select a from t10 order by a desc; +--echo ## +0, was +11 +eval $print_lock_rec_lock_created; +drop table t10, t11; + +--echo # Range access +create table t10 (a int, b varchar(100), index(a)) engine=innodb; +insert into t10 select seq, uuid() from seq_1_to_10000; +create table t11(a int) engine=innodb; +--replace_column 9 # +explain +insert into t11 select a from t10 where a > 30; +insert into t11 select a from t10 where a > 30; +--echo ## +11, was +11 +eval $print_lock_rec_lock_created; +drop table t10, t11; + +set innodb_table_lock_on_full_scan=@old_innodb_table_lock_on_full_scan; diff --git a/mysql-test/suite/innodb/r/autoinc_debug,table_lock.rdiff b/mysql-test/suite/innodb/r/autoinc_debug,table_lock.rdiff new file mode 100644 index 0000000000000..bb8db277dda69 --- /dev/null +++ b/mysql-test/suite/innodb/r/autoinc_debug,table_lock.rdiff @@ -0,0 +1,20 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/autoinc_debug.result 2025-02-19 14:14:39.056154959 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/autoinc_debug.reject 2026-03-05 16:31:06.934494369 +1100 +@@ -132,7 +132,7 @@ + # T1: Wait for (T3) auto increment lock on t1 causing T1 -> T3 -> T2 -> T1 deadlock + SET debug_dbug = '+d,innodb_deadlock_victim_self'; + INSERT INTO t1(col2) VALUES(200); +-ERROR HY000: Failed to read auto-increment value from storage engine ++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction + # The transaction should have been rolled back + SELECT * FROM t1; + col1 col2 +@@ -152,7 +152,7 @@ + SELECT * FROM t1; + col1 col2 + 1 100 +-2 100 ++3 100 + DROP TABLE t1; + SELECT * FROM t2; + col1 diff --git a/mysql-test/suite/innodb/r/avoid_deadlock_with_blocked,table_lock.rdiff b/mysql-test/suite/innodb/r/avoid_deadlock_with_blocked,table_lock.rdiff new file mode 100644 index 0000000000000..3133c1a29dcb0 --- /dev/null +++ b/mysql-test/suite/innodb/r/avoid_deadlock_with_blocked,table_lock.rdiff @@ -0,0 +1,70 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/avoid_deadlock_with_blocked.result 2026-03-05 16:40:26.246544601 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/avoid_deadlock_with_blocked.reject 2026-03-12 11:08:57.116399743 +1100 +@@ -32,8 +32,7 @@ + 1 + COMMIT; + connection con2; +-id +-1 ++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction + COMMIT; + # The scenario when we bypass X<-S pair: + # , +@@ -57,16 +56,12 @@ + connection con1; + SET DEBUG_SYNC = 'now WAIT_FOR con3_will_wait'; + SELECT * FROM t1 FOR UPDATE; +-id +-1 + COMMIT; + connection con2; +-id +-1 ++ERROR HY000: Lock wait timeout exceeded; try restarting transaction + COMMIT; + connection con3; +-id +-1 ++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction + COMMIT; + # A variant of the above scenario: + # , +@@ -87,7 +82,8 @@ + INSERT INTO t1 VALUES (0); + ROLLBACK; + connection con2; +-ERROR 40001: Deadlock found when trying to get lock; try restarting transaction ++id ++1 + COMMIT; + # More complicated scenario: + # , +@@ -155,8 +151,7 @@ + 1 + COMMIT; + connection con3; +-id +-1 ++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction + COMMIT; + # A secenario, where con1 has to bypass two transactions: + # +@@ -181,16 +176,12 @@ + connection con1; + SET DEBUG_SYNC = 'now WAIT_FOR con3_will_wait'; + SELECT * FROM t1 WHERE id=1 FOR UPDATE; +-id +-1 + COMMIT; + connection con2; +-id +-1 ++ERROR HY000: Lock wait timeout exceeded; try restarting transaction + COMMIT; + connection con3; +-id +-1 ++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction + COMMIT; + connection default; + disconnect con1; diff --git a/mysql-test/suite/innodb/r/avoid_deadlock_with_blocked.result b/mysql-test/suite/innodb/r/avoid_deadlock_with_blocked.result index 4e0cca428987a..0cd49e1625424 100644 --- a/mysql-test/suite/innodb/r/avoid_deadlock_with_blocked.result +++ b/mysql-test/suite/innodb/r/avoid_deadlock_with_blocked.result @@ -1,3 +1,5 @@ +set @old_innodb_lock_wait_timeout=@@global.innodb_lock_wait_timeout; +set global innodb_lock_wait_timeout=1; connect stop_purge,localhost,root; START TRANSACTION WITH CONSISTENT SNAPSHOT; connect con1,localhost,root,,; @@ -196,3 +198,4 @@ disconnect con2; disconnect con3; disconnect stop_purge; DROP TABLE t1; +set global innodb_lock_wait_timeout=@old_innodb_lock_wait_timeout; diff --git a/mysql-test/suite/innodb/r/deadlock_on_lock_upgrade,table_lock.rdiff b/mysql-test/suite/innodb/r/deadlock_on_lock_upgrade,table_lock.rdiff new file mode 100644 index 0000000000000..65810af995688 --- /dev/null +++ b/mysql-test/suite/innodb/r/deadlock_on_lock_upgrade,table_lock.rdiff @@ -0,0 +1,12 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_on_lock_upgrade.result 2025-02-19 14:14:39.056154959 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_on_lock_upgrade.reject 2026-03-05 16:41:41.114018319 +1100 +@@ -69,8 +69,7 @@ + 2 + COMMIT; + connection waiter; +-id +-1 ++ERROR 40001: Deadlock found when trying to get lock; try restarting transaction + connection default; + disconnect holder; + disconnect waiter; diff --git a/mysql-test/suite/innodb/r/deadlock_victim_race,table_lock.rdiff b/mysql-test/suite/innodb/r/deadlock_victim_race,table_lock.rdiff new file mode 100644 index 0000000000000..eacae7dc7d2cc --- /dev/null +++ b/mysql-test/suite/innodb/r/deadlock_victim_race,table_lock.rdiff @@ -0,0 +1,37 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_victim_race.result 2026-03-06 15:18:14.783771049 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_victim_race.reject 2026-03-06 15:18:26.339686475 +1100 +@@ -12,7 +12,6 @@ + connect con_2,localhost,root,,; + SET TRANSACTION ISOLATION LEVEL READ COMMITTED; + BEGIN; +-SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont EXECUTE 2'; + SET SESSION innodb_lock_wait_timeout=1; + UPDATE t SET b = 100; + connect con_3,localhost,root,,; +@@ -21,23 +20,22 @@ + SELECT * FROM t WHERE a = 30 FOR UPDATE; + a b + 30 30 +-SET DEBUG_SYNC='now WAIT_FOR upd_locked'; + SET DEBUG_SYNC = 'lock_wait_start SIGNAL sel_locked'; + SELECT * FROM t WHERE a = 20 FOR UPDATE; + connection default; + SET DEBUG_SYNC='now WAIT_FOR sel_locked'; + ROLLBACK; + SET DEBUG_SYNC='now SIGNAL upd_cont'; +-SET DEBUG_SYNC='now WAIT_FOR upd_locked'; + SET SESSION innodb_lock_wait_timeout=1; + SELECT * FROM t WHERE a = 10 FOR UPDATE; +-ERROR HY000: Lock wait timeout exceeded; try restarting transaction ++a b ++10 10 + SET DEBUG_SYNC="now SIGNAL upd_cont"; + connection con_3; + a b + 20 20 + connection con_2; +-ERROR 40001: Deadlock found when trying to get lock; try restarting transaction ++ERROR HY000: Lock wait timeout exceeded; try restarting transaction + disconnect con_3; + disconnect con_2; + connection default; diff --git a/mysql-test/suite/innodb/r/deadlock_victim_race.result b/mysql-test/suite/innodb/r/deadlock_victim_race.result index 061edc775f048..93286fdbb5040 100644 --- a/mysql-test/suite/innodb/r/deadlock_victim_race.result +++ b/mysql-test/suite/innodb/r/deadlock_victim_race.result @@ -13,6 +13,7 @@ connect con_2,localhost,root,,; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN; SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont EXECUTE 2'; +SET SESSION innodb_lock_wait_timeout=1; UPDATE t SET b = 100; connect con_3,localhost,root,,; BEGIN; @@ -27,7 +28,7 @@ connection default; SET DEBUG_SYNC='now WAIT_FOR sel_locked'; ROLLBACK; SET DEBUG_SYNC='now SIGNAL upd_cont'; -SET DEBUG_SYNC="now WAIT_FOR upd_locked"; +SET DEBUG_SYNC='now WAIT_FOR upd_locked'; SET SESSION innodb_lock_wait_timeout=1; SELECT * FROM t WHERE a = 10 FOR UPDATE; ERROR HY000: Lock wait timeout exceeded; try restarting transaction diff --git a/mysql-test/suite/innodb/r/deadlock_wait_lock_race,table_lock.rdiff b/mysql-test/suite/innodb/r/deadlock_wait_lock_race,table_lock.rdiff new file mode 100644 index 0000000000000..78963e3b74e3a --- /dev/null +++ b/mysql-test/suite/innodb/r/deadlock_wait_lock_race,table_lock.rdiff @@ -0,0 +1,19 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_wait_lock_race.result 2026-03-06 15:32:50.445412134 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_wait_lock_race.reject 2026-03-06 15:33:24.125169729 +1100 +@@ -10,15 +10,12 @@ + connect con_2,localhost,root,,; + SET TRANSACTION ISOLATION LEVEL READ COMMITTED; + BEGIN; +-SET DEBUG_SYNC = 'lock_trx_handle_wait_before_unlocked_wait_lock_check SIGNAL upd_locked WAIT_FOR upd_cont'; + set session innodb_lock_wait_timeout = 1; + UPDATE t SET b = 100; + connection default; +-SET DEBUG_SYNC="now WAIT_FOR upd_locked"; +-SET DEBUG_SYNC="lock_wait_before_suspend SIGNAL upd_cont"; + SELECT * FROM t WHERE a = 10 FOR UPDATE; + connection con_2; +-ERROR 40001: Deadlock found when trying to get lock; try restarting transaction ++ERROR HY000: Lock wait timeout exceeded; try restarting transaction + disconnect con_2; + connection default; + a b diff --git a/mysql-test/suite/innodb/r/deadlock_wait_lock_race.result b/mysql-test/suite/innodb/r/deadlock_wait_lock_race.result index 874f5af47d029..2149f21c70d87 100644 --- a/mysql-test/suite/innodb/r/deadlock_wait_lock_race.result +++ b/mysql-test/suite/innodb/r/deadlock_wait_lock_race.result @@ -11,6 +11,7 @@ connect con_2,localhost,root,,; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN; SET DEBUG_SYNC = 'lock_trx_handle_wait_before_unlocked_wait_lock_check SIGNAL upd_locked WAIT_FOR upd_cont'; +set session innodb_lock_wait_timeout = 1; UPDATE t SET b = 100; connection default; SET DEBUG_SYNC="now WAIT_FOR upd_locked"; diff --git a/mysql-test/suite/innodb/r/deadlock_wait_thr_race,table_lock.rdiff b/mysql-test/suite/innodb/r/deadlock_wait_thr_race,table_lock.rdiff new file mode 100644 index 0000000000000..fa10579d9007e --- /dev/null +++ b/mysql-test/suite/innodb/r/deadlock_wait_thr_race,table_lock.rdiff @@ -0,0 +1,26 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_wait_thr_race.result 2026-03-06 15:31:05.910164017 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_wait_thr_race.reject 2026-03-06 15:31:16.214089943 +1100 +@@ -9,22 +9,18 @@ + 20 20 + connect con_2,localhost,root,,; + SET TRANSACTION ISOLATION LEVEL READ COMMITTED; +-SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont'; + SET DEBUG_SYNC = 'trx_t_release_locks_enter SIGNAL sel_cont WAIT_FOR upd_cont_2'; + set session innodb_lock_wait_timeout = 1; + BEGIN; + UPDATE t SET b = 100; + connection default; +-SET DEBUG_SYNC="now WAIT_FOR upd_locked"; + SET DEBUG_SYNC="deadlock_report_before_lock_releasing SIGNAL upd_cont WAIT_FOR sel_cont"; +-SET DEBUG_SYNC="lock_wait_before_suspend SIGNAL sel_before_suspend"; + SELECT * FROM t WHERE a = 10 FOR UPDATE;; + connect con_3,localhost,root,,; +-SET DEBUG_SYNC="now WAIT_FOR sel_before_suspend"; + SET DEBUG_SYNC="now SIGNAL upd_cont_2"; + disconnect con_3; + connection con_2; +-ERROR 40001: Deadlock found when trying to get lock; try restarting transaction ++ERROR HY000: Lock wait timeout exceeded; try restarting transaction + disconnect con_2; + connection default; + a b diff --git a/mysql-test/suite/innodb/r/deadlock_wait_thr_race.result b/mysql-test/suite/innodb/r/deadlock_wait_thr_race.result index 6992a447c071b..06718e7eaa94a 100644 --- a/mysql-test/suite/innodb/r/deadlock_wait_thr_race.result +++ b/mysql-test/suite/innodb/r/deadlock_wait_thr_race.result @@ -11,6 +11,7 @@ connect con_2,localhost,root,,; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont'; SET DEBUG_SYNC = 'trx_t_release_locks_enter SIGNAL sel_cont WAIT_FOR upd_cont_2'; +set session innodb_lock_wait_timeout = 1; BEGIN; UPDATE t SET b = 100; connection default; diff --git a/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks,table_lock.rdiff b/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks,table_lock.rdiff new file mode 100644 index 0000000000000..d425b3912f88c --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks,table_lock.rdiff @@ -0,0 +1,53 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks.result 2026-03-05 15:48:16.984108689 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks.reject 2026-03-05 15:48:53.687914366 +1100 +@@ -95,29 +95,26 @@ + connection con_str_lock_row4; + SELECT * FROM ```t'\"_str` WHERE c1 = '4' FOR UPDATE; + connection con_verify_innodb_locks; ++Timeout waiting for rows in INNODB_LOCKS to appear + SELECT lock_mode, lock_type, lock_table, lock_index, lock_rec, lock_data + FROM INFORMATION_SCHEMA.INNODB_LOCKS ORDER BY lock_data; + lock_mode lock_type lock_table lock_index lock_rec lock_data +-X RECORD `test`.```t'\"_str` PRIMARY 1 supremum pseudo-record +-X RECORD `test`.```t'\"_str` PRIMARY 1 supremum pseudo-record +-X RECORD `test`.```t'\"_str` PRIMARY 2 '1', 'abc', '''abc', 'abc''', 'a''bc', 'a''bc''', '''abc''''' +-X RECORD `test`.```t'\"_str` PRIMARY 2 '1', 'abc', '''abc', 'abc''', 'a''bc', 'a''bc''', '''abc''''' +-X RECORD `test`.```t'\"_str` PRIMARY 3 '2', 'abc', '"abc', 'abc"', 'a"bc', 'a"bc"', '"abc""' +-X RECORD `test`.```t'\"_str` PRIMARY 3 '2', 'abc', '"abc', 'abc"', 'a"bc', 'a"bc"', '"abc""' +-X RECORD `test`.```t'\"_str` PRIMARY 4 '3', 'abc', '\\abc', 'abc\\', 'a\\bc', 'a\\bc\\', '\\abc\\\\' +-X RECORD `test`.```t'\"_str` PRIMARY 4 '3', 'abc', '\\abc', 'abc\\', 'a\\bc', 'a\\bc\\', '\\abc\\\\' +-X RECORD `test`.```t'\"_str` PRIMARY 5 '4', 'abc', '\0abc', 'abc\0', 'a\0bc', 'a\0bc\0', 'a\0bc\0\0' +-X RECORD `test`.```t'\"_str` PRIMARY 5 '4', 'abc', '\0abc', 'abc\0', 'a\0bc', 'a\0bc\0', 'a\0bc\0\0' +-X RECORD `test`.`t_max` PRIMARY 2 127, 255, 32767, 65535, 8388607, 16777215, 2147483647, 4294967295, 9223372036854775807, 18446744073709551615 +-X RECORD `test`.`t_max` PRIMARY 2 127, 255, 32767, 65535, 8388607, 16777215, 2147483647, 4294967295, 9223372036854775807, 18446744073709551615 +-X RECORD `test`.`t_min` PRIMARY 2 -128, 0, -32768, 0, -8388608, 0, -2147483648, 0, -9223372036854775808, 0 +-X RECORD `test`.`t_min` PRIMARY 2 -128, 0, -32768, 0, -8388608, 0, -2147483648, 0, -9223372036854775808, 0 ++IX TABLE `test`.```t'\"_str` NULL NULL NULL ++IX TABLE `test`.```t'\"_str` NULL NULL NULL ++IX TABLE `test`.```t'\"_str` NULL NULL NULL ++IX TABLE `test`.```t'\"_str` NULL NULL NULL ++IX TABLE `test`.```t'\"_str` NULL NULL NULL ++X TABLE `test`.```t'\"_str` NULL NULL NULL ++X TABLE `test`.`t_max` NULL NULL NULL ++X TABLE `test`.`t_max` NULL NULL NULL ++X TABLE `test`.`t_min` NULL NULL NULL ++X TABLE `test`.`t_min` NULL NULL NULL + SELECT lock_table, COUNT(*) FROM INFORMATION_SCHEMA.INNODB_LOCKS + GROUP BY lock_table; + lock_table COUNT(*) + `test`.`t_max` 2 + `test`.`t_min` 2 +-`test`.```t'\"_str` 10 ++`test`.```t'\"_str` 6 + set @save_sql_mode = @@sql_mode; + SET SQL_MODE='ANSI_QUOTES'; + SELECT lock_table, COUNT(*) FROM INFORMATION_SCHEMA.INNODB_LOCKS +@@ -125,7 +122,7 @@ + lock_table COUNT(*) + "test"."t_max" 2 + "test"."t_min" 2 +-"test"."`t'\""_str" 10 ++"test"."`t'\""_str" 6 + SET @@sql_mode=@save_sql_mode; + connection con_lock; + COMMIT; diff --git a/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks.result b/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks.result index a410362ab525f846535b8d12f6b86fd9527d6b05..1bca540557f8f4aea0a5800acea20d18186b3ad6 100644 GIT binary patch delta 38 ucmX@Dby{n~I-be91j8p^7F6B5j)$FjbGLvr<78QZyvYgz;hP18te62OJq+Ri delta 45 zcmX@Dby{n~I-be>ys4Y}c^5HF_7^-fd6}Td 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. + INSERT IGNORE INTO t1 VALUES + (1, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)), + (2, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)); +-ERROR 42000: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. + CHECK TABLE t1; + Table Op Msg_type Msg_text + test.t1 check status OK diff --git a/mysql-test/suite/innodb/r/insert_into_empty,4k,table_lock.rdiff b/mysql-test/suite/innodb/r/insert_into_empty,4k,table_lock.rdiff new file mode 100644 index 0000000000000..c6770cb8f45d4 --- /dev/null +++ b/mysql-test/suite/innodb/r/insert_into_empty,4k,table_lock.rdiff @@ -0,0 +1,54 @@ +--- ../src/mysql-test/suite/innodb/r/insert_into_empty.result 2026-03-03 14:40:28.037988631 +1100 ++++ ../src/mysql-test/suite/innodb/r/insert_into_empty,4k.reject 2026-03-04 17:15:37.860553099 +1100 +@@ -162,13 +162,10 @@ + INSERT INTO t1 VALUES (0,1); + ERROR 21S01: Column count doesn't match value count at row 1 + INSERT INTO t1 VALUES (2); +-ERROR 23000: Duplicate entry '2' for key 'PRIMARY' + COMMIT; + SET GLOBAL innodb_limit_optimistic_insert_debug = @save_limit; + SELECT * FROM t1; + a +-0 +-1 + 2 + DROP TABLE t1; + # +@@ -451,7 +448,7 @@ + INSERT IGNORE INTO t1 VALUES + (1, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)), + (2, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)); +-ERROR 42000: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. ++ERROR 42000: Row size too large (> 1982). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. + CHECK TABLE t1; + Table Op Msg_type Msg_text + test.t1 check status OK +@@ -542,28 +539,6 @@ + commit; + DROP TABLE t1; + # +-# MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' +-# failed in cmp_rec_rec_simple_field +-# +-CREATE TABLE t1(a BLOB, b VARCHAR(2048), PRIMARY KEY (b)) ENGINE=InnoDB; +-INSERT INTO t1 VALUES (REPEAT('x',4805),'a'), (REPEAT('x',16111),'b'), +-(REPEAT('x',65535),'c'), (REPEAT('x',11312),'d'), +-(REPEAT('x',35177),'e'), (REPEAT('x',65535),'f'), +-(REPEAT('x',1988),'g'), (NULL,REPEAT('x',2048)), +-(REPEAT('x',2503),'h'), (REPEAT('x',33152),'i'), +-(REPEAT('x',65535),'j'), (REPEAT('x',1988),'k'), +-(REPEAT('x',65535),'l'), (REPEAT('x',65535),'m'), +-(REPEAT('x',65535),'n'), (REPEAT('x',65535),'o'), +-(REPEAT('x',1988),'p'), (REPEAT('x',2503),'q'), +-(REPEAT('x',65535),'r'), (REPEAT('x',65535),'s'), +-(REPEAT('x',65535),'t'), (REPEAT('x',3169),'u'), +-(REPEAT('x',7071),'v'), (REPEAT('x',16111),'w'), +-(REPEAT('x',2325),'x'), (REPEAT('x',33152),'y'), +-(REPEAT('x',65535),'z'), (REPEAT('x',65535),'aa'), +-(REPEAT('x',16111),'bb'), (REPEAT('x',4805),'cc'), +-(REPEAT('x',65535),'dd'); +-DROP TABLE t1; +-# + # Assertion `page_dir_get_n_heap(new_page) == 2U' failed + # in dberr_t PageBulk::init() + # diff --git a/mysql-test/suite/innodb/r/insert_into_empty,64k,table_lock.rdiff b/mysql-test/suite/innodb/r/insert_into_empty,64k,table_lock.rdiff new file mode 100644 index 0000000000000..914d99406d1d8 --- /dev/null +++ b/mysql-test/suite/innodb/r/insert_into_empty,64k,table_lock.rdiff @@ -0,0 +1,29 @@ +--- ../src/mysql-test/suite/innodb/r/insert_into_empty.result 2026-03-03 14:40:28.037988631 +1100 ++++ ../src/mysql-test/suite/innodb/r/insert_into_empty,64k.reject 2026-03-04 17:15:09.304749650 +1100 +@@ -162,13 +162,10 @@ + INSERT INTO t1 VALUES (0,1); + ERROR 21S01: Column count doesn't match value count at row 1 + INSERT INTO t1 VALUES (2); +-ERROR 23000: Duplicate entry '2' for key 'PRIMARY' + COMMIT; + SET GLOBAL innodb_limit_optimistic_insert_debug = @save_limit; + SELECT * FROM t1; + a +-0 +-1 + 2 + DROP TABLE t1; + # +@@ -446,12 +443,9 @@ + c09 text, c10 text, c11 text, c12 text) ENGINE=InnoDB; + SET GLOBAL INNODB_DEFAULT_ROW_FORMAT= COMPACT; + ALTER TABLE t1 FORCE; +-Warnings: +-Warning 139 Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. + INSERT IGNORE INTO t1 VALUES + (1, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)), + (2, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)); +-ERROR 42000: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. + CHECK TABLE t1; + Table Op Msg_type Msg_text + test.t1 check status OK diff --git a/mysql-test/suite/innodb/r/insert_into_empty,8k,table_lock.rdiff b/mysql-test/suite/innodb/r/insert_into_empty,8k,table_lock.rdiff new file mode 100644 index 0000000000000..59a18a80065f7 --- /dev/null +++ b/mysql-test/suite/innodb/r/insert_into_empty,8k,table_lock.rdiff @@ -0,0 +1,54 @@ +--- ../src/mysql-test/suite/innodb/r/insert_into_empty.result 2026-03-03 14:40:28.037988631 +1100 ++++ ../src/mysql-test/suite/innodb/r/insert_into_empty,8k.reject 2026-03-04 17:15:50.648465080 +1100 +@@ -162,13 +162,10 @@ + INSERT INTO t1 VALUES (0,1); + ERROR 21S01: Column count doesn't match value count at row 1 + INSERT INTO t1 VALUES (2); +-ERROR 23000: Duplicate entry '2' for key 'PRIMARY' + COMMIT; + SET GLOBAL innodb_limit_optimistic_insert_debug = @save_limit; + SELECT * FROM t1; + a +-0 +-1 + 2 + DROP TABLE t1; + # +@@ -451,7 +448,7 @@ + INSERT IGNORE INTO t1 VALUES + (1, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)), + (2, REPEAT('x',4805), REPEAT('t',2211), REPEAT('u',974), REPEAT('e',871), REPEAT('z',224), REPEAT('j',978), REPEAT('n',190), REPEAT('t',888), REPEAT('x',32768), REPEAT('e',968), REPEAT('b',913), REPEAT('x',12107)); +-ERROR 42000: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. ++ERROR 42000: Row size too large (> 4030). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. + CHECK TABLE t1; + Table Op Msg_type Msg_text + test.t1 check status OK +@@ -542,28 +539,6 @@ + commit; + DROP TABLE t1; + # +-# MDEV-35475 Assertion `!rec_offs_nth_extern(offsets1, n)' +-# failed in cmp_rec_rec_simple_field +-# +-CREATE TABLE t1(a BLOB, b VARCHAR(2048), PRIMARY KEY (b)) ENGINE=InnoDB; +-INSERT INTO t1 VALUES (REPEAT('x',4805),'a'), (REPEAT('x',16111),'b'), +-(REPEAT('x',65535),'c'), (REPEAT('x',11312),'d'), +-(REPEAT('x',35177),'e'), (REPEAT('x',65535),'f'), +-(REPEAT('x',1988),'g'), (NULL,REPEAT('x',2048)), +-(REPEAT('x',2503),'h'), (REPEAT('x',33152),'i'), +-(REPEAT('x',65535),'j'), (REPEAT('x',1988),'k'), +-(REPEAT('x',65535),'l'), (REPEAT('x',65535),'m'), +-(REPEAT('x',65535),'n'), (REPEAT('x',65535),'o'), +-(REPEAT('x',1988),'p'), (REPEAT('x',2503),'q'), +-(REPEAT('x',65535),'r'), (REPEAT('x',65535),'s'), +-(REPEAT('x',65535),'t'), (REPEAT('x',3169),'u'), +-(REPEAT('x',7071),'v'), (REPEAT('x',16111),'w'), +-(REPEAT('x',2325),'x'), (REPEAT('x',33152),'y'), +-(REPEAT('x',65535),'z'), (REPEAT('x',65535),'aa'), +-(REPEAT('x',16111),'bb'), (REPEAT('x',4805),'cc'), +-(REPEAT('x',65535),'dd'); +-DROP TABLE t1; +-# + # Assertion `page_dir_get_n_heap(new_page) == 2U' failed + # in dberr_t PageBulk::init() + # diff --git a/mysql-test/suite/innodb/r/insert_into_empty,table_lock.rdiff b/mysql-test/suite/innodb/r/insert_into_empty,table_lock.rdiff new file mode 100644 index 0000000000000..0c7e29831af67 --- /dev/null +++ b/mysql-test/suite/innodb/r/insert_into_empty,table_lock.rdiff @@ -0,0 +1,16 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/insert_into_empty.result 2026-03-03 14:40:28.037988631 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/insert_into_empty.reject 2026-03-04 17:22:46.425602626 +1100 +@@ -162,13 +162,10 @@ + INSERT INTO t1 VALUES (0,1); + ERROR 21S01: Column count doesn't match value count at row 1 + INSERT INTO t1 VALUES (2); +-ERROR 23000: Duplicate entry '2' for key 'PRIMARY' + COMMIT; + SET GLOBAL innodb_limit_optimistic_insert_debug = @save_limit; + SELECT * FROM t1; + a +-0 +-1 + 2 + DROP TABLE t1; + # diff --git a/mysql-test/suite/innodb/r/lock_delete_updated,table_lock.rdiff b/mysql-test/suite/innodb/r/lock_delete_updated,table_lock.rdiff new file mode 100644 index 0000000000000..e0b84fa891542 --- /dev/null +++ b/mysql-test/suite/innodb/r/lock_delete_updated,table_lock.rdiff @@ -0,0 +1,17 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/lock_delete_updated.result 2025-01-22 12:27:31.316598027 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/lock_delete_updated.reject 2026-03-05 14:27:02.066267800 +1100 +@@ -10,13 +10,12 @@ + UPDATE t SET a = 1; + COMMIT; + connection con1; +-ERROR 40001: Deadlock found when trying to get lock; try restarting transaction + disconnect con1; + connection default; + # The above DELETE must delete all the rows in the table, so the + # following SELECT must show 0 rows. + SELECT count(*) FROM t; + count(*) +-1 ++0 + SET DEBUG_SYNC="reset"; + DROP TABLE t; diff --git a/mysql-test/suite/innodb/r/lock_isolation,table_lock.rdiff b/mysql-test/suite/innodb/r/lock_isolation,table_lock.rdiff new file mode 100644 index 0000000000000..f4d02e940b42e --- /dev/null +++ b/mysql-test/suite/innodb/r/lock_isolation,table_lock.rdiff @@ -0,0 +1,11 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/lock_isolation.result 2025-09-05 14:46:23.964928913 +1000 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/lock_isolation.reject 2026-03-05 14:28:40.077672334 +1100 +@@ -110,7 +110,7 @@ + connection default; + SELECT * FROM t; + a b +-10 1 ++10 20 + 10 20 + TRUNCATE TABLE t; + # diff --git a/mysql-test/suite/innodb/r/lock_memory_debug,table_lock.rdiff b/mysql-test/suite/innodb/r/lock_memory_debug,table_lock.rdiff new file mode 100644 index 0000000000000..259f1185e2f30 --- /dev/null +++ b/mysql-test/suite/innodb/r/lock_memory_debug,table_lock.rdiff @@ -0,0 +1,12 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/lock_memory_debug.result 2025-05-07 10:38:15.869311858 +1000 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/lock_memory_debug.reject 2026-03-05 14:31:42.484616778 +1100 +@@ -6,8 +6,7 @@ + INSERT INTO t1 VALUES (1),(2),(3),(4),(5); + SET STATEMENT debug_dbug='+d,innodb_skip_lock_bitmap' FOR + INSERT INTO t1 SELECT a.* FROM t1 a, t1 b, t1 c, t1 d, t1 e, t1 f, t1 g; +-ERROR HY000: The total number of locks exceeds the lock table size + SELECT COUNT(*) FROM t1; + COUNT(*) +-5 ++78130 + DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/mdev-14846,table_lock.rdiff b/mysql-test/suite/innodb/r/mdev-14846,table_lock.rdiff new file mode 100644 index 0000000000000..755c2e4b391ef --- /dev/null +++ b/mysql-test/suite/innodb/r/mdev-14846,table_lock.rdiff @@ -0,0 +1,12 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/mdev-14846.result 2025-10-15 11:15:55.221082220 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/mdev-14846.reject 2026-03-05 16:11:28.967240949 +1100 +@@ -54,8 +54,8 @@ + 2 MATERIALIZED t3 ALL NULL NULL NULL NULL # + 2 MATERIALIZED t2 ALL NULL NULL NULL NULL # + UPDATE v4, t1 SET t1.pk = 76 WHERE t1.f2 IN ( SELECT t2.f FROM t2 INNER JOIN t3 ); +-connection default; + ERROR 40001: Deadlock found when trying to get lock; try restarting transaction ++connection default; + connection con1; + COMMIT; + disconnect con1; diff --git a/mysql-test/suite/innodb/r/row_lock,table_lock.rdiff b/mysql-test/suite/innodb/r/row_lock,table_lock.rdiff new file mode 100644 index 0000000000000..61d64d7ff63a7 --- /dev/null +++ b/mysql-test/suite/innodb/r/row_lock,table_lock.rdiff @@ -0,0 +1,12 @@ +--- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/row_lock.result 2023-11-29 12:36:54.941275754 +1100 ++++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/row_lock.reject 2026-03-05 16:08:19.816626898 +1100 +@@ -11,8 +11,8 @@ + UPDATE t4 SET d = 1 WHERE d in ( SELECT a FROM t1 ) ORDER BY c LIMIT 6; + connection con11; + UPDATE t4 SET d = 9; +-connection con12; + ERROR 40001: Deadlock found when trying to get lock; try restarting transaction ++connection con12; + connection con11; + commit; + connection default; diff --git a/mysql-test/suite/innodb/t/autoinc_debug.test b/mysql-test/suite/innodb/t/autoinc_debug.test index f62cfd8afce09..c2b6ae855ae79 100644 --- a/mysql-test/suite/innodb/t/autoinc_debug.test +++ b/mysql-test/suite/innodb/t/autoinc_debug.test @@ -2,6 +2,7 @@ --source include/have_debug.inc --source include/have_debug_sync.inc --source include/not_embedded.inc +--source include/innodb_table_lock_on_full_scan.inc --disable_query_log call mtr.add_suppression("InnoDB: Transaction was aborted due to "); @@ -128,8 +129,14 @@ SAVEPOINT s1; SET DEBUG_SYNC='now WAIT_FOR t3_waiting'; --echo # T1: Wait for (T3) auto increment lock on t1 causing T1 -> T3 -> T2 -> T1 deadlock SET debug_dbug = '+d,innodb_deadlock_victim_self'; ---error ER_AUTOINC_READ_FAILED -INSERT INTO t1(col2) VALUES(200); +if ($MTR_COMBINATION_ROW_LOCK) { + --error ER_AUTOINC_READ_FAILED + INSERT INTO t1(col2) VALUES(200); +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_DEADLOCK + INSERT INTO t1(col2) VALUES(200); +} --echo # The transaction should have been rolled back SELECT * FROM t1; diff --git a/mysql-test/suite/innodb/t/avoid_deadlock_with_blocked.test b/mysql-test/suite/innodb/t/avoid_deadlock_with_blocked.test index aa55b1ba00850..61c3d43eb5f00 100644 --- a/mysql-test/suite/innodb/t/avoid_deadlock_with_blocked.test +++ b/mysql-test/suite/innodb/t/avoid_deadlock_with_blocked.test @@ -2,11 +2,15 @@ --source include/have_debug.inc --source include/have_debug_sync.inc --source include/count_sessions.inc +--source include/innodb_table_lock_on_full_scan.inc --disable_query_log call mtr.add_suppression("InnoDB: Transaction was aborted due to "); --enable_query_log +set @old_innodb_lock_wait_timeout=@@global.innodb_lock_wait_timeout; +set global innodb_lock_wait_timeout=1; + connect stop_purge,localhost,root; START TRANSACTION WITH CONSISTENT SNAPSHOT; @@ -42,7 +46,13 @@ INSERT INTO t1 (id) VALUES (1); COMMIT; --connection con2 +if ($MTR_COMBINATION_ROW_LOCK) { + --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_DEADLOCK --reap +} COMMIT; --echo # The scenario when we bypass X<-S pair: @@ -68,15 +78,35 @@ INSERT INTO t1 (id) VALUES (1); --connection con1 SET DEBUG_SYNC = 'now WAIT_FOR con3_will_wait'; +if ($MTR_COMBINATION_ROW_LOCK) { + SELECT * FROM t1 FOR UPDATE; +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --disable_result_log + --error 0,ER_LOCK_WAIT_TIMEOUT SELECT * FROM t1 FOR UPDATE; + --enable_result_log +} COMMIT; --connection con2 +if ($MTR_COMBINATION_ROW_LOCK) { --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_WAIT_TIMEOUT + --reap +} COMMIT; --connection con3 +if ($MTR_COMBINATION_ROW_LOCK) { + --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_DEADLOCK --reap +} COMMIT; # @@ -100,8 +130,13 @@ INSERT INTO t1 (id) VALUES (1); ROLLBACK; --connection con2 +if ($MTR_COMBINATION_ROW_LOCK) { --error ER_LOCK_DEADLOCK --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --reap +} COMMIT; --echo # More complicated scenario: @@ -180,7 +215,13 @@ INSERT INTO t1 (id) VALUES (1); COMMIT; --connection con3 +if ($MTR_COMBINATION_ROW_LOCK) { + --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_DEADLOCK --reap +} COMMIT; --echo # A secenario, where con1 has to bypass two transactions: @@ -206,15 +247,35 @@ INSERT INTO t1 (id) VALUES (1); --connection con1 SET DEBUG_SYNC = 'now WAIT_FOR con3_will_wait'; +if ($MTR_COMBINATION_ROW_LOCK) { + SELECT * FROM t1 WHERE id=1 FOR UPDATE; +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --disable_result_log + --error 0,ER_LOCK_WAIT_TIMEOUT SELECT * FROM t1 WHERE id=1 FOR UPDATE; + --enable_result_log +} COMMIT; --connection con2 +if ($MTR_COMBINATION_ROW_LOCK) { --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_WAIT_TIMEOUT + --reap +} COMMIT; --connection con3 +if ($MTR_COMBINATION_ROW_LOCK) { + --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_DEADLOCK --reap +} COMMIT; --connection default @@ -225,4 +286,5 @@ INSERT INTO t1 (id) VALUES (1); DROP TABLE t1; +set global innodb_lock_wait_timeout=@old_innodb_lock_wait_timeout; --source include/wait_until_count_sessions.inc diff --git a/mysql-test/suite/innodb/t/deadlock_on_lock_upgrade.test b/mysql-test/suite/innodb/t/deadlock_on_lock_upgrade.test index 79adbe22021af..dfa7d3a6666cd 100644 --- a/mysql-test/suite/innodb/t/deadlock_on_lock_upgrade.test +++ b/mysql-test/suite/innodb/t/deadlock_on_lock_upgrade.test @@ -6,6 +6,7 @@ --source include/have_debug.inc --source include/have_debug_sync.inc --source include/count_sessions.inc +--source include/innodb_table_lock_on_full_scan.inc --connection default # There are various scenarious in which a transaction already holds "half" @@ -130,7 +131,13 @@ INSERT INTO t (`id`) VALUES (1), (2); COMMIT; --connection waiter +if ($MTR_COMBINATION_ROW_LOCK) { --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_DEADLOCK + --reap +} --connection default diff --git a/mysql-test/suite/innodb/t/deadlock_victim_race.test b/mysql-test/suite/innodb/t/deadlock_victim_race.test index dac6be698fc60..51e93b5911b94 100644 --- a/mysql-test/suite/innodb/t/deadlock_victim_race.test +++ b/mysql-test/suite/innodb/t/deadlock_victim_race.test @@ -1,6 +1,7 @@ --source include/have_innodb.inc --source include/have_debug_sync.inc --source include/count_sessions.inc +--source include/innodb_table_lock_on_full_scan.inc --disable_query_log call mtr.add_suppression("InnoDB: Transaction was aborted due to "); @@ -36,10 +37,13 @@ SET TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN; # trx 2 # The first time it will be hit on trying to lock (20,20), the second hit # will be on (30,30). -SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont EXECUTE 2'; +if ($MTR_COMBINATION_ROW_LOCK) { + SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont EXECUTE 2'; +} # We must not modify primary key fields to cause rr_sequential() read record # function choosing in mysql_update(), i.e. both query_plan.using_filesort and # query_plan.using_io_buffer must be false during init_read_record() call. +SET SESSION innodb_lock_wait_timeout=1; --send UPDATE t SET b = 100 --connect(con_3,localhost,root,,) @@ -51,7 +55,9 @@ BEGIN; # trx 3 # weight. UPDATE t2 SET a = a + 100; SELECT * FROM t WHERE a = 30 FOR UPDATE; -SET DEBUG_SYNC='now WAIT_FOR upd_locked'; +if ($MTR_COMBINATION_ROW_LOCK) { + SET DEBUG_SYNC='now WAIT_FOR upd_locked'; +} # Locking queue: # (10,10) (20,20) (30,30) # ^ ^ ^ @@ -75,7 +81,9 @@ SET DEBUG_SYNC='now WAIT_FOR sel_locked'; ROLLBACK; SET DEBUG_SYNC='now SIGNAL upd_cont'; -SET DEBUG_SYNC="now WAIT_FOR upd_locked"; +if ($MTR_COMBINATION_ROW_LOCK) { + SET DEBUG_SYNC='now WAIT_FOR upd_locked'; +} # Locking queue: # (10,10) (20,20) (30,30) # ^ ^ ^ @@ -87,10 +95,15 @@ SET DEBUG_SYNC="now WAIT_FOR upd_locked"; # lock on (30,30). But the deadlock has not been determined yet. SET SESSION innodb_lock_wait_timeout=1; ---error ER_LOCK_WAIT_TIMEOUT -# The deadlock will be determined in lock_wait() after lock wait timeout -# expired. -SELECT * FROM t WHERE a = 10 FOR UPDATE; +if ($MTR_COMBINATION_ROW_LOCK) { + --error ER_LOCK_WAIT_TIMEOUT + # The deadlock will be determined in lock_wait() after lock wait timeout + # expired. + SELECT * FROM t WHERE a = 10 FOR UPDATE; +} +if ($MTR_COMBINATION_TABLE_LOCK) { + SELECT * FROM t WHERE a = 10 FOR UPDATE; +} SET DEBUG_SYNC="now SIGNAL upd_cont"; --connection con_3 @@ -103,8 +116,14 @@ SET DEBUG_SYNC="now SIGNAL upd_cont"; # trx 2 was choosen as a victim (see lock_cancel_waiting_and_release() call # from Deadlock::report() for details). The try to update non-locked record # will cause assertion if the bug is not fixed. ---error ER_LOCK_DEADLOCK ---reap +if ($MTR_COMBINATION_ROW_LOCK) { + --error ER_LOCK_DEADLOCK + --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_WAIT_TIMEOUT + --reap +} --disconnect con_3 --disconnect con_2 diff --git a/mysql-test/suite/innodb/t/deadlock_wait_lock_race.test b/mysql-test/suite/innodb/t/deadlock_wait_lock_race.test index 27d02898b724f..cf19de0055c6b 100644 --- a/mysql-test/suite/innodb/t/deadlock_wait_lock_race.test +++ b/mysql-test/suite/innodb/t/deadlock_wait_lock_race.test @@ -1,6 +1,7 @@ --source include/have_innodb.inc --source include/have_debug_sync.inc --source include/count_sessions.inc +--source include/innodb_table_lock_on_full_scan.inc --disable_query_log call mtr.add_suppression("InnoDB: Transaction was aborted due to "); @@ -42,15 +43,20 @@ SET TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN; # trx 2 # The first time it will be hit on trying to lock (20,20), the second hit # will be on (30,30). -SET DEBUG_SYNC = 'lock_trx_handle_wait_before_unlocked_wait_lock_check SIGNAL upd_locked WAIT_FOR upd_cont'; +if ($MTR_COMBINATION_ROW_LOCK) { + SET DEBUG_SYNC = 'lock_trx_handle_wait_before_unlocked_wait_lock_check SIGNAL upd_locked WAIT_FOR upd_cont'; +} +set session innodb_lock_wait_timeout = 1; # We must not modify primary key fields to cause rr_sequential() read record # function choosing in mysql_update(), i.e. both query_plan.using_filesort and # query_plan.using_io_buffer must be false during init_read_record() call. --send UPDATE t SET b = 100 --connection default -SET DEBUG_SYNC="now WAIT_FOR upd_locked"; -SET DEBUG_SYNC="lock_wait_before_suspend SIGNAL upd_cont"; +if ($MTR_COMBINATION_ROW_LOCK) { + SET DEBUG_SYNC="now WAIT_FOR upd_locked"; + SET DEBUG_SYNC="lock_wait_before_suspend SIGNAL upd_cont"; +} --send SELECT * FROM t WHERE a = 10 FOR UPDATE --connection con_2 @@ -58,8 +64,14 @@ SET DEBUG_SYNC="lock_wait_before_suspend SIGNAL upd_cont"; # instead of DB_DEADLOCK, row_search_mvcc() of trx 2 behaves so as if (20,20) # was locked. Some debug assertion must crash the server. If the bug is fixed, # trx 2 must just be rolled back by deadlock detector. ---error ER_LOCK_DEADLOCK ---reap +if ($MTR_COMBINATION_ROW_LOCK) { + --error ER_LOCK_DEADLOCK + --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_WAIT_TIMEOUT + --reap +} --disconnect con_2 diff --git a/mysql-test/suite/innodb/t/deadlock_wait_thr_race.test b/mysql-test/suite/innodb/t/deadlock_wait_thr_race.test index 318db88a470e7..bd5b9e7183333 100644 --- a/mysql-test/suite/innodb/t/deadlock_wait_thr_race.test +++ b/mysql-test/suite/innodb/t/deadlock_wait_thr_race.test @@ -1,6 +1,7 @@ --source include/have_innodb.inc --source include/have_debug_sync.inc --source include/count_sessions.inc +--source include/innodb_table_lock_on_full_scan.inc --disable_query_log call mtr.add_suppression("InnoDB: Transaction was aborted due to "); @@ -40,8 +41,11 @@ SELECT * FROM t WHERE a = 20 FOR UPDATE; # RC is necessary to do semi-consistent read SET TRANSACTION ISOLATION LEVEL READ COMMITTED; # It will be hit on trying to lock (20,20). -SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont'; +if ($MTR_COMBINATION_ROW_LOCK) { + SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont'; +} SET DEBUG_SYNC = 'trx_t_release_locks_enter SIGNAL sel_cont WAIT_FOR upd_cont_2'; +set session innodb_lock_wait_timeout = 1; BEGIN; # trx 2 # We must not modify primary key fields to cause rr_sequential() read record # function choosing in mysql_update(), i.e. both query_plan.using_filesort and @@ -50,21 +54,33 @@ BEGIN; # trx 2 --send UPDATE t SET b = 100 --connection default -SET DEBUG_SYNC="now WAIT_FOR upd_locked"; +if ($MTR_COMBINATION_ROW_LOCK) { + SET DEBUG_SYNC="now WAIT_FOR upd_locked"; +} SET DEBUG_SYNC="deadlock_report_before_lock_releasing SIGNAL upd_cont WAIT_FOR sel_cont"; -SET DEBUG_SYNC="lock_wait_before_suspend SIGNAL sel_before_suspend"; +if ($MTR_COMBINATION_ROW_LOCK) { + SET DEBUG_SYNC="lock_wait_before_suspend SIGNAL sel_before_suspend"; +} # If the bug is not fixed, the following SELECT will crash, as the above UPDATE # will reset trx->lock.wait_thr during rollback --send SELECT * FROM t WHERE a = 10 FOR UPDATE; --connect(con_3,localhost,root,,) -SET DEBUG_SYNC="now WAIT_FOR sel_before_suspend"; +if ($MTR_COMBINATION_ROW_LOCK) { + SET DEBUG_SYNC="now WAIT_FOR sel_before_suspend"; +} SET DEBUG_SYNC="now SIGNAL upd_cont_2"; --disconnect con_3 --connection con_2 ---error ER_LOCK_DEADLOCK ---reap +if ($MTR_COMBINATION_ROW_LOCK) { + --error ER_LOCK_DEADLOCK + --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_WAIT_TIMEOUT + --reap +} --disconnect con_2 --connection default diff --git a/mysql-test/suite/innodb/t/innodb_i_s_innodb_locks.test b/mysql-test/suite/innodb/t/innodb_i_s_innodb_locks.test index e6e46dbf556bc..4b0c07b52323b 100644 --- a/mysql-test/suite/innodb/t/innodb_i_s_innodb_locks.test +++ b/mysql-test/suite/innodb/t/innodb_i_s_innodb_locks.test @@ -4,6 +4,7 @@ # -- source include/have_innodb.inc +--source include/innodb_table_lock_on_full_scan.inc SET @save_timeout=@@GLOBAL.innodb_lock_wait_timeout; SET GLOBAL innodb_lock_wait_timeout=100000000; @@ -134,6 +135,7 @@ if (!$success) -- echo Timeout waiting for rows in INNODB_LOCKS to appear } +--sorted_result SELECT lock_mode, lock_type, lock_table, lock_index, lock_rec, lock_data FROM INFORMATION_SCHEMA.INNODB_LOCKS ORDER BY lock_data; diff --git a/mysql-test/suite/innodb/t/innodb_i_s_innodb_trx.test b/mysql-test/suite/innodb/t/innodb_i_s_innodb_trx.test index 745e1d94a1222..5ca5c05bce945 100644 --- a/mysql-test/suite/innodb/t/innodb_i_s_innodb_trx.test +++ b/mysql-test/suite/innodb/t/innodb_i_s_innodb_trx.test @@ -1,4 +1,5 @@ --source include/have_innodb.inc +--source include/innodb_table_lock_on_full_scan.inc # # Test that transaction data is correctly "visualized" in diff --git a/mysql-test/suite/innodb/t/innodb_information_schema.test b/mysql-test/suite/innodb/t/innodb_information_schema.test index 1fbf9f7eb5761..bf8015026bd6b 100644 --- a/mysql-test/suite/innodb/t/innodb_information_schema.test +++ b/mysql-test/suite/innodb/t/innodb_information_schema.test @@ -4,6 +4,7 @@ # -- source include/have_innodb.inc +--source include/innodb_table_lock_on_full_scan.inc # lock data that is part of result set for this testcase # is retreived using buf_page_try_get. i.e only show if page diff --git a/mysql-test/suite/innodb/t/innodb_lock_wait_timeout_1.test b/mysql-test/suite/innodb/t/innodb_lock_wait_timeout_1.test index 56a86a2c4d90b..1021e35dd4864 100644 --- a/mysql-test/suite/innodb/t/innodb_lock_wait_timeout_1.test +++ b/mysql-test/suite/innodb/t/innodb_lock_wait_timeout_1.test @@ -1,4 +1,5 @@ --source include/have_innodb.inc +--source include/innodb_table_lock_on_full_scan.inc --echo # --echo # Bug #40113: Embedded SELECT inside UPDATE or DELETE can timeout @@ -199,7 +200,18 @@ connection con1; begin; --echo # Since there is another distinct record in the derived table --echo # the previous matching record in t1 -- (2,null) -- was unlocked. -delete from t1; +set @old_innodb_lock_wait_timeout=@@innodb_lock_wait_timeout; +set innodb_lock_wait_timeout=1; +if ($MTR_COMBINATION_ROW_LOCK) { + delete from t1; +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --disable_result_log + --error ER_LOCK_WAIT_TIMEOUT + delete from t1; + --enable_result_log +} +set innodb_lock_wait_timeout=@old_innodb_lock_wait_timeout; --echo # We will need the contents of the table again. rollback; select * from t1; diff --git a/mysql-test/suite/innodb/t/innodb_trx_weight.test b/mysql-test/suite/innodb/t/innodb_trx_weight.test index a8c29a1a8a847..3e8466df3e267 100644 --- a/mysql-test/suite/innodb/t/innodb_trx_weight.test +++ b/mysql-test/suite/innodb/t/innodb_trx_weight.test @@ -11,6 +11,7 @@ call mtr.add_suppression("InnoDB: Transaction was aborted due to "); --enable_query_log -- source include/have_innodb.inc +--source include/innodb_table_lock_on_full_scan.inc SET default_storage_engine=InnoDB; @@ -60,6 +61,7 @@ INSERT INTO t3 SELECT * FROM t3; # test locking weight +-- let $insert_deadlock = 0 -- let $con1_extra_sql = -- let $con1_extra_sql_present = 0 -- let $con2_extra_sql = SELECT * FROM t3 FOR UPDATE @@ -72,6 +74,9 @@ INSERT INTO t3 SELECT * FROM t3; -- let $con2_extra_sql = SELECT * FROM t3 FOR UPDATE -- let $con2_extra_sql_present = 1 -- let $con1_should_be_rolledback = 1 +if ($MTR_COMBINATION_TABLE_LOCK) { + -- let $insert_deadlock = 1 +} -- source include/innodb_trx_weight.inc -- let $con1_extra_sql = INSERT INTO t4 VALUES (1), (1), (1), (1), (1), (1) @@ -90,6 +95,7 @@ INSERT INTO t3 SELECT * FROM t3; -- let $con1_should_be_rolledback = 0 -- source include/innodb_trx_weight.inc +-- let $insert_deadlock = 0 -- let $con1_extra_sql = INSERT INTO t4 VALUES (1), (1), (1) -- let $con1_extra_sql_present = 1 -- let $con2_extra_sql = INSERT INTO t5_nontrans VALUES (1) diff --git a/mysql-test/suite/innodb/t/insert_into_empty.test b/mysql-test/suite/innodb/t/insert_into_empty.test index 9c544a3532eec..770ba056af049 100644 --- a/mysql-test/suite/innodb/t/insert_into_empty.test +++ b/mysql-test/suite/innodb/t/insert_into_empty.test @@ -1,5 +1,6 @@ --source include/have_innodb.inc --source include/innodb_page_size.inc +--source include/innodb_table_lock_on_full_scan.inc --source include/have_sequence.inc --source include/maybe_debug.inc --source include/have_partition.inc @@ -171,10 +172,17 @@ SET GLOBAL innodb_limit_optimistic_insert_debug = 2; BEGIN; SELECT * FROM t1 LOCK IN SHARE MODE; INSERT INTO t1 VALUES (0),(1),(2); +--disable_ps_protocol --error ER_WRONG_VALUE_COUNT_ON_ROW INSERT INTO t1 VALUES (0,1); ---error ER_DUP_ENTRY -INSERT INTO t1 VALUES (2); +--enable_ps_protocol +if ($MTR_COMBINATION_ROW_LOCK) { + --error ER_DUP_ENTRY + INSERT INTO t1 VALUES (2); +} +if ($MTR_COMBINATION_TABLE_LOCK) { + INSERT INTO t1 VALUES (2); +} COMMIT; --error 0,ER_UNKNOWN_SYSTEM_VARIABLE diff --git a/mysql-test/suite/innodb/t/lock_delete_updated.test b/mysql-test/suite/innodb/t/lock_delete_updated.test index 8697ff595ab0b..60ed572e7d6dc 100644 --- a/mysql-test/suite/innodb/t/lock_delete_updated.test +++ b/mysql-test/suite/innodb/t/lock_delete_updated.test @@ -2,6 +2,7 @@ --source include/count_sessions.inc --source include/have_debug.inc --source include/have_debug_sync.inc +--source include/innodb_table_lock_on_full_scan.inc --disable_query_log call mtr.add_suppression("InnoDB: Transaction was aborted due to "); @@ -23,8 +24,13 @@ UPDATE t SET a = 1; COMMIT; connection con1; -error ER_LOCK_DEADLOCK; -reap; +if ($MTR_COMBINATION_ROW_LOCK) { + error ER_LOCK_DEADLOCK; + reap; +} +if ($MTR_COMBINATION_TABLE_LOCK) { + reap; +} disconnect con1; connection default; diff --git a/mysql-test/suite/innodb/t/lock_isolation.test b/mysql-test/suite/innodb/t/lock_isolation.test index 55305fab27e5f..d30b97f2be36e 100644 --- a/mysql-test/suite/innodb/t/lock_isolation.test +++ b/mysql-test/suite/innodb/t/lock_isolation.test @@ -2,6 +2,7 @@ --source include/count_sessions.inc --source include/have_debug.inc --source include/have_debug_sync.inc +--source include/innodb_table_lock_on_full_scan.inc --disable_query_log call mtr.add_suppression("InnoDB: Transaction was aborted due to "); diff --git a/mysql-test/suite/innodb/t/lock_memory_debug.test b/mysql-test/suite/innodb/t/lock_memory_debug.test index 58a76740dcb0b..c67b92ac002ee 100644 --- a/mysql-test/suite/innodb/t/lock_memory_debug.test +++ b/mysql-test/suite/innodb/t/lock_memory_debug.test @@ -1,5 +1,6 @@ --source include/have_innodb.inc --source include/have_debug.inc +--source include/innodb_table_lock_on_full_scan.inc --echo # --echo # MDEV-34166 Server could hang with BP < 80M under stress @@ -13,9 +14,15 @@ call mtr.add_suppression("InnoDB: Transaction was aborted due to "); CREATE TABLE t1 (col1 INT) ENGINE=InnoDB; INSERT INTO t1 VALUES (1),(2),(3),(4),(5); ---error ER_LOCK_TABLE_FULL -SET STATEMENT debug_dbug='+d,innodb_skip_lock_bitmap' FOR -INSERT INTO t1 SELECT a.* FROM t1 a, t1 b, t1 c, t1 d, t1 e, t1 f, t1 g; +if ($MTR_COMBINATION_ROW_LOCK) { + --error ER_LOCK_TABLE_FULL + SET STATEMENT debug_dbug='+d,innodb_skip_lock_bitmap' FOR + INSERT INTO t1 SELECT a.* FROM t1 a, t1 b, t1 c, t1 d, t1 e, t1 f, t1 g; +} +if ($MTR_COMBINATION_TABLE_LOCK) { + SET STATEMENT debug_dbug='+d,innodb_skip_lock_bitmap' FOR + INSERT INTO t1 SELECT a.* FROM t1 a, t1 b, t1 c, t1 d, t1 e, t1 f, t1 g; +} SELECT COUNT(*) FROM t1; diff --git a/mysql-test/suite/innodb/t/mdev-14846.test b/mysql-test/suite/innodb/t/mdev-14846.test index fac010871feda..d2e394fe14b26 100644 --- a/mysql-test/suite/innodb/t/mdev-14846.test +++ b/mysql-test/suite/innodb/t/mdev-14846.test @@ -3,6 +3,7 @@ --source include/have_debug_sync.inc --source include/innodb_stable_estimates.inc +--source include/innodb_table_lock_on_full_scan.inc --disable_query_log call mtr.add_suppression("InnoDB: Transaction was aborted due to "); @@ -63,13 +64,24 @@ disconnect con2; SET DEBUG_SYNC='now WAIT_FOR con1_dml2'; --replace_column 9 # explain UPDATE v4, t1 SET t1.pk = 76 WHERE t1.f2 IN ( SELECT t2.f FROM t2 INNER JOIN t3 ); -UPDATE v4, t1 SET t1.pk = 76 WHERE t1.f2 IN ( SELECT t2.f FROM t2 INNER JOIN t3 ); +if ($MTR_COMBINATION_ROW_LOCK) { + UPDATE v4, t1 SET t1.pk = 76 WHERE t1.f2 IN ( SELECT t2.f FROM t2 INNER JOIN t3 ); +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_DEADLOCK + UPDATE v4, t1 SET t1.pk = 76 WHERE t1.f2 IN ( SELECT t2.f FROM t2 INNER JOIN t3 ); +} # It holds the record lock on table t1 and tries to acquire record lock on t3. # leads to deadlock (con1 trx is waiting for default trx and vice versa) --connection default ---error ER_LOCK_DEADLOCK ---reap +if ($MTR_COMBINATION_ROW_LOCK) { + --error ER_LOCK_DEADLOCK + --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --reap +} connection con1; COMMIT; diff --git a/mysql-test/suite/innodb/t/row_lock.test b/mysql-test/suite/innodb/t/row_lock.test index 2598189d0e2d6..df1792b19943e 100644 --- a/mysql-test/suite/innodb/t/row_lock.test +++ b/mysql-test/suite/innodb/t/row_lock.test @@ -9,6 +9,7 @@ call mtr.add_suppression("InnoDB: Transaction was aborted due to "); --enable_query_log --source include/have_innodb.inc +--source include/innodb_table_lock_on_full_scan.inc CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB; INSERT INTO t1 VALUES (1,1),(2,2); @@ -38,11 +39,22 @@ let $wait_condition= trx_query like "%SELECT a FROM t1%"; --source include/wait_condition.inc -UPDATE t4 SET d = 9; +if ($MTR_COMBINATION_ROW_LOCK) { + UPDATE t4 SET d = 9; +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --error ER_LOCK_DEADLOCK + UPDATE t4 SET d = 9; +} --connection con12 ---error ER_LOCK_DEADLOCK ---reap +if ($MTR_COMBINATION_ROW_LOCK) { + --error ER_LOCK_DEADLOCK + --reap +} +if ($MTR_COMBINATION_TABLE_LOCK) { + --reap +} --connection con11 commit; --connection default diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result index cad0893798fc2..41a3abb995405 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result +++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result @@ -1594,6 +1594,18 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST OFF,ON READ_ONLY NO COMMAND_LINE_ARGUMENT OPTIONAL +VARIABLE_NAME INNODB_TABLE_LOCK_ON_FULL_SCAN +SESSION_VALUE OFF +DEFAULT_VALUE OFF +VARIABLE_SCOPE SESSION +VARIABLE_TYPE BOOLEAN +VARIABLE_COMMENT Whether to acquire table lock when SQL layer advises full scan +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,ON +READ_ONLY NO +COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME INNODB_TEMP_DATA_FILE_PATH SESSION_VALUE NULL DEFAULT_VALUE ibtmp1:12M:autoextend diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index e1b9e605a246a..7cccc5f751eb5 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -9496,6 +9496,8 @@ int ha_partition::extra(enum ha_extra_function operation) case HA_EXTRA_ABORT_COPY: case HA_EXTRA_BEGIN_ALTER_IGNORE_COPY: DBUG_RETURN(loop_partitions(extra_cb, &operation)); + case HA_EXTRA_FULL_SCAN: + break; default: { /* Temporary crash to discover what is wrong */ @@ -9572,6 +9574,8 @@ int ha_partition::extra_opt(enum ha_extra_function operation, ulong arg) case HA_EXTRA_CACHE: prepare_extra_cache(arg); DBUG_RETURN(0); + case HA_EXTRA_FULL_SCAN: + DBUG_RETURN(0); default: DBUG_ASSERT(0); } diff --git a/sql/handler.cc b/sql/handler.cc index 600a20edfe2cd..4c32fecb594e4 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -8697,6 +8697,13 @@ int get_select_field_pos(Alter_info *alter_info, int select_field_count, return select_field_pos; } +void init_table_full_scan_if_needed(TABLE *table, Item *cond, ha_rows limit) +{ + if (!cond) + table->file->extra_opt(HA_EXTRA_FULL_SCAN, + limit < ULONG_MAX ? (ulong) limit : ULONG_MAX); +} + bool Table_scope_and_contents_source_st::vers_check_system_fields( THD *thd, Alter_info *alter_info, const Lex_table_name &table_name, diff --git a/sql/handler.h b/sql/handler.h index d45e8c2d00b26..9b09c0e5ef0a0 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -5813,4 +5813,5 @@ int get_select_field_pos(Alter_info *alter_info, int select_field_count, #ifndef DBUG_OFF String dbug_format_row(TABLE *table, const uchar *rec, bool print_names= true); #endif /* DBUG_OFF */ +void init_table_full_scan_if_needed(TABLE *table, Item *cond, ha_rows limit); #endif /* HANDLER_INCLUDED */ diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index fed5b4c7f7ec8..15577fbe64e4d 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -774,6 +774,7 @@ bool Sql_cmd_delete::delete_from_single_table(THD *thd) if (select && select->quick && select->quick->reset()) goto got_error; + init_table_full_scan_if_needed(table, conds, limit); if (query_plan.index == MAX_KEY || (select && select->quick)) error= init_read_record(&info, thd, table, select, file_sort, 1, 1, FALSE); else diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 633b65499b578..9abb266e2d109 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -25338,6 +25338,7 @@ int test_if_use_dynamic_range_scan(JOIN_TAB *join_tab) int join_init_read_record(JOIN_TAB *tab) { bool need_unpacking= FALSE; + /* TODO: s/tab->join/join/g in this function */ JOIN *join= tab->join; /* Note: the query plan tree for the below operations is constructed in @@ -25387,7 +25388,11 @@ int join_init_read_record(JOIN_TAB *tab) */ save_copy= tab->read_record.copy_field; save_copy_end= tab->read_record.copy_field_end; - + + init_table_full_scan_if_needed(tab->table, + tab->select ? tab->select->cond : NULL, + join->unit->lim.get_select_limit()); + /* JT_NEXT means that we should use an index scan on index 'tab->index' However if filesort is set, the table was already sorted above @@ -25459,6 +25464,9 @@ join_read_first(JOIN_TAB *tab) tab->table->status=0; tab->read_record.read_record_func= join_read_next; tab->read_record.table=table; + init_table_full_scan_if_needed(tab->table, + tab->select ? tab->select->cond : NULL, + tab->join->unit->lim.get_select_limit()); if (!table->file->inited) error= table->file->ha_index_init(tab->index, tab->sorted); if (likely(!error)) @@ -25498,6 +25506,9 @@ join_read_last(JOIN_TAB *tab) tab->table->status=0; tab->read_record.read_record_func= join_read_prev; tab->read_record.table=table; + init_table_full_scan_if_needed(tab->table, + tab->select ? tab->select->cond : NULL, + tab->join->unit->lim.get_select_limit()); if (!table->file->inited) error= table->file->ha_index_init(tab->index, 1); if (likely(!error)) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 76e3b7fb47ca8..0a76d3b8bbf38 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -791,6 +791,8 @@ bool Sql_cmd_update::update_single_table(THD *thd) Full index scan must be started with init_read_record_idx */ + init_table_full_scan_if_needed(table, conds, limit); + if (query_plan.index == MAX_KEY || (select && select->quick)) error= init_read_record(&info, thd, table, select, NULL, 0, 1, FALSE); else @@ -886,6 +888,7 @@ bool Sql_cmd_update::update_single_table(THD *thd) if (select && select->quick && select->quick->reset()) goto err; table->file->try_semi_consistent_read(1); + init_table_full_scan_if_needed(table, conds, limit); if (init_read_record(&info, thd, table, select, file_sort, 0, 1, FALSE)) goto err; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 4a0b72651f8f2..a31728287a45d 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -864,6 +864,10 @@ static MYSQL_THDVAR_BOOL(ft_enable_stopword, PLUGIN_VAR_OPCMDARG, NULL, NULL, /* default */ TRUE); +static MYSQL_THDVAR_BOOL(table_lock_on_full_scan, PLUGIN_VAR_OPCMDARG, + "Whether to acquire table lock when SQL layer advises full scan", + NULL, NULL, FALSE); + static MYSQL_THDVAR_UINT(lock_wait_timeout, PLUGIN_VAR_RQCMDARG, "Timeout in seconds an InnoDB transaction may wait for a lock before being rolled back. The value 100000000 is infinite timeout.", NULL, NULL, 50, 0, 100000000, 0); @@ -15940,6 +15944,31 @@ ha_innobase::extra( return(0); } +int +ha_innobase::extra_opt( +/*===============*/ + enum ha_extra_function operation, + /*!< in: HA_EXTRA_FLUSH or some other flag */ + ulong arg) +{ + bool handled = false; + switch (operation) { + case HA_EXTRA_FULL_SCAN: + handled = true; + if (THDVAR(ha_thd(), table_lock_on_full_scan) && + !m_prebuilt->skip_locked && arg == ULONG_MAX) + m_prebuilt->full_table_scan = true; + break; + default:/* Do nothing */ + ; + } + + if (!handled) + return extra(operation); + + return(0); +} + /** MySQL calls this method at the end of each statement */ int @@ -15960,6 +15989,7 @@ ha_innobase::reset() m_prebuilt->autoinc_last_value = 0; m_prebuilt->skip_locked = false; + m_prebuilt->full_table_scan = false; return(0); } @@ -19926,6 +19956,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(ft_num_word_optimize), MYSQL_SYSVAR(ft_sort_pll_degree), MYSQL_SYSVAR(lock_wait_timeout), + MYSQL_SYSVAR(table_lock_on_full_scan), MYSQL_SYSVAR(deadlock_detect), MYSQL_SYSVAR(deadlock_report), MYSQL_SYSVAR(page_size), diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 9ac4eed4f1f52..a499a20ee4261 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -177,6 +177,8 @@ class ha_innobase final : public handler int extra(ha_extra_function operation) override; + int extra_opt(ha_extra_function operation, ulong arg) override; + int reset() override; int external_lock(THD *thd, int lock_type) override; diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 63858f25f023e..b1dcb147b2a22 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -695,6 +695,11 @@ struct row_prebuilt_t { /** The MySQL table object */ TABLE* m_mysql_table; + /** If TRUE, the SQL layer has advised that we're doing full scan. + A locking read will acquire a table-level lock (X or S) instead + of acquiring row-level locks. */ + bool full_table_scan; + /** Get template by dict_table_t::cols[] number */ const mysql_row_templ_t* get_template_by_col(ulint col) const { diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index fcbc9e955599d..64c6c3404c456 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -4718,8 +4718,10 @@ row_search_mvcc( } else { wait_table_again: err = lock_table(prebuilt->table, nullptr, - prebuilt->select_lock_type == LOCK_S - ? LOCK_IS : LOCK_IX, thr); + prebuilt->full_table_scan ? + prebuilt->select_lock_type : + (prebuilt->select_lock_type == LOCK_S + ? LOCK_IS : LOCK_IX), thr); if (err != DB_SUCCESS) { diff --git a/storage/mroonga/ha_mroonga.cpp b/storage/mroonga/ha_mroonga.cpp index 37a690e11880c..933deee7221cd 100644 --- a/storage/mroonga/ha_mroonga.cpp +++ b/storage/mroonga/ha_mroonga.cpp @@ -564,6 +564,9 @@ static const char *mrn_inspect_extra_function(enum ha_extra_function operation) case HA_EXTRA_BEGIN_ALTER_IGNORE_COPY: inspected = "HA_EXTRA_BEGIN_ALTER_IGNORE_COPY"; break; + case HA_EXTRA_FULL_SCAN: + inspected = "HA_EXTRA_FULL_SCAN"; + break; #ifdef MRN_HAVE_HA_EXTRA_EXPORT case HA_EXTRA_EXPORT: inspected = "HA_EXTRA_EXPORT"; From 8ab8cc5069aa00b09dfc460efe47d20196b25cba Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Fri, 5 Jun 2026 12:52:00 +0530 Subject: [PATCH 2/2] MDEV-24813 Drop low-value table_lock test combinations The three deadlock_*_race tests cannot reach their DEBUG_SYNC race under a table lock (it degenerates to a timeout), and the three I_S tests only restate a lock-mode change already covered by innodb_full_scan.test. - Addressed review comments --- .../main/innodb_full_scan,table_lock.rdiff | 121 +++++++++++ ...l_scan-master.opt => innodb_full_scan.opt} | 0 mysql-test/main/innodb_full_scan.result | 154 +++++--------- mysql-test/main/innodb_full_scan.test | 200 ++++++++++++------ .../r/deadlock_victim_race,table_lock.rdiff | 37 ---- .../innodb/r/deadlock_victim_race.result | 3 +- .../deadlock_wait_lock_race,table_lock.rdiff | 19 -- .../innodb/r/deadlock_wait_lock_race.result | 1 - .../r/deadlock_wait_thr_race,table_lock.rdiff | 26 --- .../innodb/r/deadlock_wait_thr_race.result | 1 - .../innodb_i_s_innodb_locks,table_lock.rdiff | 53 ----- .../innodb/r/innodb_i_s_innodb_locks.result | Bin 5451 -> 5451 bytes .../r/innodb_i_s_innodb_trx,table_lock.rdiff | 11 - ...innodb_information_schema,table_lock.rdiff | 62 ------ .../suite/innodb/t/deadlock_victim_race.test | 37 +--- .../innodb/t/deadlock_wait_lock_race.test | 22 +- .../innodb/t/deadlock_wait_thr_race.test | 28 +-- .../innodb/t/innodb_i_s_innodb_locks.test | 2 - .../suite/innodb/t/innodb_i_s_innodb_trx.test | 1 - .../innodb/t/innodb_information_schema.test | 1 - .../suite/innodb/t/insert_into_empty.test | 2 + .../suite/sys_vars/r/sysvars_innodb.result | 2 +- storage/innobase/handler/ha_innodb.cc | 52 ++--- 23 files changed, 362 insertions(+), 473 deletions(-) create mode 100644 mysql-test/main/innodb_full_scan,table_lock.rdiff rename mysql-test/main/{innodb_full_scan-master.opt => innodb_full_scan.opt} (100%) delete mode 100644 mysql-test/suite/innodb/r/deadlock_victim_race,table_lock.rdiff delete mode 100644 mysql-test/suite/innodb/r/deadlock_wait_lock_race,table_lock.rdiff delete mode 100644 mysql-test/suite/innodb/r/deadlock_wait_thr_race,table_lock.rdiff delete mode 100644 mysql-test/suite/innodb/r/innodb_i_s_innodb_locks,table_lock.rdiff delete mode 100644 mysql-test/suite/innodb/r/innodb_i_s_innodb_trx,table_lock.rdiff delete mode 100644 mysql-test/suite/innodb/r/innodb_information_schema,table_lock.rdiff diff --git a/mysql-test/main/innodb_full_scan,table_lock.rdiff b/mysql-test/main/innodb_full_scan,table_lock.rdiff new file mode 100644 index 0000000000000..5bf2581eb6042 --- /dev/null +++ b/mysql-test/main/innodb_full_scan,table_lock.rdiff @@ -0,0 +1,121 @@ +--- innodb_full_scan.result ++++ innodb_full_scan,table_lock.result +@@ -10,14 +10,14 @@ + 42 + COMMIT; + # lock_rec_created +-1 ++0 + BEGIN; + SELECT * FROM t for update; + a + 42 + COMMIT; + # lock_rec_created +-1 ++0 + ## SELECT without explicit transaction + SELECT * FROM t; + a +@@ -55,13 +55,13 @@ + UNION + select * from t1; + # lock_rec_created +-3 ++1 + insert into t2 + select * from t1 + UNION + select * from t1 where t1.a = 10; + # lock_rec_created +-3 ++1 + insert into t2 + select * from t1 where t1.a > 10 + UNION +@@ -73,7 +73,7 @@ + UNION + select * from t1 where t1.a > 10; + # lock_rec_created +-1 ++0 + drop table t1, t2; + # CREATE ... SELECT with UNION + create table t1 (a int primary key, b int) engine=innodb; +@@ -83,13 +83,13 @@ + UNION + select * from t1; + # lock_rec_created +-3 ++1 + create or replace table t2 (a int, b int) engine=innodb + select * from t1 + UNION + select * from t1 where t1.a = 10; + # lock_rec_created +-3 ++1 + create or replace table t2 (a int, b int) engine=innodb + select * from t1 where t1.a > 10 + UNION +@@ -101,14 +101,14 @@ + UNION + select * from t1 where t1.a > 10; + # lock_rec_created +-1 ++0 + drop table t1, t2; + # UPDATE + create table t1 (a int primary key, b int) engine=innodb; + insert into t1 select seq, seq from seq_1_to_100; + UPDATE t1 SET b = b + 3; + # lock_rec_created +-1 ++0 + UPDATE t1 SET b = b + 3 WHERE a = 10; + # lock_rec_created + 1 +@@ -118,7 +118,7 @@ + # DELETE + DELETE FROM t1; + # lock_rec_created +-1 ++0 + DELETE FROM t1 WHERE a = 10; + # lock_rec_created + 1 +@@ -135,7 +135,7 @@ + select * from t1 join t2 on t1.a = t2.a LOCK IN SHARE MODE; + commit; + # lock_rec_created +-2 ++1 + begin; + select * from t1 join t2 on t1.a = t2.b LOCK IN SHARE MODE; + commit; +@@ -145,7 +145,7 @@ + select * from t1 join t2 on t1.b = t2.b LOCK IN SHARE MODE; + commit; + # lock_rec_created +-2 ++1 + drop table t1, t2; + # Full index scan + create table t10 (a int, b varchar(100), index(a)) engine=innodb; +@@ -153,14 +153,14 @@ + create table t11(a int) engine=innodb; + insert into t11 select a from t10; + # lock_rec_created +-11 ++0 + drop table t10, t11; + create table t10 (a int, b varchar(100), index(a)) engine=innodb; + insert into t10 select seq, uuid() from seq_1_to_10000; + create table t11(a int) engine=innodb; + insert into t11 select a from t10 order by a desc; + # lock_rec_created +-11 ++0 + drop table t10, t11; + # Range access + create table t10 (a int, b varchar(100), index(a)) engine=innodb; diff --git a/mysql-test/main/innodb_full_scan-master.opt b/mysql-test/main/innodb_full_scan.opt similarity index 100% rename from mysql-test/main/innodb_full_scan-master.opt rename to mysql-test/main/innodb_full_scan.opt diff --git a/mysql-test/main/innodb_full_scan.result b/mysql-test/main/innodb_full_scan.result index 5c2bfedaa5481..d8eddbdfda22a 100644 --- a/mysql-test/main/innodb_full_scan.result +++ b/mysql-test/main/innodb_full_scan.result @@ -1,37 +1,28 @@ # # MDEV-24813 Locking full table scan fails to use table-level locking # -set @old_innodb_table_lock_on_full_scan=@@innodb_table_lock_on_full_scan; -set innodb_table_lock_on_full_scan=1; CREATE TABLE t (a INT PRIMARY KEY) ENGINE=InnoDB; insert into t values (42); # SELECT -set @init=(SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'); BEGIN; SELECT * FROM t LOCK IN SHARE MODE; a 42 COMMIT; -## +0, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -0 +# lock_rec_created +1 BEGIN; SELECT * FROM t for update; a 42 COMMIT; -## +0, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -0 +# lock_rec_created +1 ## SELECT without explicit transaction SELECT * FROM t; a 42 -### +0, was +0 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT +# lock_rec_created 0 # SELECT with a condition BEGIN; @@ -39,27 +30,21 @@ select * from t where a > 10 lock in share mode; a 42 COMMIT; -### +1, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT +# lock_rec_created 1 BEGIN; SELECT * FROM t where a > 10 for update; a 42 COMMIT; -## +1, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -2 +# lock_rec_created +1 ## SELECT with a condition without explicit transaction SELECT * FROM t where a > 10; a 42 -### +0, was +0 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -2 +# lock_rec_created +0 DROP TABLE t; # INSERT INTO ... SELECT with UNION create table t1 (a int primary key, b int) engine=innodb; @@ -69,34 +54,26 @@ insert into t2 select * from t1 where t1.a = 10 UNION select * from t1; -## +1, was +3 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT +# lock_rec_created 3 insert into t2 select * from t1 UNION select * from t1 where t1.a = 10; -## +1, was +3 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -4 +# lock_rec_created +3 insert into t2 select * from t1 where t1.a > 10 UNION select * from t1; -## +1, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -5 +# lock_rec_created +1 insert into t2 select * from t1 UNION select * from t1 where t1.a > 10; -## +0, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -5 +# lock_rec_created +1 drop table t1, t2; # CREATE ... SELECT with UNION create table t1 (a int primary key, b int) engine=innodb; @@ -105,69 +82,49 @@ create table t2 (a int, b int) engine=innodb select * from t1 where t1.a = 10 UNION select * from t1; -## +1, was +3 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -6 +# lock_rec_created +3 create or replace table t2 (a int, b int) engine=innodb select * from t1 UNION select * from t1 where t1.a = 10; -## +1, was +3 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -7 +# lock_rec_created +3 create or replace table t2 (a int, b int) engine=innodb select * from t1 where t1.a > 10 UNION select * from t1; -## +1, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -8 +# lock_rec_created +1 create or replace table t2 (a int, b int) engine=innodb select * from t1 UNION select * from t1 where t1.a > 10; -## +0, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -8 +# lock_rec_created +1 drop table t1, t2; # UPDATE create table t1 (a int primary key, b int) engine=innodb; insert into t1 select seq, seq from seq_1_to_100; UPDATE t1 SET b = b + 3; -## +0, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -8 +# lock_rec_created +1 UPDATE t1 SET b = b + 3 WHERE a = 10; -## +1, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -9 +# lock_rec_created +1 UPDATE t1 SET b = b + 3 WHERE a > 10; -## +1, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -10 +# lock_rec_created +1 # DELETE DELETE FROM t1; -## +0, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -10 +# lock_rec_created +1 DELETE FROM t1 WHERE a = 10; -## +1, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -11 +# lock_rec_created +1 DELETE FROM t1 WHERE a > 10; -## +1, was +1 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -12 +# lock_rec_created +1 drop table t1; # JOIN create table t1 (a int primary key, b int) engine=innodb; @@ -177,43 +134,33 @@ insert into t2 select seq, seq from seq_1_to_100; begin; select * from t1 join t2 on t1.a = t2.a LOCK IN SHARE MODE; commit; -## +1, was +2 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -13 +# lock_rec_created +2 begin; select * from t1 join t2 on t1.a = t2.b LOCK IN SHARE MODE; commit; -## +2, was +2 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -15 +# lock_rec_created +2 begin; select * from t1 join t2 on t1.b = t2.b LOCK IN SHARE MODE; commit; -## +1, was +2 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -16 +# lock_rec_created +2 drop table t1, t2; # Full index scan create table t10 (a int, b varchar(100), index(a)) engine=innodb; insert into t10 select seq, uuid() from seq_1_to_10000; create table t11(a int) engine=innodb; insert into t11 select a from t10; -## +0, was +11 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -16 +# lock_rec_created +11 drop table t10, t11; create table t10 (a int, b varchar(100), index(a)) engine=innodb; insert into t10 select seq, uuid() from seq_1_to_10000; create table t11(a int) engine=innodb; insert into t11 select a from t10 order by a desc; -## +0, was +11 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -16 +# lock_rec_created +11 drop table t10, t11; # Range access create table t10 (a int, b varchar(100), index(a)) engine=innodb; @@ -224,9 +171,6 @@ insert into t11 select a from t10 where a > 30; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t10 range a a 5 NULL # Using where; Using index insert into t11 select a from t10 where a > 30; -## +11, was +11 -SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; -COUNT -27 +# lock_rec_created +11 drop table t10, t11; -set innodb_table_lock_on_full_scan=@old_innodb_table_lock_on_full_scan; diff --git a/mysql-test/main/innodb_full_scan.test b/mysql-test/main/innodb_full_scan.test index ceff613a6eec0..31eaf40d669ec 100644 --- a/mysql-test/main/innodb_full_scan.test +++ b/mysql-test/main/innodb_full_scan.test @@ -1,55 +1,73 @@ --source include/have_sequence.inc --source include/have_innodb.inc --source include/no_view_protocol.inc +--source include/innodb_table_lock_on_full_scan.inc --echo # --echo # MDEV-24813 Locking full table scan fails to use table-level locking --echo # -set @old_innodb_table_lock_on_full_scan=@@innodb_table_lock_on_full_scan; -set innodb_table_lock_on_full_scan=1; - CREATE TABLE t (a INT PRIMARY KEY) ENGINE=InnoDB; insert into t values (42); --echo # SELECT -# The .opt file sets innodb_monitor_enable=lock_rec_lock_created -set @init=(SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'); -let print_lock_rec_lock_created=SELECT (COUNT-@init) as COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +# The .opt file sets innodb_monitor_enable=lock_rec_lock_created. +# $rec_lock_created reads the counter; capture it into $start_val before each +# measured statement and into $end_val after, then print ($end_val - $start_val) +# as the per-statement number of record locks created. +let $rec_lock_created= SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME = 'lock_rec_lock_created'; +let $start_val= `$rec_lock_created`; BEGIN; SELECT * FROM t LOCK IN SHARE MODE; COMMIT; ---echo ## +0, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; +let $start_val= `$rec_lock_created`; BEGIN; SELECT * FROM t for update; COMMIT; ---echo ## +0, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; --echo ## SELECT without explicit transaction +let $start_val= `$rec_lock_created`; SELECT * FROM t; ---echo ### +0, was +0 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; --echo # SELECT with a condition +let $start_val= `$rec_lock_created`; BEGIN; select * from t where a > 10 lock in share mode; COMMIT; ---echo ### +1, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; +let $start_val= `$rec_lock_created`; BEGIN; SELECT * FROM t where a > 10 for update; COMMIT; ---echo ## +1, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; --echo ## SELECT with a condition without explicit transaction +let $start_val= `$rec_lock_created`; SELECT * FROM t where a > 10; ---echo ### +0, was +0 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; DROP TABLE t; @@ -57,66 +75,90 @@ DROP TABLE t; create table t1 (a int primary key, b int) engine=innodb; insert into t1 select seq, seq from seq_1_to_100; create table t2 (a int, b int) engine=innodb; +let $start_val= `$rec_lock_created`; insert into t2 select * from t1 where t1.a = 10 UNION select * from t1; ---echo ## +1, was +3 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; +let $start_val= `$rec_lock_created`; insert into t2 select * from t1 UNION select * from t1 where t1.a = 10; ---echo ## +1, was +3 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; +let $start_val= `$rec_lock_created`; insert into t2 select * from t1 where t1.a > 10 UNION select * from t1; ---echo ## +1, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; +let $start_val= `$rec_lock_created`; insert into t2 select * from t1 UNION select * from t1 where t1.a > 10; ---echo ## +0, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; drop table t1, t2; --echo # CREATE ... SELECT with UNION create table t1 (a int primary key, b int) engine=innodb; insert into t1 select seq, seq from seq_1_to_100; +let $start_val= `$rec_lock_created`; create table t2 (a int, b int) engine=innodb select * from t1 where t1.a = 10 UNION select * from t1; ---echo ## +1, was +3 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; +let $start_val= `$rec_lock_created`; create or replace table t2 (a int, b int) engine=innodb select * from t1 UNION select * from t1 where t1.a = 10; ---echo ## +1, was +3 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; +let $start_val= `$rec_lock_created`; create or replace table t2 (a int, b int) engine=innodb select * from t1 where t1.a > 10 UNION select * from t1; ---echo ## +1, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; +let $start_val= `$rec_lock_created`; create or replace table t2 (a int, b int) engine=innodb select * from t1 UNION select * from t1 where t1.a > 10; ---echo ## +0, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; drop table t1, t2; @@ -124,26 +166,48 @@ drop table t1, t2; create table t1 (a int primary key, b int) engine=innodb; insert into t1 select seq, seq from seq_1_to_100; +let $start_val= `$rec_lock_created`; UPDATE t1 SET b = b + 3; ---echo ## +0, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; + +let $start_val= `$rec_lock_created`; UPDATE t1 SET b = b + 3 WHERE a = 10; ---echo ## +1, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; + +let $start_val= `$rec_lock_created`; UPDATE t1 SET b = b + 3 WHERE a > 10; ---echo ## +1, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; --echo # DELETE +let $start_val= `$rec_lock_created`; DELETE FROM t1; ---echo ## +0, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; + +let $start_val= `$rec_lock_created`; DELETE FROM t1 WHERE a = 10; ---echo ## +1, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; + +let $start_val= `$rec_lock_created`; DELETE FROM t1 WHERE a > 10; ---echo ## +1, was +1 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; drop table t1; @@ -153,29 +217,38 @@ insert into t1 select seq, seq from seq_1_to_100; create table t2 (a int primary key, b int) engine=innodb; insert into t2 select seq, seq from seq_1_to_100; +let $start_val= `$rec_lock_created`; --disable_result_log begin; select * from t1 join t2 on t1.a = t2.a LOCK IN SHARE MODE; commit; --enable_result_log ---echo ## +1, was +2 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; +let $start_val= `$rec_lock_created`; --disable_result_log begin; select * from t1 join t2 on t1.a = t2.b LOCK IN SHARE MODE; commit; --enable_result_log ---echo ## +2, was +2 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; +let $start_val= `$rec_lock_created`; --disable_result_log begin; select * from t1 join t2 on t1.b = t2.b LOCK IN SHARE MODE; commit; --enable_result_log ---echo ## +1, was +2 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; drop table t1, t2; @@ -183,17 +256,23 @@ drop table t1, t2; create table t10 (a int, b varchar(100), index(a)) engine=innodb; insert into t10 select seq, uuid() from seq_1_to_10000; create table t11(a int) engine=innodb; +let $start_val= `$rec_lock_created`; insert into t11 select a from t10; ---echo ## +0, was +11 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; drop table t10, t11; create table t10 (a int, b varchar(100), index(a)) engine=innodb; insert into t10 select seq, uuid() from seq_1_to_10000; create table t11(a int) engine=innodb; +let $start_val= `$rec_lock_created`; insert into t11 select a from t10 order by a desc; ---echo ## +0, was +11 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; drop table t10, t11; --echo # Range access @@ -203,9 +282,10 @@ create table t11(a int) engine=innodb; --replace_column 9 # explain insert into t11 select a from t10 where a > 30; +let $start_val= `$rec_lock_created`; insert into t11 select a from t10 where a > 30; ---echo ## +11, was +11 -eval $print_lock_rec_lock_created; +let $end_val= `$rec_lock_created`; +let $result= `SELECT $end_val - $start_val`; +echo # lock_rec_created; +echo $result; drop table t10, t11; - -set innodb_table_lock_on_full_scan=@old_innodb_table_lock_on_full_scan; diff --git a/mysql-test/suite/innodb/r/deadlock_victim_race,table_lock.rdiff b/mysql-test/suite/innodb/r/deadlock_victim_race,table_lock.rdiff deleted file mode 100644 index eacae7dc7d2cc..0000000000000 --- a/mysql-test/suite/innodb/r/deadlock_victim_race,table_lock.rdiff +++ /dev/null @@ -1,37 +0,0 @@ ---- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_victim_race.result 2026-03-06 15:18:14.783771049 +1100 -+++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_victim_race.reject 2026-03-06 15:18:26.339686475 +1100 -@@ -12,7 +12,6 @@ - connect con_2,localhost,root,,; - SET TRANSACTION ISOLATION LEVEL READ COMMITTED; - BEGIN; --SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont EXECUTE 2'; - SET SESSION innodb_lock_wait_timeout=1; - UPDATE t SET b = 100; - connect con_3,localhost,root,,; -@@ -21,23 +20,22 @@ - SELECT * FROM t WHERE a = 30 FOR UPDATE; - a b - 30 30 --SET DEBUG_SYNC='now WAIT_FOR upd_locked'; - SET DEBUG_SYNC = 'lock_wait_start SIGNAL sel_locked'; - SELECT * FROM t WHERE a = 20 FOR UPDATE; - connection default; - SET DEBUG_SYNC='now WAIT_FOR sel_locked'; - ROLLBACK; - SET DEBUG_SYNC='now SIGNAL upd_cont'; --SET DEBUG_SYNC='now WAIT_FOR upd_locked'; - SET SESSION innodb_lock_wait_timeout=1; - SELECT * FROM t WHERE a = 10 FOR UPDATE; --ERROR HY000: Lock wait timeout exceeded; try restarting transaction -+a b -+10 10 - SET DEBUG_SYNC="now SIGNAL upd_cont"; - connection con_3; - a b - 20 20 - connection con_2; --ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -+ERROR HY000: Lock wait timeout exceeded; try restarting transaction - disconnect con_3; - disconnect con_2; - connection default; diff --git a/mysql-test/suite/innodb/r/deadlock_victim_race.result b/mysql-test/suite/innodb/r/deadlock_victim_race.result index 93286fdbb5040..061edc775f048 100644 --- a/mysql-test/suite/innodb/r/deadlock_victim_race.result +++ b/mysql-test/suite/innodb/r/deadlock_victim_race.result @@ -13,7 +13,6 @@ connect con_2,localhost,root,,; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN; SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont EXECUTE 2'; -SET SESSION innodb_lock_wait_timeout=1; UPDATE t SET b = 100; connect con_3,localhost,root,,; BEGIN; @@ -28,7 +27,7 @@ connection default; SET DEBUG_SYNC='now WAIT_FOR sel_locked'; ROLLBACK; SET DEBUG_SYNC='now SIGNAL upd_cont'; -SET DEBUG_SYNC='now WAIT_FOR upd_locked'; +SET DEBUG_SYNC="now WAIT_FOR upd_locked"; SET SESSION innodb_lock_wait_timeout=1; SELECT * FROM t WHERE a = 10 FOR UPDATE; ERROR HY000: Lock wait timeout exceeded; try restarting transaction diff --git a/mysql-test/suite/innodb/r/deadlock_wait_lock_race,table_lock.rdiff b/mysql-test/suite/innodb/r/deadlock_wait_lock_race,table_lock.rdiff deleted file mode 100644 index 78963e3b74e3a..0000000000000 --- a/mysql-test/suite/innodb/r/deadlock_wait_lock_race,table_lock.rdiff +++ /dev/null @@ -1,19 +0,0 @@ ---- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_wait_lock_race.result 2026-03-06 15:32:50.445412134 +1100 -+++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_wait_lock_race.reject 2026-03-06 15:33:24.125169729 +1100 -@@ -10,15 +10,12 @@ - connect con_2,localhost,root,,; - SET TRANSACTION ISOLATION LEVEL READ COMMITTED; - BEGIN; --SET DEBUG_SYNC = 'lock_trx_handle_wait_before_unlocked_wait_lock_check SIGNAL upd_locked WAIT_FOR upd_cont'; - set session innodb_lock_wait_timeout = 1; - UPDATE t SET b = 100; - connection default; --SET DEBUG_SYNC="now WAIT_FOR upd_locked"; --SET DEBUG_SYNC="lock_wait_before_suspend SIGNAL upd_cont"; - SELECT * FROM t WHERE a = 10 FOR UPDATE; - connection con_2; --ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -+ERROR HY000: Lock wait timeout exceeded; try restarting transaction - disconnect con_2; - connection default; - a b diff --git a/mysql-test/suite/innodb/r/deadlock_wait_lock_race.result b/mysql-test/suite/innodb/r/deadlock_wait_lock_race.result index 2149f21c70d87..874f5af47d029 100644 --- a/mysql-test/suite/innodb/r/deadlock_wait_lock_race.result +++ b/mysql-test/suite/innodb/r/deadlock_wait_lock_race.result @@ -11,7 +11,6 @@ connect con_2,localhost,root,,; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN; SET DEBUG_SYNC = 'lock_trx_handle_wait_before_unlocked_wait_lock_check SIGNAL upd_locked WAIT_FOR upd_cont'; -set session innodb_lock_wait_timeout = 1; UPDATE t SET b = 100; connection default; SET DEBUG_SYNC="now WAIT_FOR upd_locked"; diff --git a/mysql-test/suite/innodb/r/deadlock_wait_thr_race,table_lock.rdiff b/mysql-test/suite/innodb/r/deadlock_wait_thr_race,table_lock.rdiff deleted file mode 100644 index fa10579d9007e..0000000000000 --- a/mysql-test/suite/innodb/r/deadlock_wait_thr_race,table_lock.rdiff +++ /dev/null @@ -1,26 +0,0 @@ ---- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_wait_thr_race.result 2026-03-06 15:31:05.910164017 +1100 -+++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/deadlock_wait_thr_race.reject 2026-03-06 15:31:16.214089943 +1100 -@@ -9,22 +9,18 @@ - 20 20 - connect con_2,localhost,root,,; - SET TRANSACTION ISOLATION LEVEL READ COMMITTED; --SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont'; - SET DEBUG_SYNC = 'trx_t_release_locks_enter SIGNAL sel_cont WAIT_FOR upd_cont_2'; - set session innodb_lock_wait_timeout = 1; - BEGIN; - UPDATE t SET b = 100; - connection default; --SET DEBUG_SYNC="now WAIT_FOR upd_locked"; - SET DEBUG_SYNC="deadlock_report_before_lock_releasing SIGNAL upd_cont WAIT_FOR sel_cont"; --SET DEBUG_SYNC="lock_wait_before_suspend SIGNAL sel_before_suspend"; - SELECT * FROM t WHERE a = 10 FOR UPDATE;; - connect con_3,localhost,root,,; --SET DEBUG_SYNC="now WAIT_FOR sel_before_suspend"; - SET DEBUG_SYNC="now SIGNAL upd_cont_2"; - disconnect con_3; - connection con_2; --ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -+ERROR HY000: Lock wait timeout exceeded; try restarting transaction - disconnect con_2; - connection default; - a b diff --git a/mysql-test/suite/innodb/r/deadlock_wait_thr_race.result b/mysql-test/suite/innodb/r/deadlock_wait_thr_race.result index 06718e7eaa94a..6992a447c071b 100644 --- a/mysql-test/suite/innodb/r/deadlock_wait_thr_race.result +++ b/mysql-test/suite/innodb/r/deadlock_wait_thr_race.result @@ -11,7 +11,6 @@ connect con_2,localhost,root,,; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont'; SET DEBUG_SYNC = 'trx_t_release_locks_enter SIGNAL sel_cont WAIT_FOR upd_cont_2'; -set session innodb_lock_wait_timeout = 1; BEGIN; UPDATE t SET b = 100; connection default; diff --git a/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks,table_lock.rdiff b/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks,table_lock.rdiff deleted file mode 100644 index d425b3912f88c..0000000000000 --- a/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks,table_lock.rdiff +++ /dev/null @@ -1,53 +0,0 @@ ---- /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks.result 2026-03-05 15:48:16.984108689 +1100 -+++ /home/ycp/source/mariadb-server/11.4/src/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks.reject 2026-03-05 15:48:53.687914366 +1100 -@@ -95,29 +95,26 @@ - connection con_str_lock_row4; - SELECT * FROM ```t'\"_str` WHERE c1 = '4' FOR UPDATE; - connection con_verify_innodb_locks; -+Timeout waiting for rows in INNODB_LOCKS to appear - SELECT lock_mode, lock_type, lock_table, lock_index, lock_rec, lock_data - FROM INFORMATION_SCHEMA.INNODB_LOCKS ORDER BY lock_data; - lock_mode lock_type lock_table lock_index lock_rec lock_data --X RECORD `test`.```t'\"_str` PRIMARY 1 supremum pseudo-record --X RECORD `test`.```t'\"_str` PRIMARY 1 supremum pseudo-record --X RECORD `test`.```t'\"_str` PRIMARY 2 '1', 'abc', '''abc', 'abc''', 'a''bc', 'a''bc''', '''abc''''' --X RECORD `test`.```t'\"_str` PRIMARY 2 '1', 'abc', '''abc', 'abc''', 'a''bc', 'a''bc''', '''abc''''' --X RECORD `test`.```t'\"_str` PRIMARY 3 '2', 'abc', '"abc', 'abc"', 'a"bc', 'a"bc"', '"abc""' --X RECORD `test`.```t'\"_str` PRIMARY 3 '2', 'abc', '"abc', 'abc"', 'a"bc', 'a"bc"', '"abc""' --X RECORD `test`.```t'\"_str` PRIMARY 4 '3', 'abc', '\\abc', 'abc\\', 'a\\bc', 'a\\bc\\', '\\abc\\\\' --X RECORD `test`.```t'\"_str` PRIMARY 4 '3', 'abc', '\\abc', 'abc\\', 'a\\bc', 'a\\bc\\', '\\abc\\\\' --X RECORD `test`.```t'\"_str` PRIMARY 5 '4', 'abc', '\0abc', 'abc\0', 'a\0bc', 'a\0bc\0', 'a\0bc\0\0' --X RECORD `test`.```t'\"_str` PRIMARY 5 '4', 'abc', '\0abc', 'abc\0', 'a\0bc', 'a\0bc\0', 'a\0bc\0\0' --X RECORD `test`.`t_max` PRIMARY 2 127, 255, 32767, 65535, 8388607, 16777215, 2147483647, 4294967295, 9223372036854775807, 18446744073709551615 --X RECORD `test`.`t_max` PRIMARY 2 127, 255, 32767, 65535, 8388607, 16777215, 2147483647, 4294967295, 9223372036854775807, 18446744073709551615 --X RECORD `test`.`t_min` PRIMARY 2 -128, 0, -32768, 0, -8388608, 0, -2147483648, 0, -9223372036854775808, 0 --X RECORD `test`.`t_min` PRIMARY 2 -128, 0, -32768, 0, -8388608, 0, -2147483648, 0, -9223372036854775808, 0 -+IX TABLE `test`.```t'\"_str` NULL NULL NULL -+IX TABLE `test`.```t'\"_str` NULL NULL NULL -+IX TABLE `test`.```t'\"_str` NULL NULL NULL -+IX TABLE `test`.```t'\"_str` NULL NULL NULL -+IX TABLE `test`.```t'\"_str` NULL NULL NULL -+X TABLE `test`.```t'\"_str` NULL NULL NULL -+X TABLE `test`.`t_max` NULL NULL NULL -+X TABLE `test`.`t_max` NULL NULL NULL -+X TABLE `test`.`t_min` NULL NULL NULL -+X TABLE `test`.`t_min` NULL NULL NULL - SELECT lock_table, COUNT(*) FROM INFORMATION_SCHEMA.INNODB_LOCKS - GROUP BY lock_table; - lock_table COUNT(*) - `test`.`t_max` 2 - `test`.`t_min` 2 --`test`.```t'\"_str` 10 -+`test`.```t'\"_str` 6 - set @save_sql_mode = @@sql_mode; - SET SQL_MODE='ANSI_QUOTES'; - SELECT lock_table, COUNT(*) FROM INFORMATION_SCHEMA.INNODB_LOCKS -@@ -125,7 +122,7 @@ - lock_table COUNT(*) - "test"."t_max" 2 - "test"."t_min" 2 --"test"."`t'\""_str" 10 -+"test"."`t'\""_str" 6 - SET @@sql_mode=@save_sql_mode; - connection con_lock; - COMMIT; diff --git a/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks.result b/mysql-test/suite/innodb/r/innodb_i_s_innodb_locks.result index 1bca540557f8f4aea0a5800acea20d18186b3ad6..a410362ab525f846535b8d12f6b86fd9527d6b05 100644 GIT binary patch delta 45 zcmX@Dby{n~I-be>ys4Y}c^5HF_7^-fd6}Tdlock.wait_thr during rollback --send SELECT * FROM t WHERE a = 10 FOR UPDATE; --connect(con_3,localhost,root,,) -if ($MTR_COMBINATION_ROW_LOCK) { - SET DEBUG_SYNC="now WAIT_FOR sel_before_suspend"; -} +SET DEBUG_SYNC="now WAIT_FOR sel_before_suspend"; SET DEBUG_SYNC="now SIGNAL upd_cont_2"; --disconnect con_3 --connection con_2 -if ($MTR_COMBINATION_ROW_LOCK) { - --error ER_LOCK_DEADLOCK - --reap -} -if ($MTR_COMBINATION_TABLE_LOCK) { - --error ER_LOCK_WAIT_TIMEOUT - --reap -} +--error ER_LOCK_DEADLOCK +--reap --disconnect con_2 --connection default diff --git a/mysql-test/suite/innodb/t/innodb_i_s_innodb_locks.test b/mysql-test/suite/innodb/t/innodb_i_s_innodb_locks.test index 4b0c07b52323b..e6e46dbf556bc 100644 --- a/mysql-test/suite/innodb/t/innodb_i_s_innodb_locks.test +++ b/mysql-test/suite/innodb/t/innodb_i_s_innodb_locks.test @@ -4,7 +4,6 @@ # -- source include/have_innodb.inc ---source include/innodb_table_lock_on_full_scan.inc SET @save_timeout=@@GLOBAL.innodb_lock_wait_timeout; SET GLOBAL innodb_lock_wait_timeout=100000000; @@ -135,7 +134,6 @@ if (!$success) -- echo Timeout waiting for rows in INNODB_LOCKS to appear } ---sorted_result SELECT lock_mode, lock_type, lock_table, lock_index, lock_rec, lock_data FROM INFORMATION_SCHEMA.INNODB_LOCKS ORDER BY lock_data; diff --git a/mysql-test/suite/innodb/t/innodb_i_s_innodb_trx.test b/mysql-test/suite/innodb/t/innodb_i_s_innodb_trx.test index 5ca5c05bce945..745e1d94a1222 100644 --- a/mysql-test/suite/innodb/t/innodb_i_s_innodb_trx.test +++ b/mysql-test/suite/innodb/t/innodb_i_s_innodb_trx.test @@ -1,5 +1,4 @@ --source include/have_innodb.inc ---source include/innodb_table_lock_on_full_scan.inc # # Test that transaction data is correctly "visualized" in diff --git a/mysql-test/suite/innodb/t/innodb_information_schema.test b/mysql-test/suite/innodb/t/innodb_information_schema.test index bf8015026bd6b..1fbf9f7eb5761 100644 --- a/mysql-test/suite/innodb/t/innodb_information_schema.test +++ b/mysql-test/suite/innodb/t/innodb_information_schema.test @@ -4,7 +4,6 @@ # -- source include/have_innodb.inc ---source include/innodb_table_lock_on_full_scan.inc # lock data that is part of result set for this testcase # is retreived using buf_page_try_get. i.e only show if page diff --git a/mysql-test/suite/innodb/t/insert_into_empty.test b/mysql-test/suite/innodb/t/insert_into_empty.test index 770ba056af049..d42abfaa03e0c 100644 --- a/mysql-test/suite/innodb/t/insert_into_empty.test +++ b/mysql-test/suite/innodb/t/insert_into_empty.test @@ -181,6 +181,8 @@ if ($MTR_COMBINATION_ROW_LOCK) { INSERT INTO t1 VALUES (2); } if ($MTR_COMBINATION_TABLE_LOCK) { +# SELECT..LOCK IN SHARE MODE takes a table lock and no record locks, +# so the empty table bulk insert stays enabled INSERT INTO t1 VALUES (2); } COMMIT; diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result index 41a3abb995405..11e4b86afc08a 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result +++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result @@ -1599,7 +1599,7 @@ SESSION_VALUE OFF DEFAULT_VALUE OFF VARIABLE_SCOPE SESSION VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Whether to acquire table lock when SQL layer advises full scan +VARIABLE_COMMENT When the SQL layer advises a full scan, acquire a single table lock instead of locking each scanned row individually. This is faster, at the cost of less granular (table-level instead of record-level) locking. NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index a31728287a45d..57e528a027cdf 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -865,7 +865,9 @@ static MYSQL_THDVAR_BOOL(ft_enable_stopword, PLUGIN_VAR_OPCMDARG, /* default */ TRUE); static MYSQL_THDVAR_BOOL(table_lock_on_full_scan, PLUGIN_VAR_OPCMDARG, - "Whether to acquire table lock when SQL layer advises full scan", + "When the SQL layer advises a full scan, acquire a single table lock " + "instead of locking each scanned row individually. This is faster, at the " + "cost of less granular (table-level instead of record-level) locking.", NULL, NULL, FALSE); static MYSQL_THDVAR_UINT(lock_wait_timeout, PLUGIN_VAR_RQCMDARG, @@ -15944,29 +15946,31 @@ ha_innobase::extra( return(0); } -int -ha_innobase::extra_opt( -/*===============*/ - enum ha_extra_function operation, - /*!< in: HA_EXTRA_FLUSH or some other flag */ - ulong arg) -{ - bool handled = false; - switch (operation) { - case HA_EXTRA_FULL_SCAN: - handled = true; - if (THDVAR(ha_thd(), table_lock_on_full_scan) && - !m_prebuilt->skip_locked && arg == ULONG_MAX) - m_prebuilt->full_table_scan = true; - break; - default:/* Do nothing */ - ; - } - - if (!handled) - return extra(operation); - - return(0); +/** SQL layer calls this function (via init_table_full_scan_if_needed()) +with HA_EXTRA_FULL_SCAN when a statement reads a whole table or +index with no WHERE condition. When innodb_table_lock_on_full_scan +is enabled, flag the scan as covering the entire table. +row_search_mvcc() takes a single table-level lock (LOCK_S/LOCK_X) +instead of locking each scanned row individually. A bounded +scan (arg < ULONG_MAX) keeps per-row locking, since it may stop early. +Every other operation is forwarded unchanged to extra(). +@param operation hint to act on +@param arg operation argument; for HA_EXTRA_FULL_SCAN the row + limit, or ULONG_MAX when the scan is unbounded +@return 0 on success, otherwise the error code returned by extra() */ +int ha_innobase::extra_opt(enum ha_extra_function operation, ulong arg) +{ + switch (operation) + { + case HA_EXTRA_FULL_SCAN: + if (THDVAR(ha_thd(), table_lock_on_full_scan) && + !m_prebuilt->skip_locked && arg == ULONG_MAX) + m_prebuilt->full_table_scan = true; + return 0; + default:/* Do nothing */ + ; + } + return extra(operation); } /**