@ -42,7 +42,6 @@
# include "commands/trigger.h"
# include "executor/execPartition.h"
# include "executor/executor.h"
# include "executor/nodeMerge.h"
# include "executor/nodeModifyTable.h"
# include "foreign/fdwapi.h"
# include "miscadmin.h"
@ -63,17 +62,17 @@ static bool ExecOnConflictUpdate(ModifyTableState *mtstate,
EState * estate ,
bool canSetTag ,
TupleTableSlot * * returning ) ;
static TupleTableSlot * ExecPrepareTupleRouting ( ModifyTableState * mtstate ,
EState * estate ,
PartitionTupleRouting * proute ,
ResultRelInfo * targetRelInfo ,
TupleTableSlot * slot ) ;
static ResultRelInfo * getTargetResultRelInfo ( ModifyTableState * node ) ;
static void ExecSetupChildParentMapForTcs ( ModifyTableState * mtstate ) ;
static void ExecSetupChildParentMapForSubplan ( ModifyTableState * mtstate ) ;
static TupleConversionMap * tupconv_map_for_subplan ( ModifyTableState * node ,
int whichplan ) ;
/* flags for mt_merge_subcommands */
# define MERGE_INSERT 0x01
# define MERGE_UPDATE 0x02
# define MERGE_DELETE 0x04
/*
* Verify that the tuples to be produced by INSERT or UPDATE match the
* target relation ' s rowtype
@ -260,12 +259,11 @@ ExecCheckTIDVisible(EState *estate,
* Returns RETURNING result if any , otherwise NULL .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
extern TupleTableSlot *
static TupleTableSlot *
ExecInsert ( ModifyTableState * mtstate ,
TupleTableSlot * slot ,
TupleTableSlot * planSlot ,
EState * estate ,
MergeActionState * actionState ,
bool canSetTag )
{
HeapTuple tuple ;
@ -392,17 +390,9 @@ ExecInsert(ModifyTableState *mtstate,
* partition , we should instead check UPDATE policies , because we are
* executing policies defined on the target table , and not those
* defined on the child partitions .
*
* If we ' re running MERGE , we refer to the action that we ' re executing
* to know if we ' re doing an INSERT or UPDATE to a partition table .
*/
if ( mtstate - > operation = = CMD_UPDATE )
wco_kind = WCO_RLS_UPDATE_CHECK ;
else if ( mtstate - > operation = = CMD_MERGE )
wco_kind = ( actionState - > commandType = = CMD_UPDATE ) ?
WCO_RLS_UPDATE_CHECK : WCO_RLS_INSERT_CHECK ;
else
wco_kind = WCO_RLS_INSERT_CHECK ;
wco_kind = ( mtstate - > operation = = CMD_UPDATE ) ?
WCO_RLS_UPDATE_CHECK : WCO_RLS_INSERT_CHECK ;
/*
* ExecWithCheckOptions ( ) will skip any WCOs which are not of the kind
@ -627,19 +617,10 @@ ExecInsert(ModifyTableState *mtstate,
* passed to foreign table triggers ; it is NULL when the foreign
* table has no relevant triggers .
*
* MERGE passes actionState of the action it ' s currently executing ;
* regular DELETE passes NULL . This is used by ExecDelete to know if it ' s
* being called from MERGE or regular DELETE operation .
*
* If the DELETE fails because the tuple is concurrently updated / deleted
* by this or some other transaction , hufdp is filled with the reason as
* well as other important information . Currently only MERGE needs this
* information .
*
* Returns RETURNING result if any , otherwise NULL .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
TupleTableSlot *
static TupleTableSlot *
ExecDelete ( ModifyTableState * mtstate ,
ItemPointer tupleid ,
HeapTuple oldtuple ,
@ -648,8 +629,6 @@ ExecDelete(ModifyTableState *mtstate,
EState * estate ,
bool * tupleDeleted ,
bool processReturning ,
HeapUpdateFailureData * hufdp ,
MergeActionState * actionState ,
bool canSetTag )
{
ResultRelInfo * resultRelInfo ;
@ -662,14 +641,6 @@ ExecDelete(ModifyTableState *mtstate,
if ( tupleDeleted )
* tupleDeleted = false ;
/*
* Initialize hufdp . Since the caller is only interested in the failure
* status , initialize with the state that is used to indicate successful
* operation .
*/
if ( hufdp )
hufdp - > result = HeapTupleMayBeUpdated ;
/*
* get information on the ( current ) result relation
*/
@ -683,7 +654,7 @@ ExecDelete(ModifyTableState *mtstate,
bool dodelete ;
dodelete = ExecBRDeleteTriggers ( estate , epqstate , resultRelInfo ,
tupleid , oldtuple , hufdp ) ;
tupleid , oldtuple ) ;
if ( ! dodelete ) /* "do nothing" */
return NULL ;
@ -750,15 +721,6 @@ ldelete:;
estate - > es_crosscheck_snapshot ,
true /* wait for commit */ ,
& hufd ) ;
/*
* Copy the necessary information , if the caller has asked for it . We
* must do this irrespective of whether the tuple was updated or
* deleted .
*/
if ( hufdp )
* hufdp = hufd ;
switch ( result )
{
case HeapTupleSelfUpdated :
@ -793,11 +755,7 @@ ldelete:;
errmsg ( " tuple to be updated was already modified by an operation triggered by the current command " ) ,
errhint ( " Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows. " ) ) ) ;
/*
* Else , already deleted by self ; nothing to do but inform
* MERGE about it anyways so that it can take necessary
* action .
*/
/* Else, already deleted by self; nothing to do */
return NULL ;
case HeapTupleMayBeUpdated :
@ -808,24 +766,14 @@ ldelete:;
ereport ( ERROR ,
( errcode ( ERRCODE_T_R_SERIALIZATION_FAILURE ) ,
errmsg ( " could not serialize access due to concurrent update " ) ) ) ;
if ( ! ItemPointerEquals ( tupleid , & hufd . ctid ) )
{
TupleTableSlot * epqslot ;
/*
* If we ' re executing MERGE , then the onus of running
* EvalPlanQual ( ) and handling its outcome lies with the
* caller .
*/
if ( actionState ! = NULL )
return NULL ;
/* Normal DELETE path. */
epqslot = EvalPlanQual ( estate ,
epqstate ,
resultRelationDesc ,
GetEPQRangeTableIndex ( resultRelInfo ) ,
resultRelInfo - > ri_RangeTableIndex ,
LockTupleExclusive ,
& hufd . ctid ,
hufd . xmax ) ;
@ -835,12 +783,7 @@ ldelete:;
goto ldelete ;
}
}
/*
* tuple already deleted ; nothing to do . But MERGE might want
* to handle it differently . We ' ve already filled - in hufdp
* with sufficient information for MERGE to look at .
*/
/* tuple already deleted; nothing to do */
return NULL ;
default :
@ -968,21 +911,10 @@ ldelete:;
* foreign table triggers ; it is NULL when the foreign table has
* no relevant triggers .
*
* MERGE passes actionState of the action it ' s currently executing ;
* regular UPDATE passes NULL . This is used by ExecUpdate to know if it ' s
* being called from MERGE or regular UPDATE operation . ExecUpdate may
* pass this information to ExecInsert if it ends up running DELETE + INSERT
* for partition key updates .
*
* If the UPDATE fails because the tuple is concurrently updated / deleted
* by this or some other transaction , hufdp is filled with the reason as
* well as other important information . Currently only MERGE needs this
* information .
*
* Returns RETURNING result if any , otherwise NULL .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
extern TupleTableSlot *
static TupleTableSlot *
ExecUpdate ( ModifyTableState * mtstate ,
ItemPointer tupleid ,
HeapTuple oldtuple ,
@ -990,9 +922,6 @@ ExecUpdate(ModifyTableState *mtstate,
TupleTableSlot * planSlot ,
EPQState * epqstate ,
EState * estate ,
bool * tuple_updated ,
HeapUpdateFailureData * hufdp ,
MergeActionState * actionState ,
bool canSetTag )
{
HeapTuple tuple ;
@ -1009,17 +938,6 @@ ExecUpdate(ModifyTableState *mtstate,
if ( IsBootstrapProcessingMode ( ) )
elog ( ERROR , " cannot UPDATE during bootstrap " ) ;
if ( tuple_updated )
* tuple_updated = false ;
/*
* Initialize hufdp . Since the caller is only interested in the failure
* status , initialize with the state that is used to indicate successful
* operation .
*/
if ( hufdp )
hufdp - > result = HeapTupleMayBeUpdated ;
/*
* get the heap tuple out of the tuple table slot , making sure we have a
* writable copy
@ -1037,7 +955,7 @@ ExecUpdate(ModifyTableState *mtstate,
resultRelInfo - > ri_TrigDesc - > trig_update_before_row )
{
slot = ExecBRUpdateTriggers ( estate , epqstate , resultRelInfo ,
tupleid , oldtuple , slot , hufdp ) ;
tupleid , oldtuple , slot ) ;
if ( slot = = NULL ) /* "do nothing" */
return NULL ;
@ -1083,6 +1001,7 @@ ExecUpdate(ModifyTableState *mtstate,
}
else
{
LockTupleMode lockmode ;
bool partition_constraint_failed ;
/*
@ -1160,9 +1079,8 @@ lreplace:;
* Row movement , part 1. Delete the tuple , but skip RETURNING
* processing . We want to return rows from INSERT .
*/
ExecDelete ( mtstate , tupleid , oldtuple , planSlot , epqstate ,
estate , & tuple_deleted , false , hufdp , NULL ,
false ) ;
ExecDelete ( mtstate , tupleid , oldtuple , planSlot , epqstate , estate ,
& tuple_deleted , false , false ) ;
/*
* For some reason if DELETE didn ' t happen ( e . g . trigger prevented
@ -1198,36 +1116,16 @@ lreplace:;
saved_tcs_map = mtstate - > mt_transition_capture - > tcs_map ;
/*
* We should convert the tuple into root ' s tuple descriptor , since
* ExecInsert ( ) starts the search from root . To do that , we need to
* retrieve the tuple conversion map for this resultRelInfo .
*
* If we ' re running MERGE then resultRelInfo is per - partition
* resultRelInfo as initialized in ExecInitPartitionInfo ( ) . Note
* that we don ' t expand inheritance for the resultRelation in case
* of MERGE and hence there is just one subplan . Whereas for
* regular UPDATE , resultRelInfo is one of the per - subplan
* resultRelInfos . In either case the position of this partition in
* tracked in ri_PartitionLeafIndex ;
*
* Retrieve the map either by looking at the resultRelInfo ' s
* position in mtstate - > resultRelInfo [ ] ( for UPDATE ) or by simply
* using the ri_PartitionLeafIndex value ( for MERGE ) .
* resultRelInfo is one of the per - subplan resultRelInfos . So we
* should convert the tuple into root ' s tuple descriptor , since
* ExecInsert ( ) starts the search from root . The tuple conversion
* map list is in the order of mtstate - > resultRelInfo [ ] , so to
* retrieve the one for this resultRel , we need to know the
* position of the resultRel in mtstate - > resultRelInfo [ ] .
*/
if ( mtstate - > operation = = CMD_MERGE )
{
map_index = resultRelInfo - > ri_PartitionLeafIndex ;
Assert ( mtstate - > rootResultRelInfo = = NULL ) ;
tupconv_map = TupConvMapForLeaf ( proute ,
mtstate - > resultRelInfo ,
map_index ) ;
}
else
{
map_index = resultRelInfo - mtstate - > resultRelInfo ;
Assert ( map_index > = 0 & & map_index < mtstate - > mt_nplans ) ;
tupconv_map = tupconv_map_for_subplan ( mtstate , map_index ) ;
}
map_index = resultRelInfo - mtstate - > resultRelInfo ;
Assert ( map_index > = 0 & & map_index < mtstate - > mt_nplans ) ;
tupconv_map = tupconv_map_for_subplan ( mtstate , map_index ) ;
tuple = ConvertPartitionTupleSlot ( tupconv_map ,
tuple ,
proute - > root_tuple_slot ,
@ -1237,16 +1135,12 @@ lreplace:;
* Prepare for tuple routing , making it look like we ' re inserting
* into the root .
*/
Assert ( mtstate - > rootResultRelInfo ! = NULL ) ;
slot = ExecPrepareTupleRouting ( mtstate , estate , proute ,
getTargetResultRelInfo ( mtstate ) ,
slot ) ;
mtstate - > rootResultRelInfo , slot ) ;
ret_slot = ExecInsert ( mtstate , slot , planSlot ,
estate , actionState , canSetTag ) ;
/* Update is successful. */
if ( tuple_updated )
* tuple_updated = true ;
estate , canSetTag ) ;
/* Revert ExecPrepareTupleRouting's node change. */
estate - > es_result_relation_info = resultRelInfo ;
@ -1284,16 +1178,7 @@ lreplace:;
estate - > es_output_cid ,
estate - > es_crosscheck_snapshot ,
true /* wait for commit */ ,
& hufd ) ;
/*
* Copy the necessary information , if the caller has asked for it . We
* must do this irrespective of whether the tuple was updated or
* deleted .
*/
if ( hufdp )
* hufdp = hufd ;
& hufd , & lockmode ) ;
switch ( result )
{
case HeapTupleSelfUpdated :
@ -1338,42 +1223,26 @@ lreplace:;
ereport ( ERROR ,
( errcode ( ERRCODE_T_R_SERIALIZATION_FAILURE ) ,
errmsg ( " could not serialize access due to concurrent update " ) ) ) ;
if ( ! ItemPointerEquals ( tupleid , & hufd . ctid ) )
{
TupleTableSlot * epqslot ;
/*
* If we ' re executing MERGE , then the onus of running
* EvalPlanQual ( ) and handling its outcome lies with the
* caller .
*/
if ( actionState ! = NULL )
return NULL ;
/* Regular UPDATE path. */
epqslot = EvalPlanQual ( estate ,
epqstate ,
resultRelationDesc ,
GetEPQRangeTableIndex ( resultRelInfo ) ,
hufd . lockmode ,
resultRelInfo - > ri_RangeTableIndex ,
lockmode ,
& hufd . ctid ,
hufd . xmax ) ;
if ( ! TupIsNull ( epqslot ) )
{
* tupleid = hufd . ctid ;
/* Normal UPDATE path */
slot = ExecFilterJunk ( resultRelInfo - > ri_junkFilter , epqslot ) ;
tuple = ExecMaterializeSlot ( slot ) ;
goto lreplace ;
}
}
/*
* tuple already deleted ; nothing to do . But MERGE might want
* to handle it differently . We ' ve already filled - in hufdp
* with sufficient information for MERGE to look at .
*/
/* tuple already deleted; nothing to do */
return NULL ;
default :
@ -1402,9 +1271,6 @@ lreplace:;
estate , false , NULL , NIL ) ;
}
if ( tuple_updated )
* tuple_updated = true ;
if ( canSetTag )
( estate - > es_processed ) + + ;
@ -1499,9 +1365,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* there ' s no historical behavior to break .
*
* It is the user ' s responsibility to prevent this situation from
* occurring . These problems are why SQL Standard similarly
* specifies that for SQL MERGE , an exception must be raised in
* the event of an attempt to update the same row twice .
* occurring . These problems are why SQL - 2003 similarly specifies
* that for SQL MERGE , an exception must be raised in the event of
* an attempt to update the same row twice .
*/
if ( TransactionIdIsCurrentTransactionId ( HeapTupleHeaderGetXmin ( tuple . t_data ) ) )
ereport ( ERROR ,
@ -1623,7 +1489,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* returning = ExecUpdate ( mtstate , & tuple . t_self , NULL ,
mtstate - > mt_conflproj , planSlot ,
& mtstate - > mt_epqstate , mtstate - > ps . state ,
NULL , NULL , NULL , canSetTag ) ;
canSetTag ) ;
ReleaseBuffer ( buffer ) ;
return true ;
@ -1661,14 +1527,6 @@ fireBSTriggers(ModifyTableState *node)
case CMD_DELETE :
ExecBSDeleteTriggers ( node - > ps . state , resultRelInfo ) ;
break ;
case CMD_MERGE :
if ( node - > mt_merge_subcommands & MERGE_INSERT )
ExecBSInsertTriggers ( node - > ps . state , resultRelInfo ) ;
if ( node - > mt_merge_subcommands & MERGE_UPDATE )
ExecBSUpdateTriggers ( node - > ps . state , resultRelInfo ) ;
if ( node - > mt_merge_subcommands & MERGE_DELETE )
ExecBSDeleteTriggers ( node - > ps . state , resultRelInfo ) ;
break ;
default :
elog ( ERROR , " unknown operation " ) ;
break ;
@ -1724,17 +1582,6 @@ fireASTriggers(ModifyTableState *node)
ExecASDeleteTriggers ( node - > ps . state , resultRelInfo ,
node - > mt_transition_capture ) ;
break ;
case CMD_MERGE :
if ( node - > mt_merge_subcommands & MERGE_DELETE )
ExecASDeleteTriggers ( node - > ps . state , resultRelInfo ,
node - > mt_transition_capture ) ;
if ( node - > mt_merge_subcommands & MERGE_UPDATE )
ExecASUpdateTriggers ( node - > ps . state , resultRelInfo ,
node - > mt_transition_capture ) ;
if ( node - > mt_merge_subcommands & MERGE_INSERT )
ExecASInsertTriggers ( node - > ps . state , resultRelInfo ,
node - > mt_transition_capture ) ;
break ;
default :
elog ( ERROR , " unknown operation " ) ;
break ;
@ -1797,7 +1644,7 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
*
* Returns a slot holding the tuple of the partition rowtype .
*/
TupleTableSlot *
static TupleTableSlot *
ExecPrepareTupleRouting ( ModifyTableState * mtstate ,
EState * estate ,
PartitionTupleRouting * proute ,
@ -2120,7 +1967,6 @@ ExecModifyTable(PlanState *pstate)
{
/* advance to next subplan if any */
node - > mt_whichplan + + ;
if ( node - > mt_whichplan < node - > mt_nplans )
{
resultRelInfo + + ;
@ -2169,12 +2015,6 @@ ExecModifyTable(PlanState *pstate)
EvalPlanQualSetSlot ( & node - > mt_epqstate , planSlot ) ;
slot = planSlot ;
if ( operation = = CMD_MERGE )
{
ExecMerge ( node , estate , slot , junkfilter , resultRelInfo ) ;
continue ;
}
tupleid = NULL ;
oldtuple = NULL ;
if ( junkfilter ! = NULL )
@ -2256,20 +2096,19 @@ ExecModifyTable(PlanState *pstate)
slot = ExecPrepareTupleRouting ( node , estate , proute ,
resultRelInfo , slot ) ;
slot = ExecInsert ( node , slot , planSlot ,
estate , NULL , node - > canSetTag ) ;
estate , node - > canSetTag ) ;
/* Revert ExecPrepareTupleRouting's state change. */
if ( proute )
estate - > es_result_relation_info = resultRelInfo ;
break ;
case CMD_UPDATE :
slot = ExecUpdate ( node , tupleid , oldtuple , slot , planSlot ,
& node - > mt_epqstate , estate ,
NULL , NULL , NULL , node - > canSetTag ) ;
& node - > mt_epqstate , estate , node - > canSetTag ) ;
break ;
case CMD_DELETE :
slot = ExecDelete ( node , tupleid , oldtuple , planSlot ,
& node - > mt_epqstate , estate ,
NULL , true , NULL , NULL , node - > canSetTag ) ;
NULL , true , node - > canSetTag ) ;
break ;
default :
elog ( ERROR , " unknown operation " ) ;
@ -2359,16 +2198,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
saved_resultRelInfo = estate - > es_result_relation_info ;
resultRelInfo = mtstate - > resultRelInfo ;
/*
* mergeTargetRelation must be set if we ' re running MERGE and mustn ' t be
* set if we ' re not .
*/
Assert ( operation ! = CMD_MERGE | | node - > mergeTargetRelation > 0 ) ;
Assert ( operation = = CMD_MERGE | | node - > mergeTargetRelation = = 0 ) ;
resultRelInfo - > ri_mergeTargetRTI = node - > mergeTargetRelation ;
i = 0 ;
foreach ( l , node - > plans )
{
@ -2447,8 +2276,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* partition key .
*/
if ( rel - > rd_rel - > relkind = = RELKIND_PARTITIONED_TABLE & &
( operation = = CMD_INSERT | | operation = = CMD_MERGE | |
update_tuple_routing_needed ) )
( operation = = CMD_INSERT | | update_tuple_routing_needed ) )
mtstate - > mt_partition_tuple_routing =
ExecSetupPartitionTupleRouting ( mtstate , rel ) ;
@ -2459,15 +2287,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if ( ! ( eflags & EXEC_FLAG_EXPLAIN_ONLY ) )
ExecSetupTransitionCaptureState ( mtstate , estate ) ;
/*
* If we are doing MERGE then setup child - parent mapping . This will be
* required in case we end up doing a partition - key update , triggering a
* tuple routing .
*/
if ( mtstate - > operation = = CMD_MERGE & &
mtstate - > mt_partition_tuple_routing ! = NULL )
ExecSetupChildParentMapForLeaf ( mtstate - > mt_partition_tuple_routing ) ;
/*
* Construct mapping from each of the per - subplan partition attnos to the
* root attno . This is required when during update row movement the tuple
@ -2659,106 +2478,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
}
}
resultRelInfo = mtstate - > resultRelInfo ;
if ( node - > mergeActionList )
{
ListCell * l ;
ExprContext * econtext ;
List * mergeMatchedActionStates = NIL ;
List * mergeNotMatchedActionStates = NIL ;
TupleDesc relationDesc = resultRelInfo - > ri_RelationDesc - > rd_att ;
mtstate - > mt_merge_subcommands = 0 ;
if ( mtstate - > ps . ps_ExprContext = = NULL )
ExecAssignExprContext ( estate , & mtstate - > ps ) ;
econtext = mtstate - > ps . ps_ExprContext ;
/* initialize slot for the existing tuple */
Assert ( mtstate - > mt_existing = = NULL ) ;
mtstate - > mt_existing =
ExecInitExtraTupleSlot ( mtstate - > ps . state ,
mtstate - > mt_partition_tuple_routing ?
NULL : relationDesc ) ;
/* initialize slot for merge actions */
Assert ( mtstate - > mt_mergeproj = = NULL ) ;
mtstate - > mt_mergeproj =
ExecInitExtraTupleSlot ( mtstate - > ps . state ,
mtstate - > mt_partition_tuple_routing ?
NULL : relationDesc ) ;
/*
* Create a MergeActionState for each action on the mergeActionList
* and add it to either a list of matched actions or not - matched
* actions .
*/
foreach ( l , node - > mergeActionList )
{
MergeAction * action = ( MergeAction * ) lfirst ( l ) ;
MergeActionState * action_state = makeNode ( MergeActionState ) ;
TupleDesc tupDesc ;
action_state - > matched = action - > matched ;
action_state - > commandType = action - > commandType ;
action_state - > whenqual = ExecInitQual ( ( List * ) action - > qual ,
& mtstate - > ps ) ;
/* create target slot for this action's projection */
tupDesc = ExecTypeFromTL ( ( List * ) action - > targetList ,
resultRelInfo - > ri_RelationDesc - > rd_rel - > relhasoids ) ;
action_state - > tupDesc = tupDesc ;
/* build action projection state */
action_state - > proj =
ExecBuildProjectionInfo ( action - > targetList , econtext ,
mtstate - > mt_mergeproj , & mtstate - > ps ,
resultRelInfo - > ri_RelationDesc - > rd_att ) ;
/*
* We create two lists - one for WHEN MATCHED actions and one
* for WHEN NOT MATCHED actions - and stick the
* MergeActionState into the appropriate list .
*/
if ( action_state - > matched )
mergeMatchedActionStates =
lappend ( mergeMatchedActionStates , action_state ) ;
else
mergeNotMatchedActionStates =
lappend ( mergeNotMatchedActionStates , action_state ) ;
switch ( action - > commandType )
{
case CMD_INSERT :
ExecCheckPlanOutput ( resultRelInfo - > ri_RelationDesc ,
action - > targetList ) ;
mtstate - > mt_merge_subcommands | = MERGE_INSERT ;
break ;
case CMD_UPDATE :
ExecCheckPlanOutput ( resultRelInfo - > ri_RelationDesc ,
action - > targetList ) ;
mtstate - > mt_merge_subcommands | = MERGE_UPDATE ;
break ;
case CMD_DELETE :
mtstate - > mt_merge_subcommands | = MERGE_DELETE ;
break ;
case CMD_NOTHING :
break ;
default :
elog ( ERROR , " unknown operation " ) ;
break ;
}
resultRelInfo - > ri_mergeState - > matchedActionStates =
mergeMatchedActionStates ;
resultRelInfo - > ri_mergeState - > notMatchedActionStates =
mergeNotMatchedActionStates ;
}
}
/* select first subplan */
mtstate - > mt_whichplan = 0 ;
subplan = ( Plan * ) linitial ( node - > plans ) ;
@ -2772,7 +2491,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* - - - no need to look first . Typically , this will be a ' ctid ' or
* ' wholerow ' attribute , but in the case of a foreign data wrapper it
* might be a set of junk attributes sufficient to identify the remote
* row . We follow this logic for MERGE , so it always has a junk attributes .
* row .
*
* If there are multiple result relations , each one needs its own junk
* filter . Note multiple rels are only possible for UPDATE / DELETE , so we
@ -2800,7 +2519,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
break ;
case CMD_UPDATE :
case CMD_DELETE :
case CMD_MERGE :
junk_filter_needed = true ;
break ;
default :
@ -2816,7 +2534,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
JunkFilter * j ;
subplan = mtstate - > mt_plans [ i ] - > plan ;
if ( operation = = CMD_INSERT | | operation = = CMD_UPDATE )
ExecCheckPlanOutput ( resultRelInfo - > ri_RelationDesc ,
subplan - > targetlist ) ;
@ -2825,9 +2542,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
resultRelInfo - > ri_RelationDesc - > rd_att - > tdhasoid ,
ExecInitExtraTupleSlot ( estate , NULL ) ) ;
if ( operation = = CMD_UPDATE | |
operation = = CMD_DELETE | |
operation = = CMD_MERGE )
if ( operation = = CMD_UPDATE | | operation = = CMD_DELETE )
{
/* For UPDATE/DELETE, find the appropriate junk attr now */
char relkind ;
@ -2840,15 +2555,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
j - > jf_junkAttNo = ExecFindJunkAttribute ( j , " ctid " ) ;
if ( ! AttributeNumberIsValid ( j - > jf_junkAttNo ) )
elog ( ERROR , " could not find junk ctid column " ) ;
if ( operation = = CMD_MERGE & &
relkind = = RELKIND_PARTITIONED_TABLE )
{
j - > jf_otherJunkAttNo = ExecFindJunkAttribute ( j , " tableoid " ) ;
if ( ! AttributeNumberIsValid ( j - > jf_otherJunkAttNo ) )
elog ( ERROR , " could not find junk tableoid column " ) ;
}
}
else if ( relkind = = RELKIND_FOREIGN_TABLE )
{