diff options
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 151 |
1 files changed, 104 insertions, 47 deletions
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; } |