diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execMain.c | 155 | ||||
-rw-r--r-- | src/backend/executor/execUtils.c | 2 | ||||
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 151 |
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; } |