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)
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)
continue; /* next tuple please */
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/*
* AFTER ROW Triggers might reference the tableoid
* 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)
* 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,
/* We know this is a newly created relation, so there are no indexes */
+ /* Free the copied tuple. */
+ heap_freetuple(tuple);
+
return true;
}
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;
if (oldtuple != newtuple && oldtuple != slottuple)
heap_freetuple(oldtuple);
if (newtuple == NULL)
+ {
+ if (should_free)
+ heap_freetuple(slottuple);
return NULL; /* "do nothing" */
+ }
}
if (newtuple != slottuple)
ExecStoreHeapTuple(newtuple, newslot, false);
slot = newslot;
}
+
+ if (should_free)
+ heap_freetuple(slottuple);
return slot;
}
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;
if (oldtuple != newtuple && oldtuple != slottuple)
heap_freetuple(oldtuple);
if (newtuple == NULL)
+ {
+ if (should_free)
+ heap_freetuple(slottuple);
return NULL; /* "do nothing" */
+ }
}
if (newtuple != slottuple)
ExecStoreHeapTuple(newtuple, newslot, false);
slot = newslot;
}
+
+ if (should_free)
+ heap_freetuple(slottuple);
return slot;
}
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
if (newSlot != NULL)
{
slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
- slottuple = ExecMaterializeSlot(slot);
+ slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
newtuple = slottuple;
}
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;
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;
* 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
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);
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,
{
/* We must return the whole tuple as a Datum. */
*isNull = false;
- return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
+ return ExecFetchSlotHeapTupleDatum(fcache->funcResultSlot);
}
else
{
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
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.
*/
/*
* Otherwise materialize the slot...
*/
- return ExecMaterializeSlot(slot);
+ ExecMaterializeSlot(slot);
+
+ return slot->tts_tuple;
}
/* --------------------------------
* --------------------------------
*/
MinimalTuple
-ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
+ExecFetchSlotMinimalTuple(TupleTableSlot *slot, bool *shouldFree)
{
MemoryContext oldContext;
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.
}
/* --------------------------------
- * 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;
* 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.
*/
if (!TTS_SHOULDFREEMIN(slot))
slot->tts_mintuple = NULL;
-
- return slot->tts_tuple;
}
/* --------------------------------
{
/* We must return the whole tuple as a Datum. */
fcinfo->isnull = false;
- value = ExecFetchSlotTupleDatum(slot);
+ value = ExecFetchSlotHeapTupleDatum(slot);
}
else
{
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecMaterializeSlot(slot);
+ HeapTuple tup = ExecFetchSlotHeapTuple(slot, true, NULL);
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
}
TupleTableSlot *slot,
uint32 hashvalue)
{
- MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+ bool shouldFree;
+ MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
int bucketno;
int batchno;
hashvalue,
&hashtable->innerBatchFile[batchno]);
}
+
+ if (shouldFree)
+ heap_free_minimal_tuple(tuple);
}
/*
TupleTableSlot *slot,
uint32 hashvalue)
{
- MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+ bool shouldFree;
+ MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
dsa_pointer shared;
int bucketno;
int batchno;
tuple);
}
++hashtable->batches[batchno].ntuples;
+
+ if (shouldFree)
+ heap_free_minimal_tuple(tuple);
}
/*
TupleTableSlot *slot,
uint32 hashvalue)
{
- MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+ bool shouldFree;
+ MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
HashJoinTuple hashTuple;
dsa_pointer shared;
int batchno;
HeapTupleHeaderClearMatch(HJTUPLE_MINTUPLE(hashTuple));
ExecParallelHashPushTuple(&hashtable->buckets.shared[bucketno],
hashTuple, shared);
+
+ if (shouldFree)
+ heap_free_minimal_tuple(tuple);
}
/*
uint32 hashvalue,
int bucketNumber)
{
- MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot);
+ bool shouldFree;
+ MinimalTuple tuple = ExecFetchSlotMinimalTuple(slot, &shouldFree);
HashJoinTuple hashTuple;
int hashTupleSize;
/* Check we are not over the total spaceAllowed, either */
if (hashtable->spaceUsed > hashtable->spaceAllowed)
ExecHashIncreaseNumBatches(hashtable);
+
+ if (shouldFree)
+ heap_free_minimal_tuple(tuple);
}
/*
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;
}
{
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();
}
* 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;
* 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
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
}
/* INSTEAD OF ROW INSERT Triggers */
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
newId = InvalidOid;
}
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
*/
if (TTS_EMPTY(slot))
ExecStoreAllNullTuple(slot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
}
else
* 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
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
}
/* INSTEAD OF ROW UPDATE Triggers */
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
}
else if (resultRelInfo->ri_FdwRoutine)
{
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
else
{
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
goto lreplace;
}
}
{
*tupleid = hufd.ctid;
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
goto lreplace;
}
}
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
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;
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,