|
|
|
@ -70,7 +70,8 @@ static TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate,
|
|
|
|
|
EState *estate,
|
|
|
|
|
PartitionTupleRouting *proute,
|
|
|
|
|
ResultRelInfo *targetRelInfo,
|
|
|
|
|
TupleTableSlot *slot);
|
|
|
|
|
TupleTableSlot *slot,
|
|
|
|
|
ResultRelInfo **partRelInfo);
|
|
|
|
|
static ResultRelInfo *getTargetResultRelInfo(ModifyTableState *node);
|
|
|
|
|
static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate);
|
|
|
|
|
static TupleConversionMap *tupconv_map_for_subplan(ModifyTableState *node,
|
|
|
|
@ -246,9 +247,10 @@ ExecCheckTIDVisible(EState *estate,
|
|
|
|
|
* Compute stored generated columns for a tuple
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot, CmdType cmdtype)
|
|
|
|
|
ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
|
|
|
|
|
EState *estate, TupleTableSlot *slot,
|
|
|
|
|
CmdType cmdtype)
|
|
|
|
|
{
|
|
|
|
|
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
|
|
|
|
|
Relation rel = resultRelInfo->ri_RelationDesc;
|
|
|
|
|
TupleDesc tupdesc = RelationGetDescr(rel);
|
|
|
|
|
int natts = tupdesc->natts;
|
|
|
|
@ -366,32 +368,48 @@ ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot, CmdType cmdtype
|
|
|
|
|
* ExecInsert
|
|
|
|
|
*
|
|
|
|
|
* For INSERT, we have to insert the tuple into the target relation
|
|
|
|
|
* and insert appropriate tuples into the index relations.
|
|
|
|
|
* (or partition thereof) and insert appropriate tuples into the index
|
|
|
|
|
* relations.
|
|
|
|
|
*
|
|
|
|
|
* Returns RETURNING result if any, otherwise NULL.
|
|
|
|
|
*
|
|
|
|
|
* This may change the currently active tuple conversion map in
|
|
|
|
|
* mtstate->mt_transition_capture, so the callers must take care to
|
|
|
|
|
* save the previous value to avoid losing track of it.
|
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
static TupleTableSlot *
|
|
|
|
|
ExecInsert(ModifyTableState *mtstate,
|
|
|
|
|
ResultRelInfo *resultRelInfo,
|
|
|
|
|
TupleTableSlot *slot,
|
|
|
|
|
TupleTableSlot *planSlot,
|
|
|
|
|
EState *estate,
|
|
|
|
|
bool canSetTag)
|
|
|
|
|
{
|
|
|
|
|
ResultRelInfo *resultRelInfo;
|
|
|
|
|
Relation resultRelationDesc;
|
|
|
|
|
List *recheckIndexes = NIL;
|
|
|
|
|
TupleTableSlot *result = NULL;
|
|
|
|
|
TransitionCaptureState *ar_insert_trig_tcs;
|
|
|
|
|
ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
|
|
|
|
|
OnConflictAction onconflict = node->onConflictAction;
|
|
|
|
|
PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the input result relation is a partitioned table, find the leaf
|
|
|
|
|
* partition to insert the tuple into.
|
|
|
|
|
*/
|
|
|
|
|
if (proute)
|
|
|
|
|
{
|
|
|
|
|
ResultRelInfo *partRelInfo;
|
|
|
|
|
|
|
|
|
|
slot = ExecPrepareTupleRouting(mtstate, estate, proute,
|
|
|
|
|
resultRelInfo, slot,
|
|
|
|
|
&partRelInfo);
|
|
|
|
|
resultRelInfo = partRelInfo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ExecMaterializeSlot(slot);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get information on the (current) result relation
|
|
|
|
|
*/
|
|
|
|
|
resultRelInfo = estate->es_result_relation_info;
|
|
|
|
|
resultRelationDesc = resultRelInfo->ri_RelationDesc;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -424,7 +442,8 @@ ExecInsert(ModifyTableState *mtstate,
|
|
|
|
|
*/
|
|
|
|
|
if (resultRelationDesc->rd_att->constr &&
|
|
|
|
|
resultRelationDesc->rd_att->constr->has_generated_stored)
|
|
|
|
|
ExecComputeStoredGenerated(estate, slot, CMD_INSERT);
|
|
|
|
|
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
|
|
|
|
|
CMD_INSERT);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* insert into foreign table: let the FDW do it
|
|
|
|
@ -459,7 +478,8 @@ ExecInsert(ModifyTableState *mtstate,
|
|
|
|
|
*/
|
|
|
|
|
if (resultRelationDesc->rd_att->constr &&
|
|
|
|
|
resultRelationDesc->rd_att->constr->has_generated_stored)
|
|
|
|
|
ExecComputeStoredGenerated(estate, slot, CMD_INSERT);
|
|
|
|
|
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
|
|
|
|
|
CMD_INSERT);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check any RLS WITH CHECK policies.
|
|
|
|
@ -521,8 +541,8 @@ ExecInsert(ModifyTableState *mtstate,
|
|
|
|
|
*/
|
|
|
|
|
vlock:
|
|
|
|
|
specConflict = false;
|
|
|
|
|
if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
|
|
|
|
|
arbiterIndexes))
|
|
|
|
|
if (!ExecCheckIndexConstraints(resultRelInfo, slot, estate,
|
|
|
|
|
&conflictTid, arbiterIndexes))
|
|
|
|
|
{
|
|
|
|
|
/* committed conflict tuple found */
|
|
|
|
|
if (onconflict == ONCONFLICT_UPDATE)
|
|
|
|
@ -582,7 +602,8 @@ ExecInsert(ModifyTableState *mtstate,
|
|
|
|
|
specToken);
|
|
|
|
|
|
|
|
|
|
/* insert index entries for tuple */
|
|
|
|
|
recheckIndexes = ExecInsertIndexTuples(slot, estate, true,
|
|
|
|
|
recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
|
|
|
|
|
slot, estate, true,
|
|
|
|
|
&specConflict,
|
|
|
|
|
arbiterIndexes);
|
|
|
|
|
|
|
|
|
@ -621,8 +642,9 @@ ExecInsert(ModifyTableState *mtstate,
|
|
|
|
|
|
|
|
|
|
/* insert index entries for tuple */
|
|
|
|
|
if (resultRelInfo->ri_NumIndices > 0)
|
|
|
|
|
recheckIndexes = ExecInsertIndexTuples(slot, estate, false, NULL,
|
|
|
|
|
NIL);
|
|
|
|
|
recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
|
|
|
|
|
slot, estate, false,
|
|
|
|
|
NULL, NIL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -707,6 +729,7 @@ ExecInsert(ModifyTableState *mtstate,
|
|
|
|
|
*/
|
|
|
|
|
static TupleTableSlot *
|
|
|
|
|
ExecDelete(ModifyTableState *mtstate,
|
|
|
|
|
ResultRelInfo *resultRelInfo,
|
|
|
|
|
ItemPointer tupleid,
|
|
|
|
|
HeapTuple oldtuple,
|
|
|
|
|
TupleTableSlot *planSlot,
|
|
|
|
@ -718,8 +741,7 @@ ExecDelete(ModifyTableState *mtstate,
|
|
|
|
|
bool *tupleDeleted,
|
|
|
|
|
TupleTableSlot **epqreturnslot)
|
|
|
|
|
{
|
|
|
|
|
ResultRelInfo *resultRelInfo;
|
|
|
|
|
Relation resultRelationDesc;
|
|
|
|
|
Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
|
|
|
|
|
TM_Result result;
|
|
|
|
|
TM_FailureData tmfd;
|
|
|
|
|
TupleTableSlot *slot = NULL;
|
|
|
|
@ -728,12 +750,6 @@ ExecDelete(ModifyTableState *mtstate,
|
|
|
|
|
if (tupleDeleted)
|
|
|
|
|
*tupleDeleted = false;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get information on the (current) result relation
|
|
|
|
|
*/
|
|
|
|
|
resultRelInfo = estate->es_result_relation_info;
|
|
|
|
|
resultRelationDesc = resultRelInfo->ri_RelationDesc;
|
|
|
|
|
|
|
|
|
|
/* BEFORE ROW DELETE Triggers */
|
|
|
|
|
if (resultRelInfo->ri_TrigDesc &&
|
|
|
|
|
resultRelInfo->ri_TrigDesc->trig_delete_before_row)
|
|
|
|
@ -1067,6 +1083,7 @@ ldelete:;
|
|
|
|
|
*/
|
|
|
|
|
static TupleTableSlot *
|
|
|
|
|
ExecUpdate(ModifyTableState *mtstate,
|
|
|
|
|
ResultRelInfo *resultRelInfo,
|
|
|
|
|
ItemPointer tupleid,
|
|
|
|
|
HeapTuple oldtuple,
|
|
|
|
|
TupleTableSlot *slot,
|
|
|
|
@ -1075,12 +1092,10 @@ ExecUpdate(ModifyTableState *mtstate,
|
|
|
|
|
EState *estate,
|
|
|
|
|
bool canSetTag)
|
|
|
|
|
{
|
|
|
|
|
ResultRelInfo *resultRelInfo;
|
|
|
|
|
Relation resultRelationDesc;
|
|
|
|
|
Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
|
|
|
|
|
TM_Result result;
|
|
|
|
|
TM_FailureData tmfd;
|
|
|
|
|
List *recheckIndexes = NIL;
|
|
|
|
|
TupleConversionMap *saved_tcs_map = NULL;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* abort the operation if not running transactions
|
|
|
|
@ -1090,12 +1105,6 @@ ExecUpdate(ModifyTableState *mtstate,
|
|
|
|
|
|
|
|
|
|
ExecMaterializeSlot(slot);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get information on the (current) result relation
|
|
|
|
|
*/
|
|
|
|
|
resultRelInfo = estate->es_result_relation_info;
|
|
|
|
|
resultRelationDesc = resultRelInfo->ri_RelationDesc;
|
|
|
|
|
|
|
|
|
|
/* BEFORE ROW UPDATE Triggers */
|
|
|
|
|
if (resultRelInfo->ri_TrigDesc &&
|
|
|
|
|
resultRelInfo->ri_TrigDesc->trig_update_before_row)
|
|
|
|
@ -1120,7 +1129,8 @@ ExecUpdate(ModifyTableState *mtstate,
|
|
|
|
|
*/
|
|
|
|
|
if (resultRelationDesc->rd_att->constr &&
|
|
|
|
|
resultRelationDesc->rd_att->constr->has_generated_stored)
|
|
|
|
|
ExecComputeStoredGenerated(estate, slot, CMD_UPDATE);
|
|
|
|
|
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
|
|
|
|
|
CMD_UPDATE);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* update in foreign table: let the FDW do it
|
|
|
|
@ -1157,7 +1167,8 @@ ExecUpdate(ModifyTableState *mtstate,
|
|
|
|
|
*/
|
|
|
|
|
if (resultRelationDesc->rd_att->constr &&
|
|
|
|
|
resultRelationDesc->rd_att->constr->has_generated_stored)
|
|
|
|
|
ExecComputeStoredGenerated(estate, slot, CMD_UPDATE);
|
|
|
|
|
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
|
|
|
|
|
CMD_UPDATE);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check any RLS UPDATE WITH CHECK policies
|
|
|
|
@ -1207,6 +1218,7 @@ lreplace:;
|
|
|
|
|
PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
|
|
|
|
|
int map_index;
|
|
|
|
|
TupleConversionMap *tupconv_map;
|
|
|
|
|
TupleConversionMap *saved_tcs_map = NULL;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Disallow an INSERT ON CONFLICT DO UPDATE that causes the
|
|
|
|
@ -1232,9 +1244,12 @@ 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, false, false /* canSetTag */ ,
|
|
|
|
|
true /* changingPart */ , &tuple_deleted, &epqslot);
|
|
|
|
|
ExecDelete(mtstate, resultRelInfo, tupleid, oldtuple, planSlot,
|
|
|
|
|
epqstate, estate,
|
|
|
|
|
false, /* processReturning */
|
|
|
|
|
false, /* canSetTag */
|
|
|
|
|
true, /* changingPart */
|
|
|
|
|
&tuple_deleted, &epqslot);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* For some reason if DELETE didn't happen (e.g. trigger prevented
|
|
|
|
@ -1274,16 +1289,6 @@ lreplace:;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Updates set the transition capture map only when a new subplan
|
|
|
|
|
* is chosen. But for inserts, it is set for each row. So after
|
|
|
|
|
* INSERT, we need to revert back to the map created for UPDATE;
|
|
|
|
|
* otherwise the next UPDATE will incorrectly use the one created
|
|
|
|
|
* for INSERT. So first save the one created for UPDATE.
|
|
|
|
|
*/
|
|
|
|
|
if (mtstate->mt_transition_capture)
|
|
|
|
|
saved_tcs_map = mtstate->mt_transition_capture->tcs_map;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* resultRelInfo is one of the per-subplan resultRelInfos. So we
|
|
|
|
|
* should convert the tuple into root's tuple descriptor, since
|
|
|
|
@ -1301,18 +1306,18 @@ lreplace:;
|
|
|
|
|
mtstate->mt_root_tuple_slot);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Prepare for tuple routing, making it look like we're inserting
|
|
|
|
|
* into the root.
|
|
|
|
|
* ExecInsert() may scribble on mtstate->mt_transition_capture, so
|
|
|
|
|
* save the currently active map.
|
|
|
|
|
*/
|
|
|
|
|
if (mtstate->mt_transition_capture)
|
|
|
|
|
saved_tcs_map = mtstate->mt_transition_capture->tcs_map;
|
|
|
|
|
|
|
|
|
|
/* Tuple routing starts from the root table. */
|
|
|
|
|
Assert(mtstate->rootResultRelInfo != NULL);
|
|
|
|
|
slot = ExecPrepareTupleRouting(mtstate, estate, proute,
|
|
|
|
|
mtstate->rootResultRelInfo, slot);
|
|
|
|
|
ret_slot = ExecInsert(mtstate, mtstate->rootResultRelInfo, slot,
|
|
|
|
|
planSlot, estate, canSetTag);
|
|
|
|
|
|
|
|
|
|
ret_slot = ExecInsert(mtstate, slot, planSlot,
|
|
|
|
|
estate, canSetTag);
|
|
|
|
|
|
|
|
|
|
/* Revert ExecPrepareTupleRouting's node change. */
|
|
|
|
|
estate->es_result_relation_info = resultRelInfo;
|
|
|
|
|
/* Clear the INSERT's tuple and restore the saved map. */
|
|
|
|
|
if (mtstate->mt_transition_capture)
|
|
|
|
|
{
|
|
|
|
|
mtstate->mt_transition_capture->tcs_original_insert_tuple = NULL;
|
|
|
|
@ -1476,7 +1481,9 @@ lreplace:;
|
|
|
|
|
|
|
|
|
|
/* insert index entries for tuple if necessary */
|
|
|
|
|
if (resultRelInfo->ri_NumIndices > 0 && update_indexes)
|
|
|
|
|
recheckIndexes = ExecInsertIndexTuples(slot, estate, false, NULL, NIL);
|
|
|
|
|
recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
|
|
|
|
|
slot, estate, false,
|
|
|
|
|
NULL, NIL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (canSetTag)
|
|
|
|
@ -1715,7 +1722,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Execute UPDATE with projection */
|
|
|
|
|
*returning = ExecUpdate(mtstate, conflictTid, NULL,
|
|
|
|
|
*returning = ExecUpdate(mtstate, resultRelInfo, conflictTid, NULL,
|
|
|
|
|
resultRelInfo->ri_onConflict->oc_ProjSlot,
|
|
|
|
|
planSlot,
|
|
|
|
|
&mtstate->mt_epqstate, mtstate->ps.state,
|
|
|
|
@ -1872,19 +1879,19 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
|
|
|
|
|
* ExecPrepareTupleRouting --- prepare for routing one tuple
|
|
|
|
|
*
|
|
|
|
|
* Determine the partition in which the tuple in slot is to be inserted,
|
|
|
|
|
* and modify mtstate and estate to prepare for it.
|
|
|
|
|
* and return its ResultRelInfo in *partRelInfo. The return value is
|
|
|
|
|
* a slot holding the tuple of the partition rowtype.
|
|
|
|
|
*
|
|
|
|
|
* Caller must revert the estate changes after executing the insertion!
|
|
|
|
|
* In mtstate, transition capture changes may also need to be reverted.
|
|
|
|
|
*
|
|
|
|
|
* Returns a slot holding the tuple of the partition rowtype.
|
|
|
|
|
* This also sets the transition table information in mtstate based on the
|
|
|
|
|
* selected partition.
|
|
|
|
|
*/
|
|
|
|
|
static TupleTableSlot *
|
|
|
|
|
ExecPrepareTupleRouting(ModifyTableState *mtstate,
|
|
|
|
|
EState *estate,
|
|
|
|
|
PartitionTupleRouting *proute,
|
|
|
|
|
ResultRelInfo *targetRelInfo,
|
|
|
|
|
TupleTableSlot *slot)
|
|
|
|
|
TupleTableSlot *slot,
|
|
|
|
|
ResultRelInfo **partRelInfo)
|
|
|
|
|
{
|
|
|
|
|
ResultRelInfo *partrel;
|
|
|
|
|
PartitionRoutingInfo *partrouteinfo;
|
|
|
|
@ -1901,11 +1908,6 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
|
|
|
|
|
partrouteinfo = partrel->ri_PartitionInfo;
|
|
|
|
|
Assert(partrouteinfo != NULL);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make it look like we are inserting into the partition.
|
|
|
|
|
*/
|
|
|
|
|
estate->es_result_relation_info = partrel;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If we're capturing transition tuples, we might need to convert from the
|
|
|
|
|
* partition rowtype to root partitioned table's rowtype.
|
|
|
|
@ -1950,6 +1952,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
|
|
|
|
|
slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*partRelInfo = partrel;
|
|
|
|
|
return slot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2016,10 +2019,8 @@ static TupleTableSlot *
|
|
|
|
|
ExecModifyTable(PlanState *pstate)
|
|
|
|
|
{
|
|
|
|
|
ModifyTableState *node = castNode(ModifyTableState, pstate);
|
|
|
|
|
PartitionTupleRouting *proute = node->mt_partition_tuple_routing;
|
|
|
|
|
EState *estate = node->ps.state;
|
|
|
|
|
CmdType operation = node->operation;
|
|
|
|
|
ResultRelInfo *saved_resultRelInfo;
|
|
|
|
|
ResultRelInfo *resultRelInfo;
|
|
|
|
|
PlanState *subplanstate;
|
|
|
|
|
JunkFilter *junkfilter;
|
|
|
|
@ -2067,17 +2068,6 @@ ExecModifyTable(PlanState *pstate)
|
|
|
|
|
subplanstate = node->mt_plans[node->mt_whichplan];
|
|
|
|
|
junkfilter = resultRelInfo->ri_junkFilter;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* es_result_relation_info must point to the currently active result
|
|
|
|
|
* relation while we are within this ModifyTable node. Even though
|
|
|
|
|
* ModifyTable nodes can't be nested statically, they can be nested
|
|
|
|
|
* dynamically (since our subplan could include a reference to a modifying
|
|
|
|
|
* CTE). So we have to save and restore the caller's value.
|
|
|
|
|
*/
|
|
|
|
|
saved_resultRelInfo = estate->es_result_relation_info;
|
|
|
|
|
|
|
|
|
|
estate->es_result_relation_info = resultRelInfo;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Fetch rows from subplan(s), and execute the required table modification
|
|
|
|
|
* for each row.
|
|
|
|
@ -2111,7 +2101,6 @@ ExecModifyTable(PlanState *pstate)
|
|
|
|
|
resultRelInfo++;
|
|
|
|
|
subplanstate = node->mt_plans[node->mt_whichplan];
|
|
|
|
|
junkfilter = resultRelInfo->ri_junkFilter;
|
|
|
|
|
estate->es_result_relation_info = resultRelInfo;
|
|
|
|
|
EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan,
|
|
|
|
|
node->mt_arowmarks[node->mt_whichplan]);
|
|
|
|
|
/* Prepare to convert transition tuples from this child. */
|
|
|
|
@ -2156,7 +2145,6 @@ ExecModifyTable(PlanState *pstate)
|
|
|
|
|
*/
|
|
|
|
|
slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);
|
|
|
|
|
|
|
|
|
|
estate->es_result_relation_info = saved_resultRelInfo;
|
|
|
|
|
return slot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2239,25 +2227,21 @@ ExecModifyTable(PlanState *pstate)
|
|
|
|
|
switch (operation)
|
|
|
|
|
{
|
|
|
|
|
case CMD_INSERT:
|
|
|
|
|
/* Prepare for tuple routing if needed. */
|
|
|
|
|
if (proute)
|
|
|
|
|
slot = ExecPrepareTupleRouting(node, estate, proute,
|
|
|
|
|
resultRelInfo, slot);
|
|
|
|
|
slot = ExecInsert(node, slot, planSlot,
|
|
|
|
|
slot = ExecInsert(node, resultRelInfo, slot, planSlot,
|
|
|
|
|
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, node->canSetTag);
|
|
|
|
|
slot = ExecUpdate(node, resultRelInfo, tupleid, oldtuple, slot,
|
|
|
|
|
planSlot, &node->mt_epqstate, estate,
|
|
|
|
|
node->canSetTag);
|
|
|
|
|
break;
|
|
|
|
|
case CMD_DELETE:
|
|
|
|
|
slot = ExecDelete(node, tupleid, oldtuple, planSlot,
|
|
|
|
|
&node->mt_epqstate, estate,
|
|
|
|
|
true, node->canSetTag,
|
|
|
|
|
false /* changingPart */ , NULL, NULL);
|
|
|
|
|
slot = ExecDelete(node, resultRelInfo, tupleid, oldtuple,
|
|
|
|
|
planSlot, &node->mt_epqstate, estate,
|
|
|
|
|
true, /* processReturning */
|
|
|
|
|
node->canSetTag,
|
|
|
|
|
false, /* changingPart */
|
|
|
|
|
NULL, NULL);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
elog(ERROR, "unknown operation");
|
|
|
|
@ -2269,15 +2253,9 @@ ExecModifyTable(PlanState *pstate)
|
|
|
|
|
* the work on next call.
|
|
|
|
|
*/
|
|
|
|
|
if (slot)
|
|
|
|
|
{
|
|
|
|
|
estate->es_result_relation_info = saved_resultRelInfo;
|
|
|
|
|
return slot;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Restore es_result_relation_info before exiting */
|
|
|
|
|
estate->es_result_relation_info = saved_resultRelInfo;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We're done, but fire AFTER STATEMENT triggers before exiting.
|
|
|
|
|
*/
|
|
|
|
@ -2298,7 +2276,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
|
|
|
|
ModifyTableState *mtstate;
|
|
|
|
|
CmdType operation = node->operation;
|
|
|
|
|
int nplans = list_length(node->plans);
|
|
|
|
|
ResultRelInfo *saved_resultRelInfo;
|
|
|
|
|
ResultRelInfo *resultRelInfo;
|
|
|
|
|
Plan *subplan;
|
|
|
|
|
ListCell *l,
|
|
|
|
@ -2346,14 +2323,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
|
|
|
|
* call ExecInitNode on each of the plans to be executed and save the
|
|
|
|
|
* results into the array "mt_plans". This is also a convenient place to
|
|
|
|
|
* verify that the proposed target relations are valid and open their
|
|
|
|
|
* indexes for insertion of new index entries. Note we *must* set
|
|
|
|
|
* estate->es_result_relation_info correctly while we initialize each
|
|
|
|
|
* sub-plan; external modules such as FDWs may depend on that (see
|
|
|
|
|
* contrib/postgres_fdw/postgres_fdw.c: postgresBeginDirectModify() as one
|
|
|
|
|
* example).
|
|
|
|
|
* indexes for insertion of new index entries.
|
|
|
|
|
*/
|
|
|
|
|
saved_resultRelInfo = estate->es_result_relation_info;
|
|
|
|
|
|
|
|
|
|
resultRelInfo = mtstate->resultRelInfo;
|
|
|
|
|
i = 0;
|
|
|
|
|
forboth(l, node->resultRelations, l1, node->plans)
|
|
|
|
@ -2400,7 +2371,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
|
|
|
|
update_tuple_routing_needed = true;
|
|
|
|
|
|
|
|
|
|
/* Now init the plan for this result rel */
|
|
|
|
|
estate->es_result_relation_info = resultRelInfo;
|
|
|
|
|
mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
|
|
|
|
|
mtstate->mt_scans[i] =
|
|
|
|
|
ExecInitExtraTupleSlot(mtstate->ps.state, ExecGetResultType(mtstate->mt_plans[i]),
|
|
|
|
@ -2424,8 +2394,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
estate->es_result_relation_info = saved_resultRelInfo;
|
|
|
|
|
|
|
|
|
|
/* Get the target relation */
|
|
|
|
|
rel = (getTargetResultRelInfo(mtstate))->ri_RelationDesc;
|
|
|
|
|
|
|
|
|
|