aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeModifyTable.c
diff options
context:
space:
mode:
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>2020-10-14 11:41:40 +0300
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>2020-10-14 11:41:40 +0300
commita04daa97a4339c38e304cd6164d37da540d665a8 (patch)
treefe77d311bc7aa4ad87db41730a3ba2cb7d1a5313 /src/backend/executor/nodeModifyTable.c
parent178f2d560dde05b356ab2f586b8bc62514f454aa (diff)
downloadpostgresql-a04daa97a4339c38e304cd6164d37da540d665a8.tar.gz
postgresql-a04daa97a4339c38e304cd6164d37da540d665a8.zip
Remove es_result_relation_info from EState.
Maintaining 'es_result_relation_info' correctly at all times has become cumbersome, especially with partitioning where each partition gets its own result relation info. Having to set and reset it across arbitrary operations has caused bugs in the past. This changes all the places that used 'es_result_relation_info', to receive the currently active ResultRelInfo via function parameters instead. Author: Amit Langote Discussion: https://www.postgresql.org/message-id/CA%2BHiwqGEmiib8FLiHMhKB%2BCH5dRgHSLc5N5wnvc4kym%2BZYpQEQ%40mail.gmail.com
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r--src/backend/executor/nodeModifyTable.c200
1 files changed, 84 insertions, 116 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index b3f7012e386..6782a2dcd28 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -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;
-
- ExecMaterializeSlot(slot);
+ PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
/*
- * get information on the (current) result relation
+ * If the input result relation is a partitioned table, find the leaf
+ * partition to insert the tuple into.
*/
- resultRelInfo = estate->es_result_relation_info;
+ if (proute)
+ {
+ ResultRelInfo *partRelInfo;
+
+ slot = ExecPrepareTupleRouting(mtstate, estate, proute,
+ resultRelInfo, slot,
+ &partRelInfo);
+ resultRelInfo = partRelInfo;
+ }
+
+ ExecMaterializeSlot(slot);
+
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
@@ -1275,16 +1290,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
* ExecInsert() starts the search from root. The tuple conversion
@@ -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.
*/
- Assert(mtstate->rootResultRelInfo != NULL);
- slot = ExecPrepareTupleRouting(mtstate, estate, proute,
- mtstate->rootResultRelInfo, slot);
+ if (mtstate->mt_transition_capture)
+ saved_tcs_map = mtstate->mt_transition_capture->tcs_map;
- ret_slot = ExecInsert(mtstate, slot, planSlot,
- estate, canSetTag);
+ /* Tuple routing starts from the root table. */
+ Assert(mtstate->rootResultRelInfo != NULL);
+ ret_slot = ExecInsert(mtstate, mtstate->rootResultRelInfo, 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.
- *
- * Caller must revert the estate changes after executing the insertion!
- * In mtstate, transition capture changes may also need to be reverted.
+ * and return its ResultRelInfo in *partRelInfo. The return value is
+ * a slot holding the tuple of the partition rowtype.
*
- * 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;
@@ -1902,11 +1909,6 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
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;
@@ -2068,17 +2069,6 @@ ExecModifyTable(PlanState *pstate)
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;