diff options
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 101 |
1 files changed, 86 insertions, 15 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 18b8589dea..439e36ee3a 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -4,7 +4,7 @@ * routines to handle ModifyTable nodes. * * Portions Copyright (c) 2012-2014, TransLattice, Inc. - * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -139,13 +139,17 @@ ExecCheckPlanOutput(Relation resultRel, List *targetList) * tupleSlot: slot holding tuple actually inserted/updated/deleted * planSlot: slot holding tuple returned by top subplan node * + * Note: If tupleSlot is NULL, the FDW should have already provided econtext's + * scan tuple. + * * Returns a slot holding the result tuple */ static TupleTableSlot * -ExecProcessReturning(ProjectionInfo *projectReturning, +ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot) { + ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning; ExprContext *econtext = projectReturning->pi_exprContext; /* @@ -155,7 +159,20 @@ ExecProcessReturning(ProjectionInfo *projectReturning, ResetExprContext(econtext); /* Make tuple and any needed join variables available to ExecProject */ - econtext->ecxt_scantuple = tupleSlot; + if (tupleSlot) + econtext->ecxt_scantuple = tupleSlot; + else + { + HeapTuple tuple; + + /* + * RETURNING expressions might reference the tableoid column, so + * initialize t_tableOid before evaluating them. + */ + Assert(!TupIsNull(econtext->ecxt_scantuple)); + tuple = ExecMaterializeSlot(econtext->ecxt_scantuple); + tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc); + } econtext->ecxt_outertuple = planSlot; /* Compute the RETURNING expressions */ @@ -308,6 +325,12 @@ ExecInsert(ModifyTableState *mtstate, /* FDW might have changed tuple */ tuple = ExecMaterializeSlot(slot); + /* + * AFTER ROW Triggers or RETURNING expressions might reference the + * tableoid column, so initialize t_tableOid before evaluating them. + */ + tuple->t_tableOid = RelationGetRelid(resultRelationDesc); + newId = InvalidOid; } else @@ -351,8 +374,7 @@ ExecInsert(ModifyTableState *mtstate, * * We loop back here if we find a conflict below, either during * the pre-check, or when we re-check after inserting the tuple - * speculatively. See the executor README for a full discussion - * of speculative insertion. + * speculatively. */ vlock: specConflict = false; @@ -491,8 +513,7 @@ ExecInsert(ModifyTableState *mtstate, /* Process RETURNING if present */ if (resultRelInfo->ri_projectReturning) - return ExecProcessReturning(resultRelInfo->ri_projectReturning, - slot, planSlot); + return ExecProcessReturning(resultRelInfo, slot, planSlot); return NULL; } @@ -562,6 +583,8 @@ ExecDelete(ItemPointer tupleid, } else if (resultRelInfo->ri_FdwRoutine) { + HeapTuple tuple; + /* * delete from foreign table: let the FDW do it * @@ -580,6 +603,15 @@ ExecDelete(ItemPointer tupleid, if (slot == NULL) /* "do nothing" */ return NULL; + + /* + * RETURNING expressions might reference the tableoid column, so + * initialize t_tableOid before evaluating them. + */ + if (slot->tts_isempty) + ExecStoreAllNullTuple(slot); + tuple = ExecMaterializeSlot(slot); + tuple->t_tableOid = RelationGetRelid(resultRelationDesc); } else { @@ -723,8 +755,7 @@ ldelete:; ExecStoreTuple(&deltuple, slot, InvalidBuffer, false); } - rslot = ExecProcessReturning(resultRelInfo->ri_projectReturning, - slot, planSlot); + rslot = ExecProcessReturning(resultRelInfo, slot, planSlot); /* * Before releasing the target tuple again, make sure rslot has a @@ -840,6 +871,12 @@ ExecUpdate(ItemPointer tupleid, /* FDW might have changed tuple */ tuple = ExecMaterializeSlot(slot); + + /* + * AFTER ROW Triggers or RETURNING expressions might reference the + * tableoid column, so initialize t_tableOid before evaluating them. + */ + tuple->t_tableOid = RelationGetRelid(resultRelationDesc); } else { @@ -1003,8 +1040,7 @@ lreplace:; /* Process RETURNING if present */ if (resultRelInfo->ri_projectReturning) - return ExecProcessReturning(resultRelInfo->ri_projectReturning, - slot, planSlot); + return ExecProcessReturning(resultRelInfo, slot, planSlot); return NULL; } @@ -1183,8 +1219,17 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, /* Project the new tuple version */ ExecProject(resultRelInfo->ri_onConflictSetProj, NULL); + /* + * Note that it is possible that the target tuple has been modified in + * this session, after the above heap_lock_tuple. We choose to not error + * out in that case, in line with ExecUpdate's treatment of similar cases. + * This can happen if an UPDATE is triggered from within ExecQual(), + * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a + * wCTE in the ON CONFLICT's SET. + */ + /* Execute UPDATE with projection */ - *returning = ExecUpdate(&tuple.t_data->t_ctid, NULL, + *returning = ExecUpdate(&tuple.t_self, NULL, mtstate->mt_conflproj, planSlot, &mtstate->mt_epqstate, mtstate->ps.state, canSetTag); @@ -1350,6 +1395,26 @@ ExecModifyTable(ModifyTableState *node) break; } + /* + * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do + * here is compute the RETURNING expressions. + */ + if (resultRelInfo->ri_usesFdwDirectModify) + { + Assert(resultRelInfo->ri_projectReturning); + + /* + * A scan slot containing the data that was actually inserted, + * updated or deleted has already been made available to + * ExecProcessReturning by IterateDirectModify, so no need to + * provide it here. + */ + slot = ExecProcessReturning(resultRelInfo, NULL, planSlot); + + estate->es_result_relation_info = saved_resultRelInfo; + return slot; + } + EvalPlanQualSetSlot(&node->mt_epqstate, planSlot); slot = planSlot; @@ -1530,6 +1595,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) subplan = (Plan *) lfirst(l); + /* Initialize the usesFdwDirectModify flag */ + resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i, + node->fdwDirectModifyPlans); + /* * Verify result relation is a valid target for the current operation */ @@ -1554,7 +1623,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags); /* Also let FDWs init themselves for foreign-table result rels */ - if (resultRelInfo->ri_FdwRoutine != NULL && + if (!resultRelInfo->ri_usesFdwDirectModify && + resultRelInfo->ri_FdwRoutine != NULL && resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL) { List *fdw_private = (List *) list_nth(node->fdwPrivLists, i); @@ -1681,7 +1751,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) /* create target slot for UPDATE SET projection */ tupDesc = ExecTypeFromTL((List *) node->onConflictSet, - false); + resultRelInfo->ri_RelationDesc->rd_rel->relhasoids); mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state); ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc); @@ -1881,7 +1951,8 @@ ExecEndModifyTable(ModifyTableState *node) { ResultRelInfo *resultRelInfo = node->resultRelInfo + i; - if (resultRelInfo->ri_FdwRoutine != NULL && + if (!resultRelInfo->ri_usesFdwDirectModify && + resultRelInfo->ri_FdwRoutine != NULL && resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL) resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state, resultRelInfo); |