From 823cddd3d3ade9142c13bda1ccde45f78de11b38 Mon Sep 17 00:00:00 2001 From: David Rowley Date: Sat, 27 Dec 2025 20:54:16 +1300 Subject: [PATCH v2 3/3] Experiment without a dedicated !hasnulls loop This makes the code smaller and seems to make some tests go faster --- src/backend/executor/execTuples.c | 83 +++++++++---------------------- 1 file changed, 24 insertions(+), 59 deletions(-) diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 9ddae9d2f91..f20ae0506c4 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1039,18 +1039,11 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, bp = tup->t_bits; next_null_until(bp, 0, natts, &nextNullAttr, &nextNullSeqEnd); firstNonCacheOffsetAttr = Min(firstNonCacheOffsetAttr, nextNullAttr); - - /* - * While we're here, we can unset hasnulls if there's no NULL found. - * Remember that we might not be deforming the entire tuple here, so - * HeapTupleHasNulls() may just be true for some later attribute. - */ - hasnulls = (nextNullAttr < natts); } else { bp = NULL; - nextNullAttr = natts; + nextNullSeqEnd = nextNullAttr = natts; } #ifdef OPTIMIZE_BYVAL @@ -1129,15 +1122,21 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, off = *offp; } - /* Handle the remaining part of the tuple. */ - if (!hasnulls) + /* + * Handle the remaining part of the tuple. Rather than going to the + * trouble of calling att_isnull(), we instead do some processing on the + * bit mask to find the next NULL bit and how many follow that then + * process using two loops, the first of the inner loops here never sees a + * NULL attribute as the loop will end before we get to a NULL attr, the + * 2nd loop takes over and processes all the NULLs and we'll go back to + * the first loop and handle any remaining non-NULL attributes. + */ + for (;;) { - /* - * If there are no NULLs before natts, then use a simple loop without - * NULL handling. - */ - for (; attnum < natts; attnum++) + for (; attnum < nextNullAttr; attnum++) { + Assert(bp == NULL || !att_isnull(attnum, bp)); + cattr = TupleDescCompactAttr(tupleDesc, attnum); /* align the offset for this attribute */ @@ -1152,53 +1151,19 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, /* move the offset beyond this attribute */ off = att_addlength_pointer(off, cattr->attlen, tp + off); } - } - else - { - /* - * Otherwise, we need to handle NULLs. Rather than going to the - * trouble of calling att_isnull(), we instead do some processing on - * the bit mask to find the next NULL bit and how many follow that - * then process using two loops, the first of the inner loops here - * never sees a NULL attribute as the loop will end before we get to a - * NULL attr, the 2nd loop takes over and processes all the NULLs and - * we'll go back to the first loop and handle any remaining non-NULL - * attributes. - */ - for (;;) - { - for (; attnum < nextNullAttr; attnum++) - { - Assert(!att_isnull(attnum, bp)); - - cattr = TupleDescCompactAttr(tupleDesc, attnum); - - /* align the offset for this attribute */ - off = att_pointer_alignby(off, - cattr->attalignby, - cattr->attlen, - tp + off); - - values[attnum] = fetchatt(cattr, tp + off); - isnull[attnum] = false; - - /* move the offset beyond this attribute */ - off = att_addlength_pointer(off, cattr->attlen, tp + off); - } - if (likely(attnum == natts)) - break; - - /* Handle the NULLs */ - for (; unlikely(attnum < nextNullSeqEnd); attnum++) - { - Assert(att_isnull(attnum, bp)); - isnull[attnum] = true; - } + if (likely(attnum >= natts)) + break; - /* Locate the next NULL, if any */ - next_null_until(bp, attnum, natts, &nextNullAttr, &nextNullSeqEnd); + /* Handle the NULLs */ + for (; unlikely(attnum < nextNullSeqEnd); attnum++) + { + Assert(att_isnull(attnum, bp)); + isnull[attnum] = true; } + + /* Locate the next NULL, if any */ + next_null_until(bp, attnum, natts, &nextNullAttr, &nextNullSeqEnd); } /* -- 2.51.0