Allow sibling call optimization in slot_getsomeattrs_int()

This changes the TupleTableSlotOps contract to make it so the
getsomeattrs() function is in charge of calling
slot_getmissingattrs().

Since this removes all code from slot_getsomeattrs_int() aside from the
getsomeattrs() call itself, we may as well adjust slot_getsomeattrs() so
that it calls getsomeattrs() directly.  We leave slot_getsomeattrs_int()
intact as this is still called from the JIT code.

Author: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Zsolt Parragi <zsolt.parragi@percona.com>
Discussion: https://postgr.es/m/CAApHDvodSVBj3ypOYbYUCJX%2BNWL%3DVZs63RNBQ_FxB_F%2B6QXF-A%40mail.gmail.com
This commit is contained in:
David Rowley 2026-03-14 13:52:09 +13:00
parent 8a879119a1
commit 4deecb52af
2 changed files with 38 additions and 33 deletions

View file

@ -73,7 +73,7 @@
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);
int reqnatts);
static inline void tts_buffer_heap_store_tuple(TupleTableSlot *slot,
HeapTuple tuple,
Buffer buffer,
@ -1108,7 +1108,10 @@ slot_deform_heap_tuple_internal(TupleTableSlot *slot, HeapTuple tuple,
* 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).
* reqnatts'th column. If there are insufficient attributes in the given
* tuple, then slot_getmissingattrs() is called to populate the
* remainder. If reqnatts is above the number of attributes in the
* slot's TupleDesc, an error is raised.
*
* This is essentially an incremental version of heap_deform_tuple:
* on each call we extract attributes up to the one needed, without
@ -1120,21 +1123,23 @@ slot_deform_heap_tuple_internal(TupleTableSlot *slot, HeapTuple tuple,
*/
static pg_attribute_always_inline void
slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
int natts)
int reqnatts)
{
bool hasnulls = HeapTupleHasNulls(tuple);
int attnum;
int natts;
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);
natts = Min(HeapTupleHeaderGetNatts(tuple->t_data), reqnatts);
/*
* Check whether the first call for this tuple, and initialize or restore
* loop state.
*/
attnum = slot->tts_nvalid;
slot->tts_nvalid = reqnatts;
if (attnum == 0)
{
/* Start from the first attribute */
@ -1199,12 +1204,15 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
/*
* 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;
/* Fetch any missing attrs and raise an error if reqnatts is invalid. */
if (unlikely(attnum < reqnatts))
slot_getmissingattrs(slot, attnum, reqnatts);
}
const TupleTableSlotOps TTSOpsVirtual = {
@ -2058,34 +2066,36 @@ slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum)
{
AttrMissing *attrmiss = NULL;
/* Check for invalid attnums */
if (unlikely(lastAttNum > slot->tts_tupleDescriptor->natts))
elog(ERROR, "invalid attribute number %d", lastAttNum);
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));
for (int attnum = startAttNum; attnum < lastAttNum; attnum++)
{
slot->tts_values[attnum] = (Datum) 0;
slot->tts_isnull[attnum] = true;
}
}
else
{
int missattnum;
/* if there is a missing values array we must process them one by one */
for (missattnum = startAttNum;
missattnum < lastAttNum;
missattnum++)
/* use attrmiss to set the missing values */
for (int attnum = startAttNum; attnum < lastAttNum; attnum++)
{
slot->tts_values[missattnum] = attrmiss[missattnum].am_value;
slot->tts_isnull[missattnum] = !attrmiss[missattnum].am_present;
slot->tts_values[attnum] = attrmiss[attnum].am_value;
slot->tts_isnull[attnum] = !attrmiss[attnum].am_present;
}
}
}
/*
* slot_getsomeattrs_int - workhorse for slot_getsomeattrs()
* slot_getsomeattrs_int
* external function to call getsomeattrs() for use in JIT
*/
void
slot_getsomeattrs_int(TupleTableSlot *slot, int attnum)
@ -2094,21 +2104,13 @@ slot_getsomeattrs_int(TupleTableSlot *slot, int attnum)
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.
* Avoid putting new code here as that would prevent the compiler from
* using the sibling call optimization for the above function.
*/
if (unlikely(slot->tts_nvalid < attnum))
{
slot_getmissingattrs(slot, slot->tts_nvalid, attnum);
slot->tts_nvalid = attnum;
}
}
/* ----------------------------------------------------------------

View file

@ -151,10 +151,12 @@ struct TupleTableSlotOps
/*
* Fill up first natts entries of tts_values and tts_isnull arrays with
* values from the tuple contained in the slot. The function may be called
* with natts more than the number of attributes available in the tuple,
* in which case it should set tts_nvalid to the number of returned
* columns.
* values from the tuple contained in the slot and set the slot's
* tts_nvalid to natts. The function may be called with an natts value
* more than the number of attributes available in the tuple, in which
* case the function must call slot_getmissingattrs() to populate the
* remaining attributes. The function must raise an ERROR if 'natts' is
* higher than the number of attributes in the slot's TupleDesc.
*/
void (*getsomeattrs) (TupleTableSlot *slot, int natts);
@ -357,8 +359,9 @@ extern void slot_getsomeattrs_int(TupleTableSlot *slot, int attnum);
static inline void
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
{
/* Populate slot with attributes up to 'attnum', if it's not already */
if (slot->tts_nvalid < attnum)
slot_getsomeattrs_int(slot, attnum);
slot->tts_ops->getsomeattrs(slot, attnum);
}
/*