Rejigger materializing and fetching a HeapTuple from a slot.
authorAndres Freund <andres@anarazel.de>
Thu, 15 Nov 2018 22:26:14 +0000 (14:26 -0800)
committerAndres Freund <andres@anarazel.de>
Thu, 15 Nov 2018 22:31:12 +0000 (14:31 -0800)
Previously materializing a slot always returned a HeapTuple. As
current work aims to reduce the reliance on HeapTuples (so other
storage systems can work efficiently), that needs to change. Thus
split the tasks of materializing a slot (i.e. making it independent
from the underlying storage / other memory contexts) from fetching a
HeapTuple from the slot.  For brevity, allow to fetch a HeapTuple from
a slot and materializing the slot at the same time, controlled by a
parameter.

For now some callers of ExecFetchSlotHeapTuple, with materialize =
true, expect that changes to the heap tuple will be reflected in the
underlying slot.  Those places will be adapted in due course, so while
not pretty, that's OK for now.

Also rename ExecFetchSlotTuple to ExecFetchSlotHeapTupleDatum and
ExecFetchSlotTupleDatum to ExecFetchSlotHeapTupleDatum, as it's likely
that future storage methods will need similar methods. There already
is ExecFetchSlotMinimalTuple, so the new names make the naming scheme
more coherent.

Author: Ashutosh Bapat and Andres Freund, with changes by Amit Khandekar
Discussion: https://postgr.es/m/20181105210039.hh4vvi4vwoq5ba2q@alap3.anarazel.de

16 files changed:
contrib/postgres_fdw/postgres_fdw.c
src/backend/commands/copy.c
src/backend/commands/createas.c
src/backend/commands/matview.c
src/backend/commands/trigger.c
src/backend/executor/execMain.c
src/backend/executor/execReplication.c
src/backend/executor/execSRF.c
src/backend/executor/execTuples.c
src/backend/executor/functions.c
src/backend/executor/nodeForeignscan.c
src/backend/executor/nodeHash.c
src/backend/executor/nodeHashjoin.c
src/backend/executor/nodeModifyTable.c
src/backend/executor/tqueue.c
src/include/executor/tuptable.h

index fd20aa96aa9b8a18e3cef7ac9f807ae811148d85..6f9c6e193fcce9035810b500636e15a59f85d3a9 100644 (file)
@@ -3947,11 +3947,12 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
    ExecStoreVirtualTuple(resultSlot);
 
    /*
-    * If we have any system columns to return, install them.
+    * If we have any system columns to return, materialize a heap tuple in the
+    * slot from column values set above and install system columns in that tuple.
     */
    if (dmstate->hasSystemCols)
    {
-       HeapTuple   resultTup = ExecMaterializeSlot(resultSlot);
+       HeapTuple   resultTup = ExecFetchSlotHeapTuple(resultSlot, true, NULL);
 
        /* ctid */
        if (dmstate->ctidAttno)
index b58a74f4e3db9d8e8f30d35e08ca84814742faa3..a9471c5ef6a1a380a4988e9f24354fd909e423d0 100644 (file)
@@ -2899,7 +2899,7 @@ CopyFrom(CopyState cstate)
            if (slot == NULL)   /* "do nothing" */
                skip_tuple = true;
            else                /* trigger might have changed tuple */
-               tuple = ExecMaterializeSlot(slot);
+               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
        }
 
        if (!skip_tuple)
@@ -2975,7 +2975,7 @@ CopyFrom(CopyState cstate)
                            continue;   /* next tuple please */
 
                        /* FDW might have changed tuple */
-                       tuple = ExecMaterializeSlot(slot);
+                       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
                        /*
                         * AFTER ROW Triggers might reference the tableoid
index d5cb62da15bd1945c69982203f6d5064b73de903..d8002e5b776d1ccb2358696c1d10629e0d8f827d 100644 (file)
@@ -589,7 +589,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
     * get the heap tuple out of the tuple table slot, making sure we have a
     * writable copy
     */
-   tuple = ExecMaterializeSlot(slot);
+   tuple = ExecCopySlotTuple(slot);
 
    /*
     * force assignment of new OID (see comments in ExecInsert)
index e1eb7c374b833402c20255db384529dc0b7cee34..9957c7074de076781c723b9f77c508f5c4992dc9 100644 (file)
@@ -484,7 +484,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
     * get the heap tuple out of the tuple table slot, making sure we have a
     * writable copy
     */
-   tuple = ExecMaterializeSlot(slot);
+   tuple = ExecCopySlotTuple(slot);
 
    heap_insert(myState->transientrel,
                tuple,
@@ -494,6 +494,9 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
 
    /* We know this is a newly created relation, so there are no indexes */
 
+   /* Free the copied tuple. */
+   heap_freetuple(tuple);
+
    return true;
 }
 
index ccb5706c1628d6de1dd7b54b3531699d06d3d424..d6f33ecbd04317be76b8cf4488386001ab58e7c5 100644 (file)
@@ -2517,7 +2517,8 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
                     TupleTableSlot *slot)
 {
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-   HeapTuple   slottuple = ExecMaterializeSlot(slot);
+   bool        should_free;
+   HeapTuple   slottuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
    HeapTuple   newtuple = slottuple;
    HeapTuple   oldtuple;
    TriggerData LocTriggerData;
@@ -2556,7 +2557,11 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
        if (oldtuple != newtuple && oldtuple != slottuple)
            heap_freetuple(oldtuple);
        if (newtuple == NULL)
+       {
+           if (should_free)
+               heap_freetuple(slottuple);
            return NULL;        /* "do nothing" */
+       }
    }
 
    if (newtuple != slottuple)
@@ -2575,6 +2580,9 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
        ExecStoreHeapTuple(newtuple, newslot, false);
        slot = newslot;
    }
+
+   if (should_free)
+       heap_freetuple(slottuple);
    return slot;
 }
 
@@ -2598,7 +2606,8 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
                     TupleTableSlot *slot)
 {
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-   HeapTuple   slottuple = ExecMaterializeSlot(slot);
+   bool        should_free;
+   HeapTuple   slottuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
    HeapTuple   newtuple = slottuple;
    HeapTuple   oldtuple;
    TriggerData LocTriggerData;
@@ -2637,7 +2646,11 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
        if (oldtuple != newtuple && oldtuple != slottuple)
            heap_freetuple(oldtuple);
        if (newtuple == NULL)
+       {
+           if (should_free)
+               heap_freetuple(slottuple);
            return NULL;        /* "do nothing" */
+       }
    }
 
    if (newtuple != slottuple)
@@ -2656,6 +2669,9 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
        ExecStoreHeapTuple(newtuple, newslot, false);
        slot = newslot;
    }
+
+   if (should_free)
+       heap_freetuple(slottuple);
    return slot;
 }
 
@@ -2976,7 +2992,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
                     TupleTableSlot *slot)
 {
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-   HeapTuple   slottuple = ExecMaterializeSlot(slot);
+   HeapTuple   slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
    HeapTuple   newtuple = slottuple;
    TriggerData LocTriggerData;
    HeapTuple   trigtuple;
@@ -3018,7 +3034,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
    if (newSlot != NULL)
    {
        slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
-       slottuple = ExecMaterializeSlot(slot);
+       slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
        newtuple = slottuple;
    }
 
@@ -3132,7 +3148,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
                     HeapTuple trigtuple, TupleTableSlot *slot)
 {
    TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-   HeapTuple   slottuple = ExecMaterializeSlot(slot);
+   HeapTuple   slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
    HeapTuple   newtuple = slottuple;
    TriggerData LocTriggerData;
    HeapTuple   oldtuple;
@@ -4262,22 +4278,22 @@ AfterTriggerExecute(AfterTriggerEvent event,
        case AFTER_TRIGGER_FDW_REUSE:
 
            /*
-            * Using ExecMaterializeSlot() rather than ExecFetchSlotTuple()
-            * ensures that tg_trigtuple does not reference tuplestore memory.
-            * (It is formally possible for the trigger function to queue
-            * trigger events that add to the same tuplestore, which can push
-            * other tuples out of memory.)  The distinction is academic,
-            * because we start with a minimal tuple that ExecFetchSlotTuple()
-            * must materialize anyway.
+            * Materialize tuple in the slot so that tg_trigtuple does not
+            * reference tuplestore memory.  (It is formally possible for the
+            * trigger function to queue trigger events that add to the same
+            * tuplestore, which can push other tuples out of memory.)  The
+            * distinction is academic, because we start with a minimal tuple
+            * that is stored as a heap tuple, constructed in different memory
+            * context, in the slot anyway.
             */
-           LocTriggerData.tg_trigtuple =
-               ExecMaterializeSlot(trig_tuple_slot1);
+           LocTriggerData.tg_trigtuple = ExecFetchSlotHeapTuple(trig_tuple_slot1,
+                                                                   true, NULL);
            LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
 
            LocTriggerData.tg_newtuple =
                ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
                 TRIGGER_EVENT_UPDATE) ?
-               ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+               ExecFetchSlotHeapTuple(trig_tuple_slot2, true, NULL) : NULL;
            LocTriggerData.tg_newtuplebuf = InvalidBuffer;
 
            break;
index ba156f8c5fcf74e782af15ca3606ee3c12ff35ae..d10e533fd1663b100fe3dc558b96baad49d419cd 100644 (file)
@@ -2549,7 +2549,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
     * is to guard against early re-use of the EPQ query.
     */
    if (!TupIsNull(slot))
-       (void) ExecMaterializeSlot(slot);
+       ExecMaterializeSlot(slot);
 
    /*
     * Clear out the test tuple.  This is needed in case the EPQ query is
index 25ba93e03c38f39a9a0523ac23f0d023faa58921..071ba8762d4f7feffb2774c2c37eb24bcdc70090 100644 (file)
@@ -418,7 +418,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
            ExecPartitionCheck(resultRelInfo, slot, estate, true);
 
        /* Materialize slot into a tuple that we can scribble upon. */
-       tuple = ExecMaterializeSlot(slot);
+       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
        /* OK, store the tuple and create index entries for it */
        simple_heap_insert(rel, tuple);
@@ -485,7 +485,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
            ExecPartitionCheck(resultRelInfo, slot, estate, true);
 
        /* Materialize slot into a tuple that we can scribble upon. */
-       tuple = ExecMaterializeSlot(slot);
+       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
        /* OK, update the tuple and index entries for it */
        simple_heap_update(rel, &searchslot->tts_tuple->t_self,
index b97b8d797ec27dd0df65d6c6f212c72d3cfa0dc6..3bffb0ea71f38375cc1ee5e0b77f5caafd24d0b8 100644 (file)
@@ -521,7 +521,7 @@ restart:
            {
                /* We must return the whole tuple as a Datum. */
                *isNull = false;
-               return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
+               return ExecFetchSlotHeapTupleDatum(fcache->funcResultSlot);
            }
            else
            {
index 9f0d9daa8293d98daf2277a3d4022f81941e1ce6..391db672d1d6b5d2bb82d9a59cbcf3f51e19594a 100644 (file)
@@ -676,23 +676,27 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
                                   slot->tts_isnull);
 }
 
-/* --------------------------------
- *     ExecFetchSlotTuple
- *         Fetch the slot's regular physical tuple.
- *
- *     If the slot contains a virtual tuple, we convert it to physical
- *     form.  The slot retains ownership of the physical tuple.
- *     If it contains a minimal tuple we convert to regular form and store
- *     that in addition to the minimal tuple (not instead of, because
- *     callers may hold pointers to Datums within the minimal tuple).
- *
- * The main difference between this and ExecMaterializeSlot() is that this
- * does not guarantee that the contained tuple is local storage.
- * Hence, the result must be treated as read-only.
- * --------------------------------
+/*
+ * ExecFetchSlotHeapTuple - fetch HeapTuple representing the slot's content
+ *
+ * The returned HeapTuple represents the slot's content as closely as
+ * possible.
+ *
+ * If materialize is true, the contents of the slots will be made independent
+ * from the underlying storage (i.e. all buffer pins are release, memory is
+ * allocated in the slot's context).
+ *
+ * If shouldFree is not-NULL it'll be set to true if the returned tuple has
+ * been allocated in the calling memory context, and must be freed by the
+ * caller (via explicit pfree() or a memory context reset).
+ *
+ * NB: If materialize is true, modifications of the returned tuple are
+ * allowed. But it depends on the type of the slot whether such modifications
+ * will also affect the slot's contents. While that is not the nicest
+ * behaviour, all such modifcations are in the process of being removed.
  */
 HeapTuple
-ExecFetchSlotTuple(TupleTableSlot *slot)
+ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
 {
    /*
     * sanity checks
@@ -700,6 +704,10 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
    Assert(slot != NULL);
    Assert(!TTS_EMPTY(slot));
 
+   /* will be used in the near future */
+   if (shouldFree)
+       *shouldFree = false;
+
    /*
     * If we have a regular physical tuple then just return it.
     */
@@ -722,7 +730,9 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
    /*
     * Otherwise materialize the slot...
     */
-   return ExecMaterializeSlot(slot);
+   ExecMaterializeSlot(slot);
+
+   return slot->tts_tuple;
 }
 
 /* --------------------------------
@@ -739,7 +749,7 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
  * --------------------------------
  */
 MinimalTuple
-ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
+ExecFetchSlotMinimalTuple(TupleTableSlot *slot, bool *shouldFree)
 {
    MemoryContext oldContext;
 
@@ -749,6 +759,9 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
    Assert(slot != NULL);
    Assert(!TTS_EMPTY(slot));
 
+   /* will be used in the near future */
+   if (shouldFree)
+       *shouldFree = false;
 
    /*
     * If we have a minimal physical tuple (local or not) then just return it.
@@ -779,40 +792,44 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
 }
 
 /* --------------------------------
- *     ExecFetchSlotTupleDatum
+ *     ExecFetchSlotHeapTupleDatum
  *         Fetch the slot's tuple as a composite-type Datum.
  *
  *     The result is always freshly palloc'd in the caller's memory context.
  * --------------------------------
  */
 Datum
-ExecFetchSlotTupleDatum(TupleTableSlot *slot)
+ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot)
 {
    HeapTuple   tup;
    TupleDesc   tupdesc;
+   bool        shouldFree;
+   Datum       ret;
 
    /* Fetch slot's contents in regular-physical-tuple form */
-   tup = ExecFetchSlotTuple(slot);
+   tup = ExecFetchSlotHeapTuple(slot, false, &shouldFree);
    tupdesc = slot->tts_tupleDescriptor;
 
    /* Convert to Datum form */
-   return heap_copy_tuple_as_datum(tup, tupdesc);
+   ret = heap_copy_tuple_as_datum(tup, tupdesc);
+
+   if (shouldFree)
+       pfree(tup);
+
+   return ret;
 }
 
-/* --------------------------------
- *     ExecMaterializeSlot
- *         Force a slot into the "materialized" state.
+/* ExecMaterializeSlot - force a slot into the "materialized" state.
  *
- *     This causes the slot's tuple to be a local copy not dependent on
- *     any external storage.  A pointer to the contained tuple is returned.
+ * This causes the slot's tuple to be a local copy not dependent on any
+ * external storage (i.e. pointing into a Buffer, or having allocations in
+ * another memory context).
  *
- *     A typical use for this operation is to prepare a computed tuple
- *     for being stored on disk.  The original data may or may not be
- *     virtual, but in any case we need a private copy for heap_insert
- *     to scribble on.
- * --------------------------------
+ * A typical use for this operation is to prepare a computed tuple for being
+ * stored on disk.  The original data may or may not be virtual, but in any
+ * case we need a private copy for heap_insert to scribble on.
  */
-HeapTuple
+void
 ExecMaterializeSlot(TupleTableSlot *slot)
 {
    MemoryContext oldContext;
@@ -828,7 +845,7 @@ ExecMaterializeSlot(TupleTableSlot *slot)
     * nothing to do.
     */
    if (slot->tts_tuple && TTS_SHOULDFREE(slot))
-       return slot->tts_tuple;
+       return;
 
    /*
     * Otherwise, copy or build a physical tuple, and store it into the slot.
@@ -868,8 +885,6 @@ ExecMaterializeSlot(TupleTableSlot *slot)
     */
    if (!TTS_SHOULDFREEMIN(slot))
        slot->tts_mintuple = NULL;
-
-   return slot->tts_tuple;
 }
 
 /* --------------------------------
index 23545896d4dd89e039fd4e4b4a3f874bb662daf7..f4dd5732198bb55581c9af056155eee25a14ad79 100644 (file)
@@ -969,7 +969,7 @@ postquel_get_single_result(TupleTableSlot *slot,
    {
        /* We must return the whole tuple as a Datum. */
        fcinfo->isnull = false;
-       value = ExecFetchSlotTupleDatum(slot);
+       value = ExecFetchSlotHeapTupleDatum(slot);
    }
    else
    {
index 5d2cd0ed717580a1317f088f4d4efcea5eca5361..f7eef32f6fef7b53e768f4553228cddebd3e38ef 100644 (file)
@@ -62,7 +62,7 @@ ForeignNext(ForeignScanState *node)
     */
    if (plan->fsSystemCol && !TupIsNull(slot))
    {
-       HeapTuple   tup = ExecMaterializeSlot(slot);
+       HeapTuple   tup = ExecFetchSlotHeapTuple(slot, true, NULL);
 
        tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
    }
index a9f812d66b8ead3bb14b587513fd398dc501b8d5..5a9f1ea3c550faaabb2e85df6599c2f5d50fee72 100644 (file)
@@ -1590,7 +1590,8 @@ ExecHashTableInsert(HashJoinTable hashtable,
                    TupleTableSlot *slot,
                    uint32 hashvalue)
 {
-   MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+   bool        shouldFree;
+   MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
    int         bucketno;
    int         batchno;
 
@@ -1664,6 +1665,9 @@ ExecHashTableInsert(HashJoinTable hashtable,
                              hashvalue,
                              &hashtable->innerBatchFile[batchno]);
    }
+
+   if (shouldFree)
+       heap_free_minimal_tuple(tuple);
 }
 
 /*
@@ -1675,7 +1679,8 @@ ExecParallelHashTableInsert(HashJoinTable hashtable,
                            TupleTableSlot *slot,
                            uint32 hashvalue)
 {
-   MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+   bool        shouldFree;
+   MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
    dsa_pointer shared;
    int         bucketno;
    int         batchno;
@@ -1723,6 +1728,9 @@ retry:
                     tuple);
    }
    ++hashtable->batches[batchno].ntuples;
+
+   if (shouldFree)
+       heap_free_minimal_tuple(tuple);
 }
 
 /*
@@ -1736,7 +1744,8 @@ ExecParallelHashTableInsertCurrentBatch(HashJoinTable hashtable,
                                        TupleTableSlot *slot,
                                        uint32 hashvalue)
 {
-   MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+   bool        shouldFree;
+   MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
    HashJoinTuple hashTuple;
    dsa_pointer shared;
    int         batchno;
@@ -1752,6 +1761,9 @@ ExecParallelHashTableInsertCurrentBatch(HashJoinTable hashtable,
    HeapTupleHeaderClearMatch(HJTUPLE_MINTUPLE(hashTuple));
    ExecParallelHashPushTuple(&hashtable->buckets.shared[bucketno],
                              hashTuple, shared);
+
+   if (shouldFree)
+       heap_free_minimal_tuple(tuple);
 }
 
 /*
@@ -2391,7 +2403,8 @@ ExecHashSkewTableInsert(HashJoinTable hashtable,
                        uint32 hashvalue,
                        int bucketNumber)
 {
-   MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+   bool        shouldFree;
+   MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
    HashJoinTuple hashTuple;
    int         hashTupleSize;
 
@@ -2419,6 +2432,9 @@ ExecHashSkewTableInsert(HashJoinTable hashtable,
    /* Check we are not over the total spaceAllowed, either */
    if (hashtable->spaceUsed > hashtable->spaceAllowed)
        ExecHashIncreaseNumBatches(hashtable);
+
+   if (shouldFree)
+       heap_free_minimal_tuple(tuple);
 }
 
 /*
index 08a8bb3426c633a92a82527d1045a6ad212cca24..d6a6ef770dd201b2471f23f4e75d1c4eca244b3d 100644 (file)
@@ -389,16 +389,22 @@ ExecHashJoinImpl(PlanState *pstate, bool parallel)
                if (batchno != hashtable->curbatch &&
                    node->hj_CurSkewBucketNo == INVALID_SKEW_BUCKET_NO)
                {
+                   bool        shouldFree;
+                   MinimalTuple mintuple = ExecFetchSlotMinimalTuple(outerTupleSlot,
+                                                                     &shouldFree);
+
                    /*
                     * Need to postpone this outer tuple to a later batch.
                     * Save it in the corresponding outer-batch file.
                     */
                    Assert(parallel_state == NULL);
                    Assert(batchno > hashtable->curbatch);
-                   ExecHashJoinSaveTuple(ExecFetchSlotMinimalTuple(outerTupleSlot),
-                                         hashvalue,
+                   ExecHashJoinSaveTuple(mintuple, hashvalue,
                                          &hashtable->outerBatchFile[batchno]);
 
+                   if (shouldFree)
+                       heap_free_minimal_tuple(mintuple);
+
                    /* Loop around, staying in HJ_NEED_NEW_OUTER state */
                    continue;
                }
@@ -1404,11 +1410,16 @@ ExecParallelHashJoinPartitionOuter(HashJoinState *hjstate)
        {
            int         batchno;
            int         bucketno;
+           bool        shouldFree;
+           MinimalTuple mintup = ExecFetchSlotMinimalTuple(slot, &shouldFree);
 
            ExecHashGetBucketAndBatch(hashtable, hashvalue, &bucketno,
                                      &batchno);
            sts_puttuple(hashtable->batches[batchno].outer_tuples,
-                        &hashvalue, ExecFetchSlotMinimalTuple(slot));
+                        &hashvalue, mintup);
+
+           if (shouldFree)
+               heap_free_minimal_tuple(mintup);
        }
        CHECK_FOR_INTERRUPTS();
    }
index 6ef694d5a4a636d9fc17b249f6b985916a427e83..a3ca336f2acdb090c7d5c65b3cc47cfaf9db1437 100644 (file)
@@ -175,7 +175,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
         * initialize t_tableOid before evaluating them.
         */
        Assert(!TupIsNull(econtext->ecxt_scantuple));
-       tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
+       tuple = ExecFetchSlotHeapTuple(econtext->ecxt_scantuple, true, NULL);
        tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
    }
    econtext->ecxt_outertuple = planSlot;
@@ -274,7 +274,7 @@ ExecInsert(ModifyTableState *mtstate,
     * get the heap tuple out of the tuple table slot, making sure we have a
     * writable copy
     */
-   tuple = ExecMaterializeSlot(slot);
+   tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
    /*
     * get information on the (current) result relation
@@ -315,7 +315,7 @@ ExecInsert(ModifyTableState *mtstate,
            return NULL;
 
        /* trigger might have changed tuple */
-       tuple = ExecMaterializeSlot(slot);
+       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
    }
 
    /* INSTEAD OF ROW INSERT Triggers */
@@ -328,7 +328,7 @@ ExecInsert(ModifyTableState *mtstate,
            return NULL;
 
        /* trigger might have changed tuple */
-       tuple = ExecMaterializeSlot(slot);
+       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
        newId = InvalidOid;
    }
@@ -346,7 +346,7 @@ ExecInsert(ModifyTableState *mtstate,
            return NULL;
 
        /* FDW might have changed tuple */
-       tuple = ExecMaterializeSlot(slot);
+       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
        /*
         * AFTER ROW Triggers or RETURNING expressions might reference the
@@ -695,7 +695,7 @@ ExecDelete(ModifyTableState *mtstate,
         */
        if (TTS_EMPTY(slot))
            ExecStoreAllNullTuple(slot);
-       tuple = ExecMaterializeSlot(slot);
+       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
        tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
    }
    else
@@ -953,7 +953,7 @@ ExecUpdate(ModifyTableState *mtstate,
     * get the heap tuple out of the tuple table slot, making sure we have a
     * writable copy
     */
-   tuple = ExecMaterializeSlot(slot);
+   tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
    /*
     * get information on the (current) result relation
@@ -972,7 +972,7 @@ ExecUpdate(ModifyTableState *mtstate,
            return NULL;
 
        /* trigger might have changed tuple */
-       tuple = ExecMaterializeSlot(slot);
+       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
    }
 
    /* INSTEAD OF ROW UPDATE Triggers */
@@ -986,7 +986,7 @@ ExecUpdate(ModifyTableState *mtstate,
            return NULL;
 
        /* trigger might have changed tuple */
-       tuple = ExecMaterializeSlot(slot);
+       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
    }
    else if (resultRelInfo->ri_FdwRoutine)
    {
@@ -1002,7 +1002,7 @@ ExecUpdate(ModifyTableState *mtstate,
            return NULL;
 
        /* FDW might have changed tuple */
-       tuple = ExecMaterializeSlot(slot);
+       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
        /*
         * AFTER ROW Triggers or RETURNING expressions might reference the
@@ -1129,7 +1129,7 @@ lreplace:;
                else
                {
                    slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
-                   tuple = ExecMaterializeSlot(slot);
+                   tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
                    goto lreplace;
                }
            }
@@ -1268,7 +1268,7 @@ lreplace:;
                    {
                        *tupleid = hufd.ctid;
                        slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
-                       tuple = ExecMaterializeSlot(slot);
+                       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
                        goto lreplace;
                    }
                }
@@ -1739,7 +1739,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
    estate->es_result_relation_info = partrel;
 
    /* Get the heap tuple out of the given slot. */
-   tuple = ExecMaterializeSlot(slot);
+   tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
 
    /*
     * If we're capturing transition tuples, we might need to convert from the
index ecdbe7f79f61dc33b930fd77c134d45ccfaede69..e47ef491928ea8c529418dde343c240fed8075a6 100644 (file)
@@ -56,11 +56,15 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
    TQueueDestReceiver *tqueue = (TQueueDestReceiver *) self;
    HeapTuple   tuple;
    shm_mq_result result;
+   bool        should_free;
 
    /* Send the tuple itself. */
-   tuple = ExecMaterializeSlot(slot);
+   tuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
    result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
 
+   if (should_free)
+       heap_freetuple(tuple);
+
    /* Check for failure. */
    if (result == SHM_MQ_DETACHED)
        return false;
index b41b400ef186f7d9d4195a0d31848695d7d7e78e..8bfa73c30ea2d3df1ac8e4e8c42bcbf7ff5e46e6 100644 (file)
@@ -185,10 +185,11 @@ extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
 extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
 extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
 extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
-extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
-extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shoulFree);
+extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
+                         bool *shouldFree);
+extern Datum ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
 extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
             TupleTableSlot *srcslot);
 extern void slot_getmissingattrs(TupleTableSlot *slot, int startAttNum,