summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVlad Lesin <vlad_lesin@mail.ru>2021-07-21 17:55:51 +0300
committerVlad Lesin <vlad_lesin@mail.ru>2021-08-06 16:29:34 +0300
commit7ec6a508c8e92ae499de7d14c247726a79a34d95 (patch)
tree93d94604d37d3839e8d2f3d2a909e2d11f87b598
parente38b372e7c983f81f62aa1c41a50360a14b8cbcf (diff)
downloadmariadb-git-7ec6a508c8e92ae499de7d14c247726a79a34d95.tar.gz
MDEV-26206 gap lock is not set if implicit lock exists
If lock type is LOCK_GAP or LOCK_ORDINARY, and the transaction holds implicit lock for the record, then explicit gap-lock will not be set for the record, as lock_rec_convert_impl_to_expl() returns true and lock_rec_convert_impl_to_expl() bypasses lock_rec_lock() call. The fix converts explicit lock to implicit one if requested lock type is not LOCK_REC_NOT_GAP. The fix also influence MDEV-14479. Before the fix lock_rec_convert_impl_to_expl() did not create explicit lock if caller's transaction owns found implicit lock. After the fix lock_rec_convert_impl_to_expl() can create explicit lock under the above conditions if the requested lock mode is not LOCK_REC_NOT_GAP. And that is why we need to check if the table is X-locked in lock_rec_convert_impl_to_expl(). innodb_information_schema test result is also changed as after the fix the following statements execution: SET autocommit=0; INSERT INTO t1 VALUES (5,10); SELECT * FROM t1 FOR UPDATE; leads to additional gap locks requests.
-rw-r--r--mysql-test/suite/innodb/r/implicit_gap_lock_convertion.result17
-rw-r--r--mysql-test/suite/innodb/r/innodb_information_schema.result2
-rw-r--r--mysql-test/suite/innodb/t/implicit_gap_lock_convertion.test21
-rw-r--r--storage/innobase/lock/lock0lock.cc19
4 files changed, 51 insertions, 8 deletions
diff --git a/mysql-test/suite/innodb/r/implicit_gap_lock_convertion.result b/mysql-test/suite/innodb/r/implicit_gap_lock_convertion.result
new file mode 100644
index 00000000000..fd197324c3e
--- /dev/null
+++ b/mysql-test/suite/innodb/r/implicit_gap_lock_convertion.result
@@ -0,0 +1,17 @@
+CREATE TABLE t(a INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB;
+INSERT INTO t VALUES (10), (30);
+connect con1,localhost,root,,;
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+BEGIN;
+INSERT INTO t VALUES (20);
+SELECT * FROM t WHERE a BETWEEN 10 AND 30;
+a
+10
+20
+30
+connection default;
+SET session innodb_lock_wait_timeout=1;
+INSERT INTO t VALUES (15);
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+disconnect con1;
+DROP TABLE t;
diff --git a/mysql-test/suite/innodb/r/innodb_information_schema.result b/mysql-test/suite/innodb/r/innodb_information_schema.result
index 70458758437..6325917c236 100644
--- a/mysql-test/suite/innodb/r/innodb_information_schema.result
+++ b/mysql-test/suite/innodb/r/innodb_information_schema.result
@@ -45,7 +45,7 @@ trx_last_foreign_key_error varchar(256) YES NULL
trx_is_read_only int(1) NO 0
trx_autocommit_non_locking int(1) NO 0
trx_state trx_weight trx_tables_in_use trx_tables_locked trx_rows_locked trx_rows_modified trx_concurrency_tickets trx_isolation_level trx_unique_checks trx_foreign_key_checks
-RUNNING 3 0 1 5 1 0 REPEATABLE READ 1 1
+RUNNING 3 0 1 6 1 0 REPEATABLE READ 1 1
trx_isolation_level trx_unique_checks trx_foreign_key_checks
SERIALIZABLE 0 0
trx_state trx_isolation_level trx_last_foreign_key_error
diff --git a/mysql-test/suite/innodb/t/implicit_gap_lock_convertion.test b/mysql-test/suite/innodb/t/implicit_gap_lock_convertion.test
new file mode 100644
index 00000000000..bf2d09ffb2e
--- /dev/null
+++ b/mysql-test/suite/innodb/t/implicit_gap_lock_convertion.test
@@ -0,0 +1,21 @@
+--source include/have_innodb.inc
+--source include/count_sessions.inc
+
+CREATE TABLE t(a INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB;
+
+INSERT INTO t VALUES (10), (30);
+
+--connect (con1,localhost,root,,)
+SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+BEGIN;
+INSERT INTO t VALUES (20);
+SELECT * FROM t WHERE a BETWEEN 10 AND 30;
+
+--connection default
+SET session innodb_lock_wait_timeout=1;
+--error ER_LOCK_WAIT_TIMEOUT
+INSERT INTO t VALUES (15);
+
+--disconnect con1
+DROP TABLE t;
+--source include/wait_until_count_sessions.inc
diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index bcdf430e476..9d527e95795 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -5205,16 +5205,18 @@ lock_sec_rec_read_check_and_lock(
if the max trx id for the page >= min trx id for the trx list or a
database recovery is running. */
- if (!page_rec_is_supremum(rec)
+ trx_t *trx = thr_get_trx(thr);
+ if (!lock_table_has(trx, index->table, LOCK_X)
+ && !page_rec_is_supremum(rec)
&& page_get_max_trx_id(block->frame) >= trx_sys.get_min_trx_id()
- && lock_rec_convert_impl_to_expl(thr_get_trx(thr), id, rec,
- index, offsets)) {
+ && lock_rec_convert_impl_to_expl(trx, id, rec,
+ index, offsets)
+ && gap_mode == LOCK_REC_NOT_GAP) {
/* We already hold an implicit exclusive lock. */
return DB_SUCCESS;
}
#ifdef WITH_WSREP
- trx_t *trx= thr_get_trx(thr);
/* If transaction scanning an unique secondary key is wsrep
high priority thread (brute force) this scanning may involve
GAP-locking in the index. As this locking happens also when
@@ -5290,9 +5292,12 @@ lock_clust_rec_read_check_and_lock(
heap_no = page_rec_get_heap_no(rec);
- if (heap_no != PAGE_HEAP_NO_SUPREMUM
- && lock_rec_convert_impl_to_expl(thr_get_trx(thr), id, rec,
- index, offsets)) {
+ trx_t *trx = thr_get_trx(thr);
+ if (!lock_table_has(trx, index->table, LOCK_X)
+ && heap_no != PAGE_HEAP_NO_SUPREMUM
+ && lock_rec_convert_impl_to_expl(trx, id, rec,
+ index, offsets)
+ && gap_mode == LOCK_REC_NOT_GAP ) {
/* We already hold an implicit exclusive lock. */
return DB_SUCCESS;
}