aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execMain.c155
-rw-r--r--src/backend/executor/execUtils.c2
-rw-r--r--src/backend/executor/nodeModifyTable.c151
3 files changed, 217 insertions, 91 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 68509fbfc6e..845fac1ae1c 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -68,6 +68,7 @@ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL;
/* decls for local routines only used within this module */
static void InitPlan(QueryDesc *queryDesc, int eflags);
+static void ExecPostprocessPlan(EState *estate);
static void ExecEndPlan(PlanState *planstate, EState *estate);
static void ExecutePlan(EState *estate, PlanState *planstate,
CmdType operation,
@@ -161,9 +162,13 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
switch (queryDesc->operation)
{
case CMD_SELECT:
- /* SELECT INTO and SELECT FOR UPDATE/SHARE need to mark tuples */
+ /*
+ * SELECT INTO, SELECT FOR UPDATE/SHARE and modifying CTEs need to
+ * mark tuples
+ */
if (queryDesc->plannedstmt->intoClause != NULL ||
- queryDesc->plannedstmt->rowMarks != NIL)
+ queryDesc->plannedstmt->rowMarks != NIL ||
+ queryDesc->plannedstmt->hasModifyingCTE)
estate->es_output_cid = GetCurrentCommandId(true);
break;
@@ -307,13 +312,19 @@ standard_ExecutorRun(QueryDesc *queryDesc,
*
* We provide a function hook variable that lets loadable plugins
* get control when ExecutorEnd is called. Such a plugin would
- * normally call standard_ExecutorEnd().
+ * normally call standard_ExecutorEnd(). Because such hooks expect
+ * to execute after all plan execution is done, we run
+ * ExecPostprocessPlan before invoking the hook.
*
* ----------------------------------------------------------------
*/
void
ExecutorEnd(QueryDesc *queryDesc)
{
+ /* Let plan nodes do any final processing required */
+ ExecPostprocessPlan(queryDesc->estate);
+
+ /* Now close down */
if (ExecutorEnd_hook)
(*ExecutorEnd_hook) (queryDesc);
else
@@ -681,7 +692,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
InitResultRelInfo(resultRelInfo,
resultRelation,
resultRelationIndex,
- operation,
estate->es_instrument);
resultRelInfo++;
}
@@ -873,24 +883,18 @@ InitPlan(QueryDesc *queryDesc, int eflags)
}
/*
- * Initialize ResultRelInfo data for one result relation
+ * Check that a proposed result relation is a legal target for the operation
+ *
+ * In most cases parser and/or planner should have noticed this already, but
+ * let's make sure. In the view case we do need a test here, because if the
+ * view wasn't rewritten by a rule, it had better have an INSTEAD trigger.
*/
void
-InitResultRelInfo(ResultRelInfo *resultRelInfo,
- Relation resultRelationDesc,
- Index resultRelationIndex,
- CmdType operation,
- int instrument_options)
+CheckValidResultRel(Relation resultRel, CmdType operation)
{
- TriggerDesc *trigDesc = resultRelationDesc->trigdesc;
+ TriggerDesc *trigDesc = resultRel->trigdesc;
- /*
- * Check valid relkind ... in most cases parser and/or planner should have
- * noticed this already, but let's make sure. In the view case we do need
- * a test here, because if the view wasn't rewritten by a rule, it had
- * better have an INSTEAD trigger.
- */
- switch (resultRelationDesc->rd_rel->relkind)
+ switch (resultRel->rd_rel->relkind)
{
case RELKIND_RELATION:
/* OK */
@@ -899,13 +903,13 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot change sequence \"%s\"",
- RelationGetRelationName(resultRelationDesc))));
+ RelationGetRelationName(resultRel))));
break;
case RELKIND_TOASTVALUE:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot change TOAST relation \"%s\"",
- RelationGetRelationName(resultRelationDesc))));
+ RelationGetRelationName(resultRel))));
break;
case RELKIND_VIEW:
switch (operation)
@@ -915,7 +919,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot insert into view \"%s\"",
- RelationGetRelationName(resultRelationDesc)),
+ RelationGetRelationName(resultRel)),
errhint("You need an unconditional ON INSERT DO INSTEAD rule or an INSTEAD OF INSERT trigger.")));
break;
case CMD_UPDATE:
@@ -923,7 +927,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot update view \"%s\"",
- RelationGetRelationName(resultRelationDesc)),
+ RelationGetRelationName(resultRel)),
errhint("You need an unconditional ON UPDATE DO INSTEAD rule or an INSTEAD OF UPDATE trigger.")));
break;
case CMD_DELETE:
@@ -931,7 +935,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot delete from view \"%s\"",
- RelationGetRelationName(resultRelationDesc)),
+ RelationGetRelationName(resultRel)),
errhint("You need an unconditional ON DELETE DO INSTEAD rule or an INSTEAD OF DELETE trigger.")));
break;
default:
@@ -943,17 +947,30 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot change foreign table \"%s\"",
- RelationGetRelationName(resultRelationDesc))));
+ RelationGetRelationName(resultRel))));
break;
default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot change relation \"%s\"",
- RelationGetRelationName(resultRelationDesc))));
+ RelationGetRelationName(resultRel))));
break;
}
+}
- /* OK, fill in the node */
+/*
+ * Initialize ResultRelInfo data for one result relation
+ *
+ * Caution: before Postgres 9.1, this function included the relkind checking
+ * that's now in CheckValidResultRel, and it also did ExecOpenIndices if
+ * appropriate. Be sure callers cover those needs.
+ */
+void
+InitResultRelInfo(ResultRelInfo *resultRelInfo,
+ Relation resultRelationDesc,
+ Index resultRelationIndex,
+ int instrument_options)
+{
MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
resultRelInfo->type = T_ResultRelInfo;
resultRelInfo->ri_RangeTableIndex = resultRelationIndex;
@@ -962,7 +979,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
resultRelInfo->ri_IndexRelationDescs = NULL;
resultRelInfo->ri_IndexRelationInfo = NULL;
/* make a copy so as not to depend on relcache info not changing... */
- resultRelInfo->ri_TrigDesc = CopyTriggerDesc(trigDesc);
+ resultRelInfo->ri_TrigDesc = CopyTriggerDesc(resultRelationDesc->trigdesc);
if (resultRelInfo->ri_TrigDesc)
{
int n = resultRelInfo->ri_TrigDesc->numtriggers;
@@ -983,16 +1000,6 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
resultRelInfo->ri_ConstraintExprs = NULL;
resultRelInfo->ri_junkFilter = NULL;
resultRelInfo->ri_projectReturning = NULL;
-
- /*
- * If there are indices on the result relation, open them and save
- * descriptors in the result relation info, so that we can add new index
- * entries for the tuples we add/update. We need not do this for a
- * DELETE, however, since deletion doesn't affect indexes.
- */
- if (resultRelationDesc->rd_rel->relhasindex &&
- operation != CMD_DELETE)
- ExecOpenIndices(resultRelInfo);
}
/*
@@ -1042,26 +1049,29 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
/*
* Open the target relation's relcache entry. We assume that an
* appropriate lock is still held by the backend from whenever the trigger
- * event got queued, so we need take no new lock here.
+ * event got queued, so we need take no new lock here. Also, we need
+ * not recheck the relkind, so no need for CheckValidResultRel.
*/
rel = heap_open(relid, NoLock);
/*
- * Make the new entry in the right context. Currently, we don't need any
- * index information in ResultRelInfos used only for triggers, so tell
- * InitResultRelInfo it's a DELETE.
+ * Make the new entry in the right context.
*/
oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
rInfo = makeNode(ResultRelInfo);
InitResultRelInfo(rInfo,
rel,
0, /* dummy rangetable index */
- CMD_DELETE,
estate->es_instrument);
estate->es_trig_target_relations =
lappend(estate->es_trig_target_relations, rInfo);
MemoryContextSwitchTo(oldcontext);
+ /*
+ * Currently, we don't need any index information in ResultRelInfos used
+ * only for triggers, so no need to call ExecOpenIndices.
+ */
+
return rInfo;
}
@@ -1123,6 +1133,54 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids)
}
/* ----------------------------------------------------------------
+ * ExecPostprocessPlan
+ *
+ * Give plan nodes a final chance to execute before shutdown
+ * ----------------------------------------------------------------
+ */
+static void
+ExecPostprocessPlan(EState *estate)
+{
+ MemoryContext oldcontext;
+ ListCell *lc;
+
+ /*
+ * Switch into per-query memory context
+ */
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ /*
+ * Make sure nodes run forward.
+ */
+ estate->es_direction = ForwardScanDirection;
+
+ /*
+ * Run any secondary ModifyTable nodes to completion, in case the main
+ * query did not fetch all rows from them. (We do this to ensure that
+ * such nodes have predictable results.)
+ */
+ foreach(lc, estate->es_auxmodifytables)
+ {
+ PlanState *ps = (PlanState *) lfirst(lc);
+
+ for (;;)
+ {
+ TupleTableSlot *slot;
+
+ /* Reset the per-output-tuple exprcontext each time */
+ ResetPerTupleExprContext(estate);
+
+ slot = ExecProcNode(ps);
+
+ if (TupIsNull(slot))
+ break;
+ }
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+}
+
+/* ----------------------------------------------------------------
* ExecEndPlan
*
* Cleans up the query plan -- closes files and frees up storage
@@ -2026,6 +2084,7 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
estate->es_instrument = parentestate->es_instrument;
estate->es_select_into = parentestate->es_select_into;
estate->es_into_oids = parentestate->es_into_oids;
+ estate->es_auxmodifytables = NIL;
/*
* The external param list is simply shared from parent. The internal
@@ -2080,7 +2139,11 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
* ExecInitSubPlan expects to be able to find these entries. Some of the
* SubPlans might not be used in the part of the plan tree we intend to
* run, but since it's not easy to tell which, we just initialize them
- * all.
+ * all. (However, if the subplan is headed by a ModifyTable node, then
+ * it must be a data-modifying CTE, which we will certainly not need to
+ * re-run, so we can skip initializing it. This is just an efficiency
+ * hack; it won't skip data-modifying CTEs for which the ModifyTable node
+ * is not at the top.)
*/
Assert(estate->es_subplanstates == NIL);
foreach(l, parentestate->es_plannedstmt->subplans)
@@ -2088,7 +2151,11 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
Plan *subplan = (Plan *) lfirst(l);
PlanState *subplanstate;
- subplanstate = ExecInitNode(subplan, estate, 0);
+ /* Don't initialize ModifyTable subplans, per comment above */
+ if (IsA(subplan, ModifyTable))
+ subplanstate = NULL;
+ else
+ subplanstate = ExecInitNode(subplan, estate, 0);
estate->es_subplanstates = lappend(estate->es_subplanstates,
subplanstate);
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 582c7ca667d..511c74eeafd 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -145,6 +145,8 @@ CreateExecutorState(void)
estate->es_subplanstates = NIL;
+ estate->es_auxmodifytables = NIL;
+
estate->es_per_tuple_exprcontext = NULL;
estate->es_epqTuple = NULL;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 12a5b2a8953..cf32dc56903 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -160,7 +160,8 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
static TupleTableSlot *
ExecInsert(TupleTableSlot *slot,
TupleTableSlot *planSlot,
- EState *estate)
+ EState *estate,
+ bool canSetTag)
{
HeapTuple tuple;
ResultRelInfo *resultRelInfo;
@@ -247,9 +248,12 @@ ExecInsert(TupleTableSlot *slot,
estate);
}
- (estate->es_processed)++;
- estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
+ if (canSetTag)
+ {
+ (estate->es_processed)++;
+ estate->es_lastoid = newId;
+ setLastTid(&(tuple->t_self));
+ }
/* AFTER ROW INSERT Triggers */
ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes);
@@ -283,7 +287,8 @@ ExecDelete(ItemPointer tupleid,
HeapTupleHeader oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
- EState *estate)
+ EState *estate,
+ bool canSetTag)
{
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
@@ -393,7 +398,8 @@ ldelete:;
*/
}
- (estate->es_processed)++;
+ if (canSetTag)
+ (estate->es_processed)++;
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo, tupleid);
@@ -467,7 +473,8 @@ ExecUpdate(ItemPointer tupleid,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
- EState *estate)
+ EState *estate,
+ bool canSetTag)
{
HeapTuple tuple;
ResultRelInfo *resultRelInfo;
@@ -621,7 +628,8 @@ lreplace:;
estate);
}
- (estate->es_processed)++;
+ if (canSetTag)
+ (estate->es_processed)++;
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple,
@@ -647,16 +655,13 @@ fireBSTriggers(ModifyTableState *node)
switch (node->operation)
{
case CMD_INSERT:
- ExecBSInsertTriggers(node->ps.state,
- node->ps.state->es_result_relations);
+ ExecBSInsertTriggers(node->ps.state, node->resultRelInfo);
break;
case CMD_UPDATE:
- ExecBSUpdateTriggers(node->ps.state,
- node->ps.state->es_result_relations);
+ ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo);
break;
case CMD_DELETE:
- ExecBSDeleteTriggers(node->ps.state,
- node->ps.state->es_result_relations);
+ ExecBSDeleteTriggers(node->ps.state, node->resultRelInfo);
break;
default:
elog(ERROR, "unknown operation");
@@ -673,16 +678,13 @@ fireASTriggers(ModifyTableState *node)
switch (node->operation)
{
case CMD_INSERT:
- ExecASInsertTriggers(node->ps.state,
- node->ps.state->es_result_relations);
+ ExecASInsertTriggers(node->ps.state, node->resultRelInfo);
break;
case CMD_UPDATE:
- ExecASUpdateTriggers(node->ps.state,
- node->ps.state->es_result_relations);
+ ExecASUpdateTriggers(node->ps.state, node->resultRelInfo);
break;
case CMD_DELETE:
- ExecASDeleteTriggers(node->ps.state,
- node->ps.state->es_result_relations);
+ ExecASDeleteTriggers(node->ps.state, node->resultRelInfo);
break;
default:
elog(ERROR, "unknown operation");
@@ -703,6 +705,8 @@ ExecModifyTable(ModifyTableState *node)
{
EState *estate = node->ps.state;
CmdType operation = node->operation;
+ ResultRelInfo *saved_resultRelInfo;
+ ResultRelInfo *resultRelInfo;
PlanState *subplanstate;
JunkFilter *junkfilter;
TupleTableSlot *slot;
@@ -712,6 +716,15 @@ ExecModifyTable(ModifyTableState *node)
HeapTupleHeader oldtuple = NULL;
/*
+ * If we've already completed processing, don't try to do more. We need
+ * this test because ExecPostprocessPlan might call us an extra time, and
+ * our subplan's nodes aren't necessarily robust against being called
+ * extra times.
+ */
+ if (node->mt_done)
+ return NULL;
+
+ /*
* On first call, fire BEFORE STATEMENT triggers before proceeding.
*/
if (node->fireBSTriggers)
@@ -720,17 +733,21 @@ ExecModifyTable(ModifyTableState *node)
node->fireBSTriggers = false;
}
+ /* Preload local variables */
+ resultRelInfo = node->resultRelInfo + node->mt_whichplan;
+ subplanstate = node->mt_plans[node->mt_whichplan];
+ junkfilter = resultRelInfo->ri_junkFilter;
+
/*
* es_result_relation_info must point to the currently active result
- * relation. (Note we assume that ModifyTable nodes can't be nested.) We
- * want it to be NULL whenever we're not within ModifyTable, though.
+ * 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.
*/
- estate->es_result_relation_info =
- estate->es_result_relations + node->mt_whichplan;
+ saved_resultRelInfo = estate->es_result_relation_info;
- /* Preload local variables */
- subplanstate = node->mt_plans[node->mt_whichplan];
- junkfilter = estate->es_result_relation_info->ri_junkFilter;
+ estate->es_result_relation_info = resultRelInfo;
/*
* Fetch rows from subplan(s), and execute the required table modification
@@ -754,9 +771,10 @@ ExecModifyTable(ModifyTableState *node)
node->mt_whichplan++;
if (node->mt_whichplan < node->mt_nplans)
{
- estate->es_result_relation_info++;
+ resultRelInfo++;
subplanstate = node->mt_plans[node->mt_whichplan];
- junkfilter = estate->es_result_relation_info->ri_junkFilter;
+ junkfilter = resultRelInfo->ri_junkFilter;
+ estate->es_result_relation_info = resultRelInfo;
EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan,
node->mt_arowmarks[node->mt_whichplan]);
continue;
@@ -778,7 +796,7 @@ ExecModifyTable(ModifyTableState *node)
Datum datum;
bool isNull;
- if (estate->es_result_relation_info->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION)
+ if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION)
{
datum = ExecGetJunkAttribute(slot,
junkfilter->jf_junkAttNo,
@@ -814,15 +832,15 @@ ExecModifyTable(ModifyTableState *node)
switch (operation)
{
case CMD_INSERT:
- slot = ExecInsert(slot, planSlot, estate);
+ slot = ExecInsert(slot, planSlot, estate, node->canSetTag);
break;
case CMD_UPDATE:
slot = ExecUpdate(tupleid, oldtuple, slot, planSlot,
- &node->mt_epqstate, estate);
+ &node->mt_epqstate, estate, node->canSetTag);
break;
case CMD_DELETE:
slot = ExecDelete(tupleid, oldtuple, planSlot,
- &node->mt_epqstate, estate);
+ &node->mt_epqstate, estate, node->canSetTag);
break;
default:
elog(ERROR, "unknown operation");
@@ -835,19 +853,21 @@ ExecModifyTable(ModifyTableState *node)
*/
if (slot)
{
- estate->es_result_relation_info = NULL;
+ estate->es_result_relation_info = saved_resultRelInfo;
return slot;
}
}
- /* Reset es_result_relation_info before exiting */
- estate->es_result_relation_info = NULL;
+ /* Restore es_result_relation_info before exiting */
+ estate->es_result_relation_info = saved_resultRelInfo;
/*
* We're done, but fire AFTER STATEMENT triggers before exiting.
*/
fireASTriggers(node);
+ node->mt_done = true;
+
return NULL;
}
@@ -861,6 +881,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
ModifyTableState *mtstate;
CmdType operation = node->operation;
int nplans = list_length(node->plans);
+ ResultRelInfo *saved_resultRelInfo;
ResultRelInfo *resultRelInfo;
TupleDesc tupDesc;
Plan *subplan;
@@ -886,32 +907,59 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->ps.state = estate;
mtstate->ps.targetlist = NIL; /* not actually used */
+ mtstate->operation = operation;
+ mtstate->canSetTag = node->canSetTag;
+ mtstate->mt_done = false;
+
mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
+ mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
mtstate->mt_nplans = nplans;
- mtstate->operation = operation;
+
/* set up epqstate with dummy subplan data for the moment */
EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
mtstate->fireBSTriggers = true;
- /* For the moment, assume our targets are exactly the global result rels */
-
/*
* call ExecInitNode on each of the plans to be executed and save the
- * results into the array "mt_plans". Note we *must* set
+ * 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; ExecContextForcesOids depends on that!
*/
- estate->es_result_relation_info = estate->es_result_relations;
+ saved_resultRelInfo = estate->es_result_relation_info;
+
+ resultRelInfo = mtstate->resultRelInfo;
i = 0;
foreach(l, node->plans)
{
subplan = (Plan *) lfirst(l);
+
+ /*
+ * Verify result relation is a valid target for the current operation
+ */
+ CheckValidResultRel(resultRelInfo->ri_RelationDesc, operation);
+
+ /*
+ * If there are indices on the result relation, open them and save
+ * descriptors in the result relation info, so that we can add new
+ * index entries for the tuples we add/update. We need not do this
+ * for a DELETE, however, since deletion doesn't affect indexes.
+ */
+ if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
+ operation != CMD_DELETE)
+ ExecOpenIndices(resultRelInfo);
+
+ /* Now init the plan for this result rel */
+ estate->es_result_relation_info = resultRelInfo;
mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
- estate->es_result_relation_info++;
+
+ resultRelInfo++;
i++;
}
- estate->es_result_relation_info = NULL;
+
+ estate->es_result_relation_info = saved_resultRelInfo;
/*
* Initialize RETURNING projections if needed.
@@ -940,8 +988,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/*
* Build a projection for each result rel.
*/
- Assert(list_length(node->returningLists) == estate->es_num_result_relations);
- resultRelInfo = estate->es_result_relations;
+ resultRelInfo = mtstate->resultRelInfo;
foreach(l, node->returningLists)
{
List *rlist = (List *) lfirst(l);
@@ -1045,7 +1092,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if (junk_filter_needed)
{
- resultRelInfo = estate->es_result_relations;
+ resultRelInfo = mtstate->resultRelInfo;
for (i = 0; i < nplans; i++)
{
JunkFilter *j;
@@ -1083,7 +1130,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
else
{
if (operation == CMD_INSERT)
- ExecCheckPlanOutput(estate->es_result_relations->ri_RelationDesc,
+ ExecCheckPlanOutput(mtstate->resultRelInfo->ri_RelationDesc,
subplan->targetlist);
}
}
@@ -1096,6 +1143,16 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if (estate->es_trig_tuple_slot == NULL)
estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate);
+ /*
+ * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
+ * to estate->es_auxmodifytables so that it will be run to completion by
+ * ExecPostprocessPlan. (It'd actually work fine to add the primary
+ * ModifyTable node too, but there's no need.)
+ */
+ if (!mtstate->canSetTag)
+ estate->es_auxmodifytables = lappend(estate->es_auxmodifytables,
+ mtstate);
+
return mtstate;
}