613 lines
14 KiB
Plaintext
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; |