diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
new file mode 100644
index 2632431..a0efe7e
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -3561,10 +3561,21 @@ lmerge_matched:
 							 errmsg("could not serialize access due to concurrent delete")));
 
 				/*
-				 * If the tuple was already deleted, set matched to false to
-				 * let caller handle it under NOT MATCHED [BY TARGET] clauses.
+				 * The target tuple was concurrently deleted by some other
+				 * transaction.  If this was a MATCHED case, the source row
+				 * still exists, so let the caller handle it using any NOT
+				 * MATCHED [BY TARGET] actions.
+				 *
+				 * If this was originally a NOT MATCHED BY SOURCE case, leave
+				 * matched true.  Since neither a source row nor a target row
+				 * now exists, there is no further action to execute.
+				 *
+				 * If this was originally MATCHED but a concurrent update had
+				 * already changed it to NOT MATCHED BY SOURCE, matched is
+				 * already false and should remain so.
 				 */
-				*matched = false;
+				if (relaction->mas_action->matchKind == MERGE_WHEN_MATCHED)
+					*matched = false;
 				goto out;
 
 			case TM_Updated:
@@ -3717,10 +3728,24 @@ lmerge_matched:
 						case TM_Deleted:
 
 							/*
-							 * tuple already deleted; tell caller to run NOT
-							 * MATCHED [BY TARGET] actions
+							 * The target tuple was concurrently deleted by
+							 * some other transaction.  If this was a MATCHED
+							 * case, the source row still exists, so let the
+							 * caller handle it using any NOT MATCHED [BY
+							 * TARGET] actions.
+							 *
+							 * If this was originally a NOT MATCHED BY SOURCE
+							 * case, leave matched true.  Since neither a
+							 * source row nor a target row now exists, there
+							 * is no further action to execute.
+							 *
+							 * If this was originally MATCHED but a concurrent
+							 * update had already changed it to NOT MATCHED BY
+							 * SOURCE, matched is already false and should
+							 * remain so.
 							 */
-							*matched = false;
+							if (was_matched)
+								*matched = false;
 							goto out;
 
 						case TM_SelfModified:
diff --git a/src/test/isolation/expected/merge-delete.out b/src/test/isolation/expected/merge-delete.out
new file mode 100644
index 897b935..c0e3b2b
--- a/src/test/isolation/expected/merge-delete.out
+++ b/src/test/isolation/expected/merge-delete.out
@@ -234,3 +234,161 @@ key|val
 (1 row)
 
 step c2: COMMIT;
+
+starting permutation: delete merge_wnmbs_src2 c1 select2 c2
+step delete: DELETE FROM target t WHERE t.key = 1;
+step merge_wnmbs_src2: 
+  MERGE INTO target t
+    USING (SELECT 2 AS key, 'merge2' AS val) s ON t.key = s.key
+    WHEN MATCHED THEN UPDATE SET key = t.key + 1, val = t.val || ' match updated by ' || s.val
+    WHEN NOT MATCHED BY SOURCE THEN UPDATE SET key = t.key + 1, val = t.val || ' not matched by source'
+    WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val)
+    RETURNING merge_action(), t.*;
+ <waiting ...>
+step c1: COMMIT;
+step merge_wnmbs_src2: <... completed>
+merge_action|key|val   
+------------+---+------
+INSERT      |  2|merge2
+(1 row)
+
+step select2: SELECT * FROM target;
+key|val   
+---+------
+  2|merge2
+(1 row)
+
+step c2: COMMIT;
+
+starting permutation: delete_pa merge_wnmbs_src2_pa c1 select2_pa c2
+step delete_pa: DELETE FROM target_pa t WHERE t.key = 1;
+step merge_wnmbs_src2_pa: 
+  MERGE INTO target_pa t
+    USING (SELECT 2 AS key, 'merge2_pa' AS val) s ON t.key = s.key
+    WHEN MATCHED THEN UPDATE SET key = t.key + 1, val = t.val || ' match updated by ' || s.val
+    WHEN NOT MATCHED BY SOURCE THEN UPDATE SET key = t.key + 1, val = t.val || ' not matched by source'
+    WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val)
+    RETURNING merge_action(), t.*;
+ <waiting ...>
+step c1: COMMIT;
+step merge_wnmbs_src2_pa: <... completed>
+merge_action|key|val      
+------------+---+---------
+INSERT      |  2|merge2_pa
+(1 row)
+
+step select2_pa: SELECT * FROM target_pa;
+key|val      
+---+---------
+  2|merge2_pa
+(1 row)
+
+step c2: COMMIT;
+
+starting permutation: delete_tg merge_wnmbs_src2_tg c1 select2_tg c2
+s1: NOTICE:  Delete: (1,setup1)
+step delete_tg: DELETE FROM target_tg t WHERE t.key = 1;
+step merge_wnmbs_src2_tg: 
+  MERGE INTO target_tg t
+    USING (SELECT 2 AS key, 'merge2_tg' AS val) s ON t.key = s.key
+    WHEN MATCHED THEN UPDATE SET key = t.key + 1, val = t.val || ' match updated by ' || s.val
+    WHEN NOT MATCHED BY SOURCE THEN UPDATE SET key = t.key + 1, val = t.val || ' not matched by source'
+    WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val)
+    RETURNING merge_action(), t.*;
+ <waiting ...>
+step c1: COMMIT;
+s2: NOTICE:  Insert: (2,merge2_tg)
+step merge_wnmbs_src2_tg: <... completed>
+merge_action|key|val      
+------------+---+---------
+INSERT      |  2|merge2_tg
+(1 row)
+
+step select2_tg: SELECT * FROM target_tg;
+key|val      
+---+---------
+  2|merge2_tg
+(1 row)
+
+step c2: COMMIT;
+
+starting permutation: update delete merge_wnmbs_src2 c1 select2 c2
+step update: UPDATE target t SET val = t.val || ' update1' WHERE t.key = 1;
+step delete: DELETE FROM target t WHERE t.key = 1;
+step merge_wnmbs_src2: 
+  MERGE INTO target t
+    USING (SELECT 2 AS key, 'merge2' AS val) s ON t.key = s.key
+    WHEN MATCHED THEN UPDATE SET key = t.key + 1, val = t.val || ' match updated by ' || s.val
+    WHEN NOT MATCHED BY SOURCE THEN UPDATE SET key = t.key + 1, val = t.val || ' not matched by source'
+    WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val)
+    RETURNING merge_action(), t.*;
+ <waiting ...>
+step c1: COMMIT;
+step merge_wnmbs_src2: <... completed>
+merge_action|key|val   
+------------+---+------
+INSERT      |  2|merge2
+(1 row)
+
+step select2: SELECT * FROM target;
+key|val   
+---+------
+  2|merge2
+(1 row)
+
+step c2: COMMIT;
+
+starting permutation: update_pa delete_pa merge_wnmbs_src2_pa c1 select2_pa c2
+step update_pa: UPDATE target_pa t SET val = t.val || ' update1' WHERE t.key = 1;
+step delete_pa: DELETE FROM target_pa t WHERE t.key = 1;
+step merge_wnmbs_src2_pa: 
+  MERGE INTO target_pa t
+    USING (SELECT 2 AS key, 'merge2_pa' AS val) s ON t.key = s.key
+    WHEN MATCHED THEN UPDATE SET key = t.key + 1, val = t.val || ' match updated by ' || s.val
+    WHEN NOT MATCHED BY SOURCE THEN UPDATE SET key = t.key + 1, val = t.val || ' not matched by source'
+    WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val)
+    RETURNING merge_action(), t.*;
+ <waiting ...>
+step c1: COMMIT;
+step merge_wnmbs_src2_pa: <... completed>
+merge_action|key|val      
+------------+---+---------
+INSERT      |  2|merge2_pa
+(1 row)
+
+step select2_pa: SELECT * FROM target_pa;
+key|val      
+---+---------
+  2|merge2_pa
+(1 row)
+
+step c2: COMMIT;
+
+starting permutation: update_tg delete_tg merge_wnmbs_src2_tg c1 select2_tg c2
+s1: NOTICE:  Update: (1,setup1) -> (1,"setup1 update1")
+step update_tg: UPDATE target_tg t SET val = t.val || ' update1' WHERE t.key = 1;
+s1: NOTICE:  Delete: (1,"setup1 update1")
+step delete_tg: DELETE FROM target_tg t WHERE t.key = 1;
+step merge_wnmbs_src2_tg: 
+  MERGE INTO target_tg t
+    USING (SELECT 2 AS key, 'merge2_tg' AS val) s ON t.key = s.key
+    WHEN MATCHED THEN UPDATE SET key = t.key + 1, val = t.val || ' match updated by ' || s.val
+    WHEN NOT MATCHED BY SOURCE THEN UPDATE SET key = t.key + 1, val = t.val || ' not matched by source'
+    WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val)
+    RETURNING merge_action(), t.*;
+ <waiting ...>
+step c1: COMMIT;
+s2: NOTICE:  Insert: (2,merge2_tg)
+step merge_wnmbs_src2_tg: <... completed>
+merge_action|key|val      
+------------+---+---------
+INSERT      |  2|merge2_tg
+(1 row)
+
+step select2_tg: SELECT * FROM target_tg;
+key|val      
+---+---------
+  2|merge2_tg
+(1 row)
+
+step c2: COMMIT;
diff --git a/src/test/isolation/specs/merge-delete.spec b/src/test/isolation/specs/merge-delete.spec
new file mode 100644
index ba5f70e..833ce1f
--- a/src/test/isolation/specs/merge-delete.spec
+++ b/src/test/isolation/specs/merge-delete.spec
@@ -50,6 +50,9 @@ setup
 step "delete" { DELETE FROM target t WHERE t.key = 1; }
 step "delete_pa" { DELETE FROM target_pa t WHERE t.key = 1; }
 step "delete_tg" { DELETE FROM target_tg t WHERE t.key = 1; }
+step "update" { UPDATE target t SET val = t.val || ' update1' WHERE t.key = 1; }
+step "update_pa" { UPDATE target_pa t SET val = t.val || ' update1' WHERE t.key = 1; }
+step "update_tg" { UPDATE target_tg t SET val = t.val || ' update1' WHERE t.key = 1; }
 step "c1" { COMMIT; }
 
 session "s2"
@@ -65,6 +68,30 @@ step "merge2_pa" { MERGE INTO target_pa
 step "merge2_tg" { MERGE INTO target_tg t USING (SELECT 1 as key, 'merge2_tg' 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 "merge_delete2" { MERGE INTO target t USING (SELECT 1 as key, 'merge_delete2' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN DELETE; }
 step "merge_delete2_tg" { MERGE INTO target_tg t USING (SELECT 1 as key, 'merge_delete2_tg' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN DELETE; }
+step "merge_wnmbs_src2" {
+  MERGE INTO target t
+    USING (SELECT 2 AS key, 'merge2' AS val) s ON t.key = s.key
+    WHEN MATCHED THEN UPDATE SET key = t.key + 1, val = t.val || ' match updated by ' || s.val
+    WHEN NOT MATCHED BY SOURCE THEN UPDATE SET key = t.key + 1, val = t.val || ' not matched by source'
+    WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val)
+    RETURNING merge_action(), t.*;
+}
+step "merge_wnmbs_src2_pa" {
+  MERGE INTO target_pa t
+    USING (SELECT 2 AS key, 'merge2_pa' AS val) s ON t.key = s.key
+    WHEN MATCHED THEN UPDATE SET key = t.key + 1, val = t.val || ' match updated by ' || s.val
+    WHEN NOT MATCHED BY SOURCE THEN UPDATE SET key = t.key + 1, val = t.val || ' not matched by source'
+    WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val)
+    RETURNING merge_action(), t.*;
+}
+step "merge_wnmbs_src2_tg" {
+  MERGE INTO target_tg t
+    USING (SELECT 2 AS key, 'merge2_tg' AS val) s ON t.key = s.key
+    WHEN MATCHED THEN UPDATE SET key = t.key + 1, val = t.val || ' match updated by ' || s.val
+    WHEN NOT MATCHED BY SOURCE THEN UPDATE SET key = t.key + 1, val = t.val || ' not matched by source'
+    WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val)
+    RETURNING merge_action(), t.*;
+}
 step "select2" { SELECT * FROM target; }
 step "select2_pa" { SELECT * FROM target_pa; }
 step "select2_tg" { SELECT * FROM target_tg; }
@@ -94,3 +121,13 @@ permutation "delete_pa" "merge2_pa" "c1"
 permutation "delete_tg" "merge2_tg" "c1" "select2_tg" "c2"
 permutation "delete" "merge_delete2" "c1" "select2" "c2"
 permutation "delete_tg" "merge_delete2_tg" "c1" "select2_tg" "c2"
+
+# Concurrent DELETE vs MERGE ... WHEN NOT MATCHED BY SOURCE
+permutation "delete" "merge_wnmbs_src2" "c1" "select2" "c2"
+permutation "delete_pa" "merge_wnmbs_src2_pa" "c1" "select2_pa" "c2"
+permutation "delete_tg" "merge_wnmbs_src2_tg" "c1" "select2_tg" "c2"
+
+# Concurrent UPDATE then DELETE vs MERGE ... WHEN NOT MATCHED BY SOURCE
+permutation "update" "delete" "merge_wnmbs_src2" "c1" "select2" "c2"
+permutation "update_pa" "delete_pa" "merge_wnmbs_src2_pa" "c1" "select2_pa" "c2"
+permutation "update_tg" "delete_tg" "merge_wnmbs_src2_tg" "c1" "select2_tg" "c2"
