2007-02-09 04:35:35 +01:00
|
|
|
--
|
|
|
|
-- Tests for some likely failure cases with combo cmin/cmax mechanism
|
|
|
|
--
|
|
|
|
CREATE TEMP TABLE combocidtest (foobar int);
|
|
|
|
BEGIN;
|
|
|
|
-- a few dummy ops to push up the CommandId counter
|
2007-11-30 22:22:54 +01:00
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
2007-02-09 04:35:35 +01:00
|
|
|
INSERT INTO combocidtest VALUES (1);
|
|
|
|
INSERT INTO combocidtest VALUES (2);
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
|
|
|
(0,1) | 10 | 1
|
|
|
|
(0,2) | 11 | 2
|
|
|
|
(2 rows)
|
|
|
|
|
|
|
|
SAVEPOINT s1;
|
|
|
|
UPDATE combocidtest SET foobar = foobar + 10;
|
|
|
|
-- here we should see only updated tuples
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
2007-11-30 22:22:54 +01:00
|
|
|
(0,3) | 12 | 11
|
|
|
|
(0,4) | 12 | 12
|
2007-02-09 04:35:35 +01:00
|
|
|
(2 rows)
|
|
|
|
|
|
|
|
ROLLBACK TO s1;
|
|
|
|
-- now we should see old tuples, but with combo CIDs starting at 0
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
|
|
|
(0,1) | 0 | 1
|
|
|
|
(0,2) | 1 | 2
|
|
|
|
(2 rows)
|
|
|
|
|
|
|
|
COMMIT;
|
|
|
|
-- combo data is not there anymore, but should still see tuples
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
|
|
|
(0,1) | 0 | 1
|
|
|
|
(0,2) | 1 | 2
|
|
|
|
(2 rows)
|
|
|
|
|
2021-03-25 08:08:03 +01:00
|
|
|
-- Test combo CIDs with portals
|
2007-02-09 04:35:35 +01:00
|
|
|
BEGIN;
|
|
|
|
INSERT INTO combocidtest VALUES (333);
|
|
|
|
DECLARE c CURSOR FOR SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
DELETE FROM combocidtest;
|
|
|
|
FETCH ALL FROM c;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
2007-11-30 22:22:54 +01:00
|
|
|
(0,1) | 1 | 1
|
|
|
|
(0,2) | 1 | 2
|
2007-02-09 04:35:35 +01:00
|
|
|
(0,5) | 0 | 333
|
|
|
|
(3 rows)
|
|
|
|
|
|
|
|
ROLLBACK;
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
2007-11-30 22:22:54 +01:00
|
|
|
(0,1) | 1 | 1
|
|
|
|
(0,2) | 1 | 2
|
2007-02-09 04:35:35 +01:00
|
|
|
(2 rows)
|
|
|
|
|
|
|
|
-- check behavior with locked tuples
|
|
|
|
BEGIN;
|
|
|
|
-- a few dummy ops to push up the CommandId counter
|
2007-11-30 22:22:54 +01:00
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
|
|
|
INSERT INTO combocidtest SELECT 1 LIMIT 0;
|
2007-02-09 04:35:35 +01:00
|
|
|
INSERT INTO combocidtest VALUES (444);
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
2007-11-30 22:22:54 +01:00
|
|
|
(0,1) | 1 | 1
|
|
|
|
(0,2) | 1 | 2
|
2007-02-09 04:35:35 +01:00
|
|
|
(0,6) | 10 | 444
|
|
|
|
(3 rows)
|
|
|
|
|
|
|
|
SAVEPOINT s1;
|
|
|
|
-- this doesn't affect cmin
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest FOR UPDATE;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
2007-11-30 22:22:54 +01:00
|
|
|
(0,1) | 1 | 1
|
|
|
|
(0,2) | 1 | 2
|
2007-02-09 04:35:35 +01:00
|
|
|
(0,6) | 10 | 444
|
|
|
|
(3 rows)
|
|
|
|
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
2007-11-30 22:22:54 +01:00
|
|
|
(0,1) | 1 | 1
|
|
|
|
(0,2) | 1 | 2
|
2007-02-09 04:35:35 +01:00
|
|
|
(0,6) | 10 | 444
|
|
|
|
(3 rows)
|
|
|
|
|
|
|
|
-- but this does
|
|
|
|
UPDATE combocidtest SET foobar = foobar + 10;
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
2007-11-30 22:22:54 +01:00
|
|
|
(0,7) | 12 | 11
|
|
|
|
(0,8) | 12 | 12
|
|
|
|
(0,9) | 12 | 454
|
2007-02-09 04:35:35 +01:00
|
|
|
(3 rows)
|
|
|
|
|
|
|
|
ROLLBACK TO s1;
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
2007-11-30 22:22:54 +01:00
|
|
|
(0,1) | 12 | 1
|
|
|
|
(0,2) | 12 | 2
|
2007-02-09 04:35:35 +01:00
|
|
|
(0,6) | 0 | 444
|
|
|
|
(3 rows)
|
|
|
|
|
|
|
|
COMMIT;
|
|
|
|
SELECT ctid,cmin,* FROM combocidtest;
|
|
|
|
ctid | cmin | foobar
|
|
|
|
-------+------+--------
|
2007-11-30 22:22:54 +01:00
|
|
|
(0,1) | 12 | 1
|
|
|
|
(0,2) | 12 | 2
|
2007-02-09 04:35:35 +01:00
|
|
|
(0,6) | 0 | 444
|
|
|
|
(3 rows)
|
|
|
|
|
Fix locking a tuple updated by an aborted (sub)transaction
When heap_lock_tuple decides to follow the update chain, it tried to
also lock any version of the tuple that was created by an update that
was subsequently rolled back. This is pointless, since for all intents
and purposes that tuple exists no more; and moreover it causes
misbehavior, as reported independently by Marko Tiikkaja and Marti
Raudsepp: some SELECT FOR UPDATE/SHARE queries may fail to return
the tuples, and assertion-enabled builds crash.
Fix by having heap_lock_updated_tuple test the xmin and return success
immediately if the tuple was created by an aborted transaction.
The condition where tuples become invisible occurs when an updated tuple
chain is followed by heap_lock_updated_tuple, which reports the problem
as HeapTupleSelfUpdated to its caller heap_lock_tuple, which in turn
propagates that code outwards possibly leading the calling code
(ExecLockRows) to believe that the tuple exists no longer.
Backpatch to 9.3. Only on 9.5 and newer this leads to a visible
failure, because of commit 27846f02c176; before that, heap_lock_tuple
skips the whole dance when the tuple is already locked by the same
transaction, because of the ancient HeapTupleSatisfiesUpdate behavior.
Still, the buggy condition may also exist in more convoluted scenarios
involving concurrent transactions, so it seems safer to fix the bug in
the old branches too.
Discussion:
https://www.postgresql.org/message-id/CABRT9RC81YUf1=jsmWopcKJEro=VoeG2ou6sPwyOUTx_qteRsg@mail.gmail.com
https://www.postgresql.org/message-id/48d3eade-98d3-8b9a-477e-1a8dc32a724d@joh.to
2016-09-09 20:54:29 +02:00
|
|
|
-- test for bug reported in
|
|
|
|
-- CABRT9RC81YUf1=jsmWopcKJEro=VoeG2ou6sPwyOUTx_qteRsg@mail.gmail.com
|
|
|
|
CREATE TABLE IF NOT EXISTS testcase(
|
|
|
|
id int PRIMARY KEY,
|
|
|
|
balance numeric
|
|
|
|
);
|
|
|
|
INSERT INTO testcase VALUES (1, 0);
|
|
|
|
BEGIN;
|
|
|
|
SELECT * FROM testcase WHERE testcase.id = 1 FOR UPDATE;
|
|
|
|
id | balance
|
|
|
|
----+---------
|
|
|
|
1 | 0
|
|
|
|
(1 row)
|
|
|
|
|
|
|
|
UPDATE testcase SET balance = balance + 400 WHERE id=1;
|
|
|
|
SAVEPOINT subxact;
|
|
|
|
UPDATE testcase SET balance = balance - 100 WHERE id=1;
|
|
|
|
ROLLBACK TO SAVEPOINT subxact;
|
|
|
|
-- should return one tuple
|
|
|
|
SELECT * FROM testcase WHERE id = 1 FOR UPDATE;
|
|
|
|
id | balance
|
|
|
|
----+---------
|
|
|
|
1 | 400
|
|
|
|
(1 row)
|
|
|
|
|
|
|
|
ROLLBACK;
|
|
|
|
DROP TABLE testcase;
|