Fix handling of updated tuples in the MERGE statement

This branch missed the IsolationUsesXactSnapshot() check.  That led to EPQ on
repeatable read and serializable isolation levels.  This commit fixes the
issue and provides a simple isolation check for that.  Backpatch through v15
where MERGE statement was introduced.

Reported-by: Tender Wang <tndrwang@gmail.com>
Discussion: https://postgr.es/m/CAPpHfdvzZSaNYdj5ac-tYRi6MuuZnYHiUkZ3D-AoY-ny8v%2BS%2Bw%40mail.gmail.com
Author: Tender Wang <tndrwang@gmail.com>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Backpatch-through: 15
This commit is contained in:
Alexander Korotkov
2026-03-05 19:47:20 +02:00
parent f51821d9cf
commit f6e63d4b8b
3 changed files with 32 additions and 0 deletions

View File

@ -3077,6 +3077,11 @@ lmerge_matched:
*inputslot;
LockTupleMode lockmode;
if (IsolationUsesXactSnapshot())
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
/*
* The target tuple was concurrently updated by some other
* transaction. Run EvalPlanQual() with the new version of

View File

@ -355,3 +355,28 @@ step c1: COMMIT;
step pa_merge2c_dup: <... completed>
ERROR: MERGE command cannot affect row a second time
step a2: ABORT;
starting permutation: merge2a c1 s1beginrr merge1 c2
step merge2a:
MERGE INTO target t
USING (SELECT 1 as key, 'merge2a' as val) s
ON s.key = t.key
WHEN NOT MATCHED THEN
INSERT VALUES (s.key, s.val)
WHEN MATCHED THEN
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
step c1: COMMIT;
step s1beginrr: BEGIN ISOLATION LEVEL REPEATABLE READ;
step merge1:
MERGE INTO target t
USING (SELECT 1 as key, 'merge1' as val) s
ON s.key = t.key
WHEN NOT MATCHED THEN
INSERT VALUES (s.key, s.val)
WHEN MATCHED THEN
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
<waiting ...>
step c2: COMMIT;
step merge1: <... completed>
ERROR: could not serialize access due to concurrent update

View File

@ -78,6 +78,7 @@ step "pa_merge3"
}
step "c1" { COMMIT; }
step "a1" { ABORT; }
step "s1beginrr" { BEGIN ISOLATION LEVEL REPEATABLE READ; }
session "s2"
setup
@ -167,3 +168,4 @@ permutation "pa_merge2" "c1" "pa_merge2a" "pa_select2" "c2" # succeeds
permutation "pa_merge3" "pa_merge2b_when" "c1" "pa_select2" "c2" # WHEN not satisfied by updated tuple
permutation "pa_merge1" "pa_merge2b_when" "c1" "pa_select2" "c2" # WHEN satisfied by updated tuple
permutation "pa_merge1" "pa_merge2c_dup" "c1" "a2"
permutation "merge2a" "c1" "s1beginrr" "merge1" "c2"