/*-------------------------------------------------------------------------
*
* execTuples . c
* Routines dealing with TupleTableSlots . These are used for resource
* management associated with tuples ( eg , releasing buffer pins for
* tuples in disk buffers , or freeing the memory occupied by transient
* tuples ) . Slots also provide access abstraction that lets us implement
* " virtual " tuples to reduce data - copying overhead .
*
* Routines dealing with the type information for tuples . Currently ,
* the type information for a tuple is an array of FormData_pg_attribute .
* This information is needed by routines manipulating tuples
* ( getattribute , formtuple , etc . ) .
*
*
* EXAMPLE OF HOW TABLE ROUTINES WORK
* Suppose we have a query such as SELECT emp . name FROM emp and we have
* a single SeqScan node in the query plan .
*
* At ExecutorStart ( )
* - - - - - - - - - - - - - - - -
*
* - ExecInitSeqScan ( ) calls ExecInitScanTupleSlot ( ) to construct a
* TupleTableSlots for the tuples returned by the access method , and
* ExecInitResultTypeTL ( ) to define the node ' s return
* type . ExecAssignScanProjectionInfo ( ) will , if necessary , create
* another TupleTableSlot for the tuples resulting from performing
* target list projections .
*
* During ExecutorRun ( )
* - - - - - - - - - - - - - - - -
* - SeqNext ( ) calls ExecStoreBufferHeapTuple ( ) to place the tuple
* returned by the access method into the scan tuple slot .
*
* - ExecSeqScan ( ) ( via ExecScan ) , if necessary , calls ExecProject ( ) ,
* putting the result of the projection in the result tuple slot . If
* not necessary , it directly returns the slot returned by SeqNext ( ) .
*
* - ExecutePlan ( ) calls the output function .
*
* The important thing to watch in the executor code is how pointers
* to the slots containing tuples are passed instead of the tuples
* themselves . This facilitates the communication of related information
* ( such as whether or not a tuple should be pfreed , what buffer contains
* this tuple , the tuple ' s tuple descriptor , etc ) . It also allows us
* to avoid physically constructing projection tuples in many cases .
*
*
* Portions Copyright ( c ) 1996 - 2025 , PostgreSQL Global Development Group
* Portions Copyright ( c ) 1994 , Regents of the University of California
*
*
* IDENTIFICATION
* src / backend / executor / execTuples . c
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres.h"
#include "access/heaptoast.h"
#include "access/htup_details.h"
#include "access/tupdesc_details.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "nodes/nodeFuncs.h"
#include "storage/bufmgr.h"
#include "utils/builtins.h"
#include "utils/expandeddatum.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
static TupleDesc ExecTypeFromTLInternal(List *targetList,
bool skipjunk);
static pg_attribute_always_inline void slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
int natts);
static inline void tts_buffer_heap_store_tuple(TupleTableSlot *slot,
HeapTuple tuple,
Buffer buffer,
bool transfer_pin);
static void tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree);
const TupleTableSlotOps TTSOpsVirtual;
const TupleTableSlotOps TTSOpsHeapTuple;
const TupleTableSlotOps TTSOpsMinimalTuple;
const TupleTableSlotOps TTSOpsBufferHeapTuple;
/*
* TupleTableSlotOps implementations .
*/
/*
* TupleTableSlotOps implementation for VirtualTupleTableSlot .
*/
static void
tts_virtual_init(TupleTableSlot *slot)
{
}
static void
tts_virtual_release(TupleTableSlot *slot)
{
}
static void
tts_virtual_clear(TupleTableSlot *slot)
{
if (unlikely(TTS_SHOULDFREE(slot)))
{
VirtualTupleTableSlot *vslot = (VirtualTupleTableSlot *) slot;
pfree(vslot->data);
vslot->data = NULL;
slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
}
slot->tts_nvalid = 0 ;
slot->tts_flags |= TTS_FLAG_EMPTY;
ItemPointerSetInvalid(&slot->tts_tid);
}
/*
* VirtualTupleTableSlots always have fully populated tts_values and
* tts_isnull arrays . So this function should never be called .
*/
static void
tts_virtual_getsomeattrs(TupleTableSlot *slot, int natts)
{
elog(ERROR, "getsomeattrs is not required to be called on a virtual tuple table slot" );
}
/*
* VirtualTupleTableSlots never provide system attributes ( except those
* handled generically , such as tableoid ) . We generally shouldn ' t get
* here , but provide a user - friendly message if we do .
*/
static Datum
tts_virtual_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
Assert(!TTS_EMPTY(slot));
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot retrieve a system column in this context" )));
return 0 ; /* silence compiler warnings */
}
/*
* VirtualTupleTableSlots never have storage tuples . We generally
* shouldn ' t get here , but provide a user - friendly message if we do .
*/
static bool
tts_virtual_is_current_xact_tuple(TupleTableSlot *slot)
{
Assert(!TTS_EMPTY(slot));
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("don't have transaction information for this type of tuple" )));
return false ; /* silence compiler warnings */
}
/*
* To materialize a virtual slot all the datums that aren ' t passed by value
* have to be copied into the slot ' s memory context . To do so , compute the
* required size , and allocate enough memory to store all attributes . That ' s
* good for cache hit ratio , but more importantly requires only memory
* allocation / deallocation .
*/
static void
tts_virtual_materialize(TupleTableSlot *slot)
{
VirtualTupleTableSlot *vslot = (VirtualTupleTableSlot *) slot;
TupleDesc desc = slot->tts_tupleDescriptor;
Size sz = 0 ;
char *data;
/* already materialized */
if (TTS_SHOULDFREE(slot))
return ;
/* compute size of memory required */
for (int natt = 0 ; natt < desc->natts; natt++)
{
CompactAttribute *att = TupleDescCompactAttr(desc, natt);
Datum val;
if (att->attbyval || slot->tts_isnull[natt])
continue ;
val = slot->tts_values[natt];
if (att->attlen == -1 &&
VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
{
/*
* We want to flatten the expanded value so that the materialized
* slot doesn ' t depend on it .
*/
sz = att_nominal_alignby(sz, att->attalignby);
sz += EOH_get_flat_size(DatumGetEOHP(val));
}
else
{
sz = att_nominal_alignby(sz, att->attalignby);
sz = att_addlength_datum(sz, att->attlen, val);
}
}
/* all data is byval */
if (sz == 0 )
return ;
/* allocate memory */
vslot->data = data = MemoryContextAlloc(slot->tts_mcxt, sz);
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
/* and copy all attributes into the pre-allocated space */
for (int natt = 0 ; natt < desc->natts; natt++)
{
CompactAttribute *att = TupleDescCompactAttr(desc, natt);
Datum val;
if (att->attbyval || slot->tts_isnull[natt])
continue ;
val = slot->tts_values[natt];
if (att->attlen == -1 &&
VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
{
Size data_length;
/*
* We want to flatten the expanded value so that the materialized
* slot doesn ' t depend on it .
*/
ExpandedObjectHeader *eoh = DatumGetEOHP(val);
data = (char *) att_nominal_alignby(data,
att->attalignby);
data_length = EOH_get_flat_size(eoh);
EOH_flatten_into(eoh, data, data_length);
slot->tts_values[natt] = PointerGetDatum(data);
data += data_length;
}
else
{
Size data_length = 0 ;
data = (char *) att_nominal_alignby(data, att->attalignby);
data_length = att_addlength_datum(data_length, att->attlen, val);
memcpy(data, DatumGetPointer(val), data_length);
slot->tts_values[natt] = PointerGetDatum(data);
data += data_length;
}
}
}
static void
tts_virtual_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
TupleDesc srcdesc = srcslot->tts_tupleDescriptor;
tts_virtual_clear(dstslot);
slot_getallattrs(srcslot);
for (int natt = 0 ; natt < srcdesc->natts; natt++)
{
dstslot->tts_values[natt] = srcslot->tts_values[natt];
dstslot->tts_isnull[natt] = srcslot->tts_isnull[natt];
}
dstslot->tts_nvalid = srcdesc->natts;
dstslot->tts_flags &= ~TTS_FLAG_EMPTY;
/* make sure storage doesn't depend on external memory */
tts_virtual_materialize(dstslot);
}
static HeapTuple
tts_virtual_copy_heap_tuple(TupleTableSlot *slot)
{
Assert(!TTS_EMPTY(slot));
return heap_form_tuple(slot->tts_tupleDescriptor,
slot->tts_values,
slot->tts_isnull);
}
static MinimalTuple
tts_virtual_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
{
Assert(!TTS_EMPTY(slot));
return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
slot->tts_values,
slot->tts_isnull,
extra);
}
/*
* TupleTableSlotOps implementation for HeapTupleTableSlot .
*/
static void
tts_heap_init(TupleTableSlot *slot)
{
}
static void
tts_heap_release(TupleTableSlot *slot)
{
}
static void
tts_heap_clear(TupleTableSlot *slot)
{
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
/* Free the memory for the heap tuple if it's allowed. */
if (TTS_SHOULDFREE(slot))
{
heap_freetuple(hslot->tuple);
slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
}
slot->tts_nvalid = 0 ;
slot->tts_flags |= TTS_FLAG_EMPTY;
ItemPointerSetInvalid(&slot->tts_tid);
hslot->off = 0 ;
hslot->tuple = NULL;
}
static void
tts_heap_getsomeattrs(TupleTableSlot *slot, int natts)
{
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
Assert(!TTS_EMPTY(slot));
slot_deform_heap_tuple(slot, hslot->tuple, &hslot->off, natts);
}
static Datum
tts_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
Assert(!TTS_EMPTY(slot));
/*
* In some code paths it ' s possible to get here with a non - materialized
* slot , in which case we can ' t retrieve system columns .
*/
if (!hslot->tuple)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot retrieve a system column in this context" )));
return heap_getsysattr(hslot->tuple, attnum,
slot->tts_tupleDescriptor, isnull);
}
static bool
tts_heap_is_current_xact_tuple(TupleTableSlot *slot)
{
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
TransactionId xmin;
Assert(!TTS_EMPTY(slot));
/*
* In some code paths it ' s possible to get here with a non - materialized
* slot , in which case we can ' t check if tuple is created by the current
* transaction .
*/
if (!hslot->tuple)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("don't have a storage tuple in this context" )));
xmin = HeapTupleHeaderGetRawXmin(hslot->tuple->t_data);
return TransactionIdIsCurrentTransactionId(xmin);
}
static void
tts_heap_materialize(TupleTableSlot *slot)
{
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
MemoryContext oldContext;
Assert(!TTS_EMPTY(slot));
/* If slot has its tuple already materialized, nothing to do. */
if (TTS_SHOULDFREE(slot))
return ;
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
/*
* Have to deform from scratch , otherwise tts_values [ ] entries could point
* into the non - materialized tuple ( which might be gone when accessed ) .
*/
slot->tts_nvalid = 0 ;
hslot->off = 0 ;
if (!hslot->tuple)
hslot->tuple = heap_form_tuple(slot->tts_tupleDescriptor,
slot->tts_values,
slot->tts_isnull);
else
{
/*
* The tuple contained in this slot is not allocated in the memory
* context of the given slot ( else it would have TTS_FLAG_SHOULDFREE
* set ) . Copy the tuple into the given slot ' s memory context .
*/
hslot->tuple = heap_copytuple(hslot->tuple);
}
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
}
static void
tts_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
HeapTuple tuple;
MemoryContext oldcontext;
oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
tuple = ExecCopySlotHeapTuple(srcslot);
MemoryContextSwitchTo(oldcontext);
ExecStoreHeapTuple(tuple, dstslot, true );
}
static HeapTuple
tts_heap_get_heap_tuple(TupleTableSlot *slot)
{
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
Assert(!TTS_EMPTY(slot));
if (!hslot->tuple)
tts_heap_materialize(slot);
return hslot->tuple;
}
static HeapTuple
tts_heap_copy_heap_tuple(TupleTableSlot *slot)
{
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
Assert(!TTS_EMPTY(slot));
if (!hslot->tuple)
tts_heap_materialize(slot);
return heap_copytuple(hslot->tuple);
}
static MinimalTuple
tts_heap_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
{
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
if (!hslot->tuple)
tts_heap_materialize(slot);
return minimal_tuple_from_heap_tuple(hslot->tuple, extra);
}
static void
tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
{
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
tts_heap_clear(slot);
slot->tts_nvalid = 0 ;
hslot->tuple = tuple;
hslot->off = 0 ;
slot->tts_flags &= ~(TTS_FLAG_EMPTY | TTS_FLAG_SHOULDFREE);
slot->tts_tid = tuple->t_self;
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
}
/*
* TupleTableSlotOps implementation for MinimalTupleTableSlot .
*/
static void
tts_minimal_init(TupleTableSlot *slot)
{
MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
/*
* Initialize the heap tuple pointer to access attributes of the minimal
* tuple contained in the slot as if its a heap tuple .
*/
mslot->tuple = &mslot->minhdr;
}
static void
tts_minimal_release(TupleTableSlot *slot)
{
}
static void
tts_minimal_clear(TupleTableSlot *slot)
{
MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
if (TTS_SHOULDFREE(slot))
{
heap_free_minimal_tuple(mslot->mintuple);
slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
}
slot->tts_nvalid = 0 ;
slot->tts_flags |= TTS_FLAG_EMPTY;
ItemPointerSetInvalid(&slot->tts_tid);
mslot->off = 0 ;
mslot->mintuple = NULL;
}
static void
tts_minimal_getsomeattrs(TupleTableSlot *slot, int natts)
{
MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
Assert(!TTS_EMPTY(slot));
slot_deform_heap_tuple(slot, mslot->tuple, &mslot->off, natts);
}
/*
* MinimalTupleTableSlots never provide system attributes . We generally
* shouldn ' t get here , but provide a user - friendly message if we do .
*/
static Datum
tts_minimal_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
Assert(!TTS_EMPTY(slot));
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot retrieve a system column in this context" )));
return 0 ; /* silence compiler warnings */
}
/*
* Within MinimalTuple abstraction transaction information is unavailable .
* We generally shouldn ' t get here , but provide a user - friendly message if
* we do .
*/
static bool
tts_minimal_is_current_xact_tuple(TupleTableSlot *slot)
{
Assert(!TTS_EMPTY(slot));
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("don't have transaction information for this type of tuple" )));
return false ; /* silence compiler warnings */
}
static void
tts_minimal_materialize(TupleTableSlot *slot)
{
MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
MemoryContext oldContext;
Assert(!TTS_EMPTY(slot));
/* If slot has its tuple already materialized, nothing to do. */
if (TTS_SHOULDFREE(slot))
return ;
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
/*
* Have to deform from scratch , otherwise tts_values [ ] entries could point
* into the non - materialized tuple ( which might be gone when accessed ) .
*/
slot->tts_nvalid = 0 ;
mslot->off = 0 ;
if (!mslot->mintuple)
{
mslot->mintuple = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
slot->tts_values,
slot->tts_isnull,
0 );
}
else
{
/*
* The minimal tuple contained in this slot is not allocated in the
* memory context of the given slot ( else it would have
* TTS_FLAG_SHOULDFREE set ) . Copy the minimal tuple into the given
* slot ' s memory context .
*/
mslot->mintuple = heap_copy_minimal_tuple(mslot->mintuple, 0 );
}
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
Assert(mslot->tuple == &mslot->minhdr);
mslot->minhdr.t_len = mslot->mintuple->t_len + MINIMAL_TUPLE_OFFSET;
mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mslot->mintuple - MINIMAL_TUPLE_OFFSET);
MemoryContextSwitchTo(oldContext);
}
static void
tts_minimal_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
MemoryContext oldcontext;
MinimalTuple mintuple;
oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
mintuple = ExecCopySlotMinimalTuple(srcslot);
MemoryContextSwitchTo(oldcontext);
ExecStoreMinimalTuple(mintuple, dstslot, true );
}
static MinimalTuple
tts_minimal_get_minimal_tuple(TupleTableSlot *slot)
{
MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
if (!mslot->mintuple)
tts_minimal_materialize(slot);
return mslot->mintuple;
}
static HeapTuple
tts_minimal_copy_heap_tuple(TupleTableSlot *slot)
{
MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
if (!mslot->mintuple)
tts_minimal_materialize(slot);
return heap_tuple_from_minimal_tuple(mslot->mintuple);
}
static MinimalTuple
tts_minimal_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
{
MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
if (!mslot->mintuple)
tts_minimal_materialize(slot);
return heap_copy_minimal_tuple(mslot->mintuple, extra);
}
static void
tts_minimal_store_tuple(TupleTableSlot *slot, MinimalTuple mtup, bool shouldFree)
{
MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
tts_minimal_clear(slot);
Assert(!TTS_SHOULDFREE(slot));
Assert(TTS_EMPTY(slot));
slot->tts_flags &= ~TTS_FLAG_EMPTY;
slot->tts_nvalid = 0 ;
mslot->off = 0 ;
mslot->mintuple = mtup;
Assert(mslot->tuple == &mslot->minhdr);
mslot->minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
/* no need to set t_self or t_tableOid since we won't allow access */
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
}
/*
* TupleTableSlotOps implementation for BufferHeapTupleTableSlot .
*/
static void
tts_buffer_heap_init(TupleTableSlot *slot)
{
}
static void
tts_buffer_heap_release(TupleTableSlot *slot)
{
}
static void
tts_buffer_heap_clear(TupleTableSlot *slot)
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
/*
* Free the memory for heap tuple if allowed . A tuple coming from buffer
* can never be freed . But we may have materialized a tuple from buffer .
* Such a tuple can be freed .
*/
if (TTS_SHOULDFREE(slot))
{
/* We should have unpinned the buffer while materializing the tuple. */
Assert(!BufferIsValid(bslot->buffer));
heap_freetuple(bslot->base.tuple);
slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
}
if (BufferIsValid(bslot->buffer))
ReleaseBuffer(bslot->buffer);
slot->tts_nvalid = 0 ;
slot->tts_flags |= TTS_FLAG_EMPTY;
ItemPointerSetInvalid(&slot->tts_tid);
bslot->base.tuple = NULL;
bslot->base.off = 0 ;
bslot->buffer = InvalidBuffer;
}
static void
tts_buffer_heap_getsomeattrs(TupleTableSlot *slot, int natts)
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
Assert(!TTS_EMPTY(slot));
slot_deform_heap_tuple(slot, bslot->base.tuple, &bslot->base.off, natts);
}
static Datum
tts_buffer_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
Assert(!TTS_EMPTY(slot));
/*
* In some code paths it ' s possible to get here with a non - materialized
* slot , in which case we can ' t retrieve system columns .
*/
if (!bslot->base.tuple)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot retrieve a system column in this context" )));
return heap_getsysattr(bslot->base.tuple, attnum,
slot->tts_tupleDescriptor, isnull);
}
static bool
tts_buffer_is_current_xact_tuple(TupleTableSlot *slot)
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
TransactionId xmin;
Assert(!TTS_EMPTY(slot));
/*
* In some code paths it ' s possible to get here with a non - materialized
* slot , in which case we can ' t check if tuple is created by the current
* transaction .
*/
if (!bslot->base.tuple)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("don't have a storage tuple in this context" )));
xmin = HeapTupleHeaderGetRawXmin(bslot->base.tuple->t_data);
return TransactionIdIsCurrentTransactionId(xmin);
}
static void
tts_buffer_heap_materialize(TupleTableSlot *slot)
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
MemoryContext oldContext;
Assert(!TTS_EMPTY(slot));
/* If slot has its tuple already materialized, nothing to do. */
if (TTS_SHOULDFREE(slot))
return ;
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
/*
* Have to deform from scratch , otherwise tts_values [ ] entries could point
* into the non - materialized tuple ( which might be gone when accessed ) .
*/
bslot->base.off = 0 ;
slot->tts_nvalid = 0 ;
if (!bslot->base.tuple)
{
/*
* Normally BufferHeapTupleTableSlot should have a tuple + buffer
* associated with it , unless it ' s materialized ( which would ' ve
* returned above ) . But when it ' s useful to allow storing virtual
* tuples in a buffer slot , which then also needs to be
* materializable .
*/
bslot->base.tuple = heap_form_tuple(slot->tts_tupleDescriptor,
slot->tts_values,
slot->tts_isnull);
}
else
{
bslot->base.tuple = heap_copytuple(bslot->base.tuple);
/*
* A heap tuple stored in a BufferHeapTupleTableSlot should have a
* buffer associated with it , unless it ' s materialized or virtual .
*/
if (likely(BufferIsValid(bslot->buffer)))
ReleaseBuffer(bslot->buffer);
bslot->buffer = InvalidBuffer;
}
/*
* We don ' t set TTS_FLAG_SHOULDFREE until after releasing the buffer , if
* any . This avoids having a transient state that would fall foul of our
* assertions that a slot with TTS_FLAG_SHOULDFREE doesn ' t own a buffer .
* In the unlikely event that ReleaseBuffer ( ) above errors out , we ' d
* effectively leak the copied tuple , but that seems fairly harmless .
*/
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
}
static void
tts_buffer_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
BufferHeapTupleTableSlot *bsrcslot = (BufferHeapTupleTableSlot *) srcslot;
BufferHeapTupleTableSlot *bdstslot = (BufferHeapTupleTableSlot *) dstslot;
/*
* If the source slot is of a different kind , or is a buffer slot that has
* been materialized / is virtual , make a new copy of the tuple . Otherwise
* make a new reference to the in - buffer tuple .
*/
if (dstslot->tts_ops != srcslot->tts_ops ||
TTS_SHOULDFREE(srcslot) ||
!bsrcslot->base.tuple)
{
MemoryContext oldContext;
ExecClearTuple(dstslot);
dstslot->tts_flags &= ~TTS_FLAG_EMPTY;
oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt);
bdstslot->base.tuple = ExecCopySlotHeapTuple(srcslot);
dstslot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
}
else
{
Assert(BufferIsValid(bsrcslot->buffer));
tts_buffer_heap_store_tuple(dstslot, bsrcslot->base.tuple,
bsrcslot->buffer, false );
/*
* The HeapTupleData portion of the source tuple might be shorter
* lived than the destination slot . Therefore copy the HeapTuple into
* our slot ' s tupdata , which is guaranteed to live long enough ( but
* will still point into the buffer ) .
*/
memcpy(&bdstslot->base.tupdata, bdstslot->base.tuple, sizeof (HeapTupleData));
bdstslot->base.tuple = &bdstslot->base.tupdata;
}
}
static HeapTuple
tts_buffer_heap_get_heap_tuple(TupleTableSlot *slot)
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
Assert(!TTS_EMPTY(slot));
if (!bslot->base.tuple)
tts_buffer_heap_materialize(slot);
return bslot->base.tuple;
}
static HeapTuple
tts_buffer_heap_copy_heap_tuple(TupleTableSlot *slot)
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
Assert(!TTS_EMPTY(slot));
if (!bslot->base.tuple)
tts_buffer_heap_materialize(slot);
return heap_copytuple(bslot->base.tuple);
}
static MinimalTuple
tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
Assert(!TTS_EMPTY(slot));
if (!bslot->base.tuple)
tts_buffer_heap_materialize(slot);
return minimal_tuple_from_heap_tuple(bslot->base.tuple, extra);
}
static inline void
tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
Buffer buffer, bool transfer_pin)
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
if (TTS_SHOULDFREE(slot))
{
/* materialized slot shouldn't have a buffer to release */
Assert(!BufferIsValid(bslot->buffer));
heap_freetuple(bslot->base.tuple);
slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
}
slot->tts_flags &= ~TTS_FLAG_EMPTY;
slot->tts_nvalid = 0 ;
bslot->base.tuple = tuple;
bslot->base.off = 0 ;
slot->tts_tid = tuple->t_self;
/*
* If tuple is on a disk page , keep the page pinned as long as we hold a
* pointer into it . We assume the caller already has such a pin . If
* transfer_pin is true , we ' ll transfer that pin to this slot , if not
* we ' ll pin it again ourselves .
*
* This is coded to optimize the case where the slot previously held a
* tuple on the same disk page : in that case releasing and re - acquiring
* the pin is a waste of cycles . This is a common situation during
* seqscans , so it ' s worth troubling over .
*/
if (bslot->buffer != buffer)
{
if (BufferIsValid(bslot->buffer))
ReleaseBuffer(bslot->buffer);
bslot->buffer = buffer;
if (!transfer_pin && BufferIsValid(buffer))
IncrBufferRefCount(buffer);
}
else if (transfer_pin && BufferIsValid(buffer))
{
/*
* In transfer_pin mode the caller won ' t know about the same - page
* optimization , so we gotta release its pin .
*/
ReleaseBuffer(buffer);
}
}
/*
* slot_deform_heap_tuple_internal
* An always inline helper function for use in slot_deform_heap_tuple to
* allow the compiler to emit specialized versions of this function for
* various combinations of " slow " and " hasnulls " . For example , if a
* given tuple has no nulls , then we needn ' t check " hasnulls " for every
* attribute that we ' re deforming . The caller can just call this
* function with hasnulls set to constant - false and have the compiler
* remove the constant - false branches and emit more optimal code .
*
* Returns the next attnum to deform , which can be equal to natts when the
* function manages to deform all requested attributes . * offp is an input and
* output parameter which is the byte offset within the tuple to start deforming
* from which , on return , gets set to the offset where the next attribute
* should be deformed from . * slowp is set to true when subsequent deforming
* of this tuple must use a version of this function with " slow " passed as
* true .
*
* Callers cannot assume when we return " attnum " ( i . e . all requested
* attributes have been deformed ) that slow mode isn ' t required for any
* additional deforming as the final attribute may have caused a switch to
* slow mode .
*/
static pg_attribute_always_inline int
slot_deform_heap_tuple_internal(TupleTableSlot *slot, HeapTuple tuple,
int attnum, int natts, bool slow,
bool hasnulls, uint32 *offp, bool *slowp)
{
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
Datum *values = slot->tts_values;
bool *isnull = slot->tts_isnull;
HeapTupleHeader tup = tuple->t_data;
char *tp; /* ptr to tuple data */
bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
bool slownext = false ;
tp = (char *) tup + tup->t_hoff;
for (; attnum < natts; attnum++)
{
CompactAttribute *thisatt = TupleDescCompactAttr(tupleDesc, attnum);
if (hasnulls && att_isnull(attnum, bp))
{
values[attnum] = (Datum) 0 ;
isnull[attnum] = true ;
if (!slow)
{
*slowp = true ;
return attnum + 1 ;
}
else
continue ;
}
isnull[attnum] = false ;
/* calculate the offset of this attribute */
if (!slow && thisatt->attcacheoff >= 0 )
*offp = thisatt->attcacheoff;
else if (thisatt->attlen == -1 )
{
/*
* We can only cache the offset for a varlena attribute if the
* offset is already suitably aligned , so that there would be no
* pad bytes in any case : then the offset will be valid for either
* an aligned or unaligned value .
*/
if (!slow && *offp == att_nominal_alignby(*offp, thisatt->attalignby))
thisatt->attcacheoff = *offp;
else
{
*offp = att_pointer_alignby(*offp,
thisatt->attalignby,
-1 ,
tp + *offp);
if (!slow)
slownext = true ;
}
}
else
{
/* not varlena, so safe to use att_nominal_alignby */
*offp = att_nominal_alignby(*offp, thisatt->attalignby);
if (!slow)
thisatt->attcacheoff = *offp;
}
values[attnum] = fetchatt(thisatt, tp + *offp);
*offp = att_addlength_pointer(*offp, thisatt->attlen, tp + *offp);
/* check if we need to switch to slow mode */
if (!slow)
{
/*
* We ' re unable to deform any further if the above code set
* ' slownext ' , or if this isn ' t a fixed - width attribute .
*/
if (slownext || thisatt->attlen <= 0 )
{
*slowp = true ;
return attnum + 1 ;
}
}
}
return natts;
}
/*
* slot_deform_heap_tuple
* Given a TupleTableSlot , extract data from the slot ' s physical tuple
* into its Datum / isnull arrays . Data is extracted up through the
* natts ' th column ( caller must ensure this is a legal column number ) .
*
* This is essentially an incremental version of heap_deform_tuple :
* on each call we extract attributes up to the one needed , without
* re - computing information about previously extracted attributes .
* slot - > tts_nvalid is the number of attributes already extracted .
*
* This is marked as always inline , so the different offp for different types
* of slots gets optimized away .
*/
static pg_attribute_always_inline void
slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
int natts)
{
bool hasnulls = HeapTupleHasNulls(tuple);
int attnum;
uint32 off; /* offset in tuple data */
bool slow; /* can we use/set attcacheoff? */
/* We can only fetch as many attributes as the tuple has. */
natts = Min(HeapTupleHeaderGetNatts(tuple->t_data), natts);
/*
* Check whether the first call for this tuple , and initialize or restore
* loop state .
*/
attnum = slot->tts_nvalid;
if (attnum == 0 )
{
/* Start from the first attribute */
off = 0 ;
slow = false ;
}
else
{
/* Restore state from previous execution */
off = *offp;
slow = TTS_SLOW(slot);
}
/*
* If ' slow ' isn ' t set , try deforming using deforming code that does not
* contain any of the extra checks required for non - fixed offset
* deforming . During deforming , if or when we find a NULL or a variable
* length attribute , we ' ll switch to a deforming method which includes the
* extra code required for non - fixed offset deforming , a . k . a slow mode .
* Because this is performance critical , we inline
* slot_deform_heap_tuple_internal passing the ' slow ' and ' hasnull '
* parameters as constants to allow the compiler to emit specialized code
* with the known - const false comparisons and subsequent branches removed .
*/
if (!slow)
{
/* Tuple without any NULLs? We can skip doing any NULL checking */
if (!hasnulls)
attnum = slot_deform_heap_tuple_internal(slot,
tuple,
attnum,
natts,
false , /* slow */
false , /* hasnulls */
&off,
&slow);
else
attnum = slot_deform_heap_tuple_internal(slot,
tuple,
attnum,
natts,
false , /* slow */
true , /* hasnulls */
&off,
&slow);
}
/* If there's still work to do then we must be in slow mode */
if (attnum < natts)
{
/* XXX is it worth adding a separate call when hasnulls is false? */
attnum = slot_deform_heap_tuple_internal(slot,
tuple,
attnum,
natts,
true , /* slow */
hasnulls,
&off,
&slow);
}
/*
* Save state for next execution
*/
slot->tts_nvalid = attnum;
*offp = off;
if (slow)
slot->tts_flags |= TTS_FLAG_SLOW;
else
slot->tts_flags &= ~TTS_FLAG_SLOW;
}
const TupleTableSlotOps TTSOpsVirtual = {
.base_slot_size = sizeof (VirtualTupleTableSlot),
.init = tts_virtual_init,
.release = tts_virtual_release,
.clear = tts_virtual_clear,
.getsomeattrs = tts_virtual_getsomeattrs,
.getsysattr = tts_virtual_getsysattr,
.materialize = tts_virtual_materialize,
.is_current_xact_tuple = tts_virtual_is_current_xact_tuple,
.copyslot = tts_virtual_copyslot,
/*
* A virtual tuple table slot can not " own " a heap tuple or a minimal
* tuple .
*/
.get_heap_tuple = NULL,
.get_minimal_tuple = NULL,
.copy_heap_tuple = tts_virtual_copy_heap_tuple,
.copy_minimal_tuple = tts_virtual_copy_minimal_tuple
};
const TupleTableSlotOps TTSOpsHeapTuple = {
.base_slot_size = sizeof (HeapTupleTableSlot),
.init = tts_heap_init,
.release = tts_heap_release,
.clear = tts_heap_clear,
.getsomeattrs = tts_heap_getsomeattrs,
.getsysattr = tts_heap_getsysattr,
.is_current_xact_tuple = tts_heap_is_current_xact_tuple,
.materialize = tts_heap_materialize,
.copyslot = tts_heap_copyslot,
.get_heap_tuple = tts_heap_get_heap_tuple,
/* A heap tuple table slot can not "own" a minimal tuple. */
.get_minimal_tuple = NULL,
.copy_heap_tuple = tts_heap_copy_heap_tuple,
.copy_minimal_tuple = tts_heap_copy_minimal_tuple
};
const TupleTableSlotOps TTSOpsMinimalTuple = {
.base_slot_size = sizeof (MinimalTupleTableSlot),
.init = tts_minimal_init,
.release = tts_minimal_release,
.clear = tts_minimal_clear,
.getsomeattrs = tts_minimal_getsomeattrs,
.getsysattr = tts_minimal_getsysattr,
.is_current_xact_tuple = tts_minimal_is_current_xact_tuple,
.materialize = tts_minimal_materialize,
.copyslot = tts_minimal_copyslot,
/* A minimal tuple table slot can not "own" a heap tuple. */
.get_heap_tuple = NULL,
.get_minimal_tuple = tts_minimal_get_minimal_tuple,
.copy_heap_tuple = tts_minimal_copy_heap_tuple,
.copy_minimal_tuple = tts_minimal_copy_minimal_tuple
};
const TupleTableSlotOps TTSOpsBufferHeapTuple = {
.base_slot_size = sizeof (BufferHeapTupleTableSlot),
.init = tts_buffer_heap_init,
.release = tts_buffer_heap_release,
.clear = tts_buffer_heap_clear,
.getsomeattrs = tts_buffer_heap_getsomeattrs,
.getsysattr = tts_buffer_heap_getsysattr,
.is_current_xact_tuple = tts_buffer_is_current_xact_tuple,
.materialize = tts_buffer_heap_materialize,
.copyslot = tts_buffer_heap_copyslot,
.get_heap_tuple = tts_buffer_heap_get_heap_tuple,
/* A buffer heap tuple table slot can not "own" a minimal tuple. */
.get_minimal_tuple = NULL,
.copy_heap_tuple = tts_buffer_heap_copy_heap_tuple,
.copy_minimal_tuple = tts_buffer_heap_copy_minimal_tuple
};
/* ----------------------------------------------------------------
* tuple table create / delete functions
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/* --------------------------------
* MakeTupleTableSlot
*
* Basic routine to make an empty TupleTableSlot of given
* TupleTableSlotType . If tupleDesc is specified the slot ' s descriptor is
* fixed for its lifetime , gaining some efficiency . If that ' s
* undesirable , pass NULL .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
TupleTableSlot *
MakeTupleTableSlot(TupleDesc tupleDesc,
const TupleTableSlotOps *tts_ops)
{
Size basesz,
allocsz;
TupleTableSlot *slot;
basesz = tts_ops->base_slot_size;
/*
* When a fixed descriptor is specified , we can reduce overhead by
* allocating the entire slot in one go .
*/
if (tupleDesc)
allocsz = MAXALIGN(basesz) +
MAXALIGN(tupleDesc->natts * sizeof (Datum)) +
MAXALIGN(tupleDesc->natts * sizeof (bool ));
else
allocsz = basesz;
slot = palloc0(allocsz);
/* const for optimization purposes, OK to modify at allocation time */
*((const TupleTableSlotOps **) &slot->tts_ops) = tts_ops;
slot->type = T_TupleTableSlot;
slot->tts_flags |= TTS_FLAG_EMPTY;
if (tupleDesc != NULL)
slot->tts_flags |= TTS_FLAG_FIXED;
slot->tts_tupleDescriptor = tupleDesc;
slot->tts_mcxt = CurrentMemoryContext;
slot->tts_nvalid = 0 ;
if (tupleDesc != NULL)
{
slot->tts_values = (Datum *)
(((char *) slot)
+ MAXALIGN(basesz));
slot->tts_isnull = (bool *)
(((char *) slot)
+ MAXALIGN(basesz)
+ MAXALIGN(tupleDesc->natts * sizeof (Datum)));
PinTupleDesc(tupleDesc);
}
/*
* And allow slot type specific initialization .
*/
slot->tts_ops->init(slot);
return slot;
}
/* --------------------------------
* ExecAllocTableSlot
*
* Create a tuple table slot within a tuple table ( which is just a List ) .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
TupleTableSlot *
ExecAllocTableSlot(List **tupleTable, TupleDesc desc,
const TupleTableSlotOps *tts_ops)
{
TupleTableSlot *slot = MakeTupleTableSlot(desc, tts_ops);
*tupleTable = lappend(*tupleTable, slot);
return slot;
}
/* --------------------------------
* ExecResetTupleTable
*
* This releases any resources ( buffer pins , tupdesc refcounts )
* held by the tuple table , and optionally releases the memory
* occupied by the tuple table data structure .
* It is expected that this routine be called by ExecEndPlan ( ) .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
void
ExecResetTupleTable(List *tupleTable, /* tuple table */
bool shouldFree) /* true if we should free memory */
{
ListCell *lc;
foreach(lc, tupleTable)
{
TupleTableSlot *slot = lfirst_node(TupleTableSlot, lc);
/* Always release resources and reset the slot to empty */
ExecClearTuple(slot);
slot->tts_ops->release(slot);
if (slot->tts_tupleDescriptor)
{
ReleaseTupleDesc(slot->tts_tupleDescriptor);
slot->tts_tupleDescriptor = NULL;
}
/* If shouldFree, release memory occupied by the slot itself */
if (shouldFree)
{
if (!TTS_FIXED(slot))
{
if (slot->tts_values)
pfree(slot->tts_values);
if (slot->tts_isnull)
pfree(slot->tts_isnull);
}
pfree(slot);
}
}
/* If shouldFree, release the list structure */
if (shouldFree)
list_free(tupleTable);
}
/* --------------------------------
* MakeSingleTupleTableSlot
*
* This is a convenience routine for operations that need a standalone
* TupleTableSlot not gotten from the main executor tuple table . It makes
* a single slot of given TupleTableSlotType and initializes it to use the
* given tuple descriptor .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
TupleTableSlot *
MakeSingleTupleTableSlot(TupleDesc tupdesc,
const TupleTableSlotOps *tts_ops)
{
TupleTableSlot *slot = MakeTupleTableSlot(tupdesc, tts_ops);
return slot;
}
/* --------------------------------
* ExecDropSingleTupleTableSlot
*
* Release a TupleTableSlot made with MakeSingleTupleTableSlot .
* DON ' T use this on a slot that ' s part of a tuple table list !
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
void
ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
{
/* This should match ExecResetTupleTable's processing of one slot */
Assert(IsA(slot, TupleTableSlot));
ExecClearTuple(slot);
slot->tts_ops->release(slot);
if (slot->tts_tupleDescriptor)
ReleaseTupleDesc(slot->tts_tupleDescriptor);
if (!TTS_FIXED(slot))
{
if (slot->tts_values)
pfree(slot->tts_values);
if (slot->tts_isnull)
pfree(slot->tts_isnull);
}
pfree(slot);
}
/* ----------------------------------------------------------------
* tuple table slot accessor functions
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/* --------------------------------
* ExecSetSlotDescriptor
*
* This function is used to set the tuple descriptor associated
* with the slot ' s tuple . The passed descriptor must have lifespan
* at least equal to the slot ' s . If it is a reference - counted descriptor
* then the reference count is incremented for as long as the slot holds
* a reference .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
void
ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
TupleDesc tupdesc) /* new tuple descriptor */
{
Assert(!TTS_FIXED(slot));
/* For safety, make sure slot is empty before changing it */
ExecClearTuple(slot);
/*
* Release any old descriptor . Also release old Datum / isnull arrays if
* present ( we don ' t bother to check if they could be re - used ) .
*/
if (slot->tts_tupleDescriptor)
ReleaseTupleDesc(slot->tts_tupleDescriptor);
if (slot->tts_values)
pfree(slot->tts_values);
if (slot->tts_isnull)
pfree(slot->tts_isnull);
/*
* Install the new descriptor ; if it ' s refcounted , bump its refcount .
*/
slot->tts_tupleDescriptor = tupdesc;
PinTupleDesc(tupdesc);
/*
* Allocate Datum / isnull arrays of the appropriate size . These must have
* the same lifetime as the slot , so allocate in the slot ' s own context .
*/
slot->tts_values = (Datum *)
MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof (Datum));
slot->tts_isnull = (bool *)
MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof (bool ));
}
/* --------------------------------
* ExecStoreHeapTuple
*
* This function is used to store an on - the - fly physical tuple into a specified
* slot in the tuple table .
*
* tuple : tuple to store
* slot : TTSOpsHeapTuple type slot to store it in
* shouldFree : true if ExecClearTuple should pfree ( ) the tuple
* when done with it
*
* shouldFree is normally set ' true ' for tuples constructed on - the - fly . But it
* can be ' false ' when the referenced tuple is held in a tuple table slot
* belonging to a lower - level executor Proc node . In this case the lower - level
* slot retains ownership and responsibility for eventually releasing the
* tuple . When this method is used , we must be certain that the upper - level
* Proc node will lose interest in the tuple sooner than the lower - level one
* does ! If you ' re not certain , copy the lower - level tuple with heap_copytuple
* and let the upper - level table slot assume ownership of the copy !
*
* Return value is just the passed - in slot pointer .
*
* If the target slot is not guaranteed to be TTSOpsHeapTuple type slot , use
* the , more expensive , ExecForceStoreHeapTuple ( ) .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
TupleTableSlot *
ExecStoreHeapTuple(HeapTuple tuple,
TupleTableSlot *slot,
bool shouldFree)
{
/*
* sanity checks
*/
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
if (unlikely(!TTS_IS_HEAPTUPLE(slot)))
elog(ERROR, "trying to store a heap tuple into wrong type of slot" );
tts_heap_store_tuple(slot, tuple, shouldFree);
slot->tts_tableOid = tuple->t_tableOid;
return slot;
}
/* --------------------------------
* ExecStoreBufferHeapTuple
*
* This function is used to store an on - disk physical tuple from a buffer
* into a specified slot in the tuple table .
*
* tuple : tuple to store
* slot : TTSOpsBufferHeapTuple type slot to store it in
* buffer : disk buffer if tuple is in a disk page , else InvalidBuffer
*
* The tuple table code acquires a pin on the buffer which is held until the
* slot is cleared , so that the tuple won ' t go away on us .
*
* Return value is just the passed - in slot pointer .
*
* If the target slot is not guaranteed to be TTSOpsBufferHeapTuple type slot ,
* use the , more expensive , ExecForceStoreHeapTuple ( ) .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
TupleTableSlot *
ExecStoreBufferHeapTuple(HeapTuple tuple,
TupleTableSlot *slot,
Buffer buffer)
{
/*
* sanity checks
*/
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
Assert(BufferIsValid(buffer));
if (unlikely(!TTS_IS_BUFFERTUPLE(slot)))
elog(ERROR, "trying to store an on-disk heap tuple into wrong type of slot" );
tts_buffer_heap_store_tuple(slot, tuple, buffer, false );
slot->tts_tableOid = tuple->t_tableOid;
return slot;
}
/*
* Like ExecStoreBufferHeapTuple , but transfer an existing pin from the caller
* to the slot , i . e . the caller doesn ' t need to , and may not , release the pin .
*/
TupleTableSlot *
ExecStorePinnedBufferHeapTuple(HeapTuple tuple,
TupleTableSlot *slot,
Buffer buffer)
{
/*
* sanity checks
*/
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
Assert(BufferIsValid(buffer));
if (unlikely(!TTS_IS_BUFFERTUPLE(slot)))
elog(ERROR, "trying to store an on-disk heap tuple into wrong type of slot" );
tts_buffer_heap_store_tuple(slot, tuple, buffer, true );
slot->tts_tableOid = tuple->t_tableOid;
return slot;
}
/*
* Store a minimal tuple into TTSOpsMinimalTuple type slot .
*
* If the target slot is not guaranteed to be TTSOpsMinimalTuple type slot ,
* use the , more expensive , ExecForceStoreMinimalTuple ( ) .
*/
TupleTableSlot *
ExecStoreMinimalTuple(MinimalTuple mtup,
TupleTableSlot *slot,
bool shouldFree)
{
/*
* sanity checks
*/
Assert(mtup != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
if (unlikely(!TTS_IS_MINIMALTUPLE(slot)))
elog(ERROR, "trying to store a minimal tuple into wrong type of slot" );
tts_minimal_store_tuple(slot, mtup, shouldFree);
return slot;
}
/*
* Store a HeapTuple into any kind of slot , performing conversion if
* necessary .
*/
void
ExecForceStoreHeapTuple(HeapTuple tuple,
TupleTableSlot *slot,
bool shouldFree)
{
if (TTS_IS_HEAPTUPLE(slot))
{
ExecStoreHeapTuple(tuple, slot, shouldFree);
}
else if (TTS_IS_BUFFERTUPLE(slot))
{
MemoryContext oldContext;
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
ExecClearTuple(slot);
slot->tts_flags &= ~TTS_FLAG_EMPTY;
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
bslot->base.tuple = heap_copytuple(tuple);
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
if (shouldFree)
pfree(tuple);
}
else
{
ExecClearTuple(slot);
heap_deform_tuple(tuple, slot->tts_tupleDescriptor,
slot->tts_values, slot->tts_isnull);
ExecStoreVirtualTuple(slot);
if (shouldFree)
{
ExecMaterializeSlot(slot);
pfree(tuple);
}
}
}
/*
* Store a MinimalTuple into any kind of slot , performing conversion if
* necessary .
*/
void
ExecForceStoreMinimalTuple(MinimalTuple mtup,
TupleTableSlot *slot,
bool shouldFree)
{
if (TTS_IS_MINIMALTUPLE(slot))
{
tts_minimal_store_tuple(slot, mtup, shouldFree);
}
else
{
HeapTupleData htup;
ExecClearTuple(slot);
htup.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
htup.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
heap_deform_tuple(&htup, slot->tts_tupleDescriptor,
slot->tts_values, slot->tts_isnull);
ExecStoreVirtualTuple(slot);
if (shouldFree)
{
ExecMaterializeSlot(slot);
pfree(mtup);
}
}
}
/* --------------------------------
* ExecStoreVirtualTuple
* Mark a slot as containing a virtual tuple .
*
* The protocol for loading a slot with virtual tuple data is :
* * Call ExecClearTuple to mark the slot empty .
* * Store data into the Datum / isnull arrays .
* * Call ExecStoreVirtualTuple to mark the slot valid .
* This is a bit unclean but it avoids one round of data copying .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
TupleTableSlot *
ExecStoreVirtualTuple(TupleTableSlot *slot)
{
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
Assert(TTS_EMPTY(slot));
slot->tts_flags &= ~TTS_FLAG_EMPTY;
slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
return slot;
}
/* --------------------------------
* ExecStoreAllNullTuple
* Set up the slot to contain a null in every column .
*
* At first glance this might sound just like ExecClearTuple , but it ' s
* entirely different : the slot ends up full , not empty .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
TupleTableSlot *
ExecStoreAllNullTuple(TupleTableSlot *slot)
{
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
/* Clear any old contents */
ExecClearTuple(slot);
/*
* Fill all the columns of the virtual tuple with nulls
*/
MemSet(slot->tts_values, 0 ,
slot->tts_tupleDescriptor->natts * sizeof (Datum));
memset(slot->tts_isnull, true ,
slot->tts_tupleDescriptor->natts * sizeof (bool ));
return ExecStoreVirtualTuple(slot);
}
/*
* Store a HeapTuple in datum form , into a slot . That always requires
* deforming it and storing it in virtual form .
*
* Until the slot is materialized , the contents of the slot depend on the
* datum .
*/
void
ExecStoreHeapTupleDatum(Datum data, TupleTableSlot *slot)
{
HeapTupleData tuple = {0 };
HeapTupleHeader td;
td = DatumGetHeapTupleHeader(data);
tuple.t_len = HeapTupleHeaderGetDatumLength(td);
tuple.t_self = td->t_ctid;
tuple.t_data = td;
ExecClearTuple(slot);
heap_deform_tuple(&tuple, slot->tts_tupleDescriptor,
slot->tts_values, slot->tts_isnull);
ExecStoreVirtualTuple(slot);
}
/*
* 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 released , 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 modifications are in the process of being removed .
*/
HeapTuple
ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
{
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(!TTS_EMPTY(slot));
/* Materialize the tuple so that the slot "owns" it, if requested. */
if (materialize)
slot->tts_ops->materialize(slot);
if (slot->tts_ops->get_heap_tuple == NULL)
{
if (shouldFree)
*shouldFree = true ;
return slot->tts_ops->copy_heap_tuple(slot);
}
else
{
if (shouldFree)
*shouldFree = false ;
return slot->tts_ops->get_heap_tuple(slot);
}
}
/* --------------------------------
* ExecFetchSlotMinimalTuple
* Fetch the slot ' s minimal physical tuple .
*
* If the given tuple table slot can hold a minimal tuple , indicated by a
* non - NULL get_minimal_tuple callback , the function returns the minimal
* tuple returned by that callback . It assumes that the minimal tuple
* returned by the callback is " owned " by the slot i . e . the slot is
* responsible for freeing the memory consumed by the tuple . Hence it sets
* * shouldFree to false , indicating that the caller should not free the
* memory consumed by the minimal tuple . In this case the returned minimal
* tuple should be considered as read - only .
*
* If that callback is not supported , it calls copy_minimal_tuple callback
* which is expected to return a copy of minimal tuple representing the
* contents of the slot . In this case * shouldFree is set to true ,
* indicating the caller that it should free the memory consumed by the
* minimal tuple . In this case the returned minimal tuple may be written
* up .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
MinimalTuple
ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
bool *shouldFree)
{
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(!TTS_EMPTY(slot));
if (slot->tts_ops->get_minimal_tuple)
{
if (shouldFree)
*shouldFree = false ;
return slot->tts_ops->get_minimal_tuple(slot);
}
else
{
if (shouldFree)
*shouldFree = true ;
return slot->tts_ops->copy_minimal_tuple(slot, 0 );
}
}
/* --------------------------------
* 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
ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot)
{
HeapTuple tup;
TupleDesc tupdesc;
bool shouldFree;
Datum ret;
/* Fetch slot's contents in regular-physical-tuple form */
tup = ExecFetchSlotHeapTuple(slot, false , &shouldFree);
tupdesc = slot->tts_tupleDescriptor;
/* Convert to Datum form */
ret = heap_copy_tuple_as_datum(tup, tupdesc);
if (shouldFree)
pfree(tup);
return ret;
}
/* ----------------------------------------------------------------
* convenience initialization routines
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/* ----------------
* ExecInitResultTypeTL
*
* Initialize result type , using the plan node ' s targetlist .
* - - - - - - - - - - - - - - - -
*/
void
ExecInitResultTypeTL(PlanState *planstate)
{
TupleDesc tupDesc = ExecTypeFromTL(planstate->plan->targetlist);
planstate->ps_ResultTupleDesc = tupDesc;
}
/* --------------------------------
* ExecInit { Result , Scan , Extra } TupleSlot [ TL ]
*
* These are convenience routines to initialize the specified slot
* in nodes inheriting the appropriate state . ExecInitExtraTupleSlot
* is used for initializing special - purpose slots .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/* ----------------
* ExecInitResultTupleSlotTL
*
* Initialize result tuple slot , using the tuple descriptor previously
* computed with ExecInitResultTypeTL ( ) .
* - - - - - - - - - - - - - - - -
*/
void
ExecInitResultSlot(PlanState *planstate, const TupleTableSlotOps *tts_ops)
{
TupleTableSlot *slot;
slot = ExecAllocTableSlot(&planstate->state->es_tupleTable,
planstate->ps_ResultTupleDesc, tts_ops);
planstate->ps_ResultTupleSlot = slot;
planstate->resultopsfixed = planstate->ps_ResultTupleDesc != NULL;
planstate->resultops = tts_ops;
planstate->resultopsset = true ;
}
/* ----------------
* ExecInitResultTupleSlotTL
*
* Initialize result tuple slot , using the plan node ' s targetlist .
* - - - - - - - - - - - - - - - -
*/
void
ExecInitResultTupleSlotTL(PlanState *planstate,
const TupleTableSlotOps *tts_ops)
{
ExecInitResultTypeTL(planstate);
ExecInitResultSlot(planstate, tts_ops);
}
/* ----------------
* ExecInitScanTupleSlot
* - - - - - - - - - - - - - - - -
*/
void
ExecInitScanTupleSlot(EState *estate, ScanState *scanstate,
TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
{
scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
tupledesc, tts_ops);
scanstate->ps.scandesc = tupledesc;
scanstate->ps.scanopsfixed = tupledesc != NULL;
scanstate->ps.scanops = tts_ops;
scanstate->ps.scanopsset = true ;
}
/* ----------------
* ExecInitExtraTupleSlot
*
* Return a newly created slot . If tupledesc is non - NULL the slot will have
* that as its fixed tupledesc . Otherwise the caller needs to use
* ExecSetSlotDescriptor ( ) to set the descriptor before use .
* - - - - - - - - - - - - - - - -
*/
TupleTableSlot *
ExecInitExtraTupleSlot(EState *estate,
TupleDesc tupledesc,
const TupleTableSlotOps *tts_ops)
{
return ExecAllocTableSlot(&estate->es_tupleTable, tupledesc, tts_ops);
}
/* ----------------
* ExecInitNullTupleSlot
*
* Build a slot containing an all - nulls tuple of the given type .
* This is used as a substitute for an input tuple when performing an
* outer join .
* - - - - - - - - - - - - - - - -
*/
TupleTableSlot *
ExecInitNullTupleSlot(EState *estate, TupleDesc tupType,
const TupleTableSlotOps *tts_ops)
{
TupleTableSlot *slot = ExecInitExtraTupleSlot(estate, tupType, tts_ops);
return ExecStoreAllNullTuple(slot);
}
/* ---------------------------------------------------------------
* Routines for setting / accessing attributes in a slot .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/*
* Fill in missing values for a TupleTableSlot .
*
* This is only exposed because it ' s needed for JIT compiled tuple
* deforming . That exception aside , there should be no callers outside of this
* file .
*/
void
slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum)
{
AttrMissing *attrmiss = NULL;
if (slot->tts_tupleDescriptor->constr)
attrmiss = slot->tts_tupleDescriptor->constr->missing;
if (!attrmiss)
{
/* no missing values array at all, so just fill everything in as NULL */
memset(slot->tts_values + startAttNum, 0 ,
(lastAttNum - startAttNum) * sizeof (Datum));
memset(slot->tts_isnull + startAttNum, 1 ,
(lastAttNum - startAttNum) * sizeof (bool ));
}
else
{
int missattnum;
/* if there is a missing values array we must process them one by one */
for (missattnum = startAttNum;
missattnum < lastAttNum;
missattnum++)
{
slot->tts_values[missattnum] = attrmiss[missattnum].am_value;
slot->tts_isnull[missattnum] = !attrmiss[missattnum].am_present;
}
}
}
/*
* slot_getsomeattrs_int - workhorse for slot_getsomeattrs ( )
*/
void
slot_getsomeattrs_int(TupleTableSlot *slot, int attnum)
{
/* Check for caller errors */
Assert(slot->tts_nvalid < attnum); /* checked in slot_getsomeattrs */
Assert(attnum > 0 );
if (unlikely(attnum > slot->tts_tupleDescriptor->natts))
elog(ERROR, "invalid attribute number %d" , attnum);
/* Fetch as many attributes as possible from the underlying tuple. */
slot->tts_ops->getsomeattrs(slot, attnum);
/*
* If the underlying tuple doesn ' t have enough attributes , tuple
* descriptor must have the missing attributes .
*/
if (unlikely(slot->tts_nvalid < attnum))
{
slot_getmissingattrs(slot, slot->tts_nvalid, attnum);
slot->tts_nvalid = attnum;
}
}
/* ----------------------------------------------------------------
* ExecTypeFromTL
*
* Generate a tuple descriptor for the result tuple of a targetlist .
* ( A parse / plan tlist must be passed , not an ExprState tlist . )
* Note that resjunk columns , if any , are included in the result .
*
* Currently there are about 4 different places where we create
* TupleDescriptors . They should all be merged , or perhaps
* be rewritten to call BuildDesc ( ) .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
TupleDesc
ExecTypeFromTL(List *targetList)
{
return ExecTypeFromTLInternal(targetList, false );
}
/* ----------------------------------------------------------------
* ExecCleanTypeFromTL
*
* Same as above , but resjunk columns are omitted from the result .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
TupleDesc
ExecCleanTypeFromTL(List *targetList)
{
return ExecTypeFromTLInternal(targetList, true );
}
static TupleDesc
ExecTypeFromTLInternal(List *targetList, bool skipjunk)
{
TupleDesc typeInfo;
ListCell *l;
int len;
int cur_resno = 1 ;
if (skipjunk)
len = ExecCleanTargetListLength(targetList);
else
len = ExecTargetListLength(targetList);
typeInfo = CreateTemplateTupleDesc(len);
foreach(l, targetList)
{
TargetEntry *tle = lfirst(l);
if (skipjunk && tle->resjunk)
continue ;
TupleDescInitEntry(typeInfo,
cur_resno,
tle->resname,
exprType((Node *) tle->expr),
exprTypmod((Node *) tle->expr),
0 );
TupleDescInitEntryCollation(typeInfo,
cur_resno,
exprCollation((Node *) tle->expr));
cur_resno++;
}
return typeInfo;
}
/*
* ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
*
* This is roughly like ExecTypeFromTL , but we work from bare expressions
* not TargetEntrys . No names are attached to the tupledesc ' s columns .
*/
TupleDesc
ExecTypeFromExprList(List *exprList)
{
TupleDesc typeInfo;
ListCell *lc;
int cur_resno = 1 ;
typeInfo = CreateTemplateTupleDesc(list_length(exprList));
foreach(lc, exprList)
{
Node *e = lfirst(lc);
TupleDescInitEntry(typeInfo,
cur_resno,
NULL,
exprType(e),
exprTypmod(e),
0 );
TupleDescInitEntryCollation(typeInfo,
cur_resno,
exprCollation(e));
cur_resno++;
}
return typeInfo;
}
/*
* ExecTypeSetColNames - set column names in a RECORD TupleDesc
*
* Column names must be provided as an alias list ( list of String nodes ) .
*/
void
ExecTypeSetColNames(TupleDesc typeInfo, List *namesList)
{
int colno = 0 ;
ListCell *lc;
/* It's only OK to change col names in a not-yet-blessed RECORD type */
Assert(typeInfo->tdtypeid == RECORDOID);
Assert(typeInfo->tdtypmod < 0 );
foreach(lc, namesList)
{
char *cname = strVal(lfirst(lc));
Form_pg_attribute attr;
/* Guard against too-long names list (probably can't happen) */
if (colno >= typeInfo->natts)
break ;
attr = TupleDescAttr(typeInfo, colno);
colno++;
/*
* Do nothing for empty aliases or dropped columns ( these cases
* probably can ' t arise in RECORD types , either )
*/
if (cname[0 ] == '\0' || attr->attisdropped)
continue ;
/* OK, assign the column name */
namestrcpy(&(attr->attname), cname);
}
}
/*
* BlessTupleDesc - make a completed tuple descriptor useful for SRFs
*
* Rowtype Datums returned by a function must contain valid type information .
* This happens " for free " if the tupdesc came from a relcache entry , but
* not if we have manufactured a tupdesc for a transient RECORD datatype .
* In that case we have to notify typcache . c of the existence of the type .
*/
TupleDesc
BlessTupleDesc(TupleDesc tupdesc)
{
if (tupdesc->tdtypeid == RECORDOID &&
tupdesc->tdtypmod < 0 )
assign_record_type_typmod(tupdesc);
return tupdesc; /* just for notational convenience */
}
/*
* TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
* supplied TupleDesc . AttInMetadata can be used in conjunction with C strings
* to produce a properly formed tuple .
*/
AttInMetadata *
TupleDescGetAttInMetadata(TupleDesc tupdesc)
{
int natts = tupdesc->natts;
int i;
Oid atttypeid;
Oid attinfuncid;
FmgrInfo *attinfuncinfo;
Oid *attioparams;
int32 *atttypmods;
AttInMetadata *attinmeta;
attinmeta = (AttInMetadata *) palloc(sizeof (AttInMetadata));
/* "Bless" the tupledesc so that we can make rowtype datums with it */
attinmeta->tupdesc = BlessTupleDesc(tupdesc);
/*
* Gather info needed later to call the " in " function for each attribute
*/
attinfuncinfo = (FmgrInfo *) palloc0(natts * sizeof (FmgrInfo));
attioparams = (Oid *) palloc0(natts * sizeof (Oid));
atttypmods = (int32 *) palloc0(natts * sizeof (int32));
for (i = 0 ; i < natts; i++)
{
Form_pg_attribute att = TupleDescAttr(tupdesc, i);
/* Ignore dropped attributes */
if (!att->attisdropped)
{
atttypeid = att->atttypid;
getTypeInputInfo(atttypeid, &attinfuncid, &attioparams[i]);
fmgr_info(attinfuncid, &attinfuncinfo[i]);
atttypmods[i] = att->atttypmod;
}
}
attinmeta->attinfuncs = attinfuncinfo;
attinmeta->attioparams = attioparams;
attinmeta->atttypmods = atttypmods;
return attinmeta;
}
/*
* BuildTupleFromCStrings - build a HeapTuple given user data in C string form .
* values is an array of C strings , one for each attribute of the return tuple .
* A NULL string pointer indicates we want to create a NULL field .
*/
HeapTuple
BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
{
TupleDesc tupdesc = attinmeta->tupdesc;
int natts = tupdesc->natts;
Datum *dvalues;
bool *nulls;
int i;
HeapTuple tuple;
dvalues = (Datum *) palloc(natts * sizeof (Datum));
nulls = (bool *) palloc(natts * sizeof (bool ));
/*
* Call the " in " function for each non - dropped attribute , even for nulls ,
* to support domains .
*/
for (i = 0 ; i < natts; i++)
{
if (!TupleDescCompactAttr(tupdesc, i)->attisdropped)
{
/* Non-dropped attributes */
dvalues[i] = InputFunctionCall(&attinmeta->attinfuncs[i],
values[i],
attinmeta->attioparams[i],
attinmeta->atttypmods[i]);
if (values[i] != NULL)
nulls[i] = false ;
else
nulls[i] = true ;
}
else
{
/* Handle dropped attributes by setting to NULL */
dvalues[i] = (Datum) 0 ;
nulls[i] = true ;
}
}
/*
* Form a tuple
*/
tuple = heap_form_tuple(tupdesc, dvalues, nulls);
/*
* Release locally palloc ' d space . XXX would probably be good to pfree
* values of pass - by - reference datums , as well .
*/
pfree(dvalues);
pfree(nulls);
return tuple;
}
/*
* HeapTupleHeaderGetDatum - convert a HeapTupleHeader pointer to a Datum .
*
* This must * not * get applied to an on - disk tuple ; the tuple should be
* freshly made by heap_form_tuple or some wrapper routine for it ( such as
* BuildTupleFromCStrings ) . Be sure also that the tupledesc used to build
* the tuple has a properly " blessed " rowtype .
*
* Formerly this was a macro equivalent to PointerGetDatum , relying on the
* fact that heap_form_tuple fills in the appropriate tuple header fields
* for a composite Datum . However , we now require that composite Datums not
* contain any external TOAST pointers . We do not want heap_form_tuple itself
* to enforce that ; more specifically , the rule applies only to actual Datums
* and not to HeapTuple structures . Therefore , HeapTupleHeaderGetDatum is
* now a function that detects whether there are externally - toasted fields
* and constructs a new tuple with inlined fields if so . We still need
* heap_form_tuple to insert the Datum header fields , because otherwise this
* code would have no way to obtain a tupledesc for the tuple .
*
* Note that if we do build a new tuple , it ' s palloc ' d in the current
* memory context . Beware of code that changes context between the initial
* heap_form_tuple / etc call and calling HeapTuple ( Header ) GetDatum .
*
* For performance - critical callers , it could be worthwhile to take extra
* steps to ensure that there aren ' t TOAST pointers in the output of
* heap_form_tuple to begin with . It ' s likely however that the costs of the
* typcache lookup and tuple disassembly / reassembly are swamped by TOAST
* dereference costs , so that the benefits of such extra effort would be
* minimal .
*
* XXX it would likely be better to create wrapper functions that produce
* a composite Datum from the field values in one step . However , there ' s
* enough code using the existing APIs that we couldn ' t get rid of this
* hack anytime soon .
*/
Datum
HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
{
Datum result;
TupleDesc tupDesc;
/* No work if there are no external TOAST pointers in the tuple */
if (!HeapTupleHeaderHasExternal(tuple))
return PointerGetDatum(tuple);
/* Use the type data saved by heap_form_tuple to look up the rowtype */
tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
HeapTupleHeaderGetTypMod(tuple));
/* And do the flattening */
result = toast_flatten_tuple_to_datum(tuple,
HeapTupleHeaderGetDatumLength(tuple),
tupDesc);
ReleaseTupleDesc(tupDesc);
return result;
}
/*
* Functions for sending tuples to the frontend ( or other specified destination )
* as though it is a SELECT result . These are used by utility commands that
* need to project directly to the destination and don ' t need or want full
* table function capability . Currently used by EXPLAIN and SHOW ALL .
*/
TupOutputState *
begin_tup_output_tupdesc(DestReceiver *dest,
TupleDesc tupdesc,
const TupleTableSlotOps *tts_ops)
{
TupOutputState *tstate;
tstate = (TupOutputState *) palloc(sizeof (TupOutputState));
tstate->slot = MakeSingleTupleTableSlot(tupdesc, tts_ops);
tstate->dest = dest;
tstate->dest->rStartup(tstate->dest, (int ) CMD_SELECT, tupdesc);
return tstate;
}
/*
* write a single tuple
*/
void
do_tup_output(TupOutputState *tstate, const Datum *values, const bool *isnull)
{
TupleTableSlot *slot = tstate->slot;
int natts = slot->tts_tupleDescriptor->natts;
/* make sure the slot is clear */
ExecClearTuple(slot);
/* insert data */
memcpy(slot->tts_values, values, natts * sizeof (Datum));
memcpy(slot->tts_isnull, isnull, natts * sizeof (bool ));
/* mark slot as containing a virtual tuple */
ExecStoreVirtualTuple(slot);
/* send the tuple to the receiver */
(void ) tstate->dest->receiveSlot(slot, tstate->dest);
/* clean up */
ExecClearTuple(slot);
}
/*
* write a chunk of text , breaking at newline characters
*
* Should only be used with a single - TEXT - attribute tupdesc .
*/
void
do_text_output_multiline(TupOutputState *tstate, const char *txt)
{
Datum values[1 ];
bool isnull[1 ] = {false };
while (*txt)
{
const char *eol;
int len;
eol = strchr(txt, '\n' );
if (eol)
{
len = eol - txt;
eol++;
}
else
{
len = strlen(txt);
eol = txt + len;
}
values[0 ] = PointerGetDatum(cstring_to_text_with_len(txt, len));
do_tup_output(tstate, values, isnull);
pfree(DatumGetPointer(values[0 ]));
txt = eol;
}
}
void
end_tup_output(TupOutputState *tstate)
{
tstate->dest->rShutdown(tstate->dest);
/* note that destroying the dest is not ours to do */
ExecDropSingleTupleTableSlot(tstate->slot);
pfree(tstate);
}
Messung V0.5 in Prozent C=92 H=96 G=93
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.87Bemerkung:
(vorverarbeitet am 2026-08-08)
¤
*Bot Zugriff