aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeModifyTable.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r--src/backend/executor/nodeModifyTable.c151
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;
}