From ceaf2c78facbc2e118152d419a2c7bcf72a63ad5 Mon Sep 17 00:00:00 2001 From: David Rowley Date: Mon, 16 Feb 2026 14:20:19 +1300 Subject: [PATCH v12 2/6] 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. --- src/backend/executor/execTuples.c | 58 ++++++++++++++++--------------- src/include/executor/tuptable.h | 13 ++++--- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index b768eae9e53..7effe954286 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -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; - } } /* ---------------------------------------------------------------- diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index a2dfd707e78..3b09abbf99f 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -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); } /* -- 2.51.0