Prevent ExecInsert() and ExecUpdate() from scribbling on the result tuple
authorTom Lane
Mon, 14 Nov 2005 17:42:55 +0000 (17:42 +0000)
committerTom Lane
Mon, 14 Nov 2005 17:42:55 +0000 (17:42 +0000)
slot of the topmost plan node when a trigger returns a modified tuple.
These appear to be the only places where a plan node's caller did not
treat the result slot as read-only, which is an assumption that nodeUnique
makes as of 8.1.  Fixes trigger-vs-DISTINCT bug reported by Frank van Vugt.

src/backend/executor/execMain.c
src/backend/executor/execUtils.c
src/include/nodes/execnodes.h

index 2a96a161c812fd6587b3178cc90d83be90ea47c8..c379802cafd8f43093758be210681e38dcb30b49 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.256 2005/10/15 02:49:16 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.257 2005/11/14 17:42:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -582,7 +582,8 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
     * initialize the executor "tuple" table.  We need slots for all the plan
     * nodes, plus possibly output slots for the junkfilter(s). At this point
     * we aren't sure if we need junkfilters, so just add slots for them
-    * unconditionally.
+    * unconditionally.  Also, if it's not a SELECT, set up a slot for use
+    * for trigger output tuples.
     */
    {
        int         nSlots = ExecCountSlotsNode(plan);
@@ -591,7 +592,14 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
            nSlots += list_length(parseTree->resultRelations);
        else
            nSlots += 1;
+       if (operation != CMD_SELECT)
+           nSlots++;
+
        estate->es_tupleTable = ExecCreateTupleTable(nSlots);
+
+       if (operation != CMD_SELECT)
+           estate->es_trig_tuple_slot =
+               ExecAllocTableSlot(estate->es_tupleTable);
    }
 
    /* mark EvalPlanQual not active */
@@ -1399,12 +1407,19 @@ ExecInsert(TupleTableSlot *slot,
        if (newtuple != tuple)  /* modified by Trigger(s) */
        {
            /*
-            * Insert modified tuple into tuple table slot, replacing the
-            * original.  We assume that it was allocated in per-tuple memory
+            * 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.
             */
-           ExecStoreTuple(newtuple, slot, InvalidBuffer, false);
+           TupleTableSlot *newslot = estate->es_trig_tuple_slot;
+
+           if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor)
+               ExecSetSlotDescriptor(newslot,
+                                     slot->tts_tupleDescriptor,
+                                     false);
+           ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
+           slot = newslot;
            tuple = newtuple;
        }
    }
@@ -1600,12 +1615,19 @@ ExecUpdate(TupleTableSlot *slot,
        if (newtuple != tuple)  /* modified by Trigger(s) */
        {
            /*
-            * Insert modified tuple into tuple table slot, replacing the
-            * original.  We assume that it was allocated in per-tuple memory
+            * 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.
             */
-           ExecStoreTuple(newtuple, slot, InvalidBuffer, false);
+           TupleTableSlot *newslot = estate->es_trig_tuple_slot;
+
+           if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor)
+               ExecSetSlotDescriptor(newslot,
+                                     slot->tts_tupleDescriptor,
+                                     false);
+           ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
+           slot = newslot;
            tuple = newtuple;
        }
    }
index 05bfc08dc7da15752d21970b51f870ea1130edbd..d9bcd1cf6fee824efdfe5dd757af399ae9ba4eb8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.126 2005/10/15 02:49:16 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.127 2005/11/14 17:42:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -187,6 +187,8 @@ CreateExecutorState(void)
 
    estate->es_junkFilter = NULL;
 
+   estate->es_trig_tuple_slot = NULL;
+
    estate->es_into_relation_descriptor = NULL;
    estate->es_into_relation_use_wal = false;
 
index 8b06e2897d905f91e45bfae3fb32a4914f244998..c638c28a02b346b3223c64c23261b6669319d4ed 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.139 2005/10/15 02:49:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.140 2005/11/14 17:42:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -304,6 +304,8 @@ typedef struct EState
    ResultRelInfo *es_result_relation_info;     /* currently active array elt */
    JunkFilter *es_junkFilter;  /* currently active junk filter */
 
+   TupleTableSlot *es_trig_tuple_slot;         /* for trigger output tuples */
+
    Relation    es_into_relation_descriptor;    /* for SELECT INTO */
    bool        es_into_relation_use_wal;