@ -2324,6 +2324,8 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
}
else
{
ItemPointerData lockedtid ;
/*
* If we generate a new candidate tuple after EvalPlanQual testing , we
* must loop back here to try again . ( We don ' t need to redo triggers ,
@ -2332,6 +2334,7 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
* to do them again . )
*/
redo_act :
lockedtid = * tupleid ;
result = ExecUpdateAct ( context , resultRelInfo , tupleid , oldtuple , slot ,
canSetTag , & updateCxt ) ;
@ -2425,6 +2428,14 @@ redo_act:
ExecInitUpdateProjection ( context - > mtstate ,
resultRelInfo ) ;
if ( resultRelInfo - > ri_needLockTagTuple )
{
UnlockTuple ( resultRelationDesc ,
& lockedtid , InplaceUpdateTupleLock ) ;
LockTuple ( resultRelationDesc ,
tupleid , InplaceUpdateTupleLock ) ;
}
/* Fetch the most recent version of old tuple. */
oldSlot = resultRelInfo - > ri_oldTupleSlot ;
if ( ! table_tuple_fetch_row_version ( resultRelationDesc ,
@ -2529,6 +2540,14 @@ ExecOnConflictUpdate(ModifyTableContext *context,
TransactionId xmin ;
bool isnull ;
/*
* Parse analysis should have blocked ON CONFLICT for all system
* relations , which includes these . There ' s no fundamental obstacle to
* supporting this ; we ' d just need to handle LOCKTAG_TUPLE like the other
* ExecUpdate ( ) caller .
*/
Assert ( ! resultRelInfo - > ri_needLockTagTuple ) ;
/* Determine lock mode to use */
lockmode = ExecUpdateLockMode ( context - > estate , resultRelInfo ) ;
@ -2854,6 +2873,7 @@ ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
{
ModifyTableState * mtstate = context - > mtstate ;
List * * mergeActions = resultRelInfo - > ri_MergeActions ;
ItemPointerData lockedtid ;
List * actionStates ;
TupleTableSlot * newslot = NULL ;
TupleTableSlot * rslot = NULL ;
@ -2890,14 +2910,32 @@ ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
* target wholerow junk attr .
*/
Assert ( tupleid ! = NULL | | oldtuple ! = NULL ) ;
ItemPointerSetInvalid ( & lockedtid ) ;
if ( oldtuple ! = NULL )
{
Assert ( ! resultRelInfo - > ri_needLockTagTuple ) ;
ExecForceStoreHeapTuple ( oldtuple , resultRelInfo - > ri_oldTupleSlot ,
false ) ;
else if ( ! table_tuple_fetch_row_version ( resultRelInfo - > ri_RelationDesc ,
tupleid ,
SnapshotAny ,
resultRelInfo - > ri_oldTupleSlot ) )
elog ( ERROR , " failed to fetch the target tuple " ) ;
}
else
{
if ( resultRelInfo - > ri_needLockTagTuple )
{
/*
* This locks even for CMD_DELETE , for CMD_NOTHING , and for tuples
* that don ' t match mas_whenqual . MERGE on system catalogs is a
* minor use case , so don ' t bother optimizing those .
*/
LockTuple ( resultRelInfo - > ri_RelationDesc , tupleid ,
InplaceUpdateTupleLock ) ;
lockedtid = * tupleid ;
}
if ( ! table_tuple_fetch_row_version ( resultRelInfo - > ri_RelationDesc ,
tupleid ,
SnapshotAny ,
resultRelInfo - > ri_oldTupleSlot ) )
elog ( ERROR , " failed to fetch the target tuple " ) ;
}
/*
* Test the join condition . If it ' s satisfied , perform a MATCHED action .
@ -2969,7 +3007,7 @@ lmerge_matched:
tupleid , NULL , newslot , & result ) )
{
if ( result = = TM_Ok )
return NULL ; /* "do nothing" */
goto out ; /* "do nothing" */
break ; /* concurrent update/delete */
}
@ -2980,11 +3018,11 @@ lmerge_matched:
{
if ( ! ExecIRUpdateTriggers ( estate , resultRelInfo ,
oldtuple , newslot ) )
return NULL ; /* "do nothing" */
goto out ; /* "do nothing" */
}
else
{
/* called table_tuple_fetch_row_version() above */
/* checked ri_needLockTagTuple above */
Assert ( oldtuple = = NULL ) ;
result = ExecUpdateAct ( context , resultRelInfo , tupleid ,
@ -3003,7 +3041,8 @@ lmerge_matched:
if ( updateCxt . crossPartUpdate )
{
mtstate - > mt_merge_updated + = 1 ;
return context - > cpUpdateReturningSlot ;
rslot = context - > cpUpdateReturningSlot ;
goto out ;
}
}
@ -3021,7 +3060,7 @@ lmerge_matched:
NULL , NULL , & result ) )
{
if ( result = = TM_Ok )
return NULL ; /* "do nothing" */
goto out ; /* "do nothing" */
break ; /* concurrent update/delete */
}
@ -3032,11 +3071,11 @@ lmerge_matched:
{
if ( ! ExecIRDeleteTriggers ( estate , resultRelInfo ,
oldtuple ) )
return NULL ; /* "do nothing" */
goto out ; /* "do nothing" */
}
else
{
/* called table_tuple_fetch_row_version() above */
/* checked ri_needLockTagTuple above */
Assert ( oldtuple = = NULL ) ;
result = ExecDeleteAct ( context , resultRelInfo , tupleid ,
@ -3118,7 +3157,7 @@ lmerge_matched:
* let caller handle it under NOT MATCHED [ BY TARGET ] clauses .
*/
* matched = false ;
return NULL ;
goto out ;
case TM_Updated :
{
@ -3192,7 +3231,7 @@ lmerge_matched:
* more to do .
*/
if ( TupIsNull ( epqslot ) )
return NULL ;
goto out ;
/*
* If we got a NULL ctid from the subplan , the
@ -3210,6 +3249,15 @@ lmerge_matched:
* we need to switch to the NOT MATCHED BY
* SOURCE case .
*/
if ( resultRelInfo - > ri_needLockTagTuple )
{
if ( ItemPointerIsValid ( & lockedtid ) )
UnlockTuple ( resultRelInfo - > ri_RelationDesc , & lockedtid ,
InplaceUpdateTupleLock ) ;
LockTuple ( resultRelInfo - > ri_RelationDesc , & context - > tmfd . ctid ,
InplaceUpdateTupleLock ) ;
lockedtid = context - > tmfd . ctid ;
}
if ( ! table_tuple_fetch_row_version ( resultRelationDesc ,
& context - > tmfd . ctid ,
SnapshotAny ,
@ -3238,7 +3286,7 @@ lmerge_matched:
* MATCHED [ BY TARGET ] actions
*/
* matched = false ;
return NULL ;
goto out ;
case TM_SelfModified :
@ -3266,13 +3314,13 @@ lmerge_matched:
/* This shouldn't happen */
elog ( ERROR , " attempted to update or delete invisible tuple " ) ;
return NULL ;
goto out ;
default :
/* see table_tuple_lock call in ExecDelete() */
elog ( ERROR , " unexpected table_tuple_lock status: %u " ,
result ) ;
return NULL ;
goto out ;
}
}
@ -3319,6 +3367,10 @@ lmerge_matched:
/*
* Successfully executed an action or no qualifying action was found .
*/
out :
if ( ItemPointerIsValid ( & lockedtid ) )
UnlockTuple ( resultRelInfo - > ri_RelationDesc , & lockedtid ,
InplaceUpdateTupleLock ) ;
return rslot ;
}
@ -3770,6 +3822,7 @@ ExecModifyTable(PlanState *pstate)
HeapTupleData oldtupdata ;
HeapTuple oldtuple ;
ItemPointer tupleid ;
bool tuplock ;
CHECK_FOR_INTERRUPTS ( ) ;
@ -4082,6 +4135,8 @@ ExecModifyTable(PlanState *pstate)
break ;
case CMD_UPDATE :
tuplock = false ;
/* Initialize projection info if first time for this table */
if ( unlikely ( ! resultRelInfo - > ri_projectNewInfoValid ) )
ExecInitUpdateProjection ( node , resultRelInfo ) ;
@ -4093,6 +4148,7 @@ ExecModifyTable(PlanState *pstate)
oldSlot = resultRelInfo - > ri_oldTupleSlot ;
if ( oldtuple ! = NULL )
{
Assert ( ! resultRelInfo - > ri_needLockTagTuple ) ;
/* Use the wholerow junk attr as the old tuple. */
ExecForceStoreHeapTuple ( oldtuple , oldSlot , false ) ;
}
@ -4101,6 +4157,11 @@ ExecModifyTable(PlanState *pstate)
/* Fetch the most recent version of old tuple. */
Relation relation = resultRelInfo - > ri_RelationDesc ;
if ( resultRelInfo - > ri_needLockTagTuple )
{
LockTuple ( relation , tupleid , InplaceUpdateTupleLock ) ;
tuplock = true ;
}
if ( ! table_tuple_fetch_row_version ( relation , tupleid ,
SnapshotAny ,
oldSlot ) )
@ -4112,6 +4173,9 @@ ExecModifyTable(PlanState *pstate)
/* Now apply the update. */
slot = ExecUpdate ( & context , resultRelInfo , tupleid , oldtuple ,
slot , node - > canSetTag ) ;
if ( tuplock )
UnlockTuple ( resultRelInfo - > ri_RelationDesc , tupleid ,
InplaceUpdateTupleLock ) ;
break ;
case CMD_DELETE :