Re: BUG #5798: Some weird error with pl/pgsql procedure - Mailing list pgsql-bugs

From Tom Lane
Subject Re: BUG #5798: Some weird error with pl/pgsql procedure
Date
Msg-id 5977.1298322458@sss.pgh.pa.us
Whole thread Raw
In response to Re: BUG #5798: Some weird error with pl/pgsql procedure  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-bugs
I wrote:
> [ theory about cause of bug #5798 ]

Attached is a proposed patch against HEAD to fix this problem.  It's a
bit hard to *prove* that it fixes the problem, since there's no easy way
to force the collision of palloc addresses.  But I think it will.

The patch changes the signature of ExecBRUpdateTriggers so that it takes
and returns a TupleTableSlot instead of a HeapTuple.  This essentially
just moves the troublesome code (the "if (newtuple != tuple)" block)
from ExecUpdate to the bottom of ExecBRUpdateTriggers.  It solves the
problem because in the case where ExecBRUpdateTriggers clobbers the
contents of the junkfilter's output slot, it can immediately update the
tuple pointer that will later be compared to see if the trigger(s)
replaced the tuple.

I chose to change ExecBRInsertTriggers, ExecIRInsertTriggers, and
ExecIRUpdateTriggers to also take and return TupleTableSlots.   These
functions don't have a bug AFAICS, but it seems better that they all
retain similar APIs.

I'm not sure whether to back-patch all four changes or limit the patch
to changing ExecBRUpdateTriggers in the back branches.  Any thoughts
about that?

            regards, tom lane

diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index cac11a6c64107a2f977d2d7e5dc0d29fce5e35c7..44f568f396414f69485ab0538b0086741e5068c9 100644
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
*************** CopyFrom(CopyState cstate)
*** 1836,1842 ****
      ResultRelInfo *resultRelInfo;
      EState       *estate = CreateExecutorState(); /* for ExecConstraints() */
      ExprContext *econtext;
!     TupleTableSlot *slot;
      MemoryContext oldcontext = CurrentMemoryContext;
      ErrorContextCallback errcontext;
      CommandId    mycid = GetCurrentCommandId(true);
--- 1836,1842 ----
      ResultRelInfo *resultRelInfo;
      EState       *estate = CreateExecutorState(); /* for ExecConstraints() */
      ExprContext *econtext;
!     TupleTableSlot *myslot;
      MemoryContext oldcontext = CurrentMemoryContext;
      ErrorContextCallback errcontext;
      CommandId    mycid = GetCurrentCommandId(true);
*************** CopyFrom(CopyState cstate)
*** 1932,1939 ****
      estate->es_result_relation_info = resultRelInfo;

      /* Set up a tuple slot too */
!     slot = ExecInitExtraTupleSlot(estate);
!     ExecSetSlotDescriptor(slot, tupDesc);

      /* Prepare to catch AFTER triggers. */
      AfterTriggerBeginQuery();
--- 1932,1941 ----
      estate->es_result_relation_info = resultRelInfo;

      /* Set up a tuple slot too */
!     myslot = ExecInitExtraTupleSlot(estate);
!     ExecSetSlotDescriptor(myslot, tupDesc);
!     /* Triggers might need a slot as well */
!     estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate);

      /* Prepare to catch AFTER triggers. */
      AfterTriggerBeginQuery();
*************** CopyFrom(CopyState cstate)
*** 1960,1965 ****
--- 1962,1968 ----

      for (;;)
      {
+         TupleTableSlot *slot;
          bool        skip_tuple;
          Oid            loaded_oid = InvalidOid;

*************** CopyFrom(CopyState cstate)
*** 1983,2014 ****
          /* Triggers and stuff need to be invoked in query context. */
          MemoryContextSwitchTo(oldcontext);

          skip_tuple = false;

          /* BEFORE ROW INSERT Triggers */
          if (resultRelInfo->ri_TrigDesc &&
              resultRelInfo->ri_TrigDesc->trig_insert_before_row)
          {
!             HeapTuple    newtuple;
!
!             newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple);

!             if (newtuple == NULL)        /* "do nothing" */
                  skip_tuple = true;
!             else if (newtuple != tuple) /* modified by Trigger(s) */
!             {
!                 heap_freetuple(tuple);
!                 tuple = newtuple;
!             }
          }

          if (!skip_tuple)
          {
              List       *recheckIndexes = NIL;

-             /* Place tuple in tuple slot */
-             ExecStoreTuple(tuple, slot, InvalidBuffer, false);
-
              /* Check the constraints of the tuple */
              if (cstate->rel->rd_att->constr)
                  ExecConstraints(resultRelInfo, slot, estate);
--- 1986,2013 ----
          /* Triggers and stuff need to be invoked in query context. */
          MemoryContextSwitchTo(oldcontext);

+         /* Place tuple in tuple slot --- but slot shouldn't free it */
+         slot = myslot;
+         ExecStoreTuple(tuple, slot, InvalidBuffer, false);
+
          skip_tuple = false;

          /* BEFORE ROW INSERT Triggers */
          if (resultRelInfo->ri_TrigDesc &&
              resultRelInfo->ri_TrigDesc->trig_insert_before_row)
          {
!             slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);

!             if (slot == NULL)        /* "do nothing" */
                  skip_tuple = true;
!             else                    /* trigger might have changed tuple */
!                 tuple = ExecMaterializeSlot(slot);
          }

          if (!skip_tuple)
          {
              List       *recheckIndexes = NIL;

              /* Check the constraints of the tuple */
              if (cstate->rel->rd_att->constr)
                  ExecConstraints(resultRelInfo, slot, estate);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 8d996a87c771d4210a97e449afdbe512420afa34..dc6ee9c266f2f9fc731a35d4ec18c23f283c477c 100644
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
*************** ExecASInsertTriggers(EState *estate, Res
*** 1909,1920 ****
                                false, NULL, NULL, NIL, NULL);
  }

! HeapTuple
  ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
!                      HeapTuple trigtuple)
  {
      TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
!     HeapTuple    newtuple = trigtuple;
      HeapTuple    oldtuple;
      TriggerData LocTriggerData;
      int            i;
--- 1909,1921 ----
                                false, NULL, NULL, NIL, NULL);
  }

! TupleTableSlot *
  ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
!                      TupleTableSlot *slot)
  {
      TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
!     HeapTuple    slottuple = ExecMaterializeSlot(slot);
!     HeapTuple    newtuple = slottuple;
      HeapTuple    oldtuple;
      TriggerData LocTriggerData;
      int            i;
*************** ExecBRInsertTriggers(EState *estate, Res
*** 1947,1958 ****
                                         relinfo->ri_TrigFunctions,
                                         relinfo->ri_TrigInstrument,
                                         GetPerTupleMemoryContext(estate));
!         if (oldtuple != newtuple && oldtuple != trigtuple)
              heap_freetuple(oldtuple);
          if (newtuple == NULL)
!             break;
      }
!     return newtuple;
  }

  void
--- 1948,1976 ----
                                         relinfo->ri_TrigFunctions,
                                         relinfo->ri_TrigInstrument,
                                         GetPerTupleMemoryContext(estate));
!         if (oldtuple != newtuple && oldtuple != slottuple)
              heap_freetuple(oldtuple);
          if (newtuple == NULL)
!             return NULL;        /* "do nothing" */
      }
!
!     if (newtuple != slottuple)
!     {
!         /*
!          * 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(newtuple, newslot, InvalidBuffer, false);
!         slot = newslot;
!     }
!     return slot;
  }

  void
*************** ExecARInsertTriggers(EState *estate, Res
*** 1966,1977 ****
                                true, NULL, trigtuple, recheckIndexes, NULL);
  }

! HeapTuple
  ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
!                      HeapTuple trigtuple)
  {
      TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
!     HeapTuple    newtuple = trigtuple;
      HeapTuple    oldtuple;
      TriggerData LocTriggerData;
      int            i;
--- 1984,1996 ----
                                true, NULL, trigtuple, recheckIndexes, NULL);
  }

! TupleTableSlot *
  ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
!                      TupleTableSlot *slot)
  {
      TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
!     HeapTuple    slottuple = ExecMaterializeSlot(slot);
!     HeapTuple    newtuple = slottuple;
      HeapTuple    oldtuple;
      TriggerData LocTriggerData;
      int            i;
*************** ExecIRInsertTriggers(EState *estate, Res
*** 2004,2015 ****
                                         relinfo->ri_TrigFunctions,
                                         relinfo->ri_TrigInstrument,
                                         GetPerTupleMemoryContext(estate));
!         if (oldtuple != newtuple && oldtuple != trigtuple)
              heap_freetuple(oldtuple);
          if (newtuple == NULL)
!             break;
      }
!     return newtuple;
  }

  void
--- 2023,2051 ----
                                         relinfo->ri_TrigFunctions,
                                         relinfo->ri_TrigInstrument,
                                         GetPerTupleMemoryContext(estate));
!         if (oldtuple != newtuple && oldtuple != slottuple)
              heap_freetuple(oldtuple);
          if (newtuple == NULL)
!             return NULL;        /* "do nothing" */
      }
!
!     if (newtuple != slottuple)
!     {
!         /*
!          * 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(newtuple, newslot, InvalidBuffer, false);
!         slot = newslot;
!     }
!     return slot;
  }

  void
*************** ExecASUpdateTriggers(EState *estate, Res
*** 2257,2288 ****
                                GetModifiedColumns(relinfo, estate));
  }

! HeapTuple
  ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
                       ResultRelInfo *relinfo,
!                      ItemPointer tupleid, HeapTuple newtuple)
  {
      TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
      TriggerData LocTriggerData;
      HeapTuple    trigtuple;
      HeapTuple    oldtuple;
-     HeapTuple    intuple = newtuple;
      TupleTableSlot *newSlot;
      int            i;
      Bitmapset  *modifiedCols;

      trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
                                     &newSlot);
      if (trigtuple == NULL)
!         return NULL;

      /*
!      * In READ COMMITTED isolation level it's possible that newtuple was
       * changed due to concurrent update.  In that case we have a raw subplan
!      * output tuple and need to run it through the junk filter.
       */
      if (newSlot != NULL)
!         intuple = newtuple = ExecRemoveJunk(relinfo->ri_junkFilter, newSlot);

      modifiedCols = GetModifiedColumns(relinfo, estate);

--- 2293,2336 ----
                                GetModifiedColumns(relinfo, estate));
  }

! TupleTableSlot *
  ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
                       ResultRelInfo *relinfo,
!                      ItemPointer tupleid, TupleTableSlot *slot)
  {
      TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+     HeapTuple    slottuple = ExecMaterializeSlot(slot);
+     HeapTuple    newtuple = slottuple;
      TriggerData LocTriggerData;
      HeapTuple    trigtuple;
      HeapTuple    oldtuple;
      TupleTableSlot *newSlot;
      int            i;
      Bitmapset  *modifiedCols;

+     /* get a copy of the on-disk tuple we are planning to update */
      trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
                                     &newSlot);
      if (trigtuple == NULL)
!         return NULL;            /* cancel the update action */

      /*
!      * In READ COMMITTED isolation level it's possible that target tuple was
       * changed due to concurrent update.  In that case we have a raw subplan
!      * output tuple in newSlot, and need to run it through the junk filter to
!      * produce an insertable tuple.
!      *
!      * Caution: more than likely, the passed-in slot is the same as the
!      * junkfilter's output slot, so we are clobbering the original value of
!      * slottuple by doing the filtering.  This is OK since neither we nor our
!      * caller have any more interest in the prior contents of that slot.
       */
      if (newSlot != NULL)
!     {
!         slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
!         slottuple = ExecMaterializeSlot(slot);
!         newtuple = slottuple;
!     }

      modifiedCols = GetModifiedColumns(relinfo, estate);

*************** ExecBRUpdateTriggers(EState *estate, EPQ
*** 2314,2326 ****
                                         relinfo->ri_TrigFunctions,
                                         relinfo->ri_TrigInstrument,
                                         GetPerTupleMemoryContext(estate));
!         if (oldtuple != newtuple && oldtuple != intuple)
              heap_freetuple(oldtuple);
          if (newtuple == NULL)
!             break;
      }
      heap_freetuple(trigtuple);
!     return newtuple;
  }

  void
--- 2362,2394 ----
                                         relinfo->ri_TrigFunctions,
                                         relinfo->ri_TrigInstrument,
                                         GetPerTupleMemoryContext(estate));
!         if (oldtuple != newtuple && oldtuple != slottuple)
              heap_freetuple(oldtuple);
          if (newtuple == NULL)
!         {
!             heap_freetuple(trigtuple);
!             return NULL;        /* "do nothing" */
!         }
      }
      heap_freetuple(trigtuple);
!
!     if (newtuple != slottuple)
!     {
!         /*
!          * 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(newtuple, newslot, InvalidBuffer, false);
!         slot = newslot;
!     }
!     return slot;
  }

  void
*************** ExecARUpdateTriggers(EState *estate, Res
*** 2342,2355 ****
      }
  }

! HeapTuple
  ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
!                      HeapTuple oldtuple, HeapTuple newtuple)
  {
      TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
      TriggerData LocTriggerData;
!     HeapTuple    intuple = newtuple;
!     HeapTuple    rettuple;
      int            i;

      LocTriggerData.type = T_TriggerData;
--- 2410,2424 ----
      }
  }

! TupleTableSlot *
  ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
!                      HeapTuple trigtuple, TupleTableSlot *slot)
  {
      TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+     HeapTuple    slottuple = ExecMaterializeSlot(slot);
+     HeapTuple    newtuple = slottuple;
      TriggerData LocTriggerData;
!     HeapTuple    oldtuple;
      int            i;

      LocTriggerData.type = T_TriggerData;
*************** ExecIRUpdateTriggers(EState *estate, Res
*** 2367,2392 ****
                                    TRIGGER_TYPE_UPDATE))
              continue;
          if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
!                             NULL, oldtuple, newtuple))
              continue;

!         LocTriggerData.tg_trigtuple = oldtuple;
!         LocTriggerData.tg_newtuple = newtuple;
          LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
          LocTriggerData.tg_newtuplebuf = InvalidBuffer;
          LocTriggerData.tg_trigger = trigger;
!         rettuple = ExecCallTriggerFunc(&LocTriggerData,
                                         i,
                                         relinfo->ri_TrigFunctions,
                                         relinfo->ri_TrigInstrument,
                                         GetPerTupleMemoryContext(estate));
!         if (newtuple != rettuple && newtuple != intuple)
!             heap_freetuple(newtuple);
!         newtuple = rettuple;
          if (newtuple == NULL)
!             break;
      }
!     return newtuple;
  }

  void
--- 2436,2477 ----
                                    TRIGGER_TYPE_UPDATE))
              continue;
          if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
!                             NULL, trigtuple, newtuple))
              continue;

!         LocTriggerData.tg_trigtuple = trigtuple;
!         LocTriggerData.tg_newtuple = oldtuple = newtuple;
          LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
          LocTriggerData.tg_newtuplebuf = InvalidBuffer;
          LocTriggerData.tg_trigger = trigger;
!         newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                         i,
                                         relinfo->ri_TrigFunctions,
                                         relinfo->ri_TrigInstrument,
                                         GetPerTupleMemoryContext(estate));
!         if (oldtuple != newtuple && oldtuple != slottuple)
!             heap_freetuple(oldtuple);
          if (newtuple == NULL)
!             return NULL;        /* "do nothing" */
      }
!
!     if (newtuple != slottuple)
!     {
!         /*
!          * 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(newtuple, newslot, InvalidBuffer, false);
!         slot = newslot;
!     }
!     return slot;
  }

  void
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 42662bdc461edfd2f9313795865577270fd33736..12a5b2a8953e19ade74f9a1c4f668532096b76c9 100644
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
*************** ExecInsert(TupleTableSlot *slot,
*** 199,258 ****
      if (resultRelInfo->ri_TrigDesc &&
          resultRelInfo->ri_TrigDesc->trig_insert_before_row)
      {
!         HeapTuple    newtuple;
!
!         newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple);

!         if (newtuple == NULL)    /* "do nothing" */
              return NULL;

!         if (newtuple != tuple)    /* modified by Trigger(s) */
!         {
!             /*
!              * Put the modified tuple into a slot for convenience of routines
!              * below.  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(resultRelationDesc);
!
!             if (newslot->tts_tupleDescriptor != tupdesc)
!                 ExecSetSlotDescriptor(newslot, tupdesc);
!             ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
!             slot = newslot;
!             tuple = newtuple;
!         }
      }

      /* INSTEAD OF ROW INSERT Triggers */
      if (resultRelInfo->ri_TrigDesc &&
          resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
      {
!         HeapTuple    newtuple;
!
!         newtuple = ExecIRInsertTriggers(estate, resultRelInfo, tuple);

!         if (newtuple == NULL)    /* "do nothing" */
              return NULL;

!         if (newtuple != tuple)    /* modified by Trigger(s) */
!         {
!             /*
!              * Put the modified tuple into a slot for convenience of routines
!              * below.  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(resultRelationDesc);
!
!             if (newslot->tts_tupleDescriptor != tupdesc)
!                 ExecSetSlotDescriptor(newslot, tupdesc);
!             ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
!             slot = newslot;
!             tuple = newtuple;
!         }

          newId = InvalidOid;
      }
--- 199,224 ----
      if (resultRelInfo->ri_TrigDesc &&
          resultRelInfo->ri_TrigDesc->trig_insert_before_row)
      {
!         slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);

!         if (slot == NULL)        /* "do nothing" */
              return NULL;

!         /* trigger might have changed tuple */
!         tuple = ExecMaterializeSlot(slot);
      }

      /* INSTEAD OF ROW INSERT Triggers */
      if (resultRelInfo->ri_TrigDesc &&
          resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
      {
!         slot = ExecIRInsertTriggers(estate, resultRelInfo, slot);

!         if (slot == NULL)        /* "do nothing" */
              return NULL;

!         /* trigger might have changed tuple */
!         tuple = ExecMaterializeSlot(slot);

          newId = InvalidOid;
      }
*************** ExecUpdate(ItemPointer tupleid,
*** 533,563 ****
      if (resultRelInfo->ri_TrigDesc &&
          resultRelInfo->ri_TrigDesc->trig_update_before_row)
      {
!         HeapTuple    newtuple;
!
!         newtuple = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
!                                         tupleid, tuple);

!         if (newtuple == NULL)    /* "do nothing" */
              return NULL;

!         if (newtuple != tuple)    /* modified by Trigger(s) */
!         {
!             /*
!              * Put the modified tuple into a slot for convenience of routines
!              * below.  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(resultRelationDesc);
!
!             if (newslot->tts_tupleDescriptor != tupdesc)
!                 ExecSetSlotDescriptor(newslot, tupdesc);
!             ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
!             slot = newslot;
!             tuple = newtuple;
!         }
      }

      /* INSTEAD OF ROW UPDATE Triggers */
--- 499,512 ----
      if (resultRelInfo->ri_TrigDesc &&
          resultRelInfo->ri_TrigDesc->trig_update_before_row)
      {
!         slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
!                                     tupleid, slot);

!         if (slot == NULL)        /* "do nothing" */
              return NULL;

!         /* trigger might have changed tuple */
!         tuple = ExecMaterializeSlot(slot);
      }

      /* INSTEAD OF ROW UPDATE Triggers */
*************** ExecUpdate(ItemPointer tupleid,
*** 565,571 ****
          resultRelInfo->ri_TrigDesc->trig_update_instead_row)
      {
          HeapTupleData oldtup;
-         HeapTuple    newtuple;

          Assert(oldtuple != NULL);
          oldtup.t_data = oldtuple;
--- 514,519 ----
*************** ExecUpdate(ItemPointer tupleid,
*** 573,601 ****
          ItemPointerSetInvalid(&(oldtup.t_self));
          oldtup.t_tableOid = InvalidOid;

!         newtuple = ExecIRUpdateTriggers(estate, resultRelInfo,
!                                         &oldtup, tuple);

!         if (newtuple == NULL)    /* "do nothing" */
              return NULL;

!         if (newtuple != tuple)    /* modified by Trigger(s) */
!         {
!             /*
!              * Put the modified tuple into a slot for convenience of routines
!              * below.  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(resultRelationDesc);
!
!             if (newslot->tts_tupleDescriptor != tupdesc)
!                 ExecSetSlotDescriptor(newslot, tupdesc);
!             ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
!             slot = newslot;
!             tuple = newtuple;
!         }
      }
      else
      {
--- 521,534 ----
          ItemPointerSetInvalid(&(oldtup.t_self));
          oldtup.t_tableOid = InvalidOid;

!         slot = ExecIRUpdateTriggers(estate, resultRelInfo,
!                                     &oldtup, slot);

!         if (slot == NULL)        /* "do nothing" */
              return NULL;

!         /* trigger might have changed tuple */
!         tuple = ExecMaterializeSlot(slot);
      }
      else
      {
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index c213ac7a4efa32ab79e80560a4a040fc4e666636..80a779ed0bdf143f058249843bebdf665925abe6 100644
*** a/src/include/commands/trigger.h
--- b/src/include/commands/trigger.h
*************** extern void ExecBSInsertTriggers(EState
*** 132,147 ****
                       ResultRelInfo *relinfo);
  extern void ExecASInsertTriggers(EState *estate,
                       ResultRelInfo *relinfo);
! extern HeapTuple ExecBRInsertTriggers(EState *estate,
                       ResultRelInfo *relinfo,
!                      HeapTuple trigtuple);
  extern void ExecARInsertTriggers(EState *estate,
                       ResultRelInfo *relinfo,
                       HeapTuple trigtuple,
                       List *recheckIndexes);
! extern HeapTuple ExecIRInsertTriggers(EState *estate,
                       ResultRelInfo *relinfo,
!                      HeapTuple trigtuple);
  extern void ExecBSDeleteTriggers(EState *estate,
                       ResultRelInfo *relinfo);
  extern void ExecASDeleteTriggers(EState *estate,
--- 132,147 ----
                       ResultRelInfo *relinfo);
  extern void ExecASInsertTriggers(EState *estate,
                       ResultRelInfo *relinfo);
! extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
                       ResultRelInfo *relinfo,
!                      TupleTableSlot *slot);
  extern void ExecARInsertTriggers(EState *estate,
                       ResultRelInfo *relinfo,
                       HeapTuple trigtuple,
                       List *recheckIndexes);
! extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
                       ResultRelInfo *relinfo,
!                      TupleTableSlot *slot);
  extern void ExecBSDeleteTriggers(EState *estate,
                       ResultRelInfo *relinfo);
  extern void ExecASDeleteTriggers(EState *estate,
*************** extern void ExecBSUpdateTriggers(EState
*** 160,179 ****
                       ResultRelInfo *relinfo);
  extern void ExecASUpdateTriggers(EState *estate,
                       ResultRelInfo *relinfo);
! extern HeapTuple ExecBRUpdateTriggers(EState *estate,
                       EPQState *epqstate,
                       ResultRelInfo *relinfo,
                       ItemPointer tupleid,
!                      HeapTuple newtuple);
  extern void ExecARUpdateTriggers(EState *estate,
                       ResultRelInfo *relinfo,
                       ItemPointer tupleid,
                       HeapTuple newtuple,
                       List *recheckIndexes);
! extern HeapTuple ExecIRUpdateTriggers(EState *estate,
                       ResultRelInfo *relinfo,
!                      HeapTuple oldtuple,
!                      HeapTuple newtuple);
  extern void ExecBSTruncateTriggers(EState *estate,
                         ResultRelInfo *relinfo);
  extern void ExecASTruncateTriggers(EState *estate,
--- 160,179 ----
                       ResultRelInfo *relinfo);
  extern void ExecASUpdateTriggers(EState *estate,
                       ResultRelInfo *relinfo);
! extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate,
                       EPQState *epqstate,
                       ResultRelInfo *relinfo,
                       ItemPointer tupleid,
!                      TupleTableSlot *slot);
  extern void ExecARUpdateTriggers(EState *estate,
                       ResultRelInfo *relinfo,
                       ItemPointer tupleid,
                       HeapTuple newtuple,
                       List *recheckIndexes);
! extern TupleTableSlot *ExecIRUpdateTriggers(EState *estate,
                       ResultRelInfo *relinfo,
!                      HeapTuple trigtuple,
!                      TupleTableSlot *slot);
  extern void ExecBSTruncateTriggers(EState *estate,
                         ResultRelInfo *relinfo);
  extern void ExecASTruncateTriggers(EState *estate,

pgsql-bugs by date:

Previous
From: Tom Lane
Date:
Subject: Re: BUG #5798: Some weird error with pl/pgsql procedure
Next
From: Tom Lane
Date:
Subject: Re: BUG #5798: Some weird error with pl/pgsql procedure