Prevent ExecInsert() and ExecUpdate() from scribbling on the result tuple
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 14 Nov 2005 17:43:13 +0000 (17:43 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 14 Nov 2005 17:43:13 +0000 (17:43 +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 14c804426d72b06f53bf95506e9d2b6ebe9cf5bc..51e12a3ac714bbabb4ecc42f32f22b37f88436ba 100644 (file)
@@ -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 27346f860d2c0e49e490c437c6dd1cb37645e6af..5a57ae334103604e8d449ecf9fa66cad8efe2eca 100644 (file)
@@ -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 f7a840f36095322efced31906e48aef43ead1d2c..92e72b71383ebba9bf4811c6462f27cbb18fe172 100644 (file)
@@ -341,6 +341,12 @@ typedef struct EState
        bool       *es_evTupleNull; /* local array of EPQ status */
        HeapTuple  *es_evTuple;         /* shared array of EPQ substitute tuples */
        bool            es_useEvalPlan; /* evaluating EPQ tuples? */
+
+       /*
+        * this field added at end of struct to avoid post-release ABI breakage
+        * in 8.1 series.  It'll be in a more logical place in 8.2.
+        */
+       TupleTableSlot *es_trig_tuple_slot;                     /* for trigger output tuples */
 } EState;