Add ExecCopySlotMinimalTupleExtra().
authorJeff Davis <[email protected]>
Tue, 25 Mar 2025 05:05:53 +0000 (22:05 -0700)
committerJeff Davis <[email protected]>
Tue, 25 Mar 2025 05:05:53 +0000 (22:05 -0700)
Allows an "extra" argument that allocates extra memory at the end of
the MinimalTuple. This is important for callers that need to store
additional data, but do not want to perform an additional allocation.

Suggested-by: David Rowley <[email protected]>
Discussion: https://postgr.es/m/CAApHDvppeqw2pNM-+ahBOJwq2QmC0hOAGsmCpC89QVmEoOvsdg@mail.gmail.com

src/backend/access/common/heaptuple.c
src/backend/executor/execTuples.c
src/backend/executor/nodeGatherMerge.c
src/backend/utils/sort/tuplesortvariants.c
src/backend/utils/sort/tuplestore.c
src/include/access/htup_details.h
src/include/executor/tuptable.h

index acd5da4ccf80ddde9c39a2ad8c96dfd84b83a07d..969d1028cae89d10c6909652dbb95bd288aa7d20 100644 (file)
@@ -1452,9 +1452,11 @@ heap_freetuple(HeapTuple htup)
 MinimalTuple
 heap_form_minimal_tuple(TupleDesc tupleDescriptor,
                        const Datum *values,
-                       const bool *isnull)
+                       const bool *isnull,
+                       Size extra)
 {
    MinimalTuple tuple;         /* return tuple */
+   char       *mem;
    Size        len,
                data_len;
    int         hoff;
@@ -1462,6 +1464,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
    int         numberOfAttributes = tupleDescriptor->natts;
    int         i;
 
+   Assert(extra == MAXALIGN(extra));
+
    if (numberOfAttributes > MaxTupleAttributeNumber)
        ereport(ERROR,
                (errcode(ERRCODE_TOO_MANY_COLUMNS),
@@ -1497,7 +1501,9 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
    /*
     * Allocate and zero the space needed.
     */
-   tuple = (MinimalTuple) palloc0(len);
+   mem = palloc0(len + extra);
+   memset(mem, 0, extra);
+   tuple = (MinimalTuple) (mem + extra);
 
    /*
     * And fill in the information.
@@ -1533,11 +1539,15 @@ heap_free_minimal_tuple(MinimalTuple mtup)
  * The result is allocated in the current memory context.
  */
 MinimalTuple
-heap_copy_minimal_tuple(MinimalTuple mtup)
+heap_copy_minimal_tuple(MinimalTuple mtup, Size extra)
 {
    MinimalTuple result;
+   char       *mem;
 
-   result = (MinimalTuple) palloc(mtup->t_len);
+   Assert(extra == MAXALIGN(extra));
+   mem = palloc(mtup->t_len + extra);
+   memset(mem, 0, extra);
+   result = (MinimalTuple) (mem + extra);
    memcpy(result, mtup, mtup->t_len);
    return result;
 }
@@ -1574,15 +1584,20 @@ heap_tuple_from_minimal_tuple(MinimalTuple mtup)
  * The result is allocated in the current memory context.
  */
 MinimalTuple
-minimal_tuple_from_heap_tuple(HeapTuple htup)
+minimal_tuple_from_heap_tuple(HeapTuple htup, Size extra)
 {
    MinimalTuple result;
+   char       *mem;
    uint32      len;
 
+   Assert(extra == MAXALIGN(extra));
    Assert(htup->t_len > MINIMAL_TUPLE_OFFSET);
    len = htup->t_len - MINIMAL_TUPLE_OFFSET;
-   result = (MinimalTuple) palloc(len);
+   mem = palloc(len + extra);
+   memset(mem, 0, extra);
+   result = (MinimalTuple) (mem + extra);
    memcpy(result, (char *) htup->t_data + MINIMAL_TUPLE_OFFSET, len);
+
    result->t_len = len;
    return result;
 }
index 7de490462d437f8c2ff313a1ae34459a62e6aa7b..8e02d68824fad80a2c39b926a37547c9e5bcc7ba 100644 (file)
@@ -298,13 +298,14 @@ tts_virtual_copy_heap_tuple(TupleTableSlot *slot)
 }
 
 static MinimalTuple
-tts_virtual_copy_minimal_tuple(TupleTableSlot *slot)
+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);
+                                  slot->tts_isnull,
+                                  extra);
 }
 
 
@@ -472,14 +473,14 @@ tts_heap_copy_heap_tuple(TupleTableSlot *slot)
 }
 
 static MinimalTuple
-tts_heap_copy_minimal_tuple(TupleTableSlot *slot)
+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);
+   return minimal_tuple_from_heap_tuple(hslot->tuple, extra);
 }
 
 static void
@@ -607,7 +608,8 @@ tts_minimal_materialize(TupleTableSlot *slot)
    {
        mslot->mintuple = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
                                                  slot->tts_values,
-                                                 slot->tts_isnull);
+                                                 slot->tts_isnull,
+                                                 0);
    }
    else
    {
@@ -617,7 +619,7 @@ tts_minimal_materialize(TupleTableSlot *slot)
         * TTS_FLAG_SHOULDFREE set).  Copy the minimal tuple into the given
         * slot's memory context.
         */
-       mslot->mintuple = heap_copy_minimal_tuple(mslot->mintuple);
+       mslot->mintuple = heap_copy_minimal_tuple(mslot->mintuple, 0);
    }
 
    slot->tts_flags |= TTS_FLAG_SHOULDFREE;
@@ -666,14 +668,14 @@ tts_minimal_copy_heap_tuple(TupleTableSlot *slot)
 }
 
 static MinimalTuple
-tts_minimal_copy_minimal_tuple(TupleTableSlot *slot)
+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);
+   return heap_copy_minimal_tuple(mslot->mintuple, extra);
 }
 
 static void
@@ -926,7 +928,7 @@ tts_buffer_heap_copy_heap_tuple(TupleTableSlot *slot)
 }
 
 static MinimalTuple
-tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot)
+tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
 {
    BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
 
@@ -935,7 +937,7 @@ tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot)
    if (!bslot->base.tuple)
        tts_buffer_heap_materialize(slot);
 
-   return minimal_tuple_from_heap_tuple(bslot->base.tuple);
+   return minimal_tuple_from_heap_tuple(bslot->base.tuple, extra);
 }
 
 static inline void
@@ -1895,7 +1897,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
    {
        if (shouldFree)
            *shouldFree = true;
-       return slot->tts_ops->copy_minimal_tuple(slot);
+       return slot->tts_ops->copy_minimal_tuple(slot, 0);
    }
 }
 
index 01a6e3a8553f0fdbc79a19cb20440053e3af1726..15f8459706773c8b98396ee5cf10410b218c3cf7 100644 (file)
@@ -735,7 +735,7 @@ gm_readnext_tuple(GatherMergeState *gm_state, int nreader, bool nowait,
     * Since we'll be buffering these across multiple calls, we need to make a
     * copy.
     */
-   return tup ? heap_copy_minimal_tuple(tup) : NULL;
+   return tup ? heap_copy_minimal_tuple(tup, 0) : NULL;
 }
 
 /*
index 4059af5bb7189b7a6fd795c579b34e8b760caeef..471d1197060d81c5ec0b33155defc121c2272f83 100644 (file)
@@ -1002,7 +1002,7 @@ tuplesort_gettupleslot(Tuplesortstate *state, bool forward, bool copy,
            *abbrev = stup.datum1;
 
        if (copy)
-           stup.tuple = heap_copy_minimal_tuple((MinimalTuple) stup.tuple);
+           stup.tuple = heap_copy_minimal_tuple((MinimalTuple) stup.tuple, 0);
 
        ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, copy);
        return true;
index d61b601053c907ecba4c48b7c699334806fac9c2..c9aecab8d66cbe06ff735f8174864237e8249eaf 100644 (file)
@@ -787,7 +787,7 @@ tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc,
    MinimalTuple tuple;
    MemoryContext oldcxt = MemoryContextSwitchTo(state->context);
 
-   tuple = heap_form_minimal_tuple(tdesc, values, isnull);
+   tuple = heap_form_minimal_tuple(tdesc, values, isnull, 0);
    USEMEM(state, GetMemoryChunkSpace(tuple));
 
    tuplestore_puttuple_common(state, tuple);
@@ -1139,7 +1139,7 @@ tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
    {
        if (copy && !should_free)
        {
-           tuple = heap_copy_minimal_tuple(tuple);
+           tuple = heap_copy_minimal_tuple(tuple, 0);
            should_free = true;
        }
        ExecStoreMinimalTuple(tuple, slot, should_free);
@@ -1590,7 +1590,7 @@ copytup_heap(Tuplestorestate *state, void *tup)
 {
    MinimalTuple tuple;
 
-   tuple = minimal_tuple_from_heap_tuple((HeapTuple) tup);
+   tuple = minimal_tuple_from_heap_tuple((HeapTuple) tup, 0);
    USEMEM(state, GetMemoryChunkSpace(tuple));
    return tuple;
 }
index 6cd4b95bfdb277fb5c3738074de63eeabf1218b4..aa957cf3b0165f7530695a812bbf237633c004c5 100644 (file)
@@ -839,11 +839,12 @@ extern void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
                              Datum *values, bool *isnull);
 extern void heap_freetuple(HeapTuple htup);
 extern MinimalTuple heap_form_minimal_tuple(TupleDesc tupleDescriptor,
-                                           const Datum *values, const bool *isnull);
+                                           const Datum *values, const bool *isnull,
+                                           Size extra);
 extern void heap_free_minimal_tuple(MinimalTuple mtup);
-extern MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup);
+extern MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup, Size extra);
 extern HeapTuple heap_tuple_from_minimal_tuple(MinimalTuple mtup);
-extern MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup);
+extern MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup, Size extra);
 extern size_t varsize_any(void *p);
 extern HeapTuple heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc);
 extern MinimalTuple minimal_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc);
index a044d78e4d05bb7a4721a0cf47bbacf82a447a9d..095e4cc82e3d8ff719f2bb903559c737f9b5185d 100644 (file)
@@ -218,8 +218,12 @@ struct TupleTableSlotOps
     * meaningful "system columns" in the copy. The copy is not be "owned" by
     * the slot i.e. the caller has to take responsibility to free memory
     * consumed by the slot.
+    *
+    * The copy has "extra" bytes (maxaligned and zeroed) available before the
+    * tuple, which is useful so that some callers may store extra data along
+    * with the minimal tuple without the need for an additional allocation.
     */
-   MinimalTuple (*copy_minimal_tuple) (TupleTableSlot *slot);
+   MinimalTuple (*copy_minimal_tuple) (TupleTableSlot *slot, Size extra);
 };
 
 /*
@@ -491,7 +495,19 @@ ExecCopySlotHeapTuple(TupleTableSlot *slot)
 static inline MinimalTuple
 ExecCopySlotMinimalTuple(TupleTableSlot *slot)
 {
-   return slot->tts_ops->copy_minimal_tuple(slot);
+   return slot->tts_ops->copy_minimal_tuple(slot, 0);
+}
+
+/*
+ * ExecCopySlotMinimalTupleExtra - return MinimalTuple allocated in caller's
+ * context, with extra bytes (maxaligned and zeroed) before the tuple for data
+ * the caller wishes to store along with the tuple (without requiring the
+ * caller to make an additional allocation).
+ */
+static inline MinimalTuple
+ExecCopySlotMinimalTupleExtra(TupleTableSlot *slot, Size extra)
+{
+   return slot->tts_ops->copy_minimal_tuple(slot, extra);
 }
 
 /*