Hi all, Alexander Korotkov <[email protected]> 于2026年2月27日周五 01:03写道: > Thank you for the confirmation and for the patch. Regarding the test > case, can we handle this without introducing a new .spec file? I > think we can add 1-2 permutations to merge-update.spec. Even existing > step should work, you only need one new which begins transaction in RR > isolation mode.
Done Please see the v2 patch. -- Thanks, Tender Wang
From 6a8dadb846338648e289ae3ac7e90eba53e35564 Mon Sep 17 00:00:00 2001 From: Tender Wang <[email protected]> Date: Tue, 24 Feb 2026 11:04:26 +0800 Subject: [PATCH v2] Fix MERGE match do update in RR isolation level. --- src/backend/executor/nodeModifyTable.c | 4 +++ src/test/isolation/expected/merge-update.out | 33 ++++++++++++++++++++ src/test/isolation/specs/merge-update.spec | 2 ++ 3 files changed, 39 insertions(+) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 793c76d4f82..8d8c9fb54e3 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -3547,6 +3547,10 @@ 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. If we are currently processing a MATCHED diff --git a/src/test/isolation/expected/merge-update.out b/src/test/isolation/expected/merge-update.out index feceacf4818..821565b4303 100644 --- a/src/test/isolation/expected/merge-update.out +++ b/src/test/isolation/expected/merge-update.out @@ -549,3 +549,36 @@ 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 + WHEN NOT MATCHED BY SOURCE THEN + UPDATE set key = t.key + 1, val = t.val || ' source not matched by merge2a' + RETURNING merge_action(), old, new, t.*; + +merge_action|old |new |key|val +------------+----------+-------------------------------+---+------------------------- +UPDATE |(1,setup1)|(2,"setup1 updated by merge2a")| 2|setup1 updated by merge2a +(1 row) + +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 diff --git a/src/test/isolation/specs/merge-update.spec b/src/test/isolation/specs/merge-update.spec index 771ee5b70cf..b902779edd6 100644 --- a/src/test/isolation/specs/merge-update.spec +++ b/src/test/isolation/specs/merge-update.spec @@ -93,6 +93,7 @@ step "pa_merge3" } step "c1" { COMMIT; } step "a1" { ABORT; } +step "s1beginrr" { BEGIN ISOLATION LEVEL REPEATABLE READ; } session "s2" setup @@ -223,3 +224,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" -- 2.34.1
