mirror of
https://github.com/postgres/postgres.git
synced 2025-10-18 04:29:09 +03:00
Fix FOR UPDATE NOWAIT on updated tuple chains
If SELECT FOR UPDATE NOWAIT tries to lock a tuple that is concurrently
being updated, it might fail to honor its NOWAIT specification and block
instead of raising an error.
Fix by adding a no-wait flag to EvalPlanQualFetch which it can pass down
to heap_lock_tuple; also use it in EvalPlanQualFetch itself to avoid
blocking while waiting for a concurrent transaction.
Authors: Craig Ringer and Thomas Munro, tweaked by Álvaro
http://www.postgresql.org/message-id/51FB6703.9090801@2ndquadrant.com
Per Thomas Munro in the course of his SKIP LOCKED feature submission,
who also provided one of the isolation test specs.
Backpatch to 9.4, because that's as far back as it applies without
conflicts (although the bug goes all the way back). To that branch also
backpatch Thomas Munro's new NOWAIT test cases, committed in master by
Heikki as commit 9ee16b49f0
.
This commit is contained in:
30
src/test/isolation/specs/nowait-4.spec
Normal file
30
src/test/isolation/specs/nowait-4.spec
Normal file
@@ -0,0 +1,30 @@
|
||||
# Test NOWAIT with an updated tuple chain.
|
||||
|
||||
setup
|
||||
{
|
||||
CREATE TABLE foo (
|
||||
id int PRIMARY KEY,
|
||||
data text NOT NULL
|
||||
);
|
||||
INSERT INTO foo VALUES (1, 'x');
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE foo;
|
||||
}
|
||||
|
||||
session "s1"
|
||||
setup { BEGIN; }
|
||||
step "s1a" { SELECT * FROM foo WHERE pg_advisory_lock(0) IS NOT NULL FOR UPDATE NOWAIT; }
|
||||
step "s1b" { COMMIT; }
|
||||
|
||||
session "s2"
|
||||
step "s2a" { SELECT pg_advisory_lock(0); }
|
||||
step "s2b" { UPDATE foo SET data = data; }
|
||||
step "s2c" { BEGIN; }
|
||||
step "s2d" { UPDATE foo SET data = data; }
|
||||
step "s2e" { SELECT pg_advisory_unlock(0); }
|
||||
step "s2f" { COMMIT; }
|
||||
|
||||
permutation "s2a" "s1a" "s2b" "s2c" "s2d" "s2e" "s1b" "s2f"
|
57
src/test/isolation/specs/nowait-5.spec
Normal file
57
src/test/isolation/specs/nowait-5.spec
Normal file
@@ -0,0 +1,57 @@
|
||||
# Test NOWAIT on an updated tuple chain
|
||||
|
||||
setup
|
||||
{
|
||||
|
||||
DROP TABLE IF EXISTS test_nowait;
|
||||
CREATE TABLE test_nowait (
|
||||
id integer PRIMARY KEY,
|
||||
value integer not null
|
||||
);
|
||||
|
||||
INSERT INTO test_nowait
|
||||
SELECT x,x FROM generate_series(1,2) x;
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE test_nowait;
|
||||
}
|
||||
|
||||
session "sl1"
|
||||
step "sl1_prep" {
|
||||
PREPARE sl1_run AS SELECT id FROM test_nowait WHERE pg_advisory_lock(0) is not null FOR UPDATE NOWAIT;
|
||||
}
|
||||
step "sl1_exec" {
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
EXECUTE sl1_run;
|
||||
SELECT xmin, xmax, ctid, * FROM test_nowait;
|
||||
}
|
||||
teardown { COMMIT; }
|
||||
|
||||
# A session that's used for an UPDATE of the rows to be locked, for when we're testing ctid
|
||||
# chain following.
|
||||
session "upd"
|
||||
step "upd_getlock" {
|
||||
SELECT pg_advisory_lock(0);
|
||||
}
|
||||
step "upd_doupdate" {
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
UPDATE test_nowait SET value = value WHERE id % 2 = 0;
|
||||
COMMIT;
|
||||
}
|
||||
step "upd_releaselock" {
|
||||
SELECT pg_advisory_unlock(0);
|
||||
}
|
||||
|
||||
# A session that acquires locks that sl1 is supposed to avoid blocking on
|
||||
session "lk1"
|
||||
step "lk1_doforshare" {
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
SELECT id FROM test_nowait WHERE id % 2 = 0 FOR SHARE;
|
||||
}
|
||||
teardown {
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
permutation "sl1_prep" "upd_getlock" "sl1_exec" "upd_doupdate" "lk1_doforshare" "upd_releaselock"
|
Reference in New Issue
Block a user