diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 0769ae3c15..72b113476e 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -3056,10 +3056,6 @@ ExecBRUpdateTriggersNew(EState *estate, EPQState *epqstate, * received in newslot. Neither we nor our callers have any further * interest in the passed-in tuple, so it's okay to overwrite newslot * with the newer data. - * - * (Typically, newslot was also generated by ExecGetUpdateNewTuple, so - * that epqslot_clean will be that same slot and the copy step below - * is not needed.) */ if (epqslot_candidate != NULL) { @@ -3068,14 +3064,36 @@ ExecBRUpdateTriggersNew(EState *estate, EPQState *epqstate, epqslot_clean = ExecGetUpdateNewTuple(relinfo, epqslot_candidate, oldslot); - if (newslot != epqslot_clean) + /* + * Typically, the caller's newslot was also generated by + * ExecGetUpdateNewTuple, so that epqslot_clean will be the same + * slot and copying is not needed. But do the right thing if it + * isn't. + */ + if (unlikely(newslot != epqslot_clean)) ExecCopySlot(newslot, epqslot_clean); + + /* + * At this point newslot contains a virtual tuple that may + * reference some fields of oldslot's tuple in some disk buffer. + * If that tuple is in a different page than the original target + * tuple, then our only pin on that buffer is oldslot's, and we're + * about to release it. Hence we'd better materialize newslot to + * ensure it doesn't contain references into an unpinned buffer. + * (We'd materialize it below anyway, but too late for safety.) + */ + ExecMaterializeSlot(newslot); } + /* + * Here we convert oldslot to a materialized slot holding trigtuple. + * Neither slot passed to the triggers will hold any buffer pin. + */ trigtuple = ExecFetchSlotHeapTuple(oldslot, true, &should_free_trig); } else { + /* Put the FDW-supplied tuple into oldslot to unify the cases */ ExecForceStoreHeapTuple(fdw_trigtuple, oldslot, false); trigtuple = fdw_trigtuple; }