diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index acd5da4ccf8..969d1028cae 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -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; } diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 7de490462d4..8e02d68824f 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -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); } } diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c index 01a6e3a8553..15f84597067 100644 --- a/src/backend/executor/nodeGatherMerge.c +++ b/src/backend/executor/nodeGatherMerge.c @@ -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; } /* diff --git a/src/backend/utils/sort/tuplesortvariants.c b/src/backend/utils/sort/tuplesortvariants.c index 4059af5bb71..471d1197060 100644 --- a/src/backend/utils/sort/tuplesortvariants.c +++ b/src/backend/utils/sort/tuplesortvariants.c @@ -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; diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c index d61b601053c..c9aecab8d66 100644 --- a/src/backend/utils/sort/tuplestore.c +++ b/src/backend/utils/sort/tuplestore.c @@ -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; } diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h index 6cd4b95bfdb..aa957cf3b01 100644 --- a/src/include/access/htup_details.h +++ b/src/include/access/htup_details.h @@ -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); diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index a044d78e4d0..095e4cc82e3 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -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); } /*