static TupleDesc ExecTypeFromTLInternal(List *targetList,
bool hasoid, bool skipjunk);
+static pg_attribute_always_inline void
+slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
+ int natts);
+static void tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, Buffer buffer);
const TupleTableSlotOps TTSOpsVirtual;
const TupleTableSlotOps TTSOpsHeapTuple;
const TupleTableSlotOps TTSOpsMinimalTuple;
-const TupleTableSlotOps TTSOpsBufferTuple;
+const TupleTableSlotOps TTSOpsBufferHeapTuple;
+
+
+/*
+ * TupleTableSlotOps implementations.
+ */
+
+/*
+ * TupleTableSlotOps implementation for VirtualTupleTableSlot.
+ */
+static void
+tts_virtual_init(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_virtual_release(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_virtual_clear(TupleTableSlot *slot)
+{
+ if (unlikely(TTS_SHOULDFREE(slot)))
+ {
+ VirtualTupleTableSlot *vslot = (VirtualTupleTableSlot *) slot;
+
+ pfree(vslot->data);
+ vslot->data = NULL;
+
+ slot->tts_flags = ~TTS_FLAG_SHOULDFREE;
+ }
+
+ slot->tts_nvalid = 0;
+ slot->tts_flags |= TTS_FLAG_EMPTY;
+}
+
+/*
+ * Attribute values are readily available in tts_values and tts_isnull array
+ * in a VirtualTupleTableSlot. So there should be no need to call either of the
+ * following two functions.
+ */
+static void
+tts_virtual_getsomeattrs(TupleTableSlot *slot, int natts)
+{
+ elog(ERROR, "getsomeattrs is not required to be called on a virtual tuple table slot");
+}
+
+static Datum
+tts_virtual_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ elog(ERROR, "virtual tuple table slot does not have system atttributes");
+}
+
+/*
+ * To materialize a virtual slot all the datums that aren't passed by value
+ * have to be copied into the slot's memory context. To do so, compute the
+ * required size, and allocate enough memory to store all attributes. That's
+ * good for cache hit ratio, but more imporantly requires only memory
+ * allocation/deallocation.
+ */
+static void
+tts_virtual_materialize(TupleTableSlot *slot)
+{
+ VirtualTupleTableSlot *vslot = (VirtualTupleTableSlot *) slot;
+ TupleDesc desc = slot->tts_tupleDescriptor;
+ Size sz = 0;
+ char *data;
+
+ /* already materialized */
+ if (TTS_SHOULDFREE(slot))
+ return;
+
+ /* compute size of memory required */
+ for (int natt = 0; natt < desc->natts; natt++)
+ {
+ Form_pg_attribute att = TupleDescAttr(desc, natt);
+ Datum val;
+
+ if (att->attbyval || slot->tts_isnull[natt])
+ continue;
+
+ val = slot->tts_values[natt];
+
+ if (att->attlen == -1 &&
+ VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
+ {
+ /*
+ * We want to flatten the expanded value so that the materialized
+ * slot doesn't depend on it.
+ */
+ sz = att_align_nominal(sz, att->attalign);
+ sz += EOH_get_flat_size(DatumGetEOHP(val));
+ }
+ else
+ {
+ sz = att_align_nominal(sz, att->attalign);
+ sz = att_addlength_datum(sz, att->attlen, val);
+ }
+ }
+
+ /* all data is byval */
+ if (sz == 0)
+ return;
+
+ /* allocate memory */
+ vslot->data = data = MemoryContextAlloc(slot->tts_mcxt, sz);
+ slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ /* and copy all attributes into the pre-allocated space */
+ for (int natt = 0; natt < desc->natts; natt++)
+ {
+ Form_pg_attribute att = TupleDescAttr(desc, natt);
+ Datum val;
+
+ if (att->attbyval || slot->tts_isnull[natt])
+ continue;
+
+ val = slot->tts_values[natt];
+
+ if (att->attlen == -1 &&
+ VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
+ {
+ Size data_length;
+
+ /*
+ * We want to flatten the expanded value so that the materialized
+ * slot doesn't depend on it.
+ */
+ ExpandedObjectHeader *eoh = DatumGetEOHP(val);
+
+ data = (char *) att_align_nominal(data,
+ att->attalign);
+ data_length = EOH_get_flat_size(eoh);
+ EOH_flatten_into(eoh, data, data_length);
+
+ slot->tts_values[natt] = PointerGetDatum(data);
+ data += data_length;
+ }
+ else
+ {
+ Size data_length = 0;
+
+ data = (char *) att_align_nominal(data, att->attalign);
+ data_length = att_addlength_datum(data_length, att->attlen, val);
+
+ memcpy(data, DatumGetPointer(val), data_length);
+
+ slot->tts_values[natt] = PointerGetDatum(data);
+ data += data_length;
+ }
+ }
+}
+
+static void
+tts_virtual_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ TupleDesc srcdesc = dstslot->tts_tupleDescriptor;
+
+ Assert(srcdesc->natts <= dstslot->tts_tupleDescriptor->natts);
+
+ tts_virtual_clear(dstslot);
+
+ slot_getallattrs(srcslot);
+
+ for (int natt = 0; natt < srcdesc->natts; natt++)
+ {
+ dstslot->tts_values[natt] = srcslot->tts_values[natt];
+ dstslot->tts_isnull[natt] = srcslot->tts_isnull[natt];
+ }
+
+ dstslot->tts_nvalid = srcdesc->natts;
+ dstslot->tts_flags &= ~TTS_FLAG_EMPTY;
+
+ /* make sure storage doesn't depend on external memory */
+ tts_virtual_materialize(dstslot);
+}
+
+static HeapTuple
+tts_virtual_copy_heap_tuple(TupleTableSlot *slot)
+{
+ Assert(!TTS_EMPTY(slot));
+
+ return heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+
+}
+
+static MinimalTuple
+tts_virtual_copy_minimal_tuple(TupleTableSlot *slot)
+{
+ Assert(!TTS_EMPTY(slot));
+
+ return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+}
+
+
+/*
+ * TupleTableSlotOps implementation for HeapTupleTableSlot.
+ */
+
+static void
+tts_heap_init(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_heap_release(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_heap_clear(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ /* Free the memory for the heap tuple if it's allowed. */
+ if (TTS_SHOULDFREE(slot))
+ {
+ heap_freetuple(hslot->tuple);
+ slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
+ }
+
+ slot->tts_nvalid = 0;
+ slot->tts_flags |= TTS_FLAG_EMPTY;
+ hslot->off = 0;
+ hslot->tuple = NULL;
+}
+
+static void
+tts_heap_getsomeattrs(TupleTableSlot *slot, int natts)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+
+ slot_deform_heap_tuple(slot, hslot->tuple, &hslot->off, natts);
+}
+
+static Datum
+tts_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ return heap_getsysattr(hslot->tuple, attnum,
+ slot->tts_tupleDescriptor, isnull);
+}
+
+static void
+tts_heap_materialize(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+ MemoryContext oldContext;
+
+ Assert(!TTS_EMPTY(slot));
+
+ /* This slot has it's tuple already materialized. Nothing to do. */
+ if (TTS_SHOULDFREE(slot))
+ return;
+
+ slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ if (!hslot->tuple)
+ hslot->tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ else
+ {
+ /*
+ * The tuple contained in this slot is not allocated in the memory
+ * context of the given slot (else it would have TTS_SHOULDFREE set).
+ * Copy the tuple into the given slot's memory context.
+ */
+ hslot->tuple = heap_copytuple(hslot->tuple);
+ }
+
+ /*
+ * Have to deform from scratch, otherwise tts_values[] entries could point
+ * into the non-materialized tuple (which might be gone when accessed).
+ */
+ slot->tts_nvalid = 0;
+ hslot->off = 0;
+
+ MemoryContextSwitchTo(oldContext);
+}
+
+static void
+tts_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ HeapTuple tuple;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
+ tuple = ExecCopySlotHeapTuple(srcslot);
+ MemoryContextSwitchTo(oldcontext);
+
+ ExecStoreHeapTuple(tuple, dstslot, true);
+}
+
+static HeapTuple
+tts_heap_get_heap_tuple(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+ if (!hslot->tuple)
+ tts_heap_materialize(slot);
+
+ return hslot->tuple;
+}
+
+static HeapTuple
+tts_heap_copy_heap_tuple(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+ if (!hslot->tuple)
+ tts_heap_materialize(slot);
+
+ return heap_copytuple(hslot->tuple);
+}
+
+static MinimalTuple
+tts_heap_copy_minimal_tuple(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ if (!hslot->tuple)
+ tts_heap_materialize(slot);
+
+ return minimal_tuple_from_heap_tuple(hslot->tuple);
+}
+
+static void
+tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ tts_heap_clear(slot);
+
+ slot->tts_nvalid = 0;
+ hslot->tuple = tuple;
+ hslot->off = 0;
+ slot->tts_flags &= ~TTS_FLAG_EMPTY;
+
+ if (shouldFree)
+ slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+}
+
+
+/*
+ * TupleTableSlotOps implementation for MinimalTupleTableSlot.
+ */
+
+static void
+tts_minimal_init(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ /*
+ * Initialize the heap tuple pointer to access attributes of the minimal
+ * tuple contained in the slot as if its a heap tuple.
+ */
+ mslot->tuple = &mslot->minhdr;
+}
+
+static void
+tts_minimal_release(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_minimal_clear(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ if (TTS_SHOULDFREE(slot))
+ {
+ heap_free_minimal_tuple(mslot->mintuple);
+ slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
+ }
+
+ slot->tts_nvalid = 0;
+ slot->tts_flags |= TTS_FLAG_EMPTY;
+ mslot->off = 0;
+ mslot->mintuple = NULL;
+}
+
+static void
+tts_minimal_getsomeattrs(TupleTableSlot *slot, int natts)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+
+ slot_deform_heap_tuple(slot, mslot->tuple, &mslot->off, natts);
+}
+
+static Datum
+tts_minimal_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ elog(ERROR, "minimal tuple table slot does not have system atttributes");
+}
+
+static void
+tts_minimal_materialize(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+ MemoryContext oldContext;
+
+ Assert(!TTS_EMPTY(slot));
+
+ /* This slot has it's tuple already materialized. Nothing to do. */
+ if (TTS_SHOULDFREE(slot))
+ return;
+
+ slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ if (!mslot->mintuple)
+ {
+ mslot->mintuple = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+ else
+ {
+ /*
+ * The minimal tuple contained in this slot is not allocated in the
+ * memory context of the given slot (else it would have TTS_SHOULDFREE
+ * set). Copy the minimal tuple into the given slot's memory context.
+ */
+ mslot->mintuple = heap_copy_minimal_tuple(mslot->mintuple);
+ }
+
+ Assert(mslot->tuple == &mslot->minhdr);
+
+ mslot->minhdr.t_len = mslot->mintuple->t_len + MINIMAL_TUPLE_OFFSET;
+ mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mslot->mintuple - MINIMAL_TUPLE_OFFSET);
+
+ MemoryContextSwitchTo(oldContext);
+
+ /*
+ * Have to deform from scratch, otherwise tts_values[] entries could point
+ * into the non-materialized tuple (which might be gone when accessed).
+ */
+ slot->tts_nvalid = 0;
+ mslot->off = 0;
+}
+
+static void
+tts_minimal_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ MemoryContext oldcontext;
+ MinimalTuple mintuple;
+
+ oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
+ mintuple = ExecCopySlotMinimalTuple(srcslot);
+ MemoryContextSwitchTo(oldcontext);
+
+ ExecStoreMinimalTuple(mintuple, dstslot, true);
+}
+
+static MinimalTuple
+tts_minimal_get_minimal_tuple(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ if (!mslot->mintuple)
+ tts_minimal_materialize(slot);
+
+ return mslot->mintuple;
+}
+
+static HeapTuple
+tts_minimal_copy_heap_tuple(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ if (!mslot->mintuple)
+ tts_minimal_materialize(slot);
+
+ return heap_tuple_from_minimal_tuple(mslot->mintuple);
+}
+
+static MinimalTuple
+tts_minimal_copy_minimal_tuple(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ if (!mslot->mintuple)
+ tts_minimal_materialize(slot);
+
+ return heap_copy_minimal_tuple(mslot->mintuple);
+}
+
+static void
+tts_minimal_store_tuple(TupleTableSlot *slot, MinimalTuple mtup, bool shouldFree)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ tts_minimal_clear(slot);
+
+ Assert(!TTS_SHOULDFREE(slot));
+ Assert(TTS_EMPTY(slot));
+
+ slot->tts_flags &= ~TTS_FLAG_EMPTY;
+ slot->tts_nvalid = 0;
+ mslot->off = 0;
+
+ mslot->mintuple = mtup;
+ Assert(mslot->tuple == &mslot->minhdr);
+ mslot->minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
+ mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
+ /* no need to set t_self or t_tableOid since we won't allow access */
+
+ if (shouldFree)
+ slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+ else
+ Assert(!TTS_SHOULDFREE(slot));
+}
+
+
+/*
+ * TupleTableSlotOps implementation for BufferHeapTupleTableSlot.
+ */
+
+static void
+tts_buffer_heap_init(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_buffer_heap_release(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_buffer_heap_clear(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ /*
+ * Free the memory for heap tuple if allowed. A tuple coming from buffer
+ * can never be freed. But we may have materialized a tuple from buffer.
+ * Such a tuple can be freed.
+ */
+ if (TTS_SHOULDFREE(slot))
+ {
+ /* We should have unpinned the buffer while materializing the tuple. */
+ Assert(!BufferIsValid(bslot->buffer));
+
+ heap_freetuple(bslot->base.tuple);
+ slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
+
+ Assert(!BufferIsValid(bslot->buffer));
+ }
+
+ if (BufferIsValid(bslot->buffer))
+ ReleaseBuffer(bslot->buffer);
+
+ slot->tts_nvalid = 0;
+ slot->tts_flags |= TTS_FLAG_EMPTY;
+ bslot->base.tuple = NULL;
+ bslot->base.off = 0;
+ bslot->buffer = InvalidBuffer;
+}
+
+static void
+tts_buffer_heap_getsomeattrs(TupleTableSlot *slot, int natts)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+
+ slot_deform_heap_tuple(slot, bslot->base.tuple, &bslot->base.off, natts);
+}
+
+static Datum
+tts_buffer_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ return heap_getsysattr(bslot->base.tuple, attnum,
+ slot->tts_tupleDescriptor, isnull);
+}
+
+static void
+tts_buffer_heap_materialize(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+ MemoryContext oldContext;
+
+ Assert(!TTS_EMPTY(slot));
+
+ /* If already materialized nothing to do. */
+ if (TTS_SHOULDFREE(slot))
+ return;
+
+ slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ /*
+ * A heap tuple stored in a BufferHeapTupleTableSlot should have a buffer
+ * associated with it, unless it's materialized (which would've returned
+ * above).
+ */
+ Assert(bslot->base.tuple);
+
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ bslot->base.tuple = heap_copytuple(bslot->base.tuple);
+ MemoryContextSwitchTo(oldContext);
+
+ /*
+ * A heap tuple stored in a BufferHeapTupleTableSlot should have a buffer
+ * associated with it, unless it's materialized.
+ */
+ Assert(BufferIsValid(bslot->buffer));
+ if (likely(BufferIsValid(bslot->buffer)))
+ ReleaseBuffer(bslot->buffer);
+ bslot->buffer = InvalidBuffer;
+
+ /*
+ * Have to deform from scratch, otherwise tts_values[] entries could point
+ * into the non-materialized tuple (which might be gone when accessed).
+ */
+ bslot->base.off = 0;
+ slot->tts_nvalid = 0;
+}
+
+static void
+tts_buffer_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ BufferHeapTupleTableSlot *bsrcslot = (BufferHeapTupleTableSlot *) srcslot;
+ BufferHeapTupleTableSlot *bdstslot = (BufferHeapTupleTableSlot *) dstslot;
+
+ /*
+ * If the source slot is of a different kind, or is a buffer slot that has
+ * been materialized, make a new copy of the tuple.
+ */
+ if (dstslot->tts_ops != srcslot->tts_ops ||
+ TTS_SHOULDFREE(srcslot))
+ {
+ MemoryContext oldContext;
+
+ ExecClearTuple(dstslot);
+ dstslot->tts_flags |= TTS_FLAG_SHOULDFREE;
+ dstslot->tts_flags &= ~TTS_FLAG_EMPTY;
+ oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt);
+ bdstslot->base.tuple = ExecCopySlotHeapTuple(srcslot);;
+ MemoryContextSwitchTo(oldContext);
+ }
+ else
+ {
+ tts_buffer_heap_store_tuple(dstslot, bsrcslot->base.tuple, bsrcslot->buffer);
+ /*
+ * Need to materialize because the HeapTupleData portion of the tuple
+ * might be in a foreign memory context. That's annoying, but until
+ * that's moved into the slot, unavoidable.
+ */
+ tts_buffer_heap_materialize(dstslot);
+ }
+}
+
+static HeapTuple
+tts_buffer_heap_get_heap_tuple(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+
+ if (!bslot->base.tuple)
+ tts_buffer_heap_materialize(slot);
+
+ return bslot->base.tuple;
+}
+
+static HeapTuple
+tts_buffer_heap_copy_heap_tuple(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+
+ if (!bslot->base.tuple)
+ tts_buffer_heap_materialize(slot);
+
+ return heap_copytuple(bslot->base.tuple);
+}
+
+static MinimalTuple
+tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+
+ if (!bslot->base.tuple)
+ tts_buffer_heap_materialize(slot);
+
+ return minimal_tuple_from_heap_tuple(bslot->base.tuple);
+}
+
+static void
+tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, Buffer buffer)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ if (TTS_SHOULDFREE(slot))
+ {
+ /* materialized slot shouldn't have a buffer to release */
+ Assert(!BufferIsValid(bslot->buffer));
+
+ heap_freetuple(bslot->base.tuple);
+ slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
+ }
+
+ slot->tts_flags &= ~TTS_FLAG_EMPTY;
+ slot->tts_nvalid = 0;
+ bslot->base.tuple = tuple;
+ bslot->base.off = 0;
+
+ /*
+ * If tuple is on a disk page, keep the page pinned as long as we hold a
+ * pointer into it. We assume the caller already has such a pin.
+ *
+ * This is coded to optimize the case where the slot previously held a
+ * tuple on the same disk page: in that case releasing and re-acquiring
+ * the pin is a waste of cycles. This is a common situation during
+ * seqscans, so it's worth troubling over.
+ */
+ if (bslot->buffer != buffer)
+ {
+ if (BufferIsValid(bslot->buffer))
+ ReleaseBuffer(bslot->buffer);
+ bslot->buffer = buffer;
+ IncrBufferRefCount(buffer);
+ }
+}
+
+/*
+ * 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).
+ *
+ * This is essentially an incremental version of heap_deform_tuple:
+ * on each call we extract attributes up to the one needed, without
+ * re-computing information about previously extracted attributes.
+ * slot->tts_nvalid is the number of attributes already extracted.
+ *
+ * This is marked as always inline, so the different offp for different types
+ * of slots gets optimized away.
+ */
+static pg_attribute_always_inline void
+slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
+ int natts)
+{
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ Datum *values = slot->tts_values;
+ bool *isnull = slot->tts_isnull;
+ HeapTupleHeader tup = tuple->t_data;
+ bool hasnulls = HeapTupleHasNulls(tuple);
+ int attnum;
+ char *tp; /* ptr to tuple data */
+ uint32 off; /* offset in tuple data */
+ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
+ 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);
+
+ /*
+ * Check whether the first call for this tuple, and initialize or restore
+ * loop state.
+ */
+ attnum = slot->tts_nvalid;
+ if (attnum == 0)
+ {
+ /* Start from the first attribute */
+ off = 0;
+ slow = false;
+ }
+ else
+ {
+ /* Restore state from previous execution */
+ off = *offp;
+ slow = TTS_SLOW(slot);
+ }
+
+ tp = (char *) tup + tup->t_hoff;
+
+ for (; attnum < natts; attnum++)
+ {
+ Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
+
+ if (hasnulls && att_isnull(attnum, bp))
+ {
+ values[attnum] = (Datum) 0;
+ isnull[attnum] = true;
+ slow = true; /* can't use attcacheoff anymore */
+ continue;
+ }
+
+ isnull[attnum] = false;
+
+ if (!slow && thisatt->attcacheoff >= 0)
+ off = thisatt->attcacheoff;
+ else if (thisatt->attlen == -1)
+ {
+ /*
+ * We can only cache the offset for a varlena attribute if the
+ * offset is already suitably aligned, so that there would be no
+ * pad bytes in any case: then the offset will be valid for either
+ * an aligned or unaligned value.
+ */
+ if (!slow &&
+ off == att_align_nominal(off, thisatt->attalign))
+ thisatt->attcacheoff = off;
+ else
+ {
+ off = att_align_pointer(off, thisatt->attalign, -1,
+ tp + off);
+ slow = true;
+ }
+ }
+ else
+ {
+ /* not varlena, so safe to use att_align_nominal */
+ off = att_align_nominal(off, thisatt->attalign);
+
+ if (!slow)
+ thisatt->attcacheoff = off;
+ }
+
+ values[attnum] = fetchatt(thisatt, tp + off);
+
+ off = att_addlength_pointer(off, thisatt->attlen, tp + off);
+
+ if (thisatt->attlen <= 0)
+ slow = true; /* can't use attcacheoff anymore */
+ }
+
+ /*
+ * 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;
+}
+
+
+const TupleTableSlotOps TTSOpsVirtual = {
+ .base_slot_size = sizeof(VirtualTupleTableSlot),
+ .init = tts_virtual_init,
+ .release = tts_virtual_release,
+ .clear = tts_virtual_clear,
+ .getsomeattrs = tts_virtual_getsomeattrs,
+ .getsysattr = tts_virtual_getsysattr,
+ .materialize = tts_virtual_materialize,
+ .copyslot = tts_virtual_copyslot,
+
+ /*
+ * A virtual tuple table slot can not "own" a heap tuple or a minimal
+ * tuple.
+ */
+ .get_heap_tuple = NULL,
+ .get_minimal_tuple = NULL,
+ .copy_heap_tuple = tts_virtual_copy_heap_tuple,
+ .copy_minimal_tuple = tts_virtual_copy_minimal_tuple
+};
+
+const TupleTableSlotOps TTSOpsHeapTuple = {
+ .base_slot_size = sizeof(HeapTupleTableSlot),
+ .init = tts_heap_init,
+ .release = tts_heap_release,
+ .clear = tts_heap_clear,
+ .getsomeattrs = tts_heap_getsomeattrs,
+ .getsysattr = tts_heap_getsysattr,
+ .materialize = tts_heap_materialize,
+ .copyslot = tts_heap_copyslot,
+ .get_heap_tuple = tts_heap_get_heap_tuple,
+
+ /* A heap tuple table slot can not "own" a minimal tuple. */
+ .get_minimal_tuple = NULL,
+ .copy_heap_tuple = tts_heap_copy_heap_tuple,
+ .copy_minimal_tuple = tts_heap_copy_minimal_tuple
+};
+
+const TupleTableSlotOps TTSOpsMinimalTuple = {
+ .base_slot_size = sizeof(MinimalTupleTableSlot),
+ .init = tts_minimal_init,
+ .release = tts_minimal_release,
+ .clear = tts_minimal_clear,
+ .getsomeattrs = tts_minimal_getsomeattrs,
+ .getsysattr = tts_minimal_getsysattr,
+ .materialize = tts_minimal_materialize,
+ .copyslot = tts_minimal_copyslot,
+
+ /* A minimal tuple table slot can not "own" a heap tuple. */
+ .get_heap_tuple = NULL,
+ .get_minimal_tuple = tts_minimal_get_minimal_tuple,
+ .copy_heap_tuple = tts_minimal_copy_heap_tuple,
+ .copy_minimal_tuple = tts_minimal_copy_minimal_tuple
+};
+
+const TupleTableSlotOps TTSOpsBufferHeapTuple = {
+ .base_slot_size = sizeof(BufferHeapTupleTableSlot),
+ .init = tts_buffer_heap_init,
+ .release = tts_buffer_heap_release,
+ .clear = tts_buffer_heap_clear,
+ .getsomeattrs = tts_buffer_heap_getsomeattrs,
+ .getsysattr = tts_buffer_heap_getsysattr,
+ .materialize = tts_buffer_heap_materialize,
+ .copyslot = tts_buffer_heap_copyslot,
+ .get_heap_tuple = tts_buffer_heap_get_heap_tuple,
+
+ /* A buffer heap tuple table slot can not "own" a minimal tuple. */
+ .get_minimal_tuple = NULL,
+ .copy_heap_tuple = tts_buffer_heap_copy_heap_tuple,
+ .copy_minimal_tuple = tts_buffer_heap_copy_minimal_tuple
+};
/* ----------------------------------------------------------------
/* --------------------------------
* MakeTupleTableSlot
*
- * Basic routine to make an empty TupleTableSlot. If tupleDesc is
- * specified the slot's descriptor is fixed for it's lifetime, gaining
- * some efficiency. If that's undesirable, pass NULL.
+ * Basic routine to make an empty TupleTableSlot of given
+ * TupleTableSlotType. If tupleDesc is specified the slot's descriptor is
+ * fixed for it's lifetime, gaining some efficiency. If that's
+ * undesirable, pass NULL.
* --------------------------------
*/
TupleTableSlot *
MakeTupleTableSlot(TupleDesc tupleDesc,
const TupleTableSlotOps *tts_ops)
{
- Size sz;
+ Size basesz, allocsz;
TupleTableSlot *slot;
+ basesz = tts_ops->base_slot_size;
/*
* When a fixed descriptor is specified, we can reduce overhead by
* allocating the entire slot in one go.
*/
if (tupleDesc)
- sz = MAXALIGN(sizeof(TupleTableSlot)) +
+ allocsz = MAXALIGN(basesz) +
MAXALIGN(tupleDesc->natts * sizeof(Datum)) +
MAXALIGN(tupleDesc->natts * sizeof(bool));
else
- sz = sizeof(TupleTableSlot);
+ allocsz = basesz;
- slot = palloc0(sz);
+ slot = palloc0(allocsz);
/* const for optimization purposes, OK to modify at allocation time */
*((const TupleTableSlotOps **) &slot->tts_ops) = tts_ops;
slot->type = T_TupleTableSlot;
slot->tts_flags |= TTS_FLAG_EMPTY;
if (tupleDesc != NULL)
slot->tts_flags |= TTS_FLAG_FIXED;
- slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = tupleDesc;
slot->tts_mcxt = CurrentMemoryContext;
- slot->tts_buffer = InvalidBuffer;
slot->tts_nvalid = 0;
- slot->tts_values = NULL;
- slot->tts_isnull = NULL;
- slot->tts_mintuple = NULL;
if (tupleDesc != NULL)
{
slot->tts_values = (Datum *)
(((char *) slot)
- + MAXALIGN(sizeof(TupleTableSlot)));
+ + MAXALIGN(basesz));
slot->tts_isnull = (bool *)
(((char *) slot)
- + MAXALIGN(sizeof(TupleTableSlot))
+ + MAXALIGN(basesz)
+ MAXALIGN(tupleDesc->natts * sizeof(Datum)));
PinTupleDesc(tupleDesc);
}
+ /*
+ * And allow slot type specific initialization.
+ */
+ slot->tts_ops->init(slot);
+
return slot;
}
/* Always release resources and reset the slot to empty */
ExecClearTuple(slot);
+ slot->tts_ops->release(slot);
if (slot->tts_tupleDescriptor)
{
ReleaseTupleDesc(slot->tts_tupleDescriptor);
/* This should match ExecResetTupleTable's processing of one slot */
Assert(IsA(slot, TupleTableSlot));
ExecClearTuple(slot);
+ slot->tts_ops->release(slot);
if (slot->tts_tupleDescriptor)
ReleaseTupleDesc(slot->tts_tupleDescriptor);
if (!TTS_FIXED(slot))
* slot in the tuple table.
*
* tuple: tuple to store
- * slot: slot to store it in
+ * slot: TTSOpsHeapTuple type slot to store it in
* shouldFree: true if ExecClearTuple should pfree() the tuple
* when done with it
*
* and let the upper-level table slot assume ownership of the copy!
*
* Return value is just the passed-in slot pointer.
+ *
+ * If the target slot is not guaranteed to be TTSOpsHeapTuple type slot, use
+ * the, more expensive, ExecForceStoreHeapTuple().
* --------------------------------
*/
TupleTableSlot *
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
- /*
- * Free any old physical tuple belonging to the slot.
- */
- if (TTS_SHOULDFREE(slot))
- {
- heap_freetuple(slot->tts_tuple);
- slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
- }
- if (TTS_SHOULDFREEMIN(slot))
- {
- heap_free_minimal_tuple(slot->tts_mintuple);
- slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
- }
-
- /*
- * Store the new tuple into the specified slot.
- */
- slot->tts_flags &= ~TTS_FLAG_EMPTY;
- if (shouldFree)
- slot->tts_flags |= TTS_FLAG_SHOULDFREE;
- slot->tts_tuple = tuple;
- slot->tts_mintuple = NULL;
-
- /* Mark extracted state invalid */
- slot->tts_nvalid = 0;
-
- /* Unpin any buffer pinned by the slot. */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = InvalidBuffer;
+ if (unlikely(!TTS_IS_HEAPTUPLE(slot)))
+ elog(ERROR, "trying to store a heap tuple into wrong type of slot");
+ tts_heap_store_tuple(slot, tuple, shouldFree);
return slot;
}
* into a specified slot in the tuple table.
*
* tuple: tuple to store
- * slot: slot to store it in
+ * slot: TTSOpsBufferHeapTuple type slot to store it in
* buffer: disk buffer if tuple is in a disk page, else InvalidBuffer
*
* The tuple table code acquires a pin on the buffer which is held until the
* slot is cleared, so that the tuple won't go away on us.
*
* Return value is just the passed-in slot pointer.
+ *
+ * If the target slot is not guaranteed to be TTSOpsBufferHeapTuple type slot,
+ * use the, more expensive, ExecForceStoreHeapTuple().
* --------------------------------
*/
TupleTableSlot *
* sanity checks
*/
Assert(tuple != NULL);
- Assert(slot != NULL);
- Assert(slot->tts_tupleDescriptor != NULL);
- Assert(BufferIsValid(buffer));
-
- /*
- * Free any old physical tuple belonging to the slot.
- */
- if (TTS_SHOULDFREE(slot))
- {
- heap_freetuple(slot->tts_tuple);
- slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
- }
- if (TTS_SHOULDFREEMIN(slot))
- {
- heap_free_minimal_tuple(slot->tts_mintuple);
- slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
- }
-
- /*
- * Store the new tuple into the specified slot.
- */
- slot->tts_flags &= ~TTS_FLAG_EMPTY;
- slot->tts_tuple = tuple;
- slot->tts_mintuple = NULL;
-
- /* Mark extracted state invalid */
- slot->tts_nvalid = 0;
-
- /*
- * Keep the disk page containing the given tuple pinned as long as we hold
- * a pointer into it. We assume the caller already has such a pin.
- *
- * This is coded to optimize the case where the slot previously held a
- * tuple on the same disk page: in that case releasing and re-acquiring the
- * pin is a waste of cycles. This is a common situation during seqscans,
- * so it's worth troubling over.
- */
- if (slot->tts_buffer != buffer)
- {
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = buffer;
- IncrBufferRefCount(buffer);
- }
+ Assert(slot != NULL);
+ Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(BufferIsValid(buffer));
+
+ if (unlikely(!TTS_IS_BUFFERTUPLE(slot)))
+ elog(ERROR, "trying to store an on-disk heap tuple into wrong type of slot");
+ tts_buffer_heap_store_tuple(slot, tuple, buffer);
return slot;
}
-/* --------------------------------
- * ExecStoreMinimalTuple
- *
- * Like ExecStoreHeapTuple, but insert a "minimal" tuple into the slot.
+/*
+ * Store a minimal tuple into TTSOpsMinimalTuple type slot.
*
- * No 'buffer' parameter since minimal tuples are never stored in relations.
- * --------------------------------
+ * If the target slot is not guaranteed to be TTSOpsMinimalTuple type slot,
+ * use the, more expensive, ExecForceStoreMinimalTuple().
*/
TupleTableSlot *
ExecStoreMinimalTuple(MinimalTuple mtup,
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
- /*
- * Free any old physical tuple belonging to the slot.
- */
- if (TTS_SHOULDFREE(slot))
- {
- heap_freetuple(slot->tts_tuple);
- slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
- }
- if (TTS_SHOULDFREEMIN(slot))
- {
- heap_free_minimal_tuple(slot->tts_mintuple);
- slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
- }
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
- /*
- * Store the new tuple into the specified slot.
- */
- slot->tts_flags &= ~TTS_FLAG_EMPTY;
- if (shouldFree)
- slot->tts_flags |= TTS_FLAG_SHOULDFREEMIN;
- slot->tts_tuple = &slot->tts_minhdr;
- slot->tts_mintuple = mtup;
-
- slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
- slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
- /* no need to set t_self or t_tableOid since we won't allow access */
-
- /* Mark extracted state invalid */
- slot->tts_nvalid = 0;
+ if (unlikely(!TTS_IS_MINIMALTUPLE(slot)))
+ elog(ERROR, "trying to store a minimal tuple into wrong type of slot");
+ tts_minimal_store_tuple(slot, mtup, shouldFree);
return slot;
}
-/* --------------------------------
- * ExecClearTuple
- *
- * This function is used to clear out a slot in the tuple table.
- *
- * NB: only the tuple is cleared, not the tuple descriptor (if any).
- * --------------------------------
+/*
+ * Store a HeapTuple into any kind of slot, performing conversion if
+ * necessary.
*/
-TupleTableSlot * /* return: slot passed */
-ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
+void
+ExecForceStoreHeapTuple(HeapTuple tuple,
+ TupleTableSlot *slot)
{
- /*
- * sanity checks
- */
- Assert(slot != NULL);
-
- /*
- * Free the old physical tuple if necessary.
- */
- if (TTS_SHOULDFREE(slot))
+ if (TTS_IS_HEAPTUPLE(slot))
{
- heap_freetuple(slot->tts_tuple);
- slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
+ ExecStoreHeapTuple(tuple, slot, false);
}
- if (TTS_SHOULDFREEMIN(slot))
+ else if (TTS_IS_BUFFERTUPLE(slot))
{
- heap_free_minimal_tuple(slot->tts_mintuple);
- slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN;
- }
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
+ MemoryContext oldContext;
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
+ ExecClearTuple(slot);
+ slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+ slot->tts_flags &= ~TTS_FLAG_EMPTY;
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ bslot->base.tuple = heap_copytuple(tuple);
+ MemoryContextSwitchTo(oldContext);
+ }
+ else
+ {
+ ExecClearTuple(slot);
+ heap_deform_tuple(tuple, slot->tts_tupleDescriptor,
+ slot->tts_values, slot->tts_isnull);
+ ExecStoreVirtualTuple(slot);
+ }
+}
- slot->tts_buffer = InvalidBuffer;
+/*
+ * Store a MinimalTuple into any kind of slot, performing conversion if
+ * necessary.
+ */
+void
+ExecForceStoreMinimalTuple(MinimalTuple mtup,
+ TupleTableSlot *slot,
+ bool shouldFree)
+{
+ if (TTS_IS_MINIMALTUPLE(slot))
+ {
+ tts_minimal_store_tuple(slot, mtup, shouldFree);
+ }
+ else
+ {
+ HeapTupleData htup;
- /*
- * Mark it empty.
- */
- slot->tts_flags |= TTS_FLAG_EMPTY;
- slot->tts_nvalid = 0;
+ ExecClearTuple(slot);
- return slot;
+ htup.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
+ htup.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
+ heap_deform_tuple(&htup, slot->tts_tupleDescriptor,
+ slot->tts_values, slot->tts_isnull);
+ ExecStoreVirtualTuple(slot);
+ }
}
/* --------------------------------
return ExecStoreVirtualTuple(slot);
}
-/* --------------------------------
- * ExecCopySlotTuple
- * Obtain a copy of a slot's regular physical tuple. The copy is
- * palloc'd in the current memory context.
- * The slot itself is undisturbed.
- *
- * This works even if the slot contains a virtual or minimal tuple;
- * however the "system columns" of the result will not be meaningful.
- * --------------------------------
- */
-HeapTuple
-ExecCopySlotTuple(TupleTableSlot *slot)
-{
- /*
- * sanity checks
- */
- Assert(slot != NULL);
- Assert(!TTS_EMPTY(slot));
-
- /*
- * If we have a physical tuple (either format) then just copy it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return heap_copytuple(slot->tts_tuple);
- if (slot->tts_mintuple)
- return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Otherwise we need to build a tuple from the Datum array.
- */
- return heap_form_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
-}
-
-/* --------------------------------
- * ExecCopySlotMinimalTuple
- * Obtain a copy of a slot's minimal physical tuple. The copy is
- * palloc'd in the current memory context.
- * The slot itself is undisturbed.
- * --------------------------------
- */
-MinimalTuple
-ExecCopySlotMinimalTuple(TupleTableSlot *slot)
-{
- /*
- * sanity checks
- */
- Assert(slot != NULL);
- Assert(!TTS_EMPTY(slot));
-
- /*
- * If we have a physical tuple then just copy it. Prefer to copy
- * tts_mintuple since that's a tad cheaper.
- */
- if (slot->tts_mintuple)
- return heap_copy_minimal_tuple(slot->tts_mintuple);
- if (slot->tts_tuple)
- {
- if (HeapTupleHeaderGetNatts(slot->tts_tuple->t_data)
- < slot->tts_tupleDescriptor->natts)
- return minimal_expand_tuple(slot->tts_tuple,
- slot->tts_tupleDescriptor);
- else
- return minimal_tuple_from_heap_tuple(slot->tts_tuple);
- }
-
- /*
- * Otherwise we need to build a tuple from the Datum array.
- */
- return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
-}
-
/*
* ExecFetchSlotHeapTuple - fetch HeapTuple representing the slot's content
*
Assert(slot != NULL);
Assert(!TTS_EMPTY(slot));
- /* will be used in the near future */
- if (shouldFree)
- *shouldFree = false;
+ /* Materialize the tuple so that the slot "owns" it, if requested. */
+ if (materialize)
+ slot->tts_ops->materialize(slot);
- /*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
+ if (slot->tts_ops->get_heap_tuple == NULL)
{
- if (HeapTupleHeaderGetNatts(slot->tts_tuple->t_data) <
- slot->tts_tupleDescriptor->natts)
- {
- HeapTuple tuple;
- MemoryContext oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
-
- tuple = heap_expand_tuple(slot->tts_tuple,
- slot->tts_tupleDescriptor);
- MemoryContextSwitchTo(oldContext);
- slot = ExecStoreHeapTuple(tuple, slot, true);
- }
- return slot->tts_tuple;
+ if (shouldFree)
+ *shouldFree = true;
+ return slot->tts_ops->copy_heap_tuple(slot);
+ }
+ else
+ {
+ if (shouldFree)
+ *shouldFree = false;
+ return slot->tts_ops->get_heap_tuple(slot);
}
-
- /*
- * Otherwise materialize the slot...
- */
- ExecMaterializeSlot(slot);
-
- return slot->tts_tuple;
}
/* --------------------------------
* ExecFetchSlotMinimalTuple
* Fetch the slot's minimal physical tuple.
*
- * If the slot contains a virtual tuple, we convert it to minimal
- * physical form. The slot retains ownership of the minimal tuple.
- * If it contains a regular tuple we convert to minimal form and store
- * that in addition to the regular tuple (not instead of, because
- * callers may hold pointers to Datums within the regular tuple).
- *
- * As above, the result must be treated as read-only.
+ * If the given tuple table slot can hold a minimal tuple, indicated by a
+ * non-NULL get_minimal_tuple callback, the function returns the minimal
+ * tuple returned by that callback. It assumes that the minimal tuple
+ * returned by the callback is "owned" by the slot i.e. the slot is
+ * responsible for freeing the memory consumed by the tuple. Hence it sets
+ * *shouldFree to false, indicating that the caller should not free the
+ * memory consumed by the minimal tuple. In this case the returned minimal
+ * tuple should be considered as read-only.
+ *
+ * If that callback is not supported, it calls copy_minimal_tuple callback
+ * which is expected to return a copy of minimal tuple represnting the
+ * contents of the slot. In this case *shouldFree is set to true,
+ * indicating the caller that it should free the memory consumed by the
+ * minimal tuple. In this case the returned minimal tuple may be written
+ * up.
* --------------------------------
*/
MinimalTuple
-ExecFetchSlotMinimalTuple(TupleTableSlot *slot, bool *shouldFree)
+ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
+ bool *shouldFree)
{
- MemoryContext oldContext;
-
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(!TTS_EMPTY(slot));
- /* will be used in the near future */
- if (shouldFree)
- *shouldFree = false;
-
- /*
- * If we have a minimal physical tuple (local or not) then just return it.
- */
- if (slot->tts_mintuple)
- return slot->tts_mintuple;
-
- /*
- * Otherwise, copy or build a minimal tuple, and store it into the slot.
- *
- * We may be called in a context that is shorter-lived than the tuple
- * slot, but we have to ensure that the materialized tuple will survive
- * anyway.
- */
- oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_mintuple = ExecCopySlotMinimalTuple(slot);
- slot->tts_flags |= TTS_FLAG_SHOULDFREEMIN;
- MemoryContextSwitchTo(oldContext);
-
- /*
- * Note: we may now have a situation where we have a local minimal tuple
- * attached to a virtual or non-local physical tuple. There seems no harm
- * in that at the moment, but if any materializes, we should change this
- * function to force the slot into minimal-tuple-only state.
- */
-
- return slot->tts_mintuple;
+ if (slot->tts_ops->get_minimal_tuple)
+ {
+ if (shouldFree)
+ *shouldFree = false;
+ return slot->tts_ops->get_minimal_tuple(slot);
+ }
+ else
+ {
+ if (shouldFree)
+ *shouldFree = true;
+ return slot->tts_ops->copy_minimal_tuple(slot);
+ }
}
/* --------------------------------
return ret;
}
-/* ExecMaterializeSlot - force a slot into the "materialized" state.
- *
- * This causes the slot's tuple to be a local copy not dependent on any
- * external storage (i.e. pointing into a Buffer, or having allocations in
- * another memory context).
- *
- * A typical use for this operation is to prepare a computed tuple for being
- * stored on disk. The original data may or may not be virtual, but in any
- * case we need a private copy for heap_insert to scribble on.
- */
-void
-ExecMaterializeSlot(TupleTableSlot *slot)
-{
- MemoryContext oldContext;
-
- /*
- * sanity checks
- */
- Assert(slot != NULL);
- Assert(!TTS_EMPTY(slot));
-
- /*
- * If we have a regular physical tuple, and it's locally palloc'd, we have
- * nothing to do.
- */
- if (slot->tts_tuple && TTS_SHOULDFREE(slot))
- return;
-
- /*
- * Otherwise, copy or build a physical tuple, and store it into the slot.
- *
- * We may be called in a context that is shorter-lived than the tuple
- * slot, but we have to ensure that the materialized tuple will survive
- * anyway.
- */
- oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_tuple = ExecCopySlotTuple(slot);
- slot->tts_flags |= TTS_FLAG_SHOULDFREE;
- MemoryContextSwitchTo(oldContext);
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
- /*
- * Mark extracted state invalid. This is important because the slot is
- * not supposed to depend any more on the previous external data; we
- * mustn't leave any dangling pass-by-reference datums in tts_values.
- * However, we have not actually invalidated any such datums, if there
- * happen to be any previously fetched from the slot. (Note in particular
- * that we have not pfree'd tts_mintuple, if there is one.)
- */
- slot->tts_nvalid = 0;
-
- /*
- * On the same principle of not depending on previous remote storage,
- * forget the mintuple if it's not local storage. (If it is local
- * storage, we must not pfree it now, since callers might have already
- * fetched datum pointers referencing it.)
- */
- if (!TTS_SHOULDFREEMIN(slot))
- slot->tts_mintuple = NULL;
-}
-
-/* --------------------------------
- * ExecCopySlot
- * Copy the source slot's contents into the destination slot.
- *
- * The destination acquires a private copy that will not go away
- * if the source is cleared.
- *
- * The caller must ensure the slots have compatible tupdescs.
- * --------------------------------
- */
-TupleTableSlot *
-ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
-{
- HeapTuple newTuple;
- MemoryContext oldContext;
-
- /*
- * There might be ways to optimize this when the source is virtual, but
- * for now just always build a physical copy. Make sure it is in the
- * right context.
- */
- oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt);
- newTuple = ExecCopySlotTuple(srcslot);
- MemoryContextSwitchTo(oldContext);
-
- return ExecStoreHeapTuple(newTuple, dstslot, true);
-}
-
-
/* ----------------------------------------------------------------
* convenience initialization routines
* ----------------------------------------------------------------
slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum)
{
AttrMissing *attrmiss = NULL;
- int missattnum;
if (slot->tts_tupleDescriptor->constr)
attrmiss = slot->tts_tupleDescriptor->constr->missing;
}
else
{
+ int missattnum;
+
/* if there is a missing values array we must process them one by one */
for (missattnum = startAttNum;
missattnum < lastAttNum;
slot->tts_values[missattnum] = attrmiss[missattnum].am_value;
slot->tts_isnull[missattnum] = !attrmiss[missattnum].am_present;
}
- }
-}
-
-/*
- * slot_getattr
- * This function fetches an attribute of the slot's current tuple.
- * It is functionally equivalent to heap_getattr, but fetches of
- * multiple attributes of the same tuple will be optimized better,
- * because we avoid O(N^2) behavior from multiple calls of
- * nocachegetattr(), even when attcacheoff isn't usable.
- *
- * A difference from raw heap_getattr is that attnums beyond the
- * slot's tupdesc's last attribute will be considered NULL even
- * when the physical tuple is longer than the tupdesc.
- */
-Datum
-slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- HeapTupleHeader tup;
-
- /*
- * system attributes are handled by heap_getsysattr
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- {
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
- }
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * return NULL or missing value if attnum is out of range according to the
- * tuple
- *
- * (We have to check this separately because of various inheritance and
- * table-alteration scenarios: the tuple could be either longer or shorter
- * than the tupdesc.)
- */
- tup = tuple->t_data;
- if (attnum > HeapTupleHeaderGetNatts(tup))
- return getmissingattr(slot->tts_tupleDescriptor, attnum, isnull);
- /*
- * check if target attribute is null: no point in groveling through tuple
- */
- if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
- {
- *isnull = true;
- return (Datum) 0;
}
-
- /*
- * Extract the attribute, along with any preceding attributes.
- */
- slot_deform_tuple(slot, attnum);
-
- /*
- * The result is acquired from tts_values array.
- */
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
}
/*
void
slot_getsomeattrs_int(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple;
- int attno;
-
/* Check for caller errors */
Assert(slot->tts_nvalid < attnum); /* slot_getsomeattr checked */
Assert(attnum > 0);
if (unlikely(attnum > slot->tts_tupleDescriptor->natts))
elog(ERROR, "invalid attribute number %d", attnum);
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
/* Fetch as many attributes as possible from the underlying tuple. */
- attno = HeapTupleHeaderGetNatts(tuple->t_data);
- attno = Min(attno, attnum);
-
- slot_deform_tuple(slot, attno);
-
- attno = slot->tts_nvalid;
+ slot->tts_ops->getsomeattrs(slot, attnum);
/*
* If the underlying tuple doesn't have enough attributes, tuple descriptor
/*----------
* The executor stores tuples in a "tuple table" which is a List of
- * independent TupleTableSlots. There are several cases we need to handle:
- * 1. physical tuple in a disk buffer page
- * 2. physical tuple constructed in palloc'ed memory
- * 3. "minimal" physical tuple constructed in palloc'ed memory
- * 4. "virtual" tuple consisting of Datum/isnull arrays
+ * independent TupleTableSlots.
+ *
+ * There's various different types of tuple table slots, each being able to
+ * store different types of tuples. Additional types of slots can be added
+ * without modifying core code. The type of a slot is determined by the
+ * TupleTableSlotOps* passed to the slot creation routine. The builtin types
+ * of slots are
+ *
+ * 1. physical tuple in a disk buffer page (TTSOpsBufferHeapTuple)
+ * 2. physical tuple constructed in palloc'ed memory (TTSOpsHeapTuple)
+ * 3. "minimal" physical tuple constructed in palloc'ed memory
+ * (TTSOpsMinimalTuple)
+ * 4. "virtual" tuple consisting of Datum/isnull arrays (TTSOpsVirtual)
+ *
*
* The first two cases are similar in that they both deal with "materialized"
* tuples, but resource management is different. For a tuple in a disk page
* parallel to case 1. Note that a minimal tuple has no "system columns".
* (Actually, it could have an OID, but we have no need to access the OID.)
*
- * A "virtual" tuple is an optimization used to minimize physical data
- * copying in a nest of plan nodes. Any pass-by-reference Datums in the
- * tuple point to storage that is not directly associated with the
- * TupleTableSlot; generally they will point to part of a tuple stored in
- * a lower plan node's output TupleTableSlot, or to a function result
+ * A "virtual" tuple is an optimization used to minimize physical data copying
+ * in a nest of plan nodes. Until materialized pass-by-reference Datums in
+ * the slot point to storage that is not directly associated with the
+ * TupleTableSlot; generally they will point to part of a tuple stored in a
+ * lower plan node's output TupleTableSlot, or to a function result
* constructed in a plan node's per-tuple econtext. It is the responsibility
- * of the generating plan node to be sure these resources are not released
- * for as long as the virtual tuple needs to be valid. We only use virtual
- * tuples in the result slots of plan nodes --- tuples to be copied anywhere
- * else need to be "materialized" into physical tuples. Note also that a
- * virtual tuple does not have any "system columns".
+ * of the generating plan node to be sure these resources are not released for
+ * as long as the virtual tuple needs to be valid or is materialized. Note
+ * also that a virtual tuple does not have any "system columns".
*
- * It is also possible for a TupleTableSlot to hold both physical and minimal
- * copies of a tuple. This is done when the slot is requested to provide
- * the format other than the one it currently holds. (Originally we attempted
- * to handle such requests by replacing one format with the other, but that
- * had the fatal defect of invalidating any pass-by-reference Datums pointing
- * into the existing slot contents.) Both copies must contain identical data
- * payloads when this is the case.
+ * The Datum/isnull arrays of a TupleTableSlot serve double duty. For virtual
+ * slots they they are the authoritative data. For the other builtin slots,
+ * the arrays contain data extracted from the tuple. (In this state, any
+ * pass-by-reference Datums point into the physical tuple.) The extracted
+ * information is built "lazily", ie, only as needed. This serves to avoid
+ * repeated extraction of data from the physical tuple.
*
- * The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
- * slot contains a virtual tuple, they are the authoritative data. When the
- * slot contains a physical tuple, the arrays contain data extracted from
- * the tuple. (In this state, any pass-by-reference Datums point into
- * the physical tuple.) The extracted information is built "lazily",
- * ie, only as needed. This serves to avoid repeated extraction of data
- * from the physical tuple.
- *
- * A TupleTableSlot can also be "empty", indicated by flag TTS_EMPTY set in
- * tts_flags, holding no valid data. This is the only valid state for a
- * freshly-created slot that has not yet had a tuple descriptor assigned to it.
- * In this state, TTS_SHOULDFREE should not be set in tts_flag, tts_tuple must
- * be NULL, tts_buffer InvalidBuffer, and tts_nvalid zero.
+ * A TupleTableSlot can also be "empty", indicated by flag TTS_FLAG_EMPTY set
+ * in tts_flags, holding no valid data. This is the only valid state for a
+ * freshly-created slot that has not yet had a tuple descriptor assigned to
+ * it. In this state, TTS_SHOULDFREE should not be set in tts_flag, tts_tuple
+ * must be NULL, tts_buffer InvalidBuffer, and tts_nvalid zero.
*
* The tupleDescriptor is simply referenced, not copied, by the TupleTableSlot
* code. The caller of ExecSetSlotDescriptor() is responsible for providing
* the slot and should be freed when the slot's reference to the tuple is
* dropped.
*
- * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
- * on the indicated buffer page; drop the pin when we release the
- * slot's reference to that buffer. (tts_shouldFree should always be
- * false in such a case, since presumably tts_tuple is pointing at the
- * buffer page.)
- *
- * tts_nvalid indicates the number of valid columns in the tts_values/isnull
- * arrays. When the slot is holding a "virtual" tuple this must be equal
- * to the descriptor's natts. When the slot is holding a physical tuple
- * this is equal to the number of columns we have extracted (we always
- * extract columns from left to right, so there are no holes).
- *
- * tts_values/tts_isnull are allocated when a descriptor is assigned to the
- * slot; they are of length equal to the descriptor's natts.
+ * tts_values/tts_isnull are allocated either when the slot is created (when
+ * the descriptor is provided), or when a descriptor is assigned to the slot;
+ * they are of length equal to the descriptor's natts.
*
- * tts_mintuple must always be NULL if the slot does not hold a "minimal"
- * tuple. When it does, tts_mintuple points to the actual MinimalTupleData
- * object (the thing to be pfree'd if tts_shouldFreeMin is true). If the slot
- * has only a minimal and not also a regular physical tuple, then tts_tuple
- * points at tts_minhdr and the fields of that struct are set correctly
- * for access to the minimal tuple; in particular, tts_minhdr.t_data points
- * MINIMAL_TUPLE_OFFSET bytes before tts_mintuple. This allows column
- * extraction to treat the case identically to regular physical tuples.
- *
- * TTS_SLOW flag in tts_flags and tts_off are saved state for
- * slot_deform_tuple, and should not be touched by any other code.
+ * The TTS_FLAG_SLOW flag and tts_off are saved state for
+ * slot_deform_heap_tuple, and should not be touched by any other code.
*----------
*/
#define TTS_FLAG_EMPTY (1 << 1)
#define TTS_EMPTY(slot) (((slot)->tts_flags & TTS_FLAG_EMPTY) != 0)
-/* should pfree tts_tuple? */
+/* should pfree tuple "owned" by the slot? */
#define TTS_FLAG_SHOULDFREE (1 << 2)
#define TTS_SHOULDFREE(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREE) != 0)
-/* should pfree tts_mintuple? */
-#define TTS_FLAG_SHOULDFREEMIN (1 << 3)
-#define TTS_SHOULDFREEMIN(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREEMIN) != 0)
-
-/* saved state for slot_deform_tuple */
-#define TTS_FLAG_SLOW (1 << 4)
+/* saved state for slot_deform_heap_tuple */
+#define TTS_FLAG_SLOW (1 << 3)
#define TTS_SLOW(slot) (((slot)->tts_flags & TTS_FLAG_SLOW) != 0)
/* fixed tuple descriptor */
-#define TTS_FLAG_FIXED (1 << 5)
+#define TTS_FLAG_FIXED (1 << 4)
#define TTS_FIXED(slot) (((slot)->tts_flags & TTS_FLAG_FIXED) != 0)
struct TupleTableSlotOps;
typedef struct TupleTableSlotOps TupleTableSlotOps;
+/* base tuple table slot type */
typedef struct TupleTableSlot
{
NodeTag type;
uint16 tts_flags; /* Boolean states */
#define FIELDNO_TUPLETABLESLOT_NVALID 2
AttrNumber tts_nvalid; /* # of valid values in tts_values */
-#define FIELDNO_TUPLETABLESLOT_TUPLE 3
- HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
const TupleTableSlotOps *const tts_ops; /* implementation of slot */
-#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 5
+#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
- MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
-#define FIELDNO_TUPLETABLESLOT_OFF 8
- uint32 tts_off; /* saved state for slot_deform_tuple */
-#define FIELDNO_TUPLETABLESLOT_VALUES 9
+#define FIELDNO_TUPLETABLESLOT_VALUES 5
Datum *tts_values; /* current per-attribute values */
-#define FIELDNO_TUPLETABLESLOT_ISNULL 10
+#define FIELDNO_TUPLETABLESLOT_ISNULL 6
bool *tts_isnull; /* current per-attribute isnull flags */
- MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
- HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
+ MemoryContext tts_mcxt; /* slot itself is in this context */
} TupleTableSlot;
/* routines for a TupleTableSlot implementation */
struct TupleTableSlotOps
{
- /* body will be replaced in later commit */
- int dummy;
+ /* Minimum size of the slot */
+ size_t base_slot_size;
+
+ /* Initialization. */
+ void (*init)(TupleTableSlot *slot);
+
+ /* Destruction. */
+ void (*release)(TupleTableSlot *slot);
+
+ /*
+ * Clear the contents of the slot. Only the contents are expected to be
+ * cleared and not the tuple descriptor. Typically an implementation of
+ * this callback should free the memory allocated for the tuple contained
+ * in the slot.
+ */
+ void (*clear)(TupleTableSlot *slot);
+
+ /*
+ * 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.
+ */
+ void (*getsomeattrs)(TupleTableSlot *slot, int natts);
+
+ /*
+ * Returns value of the given system attribute as a datum and sets isnull
+ * to false, if it's not NULL. Throws an error if the slot type does not
+ * support system attributes.
+ */
+ Datum (*getsysattr)(TupleTableSlot *slot, int attnum, bool *isnull);
+
+ /*
+ * Make the contents of the slot solely depend on the slot, and not on
+ * underlying resources (like another memory context, buffers, etc).
+ */
+ void (*materialize)(TupleTableSlot *slot);
+
+ /*
+ * Copy the contents of the source slot into the destination slot's own
+ * context. Invoked using callback of the destination slot.
+ */
+ void (*copyslot) (TupleTableSlot *dstslot, TupleTableSlot *srcslot);
+
+ /*
+ * Return a heap tuple "owned" by the slot. It is slot's responsibility to
+ * free the memory consumed by the heap tuple. If the slot can not "own" a
+ * heap tuple, it should not implement this callback and should set it as
+ * NULL.
+ */
+ HeapTuple (*get_heap_tuple)(TupleTableSlot *slot);
+
+ /*
+ * Return a minimal tuple "owned" by the slot. It is slot's responsibility
+ * to free the memory consumed by the minimal tuple. If the slot can not
+ * "own" a minimal tuple, it should not implement this callback and should
+ * set it as NULL.
+ */
+ MinimalTuple (*get_minimal_tuple)(TupleTableSlot *slot);
+
+ /*
+ * Return a copy of heap tuple representing the contents of the slot. The
+ * copy needs to be palloc'd in the current memory context. The slot
+ * itself is expected to remain unaffected. It is *not* expected to have
+ * meaningful "system columns" in the copy. The copy is not be "owned" by
+ * the slot i.e. the caller has to take responsibilty to free memory
+ * consumed by the slot.
+ */
+ HeapTuple (*copy_heap_tuple)(TupleTableSlot *slot);
+
+ /*
+ * Return a copy of minimal tuple representing the contents of the slot. The
+ * copy needs to be palloc'd in the current memory context. The slot
+ * itself is expected to remain unaffected. It is *not* expected to have
+ * meaningful "system columns" in the copy. The copy is not be "owned" by
+ * the slot i.e. the caller has to take responsibilty to free memory
+ * consumed by the slot.
+ */
+ MinimalTuple (*copy_minimal_tuple)(TupleTableSlot *slot);
};
/*
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsVirtual;
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsHeapTuple;
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsMinimalTuple;
-extern PGDLLIMPORT const TupleTableSlotOps TTSOpsBufferTuple;
+extern PGDLLIMPORT const TupleTableSlotOps TTSOpsBufferHeapTuple;
+
+#define TTS_IS_VIRTUAL(slot) ((slot)->tts_ops == &TTSOpsVirtual)
+#define TTS_IS_HEAPTUPLE(slot) ((slot)->tts_ops == &TTSOpsHeapTuple)
+#define TTS_IS_MINIMALTUPLE(slot) ((slot)->tts_ops == &TTSOpsMinimalTuple)
+#define TTS_IS_BUFFERTUPLE(slot) ((slot)->tts_ops == &TTSOpsBufferHeapTuple)
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
+
+/*
+ * Tuple table slot implementations.
+ */
+
+typedef struct VirtualTupleTableSlot
+{
+ TupleTableSlot base;
+
+ char *data; /* data for materialized slots */
+} VirtualTupleTableSlot;
+
+typedef struct HeapTupleTableSlot
+{
+ TupleTableSlot base;
+
+#define FIELDNO_HEAPTUPLETABLESLOT_TUPLE 1
+ HeapTuple tuple; /* physical tuple */
+#define FIELDNO_HEAPTUPLETABLESLOT_OFF 2
+ uint32 off; /* saved state for slot_deform_heap_tuple */
+} HeapTupleTableSlot;
+
+/* heap tuple residing in a buffer */
+typedef struct BufferHeapTupleTableSlot
+{
+ HeapTupleTableSlot base;
+
+ /*
+ * If buffer is not InvalidBuffer, then the slot is holding a pin on the
+ * indicated buffer page; drop the pin when we release the slot's
+ * reference to that buffer. (TTS_FLAG_SHOULDFREE should not be set be
+ * false in such a case, since presumably tts_tuple is pointing at the
+ * buffer page.)
+ */
+ Buffer buffer; /* tuple's buffer, or InvalidBuffer */
+} BufferHeapTupleTableSlot;
+
+typedef struct MinimalTupleTableSlot
+{
+ TupleTableSlot base;
+
+ /*
+ * In a minimal slot tuple points at minhdr and the fields of that struct
+ * are set correctly for access to the minimal tuple; in particular,
+ * minhdr.t_data points MINIMAL_TUPLE_OFFSET bytes before mintuple. This
+ * allows column extraction to treat the case identically to regular
+ * physical tuples.
+ */
+#define FIELDNO_MINIMALTUPLETABLESLOT_TUPLE 1
+ HeapTuple tuple; /* tuple wrapper */
+ MinimalTuple mintuple; /* minimal tuple, or NULL if none */
+ HeapTupleData minhdr; /* workspace for minimal-tuple-only case */
+#define FIELDNO_MINIMALTUPLETABLESLOT_OFF 4
+ uint32 off; /* saved state for slot_deform_heap_tuple */
+} MinimalTupleTableSlot;
/*
* TupIsNull -- is a TupleTableSlot empty?
extern TupleTableSlot *ExecStoreHeapTuple(HeapTuple tuple,
TupleTableSlot *slot,
bool shouldFree);
+extern void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreBufferHeapTuple(HeapTuple tuple,
TupleTableSlot *slot,
Buffer buffer);
extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
TupleTableSlot *slot,
bool shouldFree);
-extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
+extern void ExecForceStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot,
+ bool shouldFree);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
-extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
-extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shoulFree);
+extern HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
bool *shouldFree);
extern Datum ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot);
-extern void ExecMaterializeSlot(TupleTableSlot *slot);
-extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
- TupleTableSlot *srcslot);
extern void slot_getmissingattrs(TupleTableSlot *slot, int startAttNum,
int lastAttNum);
-extern Datum slot_getattr(TupleTableSlot *slot, int attnum,
- bool *isnull);
-
-/* in access/common/heaptuple.c */
-extern bool slot_attisnull(TupleTableSlot *slot, int attnum);
-extern bool slot_getsysattr(TupleTableSlot *slot, int attnum,
- Datum *value, bool *isnull);
-extern Datum getmissingattr(TupleDesc tupleDesc,
- int attnum, bool *isnull);
extern void slot_getsomeattrs_int(TupleTableSlot *slot, int attnum);
+
#ifndef FRONTEND
/*
slot_getsomeattrs(slot, slot->tts_tupleDescriptor->natts);
}
-#endif
+
+/*
+ * slot_attisnull
+ *
+ * Detect whether an attribute of the slot is null, without actually fetching
+ * it.
+ */
+static inline bool
+slot_attisnull(TupleTableSlot *slot, int attnum)
+{
+ AssertArg(attnum > 0);
+
+ if (attnum > slot->tts_nvalid)
+ slot_getsomeattrs(slot, attnum);
+
+ return slot->tts_isnull[attnum - 1];
+}
+
+/*
+ * slot_getattr - fetch one attribute of the slot's contents.
+ */
+static inline Datum
+slot_getattr(TupleTableSlot *slot, int attnum,
+ bool *isnull)
+{
+ AssertArg(attnum > 0);
+
+ if (attnum > slot->tts_nvalid)
+ slot_getsomeattrs(slot, attnum);
+
+ *isnull = slot->tts_isnull[attnum - 1];
+
+ return slot->tts_values[attnum - 1];
+}
+
+/*
+ * slot_getsysattr - fetch a system attribute of the slot's current tuple.
+ *
+ * If the slot type does not contain system attributes, this will throw an
+ * error. Hence before calling this function, callers should make sure that
+ * the slot type is the one that supports system attributes.
+ */
+static inline Datum
+slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ AssertArg(attnum < 0); /* caller error */
+
+ /* Fetch the system attribute from the underlying tuple. */
+ return slot->tts_ops->getsysattr(slot, attnum, isnull);
+}
+
+/*
+ * ExecClearTuple - clear the slot's contents
+ */
+static inline TupleTableSlot *
+ExecClearTuple(TupleTableSlot *slot)
+{
+ slot->tts_ops->clear(slot);
+
+ return slot;
+}
+
+/* ExecMaterializeSlot - force a slot into the "materialized" state.
+ *
+ * This causes the slot's tuple to be a local copy not dependent on any
+ * external storage (i.e. pointing into a Buffer, or having allocations in
+ * another memory context).
+ *
+ * A typical use for this operation is to prepare a computed tuple for being
+ * stored on disk. The original data may or may not be virtual, but in any
+ * case we need a private copy for heap_insert to scribble on.
+ */
+static inline void
+ExecMaterializeSlot(TupleTableSlot *slot)
+{
+ slot->tts_ops->materialize(slot);
+}
+
+/*
+ * ExecCopySlotHeapTuple - return HeapTuple allocated in caller's context
+ */
+static inline HeapTuple
+ExecCopySlotHeapTuple(TupleTableSlot *slot)
+{
+ Assert(!TTS_EMPTY(slot));
+
+ return slot->tts_ops->copy_heap_tuple(slot);
+}
+
+/*
+ * ExecCopySlotMinimalTuple - return MinimalTuple allocated in caller's context
+ */
+static inline MinimalTuple
+ExecCopySlotMinimalTuple(TupleTableSlot *slot)
+{
+ return slot->tts_ops->copy_minimal_tuple(slot);
+}
+
+/*
+ * ExecCopySlot - copy one slot's contents into another.
+ *
+ * If a source's system attributes are supposed to be accessed in the target
+ * slot, the target slot and source slot types need to match.
+ */
+static inline TupleTableSlot *
+ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ Assert(!TTS_EMPTY(srcslot));
+
+ dstslot->tts_ops->copyslot(dstslot, srcslot);
+
+ return dstslot;
+}
+
+#endif /* FRONTEND */
#endif /* TUPTABLE_H */