*** a/src/backend/commands/trigger.c --- b/src/backend/commands/trigger.c *************** *** 2295,2307 **** ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, } } ! bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ! HeapTuple trigtuple) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; TriggerData LocTriggerData; ! HeapTuple rettuple; int i; LocTriggerData.type = T_TriggerData; --- 2295,2307 ---- } } ! TupleTableSlot * ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ! HeapTuple trigtuple, TupleTableSlot *slot) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; TriggerData LocTriggerData; ! HeapTuple rettuple = trigtuple; int i; LocTriggerData.type = T_TriggerData; *************** *** 2333,2343 **** ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (rettuple == NULL) ! return false; /* Delete was suppressed */ ! if (rettuple != trigtuple) ! heap_freetuple(rettuple); } ! return true; } void --- 2333,2359 ---- relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (rettuple == NULL) ! return NULL; /* Delete was suppressed */ } ! ! if (rettuple != trigtuple) ! { ! /* ! * Return the modified tuple using the es_trig_tuple_slot. We assume ! * the tuple was allocated in per-tuple memory context, and therefore ! * will go away by itself. The tuple table slot should not try to ! * clear it. ! */ ! TupleTableSlot *newslot = estate->es_trig_tuple_slot; ! TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); ! ! if (newslot->tts_tupleDescriptor != tupdesc) ! ExecSetSlotDescriptor(newslot, tupdesc); ! ExecStoreTuple(rettuple, newslot, InvalidBuffer, false); ! slot = newslot; ! } ! ! return slot; } void *** a/src/backend/executor/nodeModifyTable.c --- b/src/backend/executor/nodeModifyTable.c *************** *** 573,585 **** ExecDelete(ItemPointer tupleid, if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_delete_instead_row) { ! bool dodelete; ! Assert(oldtuple != NULL); ! dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple); ! if (!dodelete) /* "do nothing" */ return NULL; } else if (resultRelInfo->ri_FdwRoutine) { --- 573,595 ---- if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_delete_instead_row) { ! /* ! * Store the heap tuple into the tuple table slot, making sure we have a ! * writable copy. We can use the trigger tuple slot. ! */ ! slot = estate->es_trig_tuple_slot; ! if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc)) ! ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc)); ! ExecStoreTuple(oldtuple, slot, InvalidBuffer, false); ! oldtuple = ExecMaterializeSlot(slot); ! slot = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple, slot); ! if (slot == NULL) /* "do nothing" */ return NULL; + + /* trigger might have changed tuple */ + oldtuple = ExecMaterializeSlot(slot); } else if (resultRelInfo->ri_FdwRoutine) { *************** *** 719,732 **** ldelete:; /* Process RETURNING if present */ if (resultRelInfo->ri_projectReturning) { - /* - * We have to put the target tuple into a slot, which means first we - * gotta fetch it. We can use the trigger tuple slot. - */ TupleTableSlot *rslot; HeapTupleData deltuple; Buffer delbuffer; if (resultRelInfo->ri_FdwRoutine) { /* FDW must have provided a slot containing the deleted row */ --- 729,750 ---- /* Process RETURNING if present */ if (resultRelInfo->ri_projectReturning) { TupleTableSlot *rslot; HeapTupleData deltuple; Buffer delbuffer; + /* + * If we fired an INSTEAD OF trigger, we should use the tuple returned + * from said trigger for the RETURNING projections. + */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->trig_delete_instead_row) + return ExecProcessReturning(resultRelInfo, slot, planSlot); + + /* + * Otherwise we have to to fetch the target tuple into a slot. We can + * use the trigger tuple slot here as well. + */ if (resultRelInfo->ri_FdwRoutine) { /* FDW must have provided a slot containing the deleted row */ *** a/src/include/commands/trigger.h --- b/src/include/commands/trigger.h *************** *** 154,162 **** extern void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple); ! extern bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ! HeapTuple trigtuple); extern void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo); extern void ExecASUpdateTriggers(EState *estate, --- 154,163 ---- ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple); ! extern TupleTableSlot *ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ! HeapTuple trigtuple, ! TupleTableSlot *slot); extern void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo); extern void ExecASUpdateTriggers(EState *estate,