Move TupleTableSlots boolean member into one flag variable.

There's several reasons for this change:
1) It reduces the total size of TupleTableSlot / reduces alignment
   padding, making the commonly accessed members fit into a single
   cacheline (but we currently do not force proper alignment, so
   that's not yet guaranteed to be helpful)
2) Combining the booleans into a flag allows to combine read/writes
   from memory.
3) With the upcoming slot abstraction changes, it allows to have core
   and extended flags, in a memory efficient way.

Author: Ashutosh Bapat and Andres Freund
Discussion: https://postgr.es/m/20180220224318.gw4oe5jadhpmcdnm@alap3.anarazel.de
This commit is contained in:
Andres Freund 2018-10-15 15:24:33 -07:00
parent 9d906f1119
commit c5257345ef
8 changed files with 116 additions and 72 deletions

View File

@ -1391,7 +1391,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
{ {
/* Restore state from previous execution */ /* Restore state from previous execution */
off = slot->tts_off; off = slot->tts_off;
slow = slot->tts_slow; slow = TTS_SLOW(slot);
} }
tp = (char *) tup + tup->t_hoff; tp = (char *) tup + tup->t_hoff;
@ -1452,7 +1452,10 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
*/ */
slot->tts_nvalid = attnum; slot->tts_nvalid = attnum;
slot->tts_off = off; slot->tts_off = off;
slot->tts_slow = slow; if (slow)
slot->tts_flags |= TTS_FLAG_SLOW;
else
slot->tts_flags &= ~TTS_FLAG_SLOW;
} }
/* /*

View File

@ -101,11 +101,10 @@ MakeTupleTableSlot(TupleDesc tupleDesc)
slot = palloc0(sz); slot = palloc0(sz);
slot->type = T_TupleTableSlot; slot->type = T_TupleTableSlot;
slot->tts_isempty = true; slot->tts_flags |= TTS_FLAG_EMPTY;
slot->tts_shouldFree = false; if (tupleDesc != NULL)
slot->tts_shouldFreeMin = false; slot->tts_flags |= TTS_FLAG_FIXED;
slot->tts_tuple = NULL; slot->tts_tuple = NULL;
slot->tts_fixedTupleDescriptor = tupleDesc != NULL;
slot->tts_tupleDescriptor = tupleDesc; slot->tts_tupleDescriptor = tupleDesc;
slot->tts_mcxt = CurrentMemoryContext; slot->tts_mcxt = CurrentMemoryContext;
slot->tts_buffer = InvalidBuffer; slot->tts_buffer = InvalidBuffer;
@ -176,7 +175,7 @@ ExecResetTupleTable(List *tupleTable, /* tuple table */
/* If shouldFree, release memory occupied by the slot itself */ /* If shouldFree, release memory occupied by the slot itself */
if (shouldFree) if (shouldFree)
{ {
if (!slot->tts_fixedTupleDescriptor) if (!TTS_FIXED(slot))
{ {
if (slot->tts_values) if (slot->tts_values)
pfree(slot->tts_values); pfree(slot->tts_values);
@ -224,7 +223,7 @@ ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
ExecClearTuple(slot); ExecClearTuple(slot);
if (slot->tts_tupleDescriptor) if (slot->tts_tupleDescriptor)
ReleaseTupleDesc(slot->tts_tupleDescriptor); ReleaseTupleDesc(slot->tts_tupleDescriptor);
if (!slot->tts_fixedTupleDescriptor) if (!TTS_FIXED(slot))
{ {
if (slot->tts_values) if (slot->tts_values)
pfree(slot->tts_values); pfree(slot->tts_values);
@ -254,7 +253,7 @@ void
ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
TupleDesc tupdesc) /* new tuple descriptor */ TupleDesc tupdesc) /* new tuple descriptor */
{ {
Assert(!slot->tts_fixedTupleDescriptor); Assert(!TTS_FIXED(slot));
/* For safety, make sure slot is empty before changing it */ /* For safety, make sure slot is empty before changing it */
ExecClearTuple(slot); ExecClearTuple(slot);
@ -325,17 +324,23 @@ ExecStoreHeapTuple(HeapTuple tuple,
/* /*
* Free any old physical tuple belonging to the slot. * Free any old physical tuple belonging to the slot.
*/ */
if (slot->tts_shouldFree) if (TTS_SHOULDFREE(slot))
{
heap_freetuple(slot->tts_tuple); heap_freetuple(slot->tts_tuple);
if (slot->tts_shouldFreeMin) slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
}
if (TTS_SHOULDFREEMIN(slot))
{
heap_free_minimal_tuple(slot->tts_mintuple); heap_free_minimal_tuple(slot->tts_mintuple);
slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
}
/* /*
* Store the new tuple into the specified slot. * Store the new tuple into the specified slot.
*/ */
slot->tts_isempty = false; slot->tts_flags &= ~TTS_FLAG_EMPTY;
slot->tts_shouldFree = shouldFree; if (shouldFree)
slot->tts_shouldFreeMin = false; slot->tts_flags |= TTS_FLAG_SHOULDFREE;
slot->tts_tuple = tuple; slot->tts_tuple = tuple;
slot->tts_mintuple = NULL; slot->tts_mintuple = NULL;
@ -382,17 +387,21 @@ ExecStoreBufferHeapTuple(HeapTuple tuple,
/* /*
* Free any old physical tuple belonging to the slot. * Free any old physical tuple belonging to the slot.
*/ */
if (slot->tts_shouldFree) if (TTS_SHOULDFREE(slot))
{
heap_freetuple(slot->tts_tuple); heap_freetuple(slot->tts_tuple);
if (slot->tts_shouldFreeMin) slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
}
if (TTS_SHOULDFREEMIN(slot))
{
heap_free_minimal_tuple(slot->tts_mintuple); heap_free_minimal_tuple(slot->tts_mintuple);
slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
}
/* /*
* Store the new tuple into the specified slot. * Store the new tuple into the specified slot.
*/ */
slot->tts_isempty = false; slot->tts_flags &= ~TTS_FLAG_EMPTY;
slot->tts_shouldFree = false;
slot->tts_shouldFreeMin = false;
slot->tts_tuple = tuple; slot->tts_tuple = tuple;
slot->tts_mintuple = NULL; slot->tts_mintuple = NULL;
@ -442,10 +451,16 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
/* /*
* Free any old physical tuple belonging to the slot. * Free any old physical tuple belonging to the slot.
*/ */
if (slot->tts_shouldFree) if (TTS_SHOULDFREE(slot))
{
heap_freetuple(slot->tts_tuple); heap_freetuple(slot->tts_tuple);
if (slot->tts_shouldFreeMin) slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
}
if (TTS_SHOULDFREEMIN(slot))
{
heap_free_minimal_tuple(slot->tts_mintuple); heap_free_minimal_tuple(slot->tts_mintuple);
slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
}
/* /*
* Drop the pin on the referenced buffer, if there is one. * Drop the pin on the referenced buffer, if there is one.
@ -458,9 +473,9 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
/* /*
* Store the new tuple into the specified slot. * Store the new tuple into the specified slot.
*/ */
slot->tts_isempty = false; slot->tts_flags &= ~TTS_FLAG_EMPTY;
slot->tts_shouldFree = false; if (shouldFree)
slot->tts_shouldFreeMin = shouldFree; slot->tts_flags |= TTS_FLAG_SHOULDFREEMIN;
slot->tts_tuple = &slot->tts_minhdr; slot->tts_tuple = &slot->tts_minhdr;
slot->tts_mintuple = mtup; slot->tts_mintuple = mtup;
@ -493,15 +508,19 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
/* /*
* Free the old physical tuple if necessary. * Free the old physical tuple if necessary.
*/ */
if (slot->tts_shouldFree) if (TTS_SHOULDFREE(slot))
{
heap_freetuple(slot->tts_tuple); heap_freetuple(slot->tts_tuple);
if (slot->tts_shouldFreeMin) slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
}
if (TTS_SHOULDFREEMIN(slot))
{
heap_free_minimal_tuple(slot->tts_mintuple); heap_free_minimal_tuple(slot->tts_mintuple);
slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
}
slot->tts_tuple = NULL; slot->tts_tuple = NULL;
slot->tts_mintuple = NULL; slot->tts_mintuple = NULL;
slot->tts_shouldFree = false;
slot->tts_shouldFreeMin = false;
/* /*
* Drop the pin on the referenced buffer, if there is one. * Drop the pin on the referenced buffer, if there is one.
@ -514,7 +533,7 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
/* /*
* Mark it empty. * Mark it empty.
*/ */
slot->tts_isempty = true; slot->tts_flags |= TTS_FLAG_EMPTY;
slot->tts_nvalid = 0; slot->tts_nvalid = 0;
return slot; return slot;
@ -539,9 +558,9 @@ ExecStoreVirtualTuple(TupleTableSlot *slot)
*/ */
Assert(slot != NULL); Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL); Assert(slot->tts_tupleDescriptor != NULL);
Assert(slot->tts_isempty); Assert(TTS_EMPTY(slot));
slot->tts_isempty = false; slot->tts_flags &= ~TTS_FLAG_EMPTY;
slot->tts_nvalid = slot->tts_tupleDescriptor->natts; slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
return slot; return slot;
@ -595,7 +614,7 @@ ExecCopySlotTuple(TupleTableSlot *slot)
* sanity checks * sanity checks
*/ */
Assert(slot != NULL); Assert(slot != NULL);
Assert(!slot->tts_isempty); Assert(!TTS_EMPTY(slot));
/* /*
* If we have a physical tuple (either format) then just copy it. * If we have a physical tuple (either format) then just copy it.
@ -627,7 +646,7 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
* sanity checks * sanity checks
*/ */
Assert(slot != NULL); Assert(slot != NULL);
Assert(!slot->tts_isempty); Assert(!TTS_EMPTY(slot));
/* /*
* If we have a physical tuple then just copy it. Prefer to copy * If we have a physical tuple then just copy it. Prefer to copy
@ -675,7 +694,7 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
* sanity checks * sanity checks
*/ */
Assert(slot != NULL); Assert(slot != NULL);
Assert(!slot->tts_isempty); Assert(!TTS_EMPTY(slot));
/* /*
* If we have a regular physical tuple then just return it. * If we have a regular physical tuple then just return it.
@ -724,7 +743,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
* sanity checks * sanity checks
*/ */
Assert(slot != NULL); Assert(slot != NULL);
Assert(!slot->tts_isempty); Assert(!TTS_EMPTY(slot));
/* /*
* If we have a minimal physical tuple (local or not) then just return it. * If we have a minimal physical tuple (local or not) then just return it.
@ -741,7 +761,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
*/ */
oldContext = MemoryContextSwitchTo(slot->tts_mcxt); oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
slot->tts_mintuple = ExecCopySlotMinimalTuple(slot); slot->tts_mintuple = ExecCopySlotMinimalTuple(slot);
slot->tts_shouldFreeMin = true; slot->tts_flags |= TTS_FLAG_SHOULDFREEMIN;
MemoryContextSwitchTo(oldContext); MemoryContextSwitchTo(oldContext);
/* /*
@ -797,13 +817,13 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* sanity checks * sanity checks
*/ */
Assert(slot != NULL); Assert(slot != NULL);
Assert(!slot->tts_isempty); Assert(!TTS_EMPTY(slot));
/* /*
* If we have a regular physical tuple, and it's locally palloc'd, we have * If we have a regular physical tuple, and it's locally palloc'd, we have
* nothing to do. * nothing to do.
*/ */
if (slot->tts_tuple && slot->tts_shouldFree) if (slot->tts_tuple && TTS_SHOULDFREE(slot))
return slot->tts_tuple; return slot->tts_tuple;
/* /*
@ -815,7 +835,7 @@ ExecMaterializeSlot(TupleTableSlot *slot)
*/ */
oldContext = MemoryContextSwitchTo(slot->tts_mcxt); oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
slot->tts_tuple = ExecCopySlotTuple(slot); slot->tts_tuple = ExecCopySlotTuple(slot);
slot->tts_shouldFree = true; slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext); MemoryContextSwitchTo(oldContext);
/* /*
@ -842,7 +862,7 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* storage, we must not pfree it now, since callers might have already * storage, we must not pfree it now, since callers might have already
* fetched datum pointers referencing it.) * fetched datum pointers referencing it.)
*/ */
if (!slot->tts_shouldFreeMin) if (!TTS_SHOULDFREEMIN(slot))
slot->tts_mintuple = NULL; slot->tts_mintuple = NULL;
return slot->tts_tuple; return slot->tts_tuple;

View File

@ -1080,7 +1080,7 @@ prepare_projection_slot(AggState *aggstate, TupleTableSlot *slot, int currentSet
aggstate->grouped_cols = grouped_cols; aggstate->grouped_cols = grouped_cols;
if (slot->tts_isempty) if (TTS_EMPTY(slot))
{ {
/* /*
* Force all values to be NULL if working on an empty input tuple * Force all values to be NULL if working on an empty input tuple

View File

@ -699,7 +699,7 @@ ExecDelete(ModifyTableState *mtstate,
* RETURNING expressions might reference the tableoid column, so * RETURNING expressions might reference the tableoid column, so
* initialize t_tableOid before evaluating them. * initialize t_tableOid before evaluating them.
*/ */
if (slot->tts_isempty) if (TTS_EMPTY(slot))
ExecStoreAllNullTuple(slot); ExecStoreAllNullTuple(slot);
tuple = ExecMaterializeSlot(slot); tuple = ExecMaterializeSlot(slot);
tuple->t_tableOid = RelationGetRelid(resultRelationDesc); tuple->t_tableOid = RelationGetRelid(resultRelationDesc);

View File

@ -60,7 +60,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts)
LLVMValueRef v_tts_values; LLVMValueRef v_tts_values;
LLVMValueRef v_tts_nulls; LLVMValueRef v_tts_nulls;
LLVMValueRef v_slotoffp; LLVMValueRef v_slotoffp;
LLVMValueRef v_slowp; LLVMValueRef v_flagsp;
LLVMValueRef v_nvalidp; LLVMValueRef v_nvalidp;
LLVMValueRef v_nvalid; LLVMValueRef v_nvalid;
LLVMValueRef v_maxatt; LLVMValueRef v_maxatt;
@ -168,7 +168,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts)
"tts_ISNULL"); "tts_ISNULL");
v_slotoffp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_OFF, ""); v_slotoffp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_OFF, "");
v_slowp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_SLOW, ""); v_flagsp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_FLAGS, "");
v_nvalidp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_NVALID, ""); v_nvalidp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_NVALID, "");
v_tupleheaderp = v_tupleheaderp =
@ -690,11 +690,14 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts)
{ {
LLVMValueRef v_off = LLVMBuildLoad(b, v_offp, ""); LLVMValueRef v_off = LLVMBuildLoad(b, v_offp, "");
LLVMValueRef v_flags;
LLVMBuildStore(b, l_int16_const(natts), v_nvalidp); LLVMBuildStore(b, l_int16_const(natts), v_nvalidp);
v_off = LLVMBuildTrunc(b, v_off, LLVMInt32Type(), ""); v_off = LLVMBuildTrunc(b, v_off, LLVMInt32Type(), "");
LLVMBuildStore(b, v_off, v_slotoffp); LLVMBuildStore(b, v_off, v_slotoffp);
LLVMBuildStore(b, l_int8_const(1), v_slowp); v_flags = LLVMBuildLoad(b, v_flagsp, "tts_flags");
v_flags = LLVMBuildOr(b, v_flags, l_int16_const(TTS_FLAG_SLOW), "");
LLVMBuildStore(b, v_flags, v_flagsp);
LLVMBuildRetVoid(b); LLVMBuildRetVoid(b);
} }

View File

@ -292,7 +292,7 @@ llvm_compile_expr(ExprState *state)
if (!desc && if (!desc &&
is && is &&
is->ps_ResultTupleSlot && is->ps_ResultTupleSlot &&
is->ps_ResultTupleSlot->tts_fixedTupleDescriptor) TTS_FIXED(is->ps_ResultTupleSlot))
desc = is->ps_ResultTupleSlot->tts_tupleDescriptor; desc = is->ps_ResultTupleSlot->tts_tupleDescriptor;
} }
else if (opcode == EEOP_OUTER_FETCHSOME) else if (opcode == EEOP_OUTER_FETCHSOME)
@ -304,7 +304,7 @@ llvm_compile_expr(ExprState *state)
if (!desc && if (!desc &&
os && os &&
os->ps_ResultTupleSlot && os->ps_ResultTupleSlot &&
os->ps_ResultTupleSlot->tts_fixedTupleDescriptor) TTS_FIXED(os->ps_ResultTupleSlot))
desc = os->ps_ResultTupleSlot->tts_tupleDescriptor; desc = os->ps_ResultTupleSlot->tts_tupleDescriptor;
} }
else else

View File

@ -340,7 +340,7 @@ ExecProject(ProjectionInfo *projInfo)
* Successfully formed a result row. Mark the result slot as containing a * Successfully formed a result row. Mark the result slot as containing a
* valid virtual tuple (inlined version of ExecStoreVirtualTuple()). * valid virtual tuple (inlined version of ExecStoreVirtualTuple()).
*/ */
slot->tts_isempty = false; slot->tts_flags &= ~TTS_FLAG_EMPTY;
slot->tts_nvalid = slot->tts_tupleDescriptor->natts; slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
return slot; return slot;

View File

@ -65,11 +65,11 @@
* ie, only as needed. This serves to avoid repeated extraction of data * ie, only as needed. This serves to avoid repeated extraction of data
* from the physical tuple. * from the physical tuple.
* *
* A TupleTableSlot can also be "empty", holding no valid data. This is * A TupleTableSlot can also be "empty", indicated by flag TTS_EMPTY set in
* the only valid state for a freshly-created slot that has not yet had a * tts_flags, holding no valid data. This is the only valid state for a
* tuple descriptor assigned to it. In this state, tts_isempty must be * freshly-created slot that has not yet had a tuple descriptor assigned to it.
* true, tts_shouldFree false, tts_tuple NULL, tts_buffer InvalidBuffer, * In this state, TTS_SHOULDFREE should not be set in tts_flag, tts_tuple must
* and tts_nvalid zero. * be NULL, tts_buffer InvalidBuffer, and tts_nvalid zero.
* *
* The tupleDescriptor is simply referenced, not copied, by the TupleTableSlot * The tupleDescriptor is simply referenced, not copied, by the TupleTableSlot
* code. The caller of ExecSetSlotDescriptor() is responsible for providing * code. The caller of ExecSetSlotDescriptor() is responsible for providing
@ -79,8 +79,9 @@
* mechanism to do more. However, the slot will increment the tupdesc * mechanism to do more. However, the slot will increment the tupdesc
* reference count if a reference-counted tupdesc is supplied.) * reference count if a reference-counted tupdesc is supplied.)
* *
* When tts_shouldFree is true, the physical tuple is "owned" by the slot * When TTS_SHOULDFREE is set in tts_flags, the physical tuple is "owned" by
* and should be freed when the slot's reference to the tuple is dropped. * the slot and should be freed when the slot's reference to the tuple is
* dropped.
* *
* If tts_buffer is not InvalidBuffer, then the slot is holding a pin * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
* on the indicated buffer page; drop the pin when we release the * on the indicated buffer page; drop the pin when we release the
@ -106,35 +107,52 @@
* MINIMAL_TUPLE_OFFSET bytes before tts_mintuple. This allows column * MINIMAL_TUPLE_OFFSET bytes before tts_mintuple. This allows column
* extraction to treat the case identically to regular physical tuples. * extraction to treat the case identically to regular physical tuples.
* *
* tts_slow/tts_off are saved state for slot_deform_tuple, and should not * TTS_SLOW flag in tts_flags and tts_off are saved state for
* be touched by any other code. * slot_deform_tuple, and should not be touched by any other code.
*---------- *----------
*/ */
/* true = slot is empty */
#define TTS_FLAG_EMPTY (1 << 1)
#define TTS_EMPTY(slot) (((slot)->tts_flags & TTS_FLAG_EMPTY) != 0)
/* should pfree tts_tuple? */
#define TTS_FLAG_SHOULDFREE (1 << 2)
#define TTS_SHOULDFREE(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREE) != 0)
/* should pfree tts_mintuple? */
#define TTS_FLAG_SHOULDFREEMIN (1 << 3)
#define TTS_SHOULDFREEMIN(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREEMIN) != 0)
/* saved state for slot_deform_tuple */
#define TTS_FLAG_SLOW (1 << 4)
#define TTS_SLOW(slot) (((slot)->tts_flags & TTS_FLAG_SLOW) != 0)
/* fixed tuple descriptor */
#define TTS_FLAG_FIXED (1 << 5)
#define TTS_FIXED(slot) (((slot)->tts_flags & TTS_FLAG_FIXED) != 0)
typedef struct TupleTableSlot typedef struct TupleTableSlot
{ {
NodeTag type; NodeTag type;
bool tts_isempty; /* true = slot is empty */ #define FIELDNO_TUPLETABLESLOT_FLAGS 1
bool tts_shouldFree; /* should pfree tts_tuple? */ uint16 tts_flags; /* Boolean states */
bool tts_shouldFreeMin; /* should pfree tts_mintuple? */ #define FIELDNO_TUPLETABLESLOT_NVALID 2
#define FIELDNO_TUPLETABLESLOT_SLOW 4 AttrNumber tts_nvalid; /* # of valid values in tts_values */
bool tts_slow; /* saved state for slot_deform_tuple */ #define FIELDNO_TUPLETABLESLOT_TUPLE 3
#define FIELDNO_TUPLETABLESLOT_TUPLE 5
HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */ HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 6 #define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */ TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
MemoryContext tts_mcxt; /* slot itself is in this context */ MemoryContext tts_mcxt; /* slot itself is in this context */
Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */ Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
#define FIELDNO_TUPLETABLESLOT_NVALID 9 #define FIELDNO_TUPLETABLESLOT_OFF 7
AttrNumber tts_nvalid; /* # of valid values in tts_values */ uint32 tts_off; /* saved state for slot_deform_tuple */
#define FIELDNO_TUPLETABLESLOT_VALUES 10 #define FIELDNO_TUPLETABLESLOT_VALUES 8
Datum *tts_values; /* current per-attribute values */ Datum *tts_values; /* current per-attribute values */
#define FIELDNO_TUPLETABLESLOT_ISNULL 11 #define FIELDNO_TUPLETABLESLOT_ISNULL 9
bool *tts_isnull; /* current per-attribute isnull flags */ bool *tts_isnull; /* current per-attribute isnull flags */
MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */ MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */ HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
#define FIELDNO_TUPLETABLESLOT_OFF 14
uint32 tts_off; /* saved state for slot_deform_tuple */
bool tts_fixedTupleDescriptor; /* descriptor can't be changed */
} TupleTableSlot; } TupleTableSlot;
#define TTS_HAS_PHYSICAL_TUPLE(slot) \ #define TTS_HAS_PHYSICAL_TUPLE(slot) \
@ -144,7 +162,7 @@ typedef struct TupleTableSlot
* TupIsNull -- is a TupleTableSlot empty? * TupIsNull -- is a TupleTableSlot empty?
*/ */
#define TupIsNull(slot) \ #define TupIsNull(slot) \
((slot) == NULL || (slot)->tts_isempty) ((slot) == NULL || TTS_EMPTY(slot))
/* in executor/execTuples.c */ /* in executor/execTuples.c */
extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc); extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc);