@@ -120,6 +120,7 @@ IvyExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
120120{
121121 ModifyTableState * mtstate = context -> mtstate ;
122122 List * * mergeActions = resultRelInfo -> ri_MergeActions ;
123+ ItemPointerData lockedtid ;
123124 List * actionStates ;
124125 TupleTableSlot * newslot = NULL ;
125126 TupleTableSlot * rslot = NULL ;
@@ -156,14 +157,32 @@ IvyExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
156157 * target wholerow junk attr.
157158 */
158159 Assert (tupleid != NULL || oldtuple != NULL );
160+ ItemPointerSetInvalid (& lockedtid );
159161 if (oldtuple != NULL )
162+ {
163+ Assert (!resultRelInfo -> ri_needLockTagTuple );
160164 ExecForceStoreHeapTuple (oldtuple , resultRelInfo -> ri_oldTupleSlot ,
161165 false);
162- else if (!table_tuple_fetch_row_version (resultRelInfo -> ri_RelationDesc ,
163- tupleid ,
164- SnapshotAny ,
165- resultRelInfo -> ri_oldTupleSlot ))
166- elog (ERROR , "failed to fetch the target tuple" );
166+ }
167+ else
168+ {
169+ if (resultRelInfo -> ri_needLockTagTuple )
170+ {
171+ /*
172+ * This locks even for CMD_DELETE, for CMD_NOTHING, and for tuples
173+ * that don't match mas_whenqual. MERGE on system catalogs is a
174+ * minor use case, so don't bother optimizing those.
175+ */
176+ LockTuple (resultRelInfo -> ri_RelationDesc , tupleid ,
177+ InplaceUpdateTupleLock );
178+ lockedtid = * tupleid ;
179+ }
180+ if (!table_tuple_fetch_row_version (resultRelInfo -> ri_RelationDesc ,
181+ tupleid ,
182+ SnapshotAny ,
183+ resultRelInfo -> ri_oldTupleSlot ))
184+ elog (ERROR , "failed to fetch the target tuple" );
185+ }
167186
168187 /*
169188 * Test the join condition. If it's satisfied, perform a MATCHED action.
@@ -356,7 +375,7 @@ IvyExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
356375 * let caller handle it under NOT MATCHED [BY TARGET] clauses.
357376 */
358377 * matched = false;
359- return NULL ;
378+ goto out ;
360379
361380 case TM_Updated :
362381 {
@@ -430,7 +449,7 @@ IvyExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
430449 * more to do.
431450 */
432451 if (TupIsNull (epqslot ))
433- return NULL ;
452+ goto out ;
434453
435454 /*
436455 * If we got a NULL ctid from the subplan, the
@@ -448,6 +467,15 @@ IvyExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
448467 * we need to switch to the NOT MATCHED BY
449468 * SOURCE case.
450469 */
470+ if (resultRelInfo -> ri_needLockTagTuple )
471+ {
472+ if (ItemPointerIsValid (& lockedtid ))
473+ UnlockTuple (resultRelInfo -> ri_RelationDesc , & lockedtid ,
474+ InplaceUpdateTupleLock );
475+ LockTuple (resultRelInfo -> ri_RelationDesc , & context -> tmfd .ctid ,
476+ InplaceUpdateTupleLock );
477+ lockedtid = context -> tmfd .ctid ;
478+ }
451479 if (!table_tuple_fetch_row_version (resultRelationDesc ,
452480 & context -> tmfd .ctid ,
453481 SnapshotAny ,
@@ -476,7 +504,7 @@ IvyExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
476504 * MATCHED [BY TARGET] actions
477505 */
478506 * matched = false;
479- return NULL ;
507+ goto out ;
480508
481509 case TM_SelfModified :
482510
@@ -504,13 +532,13 @@ IvyExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
504532
505533 /* This shouldn't happen */
506534 elog (ERROR , "attempted to update or delete invisible tuple" );
507- return NULL ;
535+ goto out ;
508536
509537 default :
510538 /* see table_tuple_lock call in ExecDelete() */
511539 elog (ERROR , "unexpected table_tuple_lock status: %u" ,
512540 result );
513- return NULL ;
541+ goto out ;
514542 }
515543 }
516544
@@ -557,6 +585,10 @@ IvyExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
557585 /*
558586 * Successfully executed an action or no qualifying action was found.
559587 */
588+ out :
589+ if (ItemPointerIsValid (& lockedtid ))
590+ UnlockTuple (resultRelInfo -> ri_RelationDesc , & lockedtid ,
591+ InplaceUpdateTupleLock );
560592 return rslot ;
561593}
562594
0 commit comments