Alvaro Herrera wrote: > Attached is a patch to fix it.
-- Álvaro Herrera http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services
*** a/src/backend/utils/time/tqual.c --- b/src/backend/utils/time/tqual.c *************** *** 789,801 **** HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, if (TransactionIdDidCommit(xmax)) return HeapTupleUpdated; ! /* no member, even just a locker, alive anymore */ if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); ! ! /* it must have aborted or crashed */ ! return HeapTupleMayBeUpdated; } if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) --- 789,814 ---- if (TransactionIdDidCommit(xmax)) return HeapTupleUpdated; ! /* ! * By here, the update in the Xmax is either aborted or crashed, but ! * what about the other members? ! */ ! if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) + { + /* + * There's no member, even just a locker, alive anymore, so we can + * mark the Xmax as invalid. + */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); ! return HeapTupleMayBeUpdated; ! } ! else ! { ! /* There are lockers running */ ! return HeapTupleBeingUpdated; ! } } if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) *** a/src/test/isolation/expected/delete-abort-savept.out --- b/src/test/isolation/expected/delete-abort-savept.out *************** *** 23,33 **** key value step s1svp: SAVEPOINT f; step s1d: DELETE FROM foo; step s1r: ROLLBACK TO f; ! step s2l: SELECT * FROM foo FOR UPDATE; key value 1 1 - step s1c: COMMIT; step s2c: COMMIT; starting permutation: s1l s1svp s1d s1r s2l s2c s1c --- 23,34 ---- step s1svp: SAVEPOINT f; step s1d: DELETE FROM foo; step s1r: ROLLBACK TO f; ! step s2l: SELECT * FROM foo FOR UPDATE; <waiting ...> ! step s1c: COMMIT; ! step s2l: <... completed> key value 1 1 step s2c: COMMIT; starting permutation: s1l s1svp s1d s1r s2l s2c s1c *************** *** 38,49 **** key value step s1svp: SAVEPOINT f; step s1d: DELETE FROM foo; step s1r: ROLLBACK TO f; ! step s2l: SELECT * FROM foo FOR UPDATE; ! key value ! ! 1 1 ! step s2c: COMMIT; ! step s1c: COMMIT; starting permutation: s1l s1svp s1d s2l s1r s1c s2c step s1l: SELECT * FROM foo FOR KEY SHARE; --- 39,46 ---- step s1svp: SAVEPOINT f; step s1d: DELETE FROM foo; step s1r: ROLLBACK TO f; ! step s2l: SELECT * FROM foo FOR UPDATE; <waiting ...> ! invalid permutation detected starting permutation: s1l s1svp s1d s2l s1r s1c s2c step s1l: SELECT * FROM foo FOR KEY SHARE; *** /dev/null --- b/src/test/isolation/expected/multixact-no-forget.out *************** *** 0 **** --- 1,28 ---- + Parsed test spec with 3 sessions + + starting permutation: s1_lock s2_update s2_abort s3_lock s1_commit + step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE; + value + + 1 + step s2_update: UPDATE dont_forget SET value = 2; + step s2_abort: ROLLBACK; + step s3_lock: SELECT * FROM dont_forget FOR UPDATE; <waiting ...> + step s1_commit: COMMIT; + step s3_lock: <... completed> + value + + + starting permutation: s1_lock s2_update s2_commit s3_lock s1_commit + step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE; + value + + 1 + step s2_update: UPDATE dont_forget SET value = 2; + step s2_commit: COMMIT; + step s3_lock: SELECT * FROM dont_forget FOR UPDATE; <waiting ...> + step s1_commit: COMMIT; + step s3_lock: <... completed> + value + + 2 *** /dev/null --- b/src/test/isolation/expected/multixact-no-forget_1.out *************** *** 0 **** --- 1,27 ---- + Parsed test spec with 3 sessions + + starting permutation: s1_lock s2_update s2_abort s3_lock s1_commit + step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE; + value + + 1 + step s2_update: UPDATE dont_forget SET value = 2; + step s2_abort: ROLLBACK; + step s3_lock: SELECT * FROM dont_forget FOR UPDATE; <waiting ...> + step s1_commit: COMMIT; + step s3_lock: <... completed> + error in steps s1_commit s3_lock: ERROR: could not serialize access due to concurrent update + + starting permutation: s1_lock s2_update s2_commit s3_lock s1_commit + step s1_lock: SELECT * FROM dont_forget FOR KEY SHARE; + value + + 1 + step s2_update: UPDATE dont_forget SET value = 2; + step s2_commit: COMMIT; + step s3_lock: SELECT * FROM dont_forget FOR UPDATE; <waiting ...> + step s1_commit: COMMIT; + step s3_lock: <... completed> + value + + 2 *** a/src/test/isolation/isolation_schedule --- b/src/test/isolation/isolation_schedule *************** *** 20,24 **** test: delete-abort-savept --- 20,25 ---- test: delete-abort-savept-2 test: aborted-keyrevoke test: multixact-no-deadlock + test: multixact-no-forget test: drop-index-concurrently-1 test: timeouts *** /dev/null --- b/src/test/isolation/specs/multixact-no-forget.spec *************** *** 0 **** --- 1,32 ---- + # If transaction A holds a lock, and transaction B does an update, + # make sure we don't forget the lock if B aborts. + setup + { + CREATE TABLE dont_forget ( + value int + ); + + INSERT INTO dont_forget VALUES (1); + } + + teardown + { + DROP TABLE dont_forget; + } + + session "s1" + setup { BEGIN; } + step "s1_lock" { SELECT * FROM dont_forget FOR KEY SHARE; } + step "s1_commit" { COMMIT; } + + session "s2" + setup { BEGIN; } + step "s2_update" { UPDATE dont_forget SET value = 2; } + step "s2_abort" { ROLLBACK; } + step "s2_commit" { COMMIT; } + + session "s3" + step "s3_lock" { SELECT * FROM dont_forget FOR UPDATE; } + + permutation "s1_lock" "s2_update" "s2_abort" "s3_lock" "s1_commit" + permutation "s1_lock" "s2_update" "s2_commit" "s3_lock" "s1_commit"
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers