polardbxengine/mysql-test/suite/innodb/t/lock_end_of_range.test

613 lines
14 KiB
Plaintext

--echo #
--echo # Bug #29508068 UNNECESSARY NEXT-KEY LOCK TAKEN
--echo #
--source include/have_debug_sync.inc
CREATE TABLE t1 (
id INT NOT NULL,
PRIMARY KEY (id ASC)
) ENGINE=InnoDB;
CREATE TABLE t2 (
id INT NOT NULL,
PRIMARY KEY (id DESC)
) ENGINE=InnoDB;
--let $TWO=2
SET @conditions = CONCAT(
'<=0 <1 <=1 <7 <=7 <=8 <9 <=9 <=10 ',
'>=10 >9 >=9 >3 >=3 >=2 >1 >=1 >0'
);
let $conditions_cnt = `
SELECT 1 + LENGTH(@conditions) - LENGTH(REPLACE(@conditions, ' ',''))
`;
--let $t=1
while($t <= 2)
{
--eval INSERT INTO t$t VALUES (1), (3), (4), (5), (6), (7), (9)
--eval ANALYZE TABLE t$t
--let $c=1
while($c <= $conditions_cnt)
{
let condition = `
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(@conditions,' ',$c),' ',-1)
`;
--let $desc=0
while($desc <= 1)
{
if($desc == 0)
{
--let $ord=ASC
}
if($desc == 1)
{
--let $ord=DESC
}
BEGIN;
eval
SELECT *
FROM t$t FORCE INDEX (PRIMARY)
WHERE id $condition
ORDER BY id $ord
FOR UPDATE;
--sorted_result
eval
SELECT index_name,lock_type,lock_mode,lock_status,lock_data
FROM performance_schema.data_locks
WHERE object_name = 't$t';
ROLLBACK;
--inc $desc
}
--inc $c
}
--eval DROP TABLE t$t
--inc $t
}
CREATE TABLE t1 (
id1 INT NOT NULL,
id2 INT NOT NULL,
PRIMARY KEY (id1 ASC, id2 ASC)
) ENGINE=InnoDB;
CREATE TABLE t2 (
id1 INT NOT NULL,
id2 INT NOT NULL,
PRIMARY KEY (id1 ASC, id2 DESC)
) ENGINE=InnoDB;
CREATE TABLE t3 (
id1 INT NOT NULL,
id2 INT NOT NULL,
PRIMARY KEY (id1 DESC, id2 ASC)
) ENGINE=InnoDB;
CREATE TABLE t4 (
id1 INT NOT NULL,
id2 INT NOT NULL,
PRIMARY KEY (id1 DESC, id2 DESC)
) ENGINE=InnoDB;
SET @conditions = CONCAT(
'<0 <1 <3 <=3 <=4 <5 <=5 ',
'>6 >5 >3 >=3 >=2 >1 >=1'
);
let $conditions_cnt = `
SELECT 1 + LENGTH(@conditions) - LENGTH(REPLACE(@conditions, ' ',''))
`;
--let $t = 1
while($t <= 4)
{
eval
INSERT INTO t$t (id1,id2) VALUES
(1,1),(1,3),(1,5),(3,1),(3,3),(3,5),(5,1),(5,3),(5,5);
--eval ANALYZE TABLE t$t
--let $c = 1
while($c <= $conditions_cnt)
{
let condition = `
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(@conditions,' ',$c),' ',-1)
`;
--let $desc=0
while($desc <= 1)
{
if($desc == 0)
{
--let $ord=ASC
}
if($desc == 1)
{
--let $ord=DESC
}
--let $field = 1
while($field <= 2)
{
if($field == 1){
--let $predicate=id1 $condition
--let $order_column=id1
}
if($field == 2){
--let $predicate=id1=3 AND id2 $condition
--let $order_column=id2
}
BEGIN;
eval
SELECT *
FROM t$t FORCE INDEX (PRIMARY)
WHERE $predicate
ORDER BY $order_column $ord
FOR UPDATE;
--sorted_result
eval
SELECT index_name,lock_type,lock_mode,lock_status,lock_data
FROM performance_schema.data_locks
WHERE object_name = 't$t';
ROLLBACK;
--inc $field
}
--inc $desc
}
--inc $c
}
--eval DROP TABLE t$t
--inc $t
}
CREATE TABLE t1 (
id VARCHAR(100) NOT NULL,
PRIMARY KEY (id(1) ASC)
) ENGINE=InnoDB;
CREATE TABLE t2 (
id VARCHAR(100) NOT NULL,
PRIMARY KEY (id(1) DESC)
) ENGINE=InnoDB;
SET @conditions = CONCAT(
'<="c" <="d" <"e" <"ee" <="ee" <="ec" <="ef" ',
'>="g" >="f" >"e" >"ee" >="ee" >="ef" >="ec"'
);
let $conditions_cnt = `
SELECT 1 + LENGTH(@conditions) - LENGTH(REPLACE(@conditions, ' ',''))
`;
--let $t = 1
while($t <= 2)
{
eval
INSERT INTO t$t
VALUES ("aa"), ("bb"), ("cc"), ("ee"), ("gg"), ("hh"), ("ii");
--eval ANALYZE TABLE t$t
--let $c = 1
while($c <= $conditions_cnt)
{
let condition = `
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(@conditions,' ',$c),' ',-1)
`;
--let $desc=0
while($desc <= 1)
{
if($desc == 0)
{
--let $ord=ASC
}
if($desc == 1)
{
--let $ord=DESC
}
BEGIN;
eval
SELECT *
FROM t$t FORCE INDEX (PRIMARY)
WHERE id $condition
ORDER BY id $ord
FOR UPDATE;
--sorted_result
eval
SELECT index_name,lock_type,lock_mode,lock_status,lock_data
FROM performance_schema.data_locks
WHERE object_name = 't$t';
ROLLBACK;
--inc $desc
}
--inc $c
}
--eval DROP TABLE t$t
--inc $t
}
CREATE TABLE t1 (
id VARCHAR(100) NOT NULL,
PRIMARY KEY (id(2) ASC)
) ENGINE=InnoDB;
CREATE TABLE t2 (
id VARCHAR(100) NOT NULL,
PRIMARY KEY (id(2) DESC)
) ENGINE=InnoDB;
SET @conditions = CONCAT(
'<="c" <="d" <"e" <"ee" <="ee" <="ec" <="ef" ',
'>="g" >="f" >"e" >"ee" >="ee" >="ef" >="ec"'
);
let $conditions_cnt = `
SELECT 1 + LENGTH(@conditions) - LENGTH(REPLACE(@conditions, ' ',''))
`;
--let $t = 1
while($t <= 2)
{
eval
INSERT INTO t$t
VALUES ("aaa"), ("bbb"), ("ccc"), ("eee"), ("ggg"), ("hhh"), ("iii");
--eval ANALYZE TABLE t$t
--let $c = 1
while($c <= $conditions_cnt)
{
let condition = `
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(@conditions,' ',$c),' ',-1)
`;
--let $desc=0
while($desc <= 1)
{
if($desc == 0)
{
--let $ord=ASC
}
if($desc == 1)
{
--let $ord=DESC
}
BEGIN;
eval
SELECT *
FROM t$t FORCE INDEX (PRIMARY)
WHERE id $condition
ORDER BY id $ord
FOR UPDATE;
--sorted_result
eval
SELECT index_name,lock_type,lock_mode,lock_status,lock_data
FROM performance_schema.data_locks
WHERE object_name = 't$t';
ROLLBACK;
--inc $desc
}
--inc $c
}
--eval DROP TABLE t$t
--inc $t
}
# In this scenario, we test what happens if the last row in the range is locked
# by another transaction. In particular con1 locks row with id=7, and con2 tries
# to lock rows with id<=7. This scenario is interesting, because when con2 goes
# to waiting state AFTER it has already "seen" the end of the range, and when it
# "wakes up" (after con1 releases the lock) it tries to read the row again.
# A faulty implementation might fail on assert, not read the row, or fail to
# lock it properly on the second attempt.
CREATE TABLE t1 (
id INT NOT NULL,
PRIMARY KEY (id ASC)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1), (3), (4), (5), (6), (7), (9);
ANALYZE TABLE t1;
--connect (con1,localhost,root,,)
--connect (con2,localhost,root,,)
--connection con1
BEGIN;
SELECT * FROM t1 WHERE id=7 FOR UPDATE;
--connection con2
BEGIN;
SET DEBUG_SYNC='lock_wait_will_wait SIGNAL con2_will_wait';
--send SELECT * FROM t1 FORCE INDEX (PRIMARY) WHERE id<=7 FOR UPDATE;
--connection con1
SET DEBUG_SYNC='now WAIT_FOR con2_will_wait';
ROLLBACK;
--connection con2
--reap
--connection default
--disconnect con1
--disconnect con2
DROP TABLE t1;
# In this scenario, we test what happens if the last row in the range is delete-
# -marked
CREATE TABLE t1 (
id INT NOT NULL,
PRIMARY KEY (id ASC)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1), (3), (4), (5), (6), (7), (9);
ANALYZE TABLE t1;
SET GLOBAL innodb_purge_stop_now = ON;
DELETE FROM t1 WHERE id=7;
BEGIN;
SELECT * FROM t1 FORCE INDEX (PRIMARY) WHERE id<=7 FOR UPDATE;
--sorted_result
SELECT index_name, lock_type, lock_mode, lock_status, lock_data
FROM performance_schema.data_locks
WHERE object_name='t1';
ROLLBACK;
SET GLOBAL innodb_purge_run_now = ON;
DROP TABLE t1;
# In this scenario, we test what happens if the last row in the range is locked
# by another transaction and is delete-marked.
# In particular con1 locks row with id=7, and con2 tries
# to lock rows with id<=7. This scenario is interesting, because when con2 goes
# to waiting state AFTER it has already "seen" the end of the range, and when it
# "wakes up" (after con1 releases the lock) it tries to read the row again.
# A faulty implementation might fail on assert, not read the row, or fail to
# lock it properly on the second attempt.
CREATE TABLE t1 (
id INT NOT NULL,
PRIMARY KEY (id ASC)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1), (3), (4), (5), (6), (7), (9);
ANALYZE TABLE t1;
SET GLOBAL innodb_purge_stop_now = ON;
DELETE FROM t1 WHERE id=7;
--connect (con1,localhost,root,,)
--connect (con2,localhost,root,,)
--connection con1
BEGIN;
SELECT * FROM t1 WHERE id=7 FOR UPDATE;
--connection con2
BEGIN;
SET DEBUG_SYNC='lock_wait_will_wait SIGNAL con2_will_wait';
--send SELECT * FROM t1 FORCE INDEX (PRIMARY) WHERE id<=7 FOR UPDATE;
--connection con1
SET DEBUG_SYNC='now WAIT_FOR con2_will_wait';
ROLLBACK;
--connection con2
--reap
--connection default
--disconnect con1
--disconnect con2
SET GLOBAL innodb_purge_run_now = ON;
DROP TABLE t1;
# In this scenario, we test what happens if the last row in the range is locked
# by another transaction and is delete-marked, and the other transaction
# undeletes this row, and then rolls back.
# In particular con1 locks row with id=7, and con2 tries
# to lock rows with id<=7. This scenario is interesting, because when con2 goes
# to waiting state AFTER it has already "seen" the end of the range, and when it
# "wakes up" (after con1 releases the lock) it tries to read the row again.
# A faulty implementation might fail on assert, not read the row, or fail to
# lock it properly on the second attempt.
CREATE TABLE t1 (
id INT NOT NULL,
PRIMARY KEY (id ASC)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1), (3), (4), (5), (6), (7), (9);
ANALYZE TABLE t1;
SET GLOBAL innodb_purge_stop_now = ON;
DELETE FROM t1 WHERE id=7;
--connect (con1,localhost,root,,)
--connect (con2,localhost,root,,)
--connection con1
BEGIN;
INSERT INTO t1 VALUES (7);
--connection con2
BEGIN;
SET DEBUG_SYNC='lock_wait_will_wait SIGNAL con2_will_wait';
--send SELECT * FROM t1 FORCE INDEX (PRIMARY) WHERE id<=7 FOR UPDATE;
--connection con1
SET DEBUG_SYNC='now WAIT_FOR con2_will_wait';
ROLLBACK;
--connection con2
--reap
--connection default
--disconnect con1
--disconnect con2
SET GLOBAL innodb_purge_run_now = ON;
DROP TABLE t1;
# In this scenario, we test what happens if the last row in the range is locked
# by another transaction and is delete-marked, and the other transaction
# undeletes this row.
# In particular con1 locks row with id=7, and con2 tries
# to lock rows with id<=7. This scenario is interesting, because when con2 goes
# to waiting state AFTER it has already "seen" the end of the range, and when it
# "wakes up" (after con1 releases the lock) it tries to read the row again.
# A faulty implementation might fail on assert, not read the row, or fail to
# lock it properly on the second attempt.
CREATE TABLE t1 (
id INT NOT NULL,
PRIMARY KEY (id ASC)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1), (3), (4), (5), (6), (7), (9);
ANALYZE TABLE t1;
SET GLOBAL innodb_purge_stop_now = ON;
DELETE FROM t1 WHERE id=7;
--connect (con1,localhost,root,,)
--connect (con2,localhost,root,,)
--connection con1
BEGIN;
INSERT INTO t1 VALUES (7);
--connection con2
BEGIN;
SET DEBUG_SYNC='lock_wait_will_wait SIGNAL con2_will_wait';
--send SELECT * FROM t1 FORCE INDEX (PRIMARY) WHERE id<=7 FOR UPDATE;
--connection con1
SET DEBUG_SYNC='now WAIT_FOR con2_will_wait';
COMMIT;
--connection con2
--reap
--connection default
--disconnect con1
--disconnect con2
SET GLOBAL innodb_purge_run_now = ON;
DROP TABLE t1;
# In this scenario, we test what happens when semi-consistent read is involved.
# In particular con1 locks row with id=6 while deleting it, and con2 tries
# to lock rows with id<=6 while updating them using semi-consistent read.
# This scenario is interesting, because when con2 goes notices it would have to
# wait, it cancels it's own waiting lock, and reports the old (non-deleted)
# version of the row to the higher layer, which then retries the read, this time
# with proper locking enabled - so it is interesting what would happen if con1
# have had commited the delete meanwhile.
CREATE TABLE t1 (
id INT NOT NULL,
val INT,
PRIMARY KEY (id ASC)
) ENGINE=InnoDB;
INSERT INTO t1 (id,val) VALUES (1,1),(2,2) ,(3,3), (4,4), (5,5), (6,6), (9,9);
--connect (con1,localhost,root,,)
--connect (con2,localhost,root,,)
--connection con1
BEGIN;
DELETE FROM t1 WHERE id=6;
--connection con2
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SET DEBUG_SYNC='
semi_consistent_read_would_wait
SIGNAL con2_would_wait
WAIT_FOR con2_can_unlock';
SET DEBUG_SYNC='
row_search_for_mysql_before_return
SIGNAL con2_returns_row
WAIT_FOR con2_can_return_row
EXECUTE 6';
--send UPDATE t1 SET val=13 WHERE id<=6
--connection con1
--let i=1
while($i < 6)
{
--echo Expecting row number $i
SET DEBUG_SYNC='now WAIT_FOR con2_returns_row';
SET DEBUG_SYNC='now SIGNAL con2_can_return_row';
--inc $i
}
SET DEBUG_SYNC='now WAIT_FOR con2_would_wait';
--sorted_result
SELECT index_name, lock_type, lock_mode, lock_status, lock_data
FROM performance_schema.data_locks
WHERE object_name='t1';
SET DEBUG_SYNC='now SIGNAL con2_can_unlock';
SET DEBUG_SYNC='now WAIT_FOR con2_returns_row';
--sorted_result
SELECT index_name, lock_type, lock_mode, lock_status, lock_data
FROM performance_schema.data_locks
WHERE object_name='t1';
COMMIT;
# Wait for purge to delete the delete-marked record
--source include/wait_innodb_all_purged.inc
--sorted_result
SELECT index_name, lock_type, lock_mode, lock_status, lock_data
FROM performance_schema.data_locks
WHERE object_name='t1';
SET DEBUG_SYNC='now SIGNAL con2_can_return_row';
--connection con2
--reap
--sorted_result
SELECT index_name, lock_type, lock_mode, lock_status, lock_data
FROM performance_schema.data_locks
WHERE object_name='t1';
ROLLBACK;
--connection default
--disconnect con1
--disconnect con2
DROP TABLE t1;