1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-20 00:42:27 +03:00

Add ExecCopySlotMinimalTupleExtra().

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 <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/CAApHDvppeqw2pNM-+ahBOJwq2QmC0hOAGsmCpC89QVmEoOvsdg@mail.gmail.com
This commit is contained in:
Jeff Davis 2025-03-24 22:05:53 -07:00
parent 4d143509cb
commit a0942f441e
7 changed files with 61 additions and 27 deletions

View File

@ -1452,9 +1452,11 @@ heap_freetuple(HeapTuple htup)
MinimalTuple MinimalTuple
heap_form_minimal_tuple(TupleDesc tupleDescriptor, heap_form_minimal_tuple(TupleDesc tupleDescriptor,
const Datum *values, const Datum *values,
const bool *isnull) const bool *isnull,
Size extra)
{ {
MinimalTuple tuple; /* return tuple */ MinimalTuple tuple; /* return tuple */
char *mem;
Size len, Size len,
data_len; data_len;
int hoff; int hoff;
@ -1462,6 +1464,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
int numberOfAttributes = tupleDescriptor->natts; int numberOfAttributes = tupleDescriptor->natts;
int i; int i;
Assert(extra == MAXALIGN(extra));
if (numberOfAttributes > MaxTupleAttributeNumber) if (numberOfAttributes > MaxTupleAttributeNumber)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_COLUMNS), (errcode(ERRCODE_TOO_MANY_COLUMNS),
@ -1497,7 +1501,9 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
/* /*
* Allocate and zero the space needed. * 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. * And fill in the information.
@ -1533,11 +1539,15 @@ heap_free_minimal_tuple(MinimalTuple mtup)
* The result is allocated in the current memory context. * The result is allocated in the current memory context.
*/ */
MinimalTuple MinimalTuple
heap_copy_minimal_tuple(MinimalTuple mtup) heap_copy_minimal_tuple(MinimalTuple mtup, Size extra)
{ {
MinimalTuple result; 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); memcpy(result, mtup, mtup->t_len);
return result; return result;
} }
@ -1574,15 +1584,20 @@ heap_tuple_from_minimal_tuple(MinimalTuple mtup)
* The result is allocated in the current memory context. * The result is allocated in the current memory context.
*/ */
MinimalTuple MinimalTuple
minimal_tuple_from_heap_tuple(HeapTuple htup) minimal_tuple_from_heap_tuple(HeapTuple htup, Size extra)
{ {
MinimalTuple result; MinimalTuple result;
char *mem;
uint32 len; uint32 len;
Assert(extra == MAXALIGN(extra));
Assert(htup->t_len > MINIMAL_TUPLE_OFFSET); Assert(htup->t_len > MINIMAL_TUPLE_OFFSET);
len = 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); memcpy(result, (char *) htup->t_data + MINIMAL_TUPLE_OFFSET, len);
result->t_len = len; result->t_len = len;
return result; return result;
} }

View File

@ -298,13 +298,14 @@ tts_virtual_copy_heap_tuple(TupleTableSlot *slot)
} }
static MinimalTuple static MinimalTuple
tts_virtual_copy_minimal_tuple(TupleTableSlot *slot) tts_virtual_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
{ {
Assert(!TTS_EMPTY(slot)); Assert(!TTS_EMPTY(slot));
return heap_form_minimal_tuple(slot->tts_tupleDescriptor, return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
slot->tts_values, slot->tts_values,
slot->tts_isnull); slot->tts_isnull,
extra);
} }
@ -472,14 +473,14 @@ tts_heap_copy_heap_tuple(TupleTableSlot *slot)
} }
static MinimalTuple static MinimalTuple
tts_heap_copy_minimal_tuple(TupleTableSlot *slot) tts_heap_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
{ {
HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot; HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
if (!hslot->tuple) if (!hslot->tuple)
tts_heap_materialize(slot); tts_heap_materialize(slot);
return minimal_tuple_from_heap_tuple(hslot->tuple); return minimal_tuple_from_heap_tuple(hslot->tuple, extra);
} }
static void static void
@ -607,7 +608,8 @@ tts_minimal_materialize(TupleTableSlot *slot)
{ {
mslot->mintuple = heap_form_minimal_tuple(slot->tts_tupleDescriptor, mslot->mintuple = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
slot->tts_values, slot->tts_values,
slot->tts_isnull); slot->tts_isnull,
0);
} }
else else
{ {
@ -617,7 +619,7 @@ tts_minimal_materialize(TupleTableSlot *slot)
* TTS_FLAG_SHOULDFREE set). Copy the minimal tuple into the given * TTS_FLAG_SHOULDFREE set). Copy the minimal tuple into the given
* slot's memory context. * 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; slot->tts_flags |= TTS_FLAG_SHOULDFREE;
@ -666,14 +668,14 @@ tts_minimal_copy_heap_tuple(TupleTableSlot *slot)
} }
static MinimalTuple static MinimalTuple
tts_minimal_copy_minimal_tuple(TupleTableSlot *slot) tts_minimal_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
{ {
MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot; MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
if (!mslot->mintuple) if (!mslot->mintuple)
tts_minimal_materialize(slot); tts_minimal_materialize(slot);
return heap_copy_minimal_tuple(mslot->mintuple); return heap_copy_minimal_tuple(mslot->mintuple, extra);
} }
static void static void
@ -926,7 +928,7 @@ tts_buffer_heap_copy_heap_tuple(TupleTableSlot *slot)
} }
static MinimalTuple static MinimalTuple
tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot) tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
{ {
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot; BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
@ -935,7 +937,7 @@ tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot)
if (!bslot->base.tuple) if (!bslot->base.tuple)
tts_buffer_heap_materialize(slot); 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 static inline void
@ -1895,7 +1897,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
{ {
if (shouldFree) if (shouldFree)
*shouldFree = true; *shouldFree = true;
return slot->tts_ops->copy_minimal_tuple(slot); return slot->tts_ops->copy_minimal_tuple(slot, 0);
} }
} }

View 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 * Since we'll be buffering these across multiple calls, we need to make a
* copy. * copy.
*/ */
return tup ? heap_copy_minimal_tuple(tup) : NULL; return tup ? heap_copy_minimal_tuple(tup, 0) : NULL;
} }
/* /*

View File

@ -1002,7 +1002,7 @@ tuplesort_gettupleslot(Tuplesortstate *state, bool forward, bool copy,
*abbrev = stup.datum1; *abbrev = stup.datum1;
if (copy) 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); ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, copy);
return true; return true;

View File

@ -787,7 +787,7 @@ tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc,
MinimalTuple tuple; MinimalTuple tuple;
MemoryContext oldcxt = MemoryContextSwitchTo(state->context); 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)); USEMEM(state, GetMemoryChunkSpace(tuple));
tuplestore_puttuple_common(state, tuple); tuplestore_puttuple_common(state, tuple);
@ -1139,7 +1139,7 @@ tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
{ {
if (copy && !should_free) if (copy && !should_free)
{ {
tuple = heap_copy_minimal_tuple(tuple); tuple = heap_copy_minimal_tuple(tuple, 0);
should_free = true; should_free = true;
} }
ExecStoreMinimalTuple(tuple, slot, should_free); ExecStoreMinimalTuple(tuple, slot, should_free);
@ -1590,7 +1590,7 @@ copytup_heap(Tuplestorestate *state, void *tup)
{ {
MinimalTuple tuple; MinimalTuple tuple;
tuple = minimal_tuple_from_heap_tuple((HeapTuple) tup); tuple = minimal_tuple_from_heap_tuple((HeapTuple) tup, 0);
USEMEM(state, GetMemoryChunkSpace(tuple)); USEMEM(state, GetMemoryChunkSpace(tuple));
return tuple; return tuple;
} }

View File

@ -839,11 +839,12 @@ extern void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
Datum *values, bool *isnull); Datum *values, bool *isnull);
extern void heap_freetuple(HeapTuple htup); extern void heap_freetuple(HeapTuple htup);
extern MinimalTuple heap_form_minimal_tuple(TupleDesc tupleDescriptor, 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 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 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 size_t varsize_any(void *p);
extern HeapTuple heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc); extern HeapTuple heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc);
extern MinimalTuple minimal_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc); extern MinimalTuple minimal_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc);

View File

@ -218,8 +218,12 @@ struct TupleTableSlotOps
* meaningful "system columns" in the copy. The copy is not be "owned" by * 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 * the slot i.e. the caller has to take responsibility to free memory
* consumed by the slot. * 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 static inline MinimalTuple
ExecCopySlotMinimalTuple(TupleTableSlot *slot) 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);
} }
/* /*