@ -149,34 +149,40 @@ ExecCheckPlanOutput(Relation resultRel, List *targetList)
/*
* ExecProcessReturning - - - evaluate a RETURNING list
*
* resultRelInfo : current result rel
* projectReturning : the projection to evaluate
* resultRelOid : result relation ' s OID
* tupleSlot : slot holding tuple actually inserted / updated / deleted
* planSlot : slot holding tuple returned by top subplan node
*
* In cross - partition UPDATE cases , projectReturning and planSlot are as
* for the source partition , and tupleSlot must conform to that . But
* resultRelOid is for the destination partition .
*
* Note : If tupleSlot is NULL , the FDW should have already provided econtext ' s
* scan tuple .
*
* Returns a slot holding the result tuple
*/
static TupleTableSlot *
ExecProcessReturning ( ResultRelInfo * resultRelInfo ,
ExecProcessReturning ( ProjectionInfo * projectReturning ,
Oid resultRelOid ,
TupleTableSlot * tupleSlot ,
TupleTableSlot * planSlot )
{
ProjectionInfo * projectReturning = resultRelInfo - > ri_projectReturning ;
ExprContext * econtext = projectReturning - > pi_exprContext ;
/* Make tuple and any needed join variables available to ExecProject */
if ( tupleSlot )
econtext - > ecxt_scantuple = tupleSlot ;
else
Assert ( econtext - > ecxt_scantuple ) ;
econtext - > ecxt_outertuple = planSlot ;
/*
* RETURNING expressions might reference the tableoid column , so
* reinitialize tts_tableOid before evaluating them .
* RETURNING expressions might reference the tableoid column , so be sure
* we expose the desired OID , ie that of the real target relation .
*/
econtext - > ecxt_scantuple - > tts_tableOid =
RelationGetRelid ( resultRelInfo - > ri_RelationDesc ) ;
econtext - > ecxt_scantuple - > tts_tableOid = resultRelOid ;
/* Compute the RETURNING expressions */
return ExecProject ( projectReturning ) ;
@ -368,6 +374,16 @@ ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot, CmdType cmdtype
* For INSERT , we have to insert the tuple into the target relation
* and insert appropriate tuples into the index relations .
*
* slot contains the new tuple value to be stored .
* planSlot is the output of the ModifyTable ' s subplan ; we use it
* to access " junk " columns that are not going to be stored .
* In a cross - partition UPDATE , srcSlot is the slot that held the
* updated tuple for the source relation ; otherwise it ' s NULL .
*
* returningRelInfo is the resultRelInfo for the source relation of a
* cross - partition UPDATE ; otherwise it ' s the current result relation .
* We use it to process RETURNING lists , for reasons explained below .
*
* Returns RETURNING result if any , otherwise NULL .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -375,6 +391,8 @@ static TupleTableSlot *
ExecInsert ( ModifyTableState * mtstate ,
TupleTableSlot * slot ,
TupleTableSlot * planSlot ,
TupleTableSlot * srcSlot ,
ResultRelInfo * returningRelInfo ,
EState * estate ,
bool canSetTag )
{
@ -677,8 +695,45 @@ ExecInsert(ModifyTableState *mtstate,
ExecWithCheckOptions ( WCO_VIEW_CHECK , resultRelInfo , slot , estate ) ;
/* Process RETURNING if present */
if ( resultRelInfo - > ri_projectReturning )
result = ExecProcessReturning ( resultRelInfo , slot , planSlot ) ;
if ( returningRelInfo - > ri_projectReturning )
{
/*
* In a cross - partition UPDATE with RETURNING , we have to use the
* source partition ' s RETURNING list , because that matches the output
* of the planSlot , while the destination partition might have
* different resjunk columns . This means we have to map the
* destination tuple back to the source ' s format so we can apply that
* RETURNING list . This is expensive , but it should be an uncommon
* corner case , so we won ' t spend much effort on making it fast .
*
* We assume that we can use srcSlot to hold the re - converted tuple .
* Note that in the common case where the child partitions both match
* the root ' s format , previous optimizations will have resulted in
* slot and srcSlot being identical , cueing us that there ' s nothing to
* do here .
*/
if ( returningRelInfo ! = resultRelInfo & & slot ! = srcSlot )
{
Relation srcRelationDesc = returningRelInfo - > ri_RelationDesc ;
AttrMap * map ;
map = build_attrmap_by_name_if_req ( RelationGetDescr ( resultRelationDesc ) ,
RelationGetDescr ( srcRelationDesc ) ) ;
if ( map )
{
TupleTableSlot * origSlot = slot ;
slot = execute_attr_map_slot ( map , slot , srcSlot ) ;
slot - > tts_tid = origSlot - > tts_tid ;
slot - > tts_tableOid = origSlot - > tts_tableOid ;
free_attrmap ( map ) ;
}
}
result = ExecProcessReturning ( returningRelInfo - > ri_projectReturning ,
RelationGetRelid ( resultRelationDesc ) ,
slot , planSlot ) ;
}
return result ;
}
@ -1027,7 +1082,9 @@ ldelete:;
}
}
rslot = ExecProcessReturning ( resultRelInfo , slot , planSlot ) ;
rslot = ExecProcessReturning ( resultRelInfo - > ri_projectReturning ,
RelationGetRelid ( resultRelationDesc ) ,
slot , planSlot ) ;
/*
* Before releasing the target tuple again , make sure rslot has a
@ -1203,6 +1260,7 @@ lreplace:;
{
bool tuple_deleted ;
TupleTableSlot * ret_slot ;
TupleTableSlot * orig_slot = slot ;
TupleTableSlot * epqslot = NULL ;
PartitionTupleRouting * proute = mtstate - > mt_partition_tuple_routing ;
int map_index ;
@ -1309,6 +1367,7 @@ lreplace:;
mtstate - > rootResultRelInfo , slot ) ;
ret_slot = ExecInsert ( mtstate , slot , planSlot ,
orig_slot , resultRelInfo ,
estate , canSetTag ) ;
/* Revert ExecPrepareTupleRouting's node change. */
@ -1505,7 +1564,9 @@ lreplace:;
/* Process RETURNING if present */
if ( resultRelInfo - > ri_projectReturning )
return ExecProcessReturning ( resultRelInfo , slot , planSlot ) ;
return ExecProcessReturning ( resultRelInfo - > ri_projectReturning ,
RelationGetRelid ( resultRelationDesc ) ,
slot , planSlot ) ;
return NULL ;
}
@ -2154,7 +2215,9 @@ ExecModifyTable(PlanState *pstate)
* ExecProcessReturning by IterateDirectModify , so no need to
* provide it here .
*/
slot = ExecProcessReturning ( resultRelInfo , NULL , planSlot ) ;
slot = ExecProcessReturning ( resultRelInfo - > ri_projectReturning ,
RelationGetRelid ( resultRelInfo - > ri_RelationDesc ) ,
NULL , planSlot ) ;
estate - > es_result_relation_info = saved_resultRelInfo ;
return slot ;
@ -2244,6 +2307,7 @@ ExecModifyTable(PlanState *pstate)
slot = ExecPrepareTupleRouting ( node , estate , proute ,
resultRelInfo , slot ) ;
slot = ExecInsert ( node , slot , planSlot ,
NULL , estate - > es_result_relation_info ,
estate , node - > canSetTag ) ;
/* Revert ExecPrepareTupleRouting's state change. */
if ( proute )