agora inbox for [email protected]
help / color / mirror / Atom feedFrom: Andres Freund <[email protected]>
Subject: [PATCH v14 6/7] Restructure TupleTableSlot to allow tuples other than HeapTuple
Date: Tue, 13 Nov 2018 12:58:25 -0800
Author: Andres Freund and Ashutosh Bapat, with changes by Amit Khandekar
Discussion: https://postgr.es/m/[email protected]
---
src/backend/access/common/heaptuple.c | 184 +--
src/backend/catalog/index.c | 4 +-
src/backend/executor/execCurrent.c | 18 +-
src/backend/executor/execExpr.c | 5 +-
src/backend/executor/execExprInterp.c | 13 +-
src/backend/executor/execGrouping.c | 5 +-
src/backend/executor/execReplication.c | 41 +-
src/backend/executor/execScan.c | 4 +-
src/backend/executor/execTuples.c | 1665 ++++++++++++++++--------
src/backend/executor/nodeAgg.c | 5 +-
src/backend/executor/nodeHashjoin.c | 17 +-
src/backend/executor/nodeModifyTable.c | 13 +
src/backend/executor/nodeSubplan.c | 1 +
src/backend/jit/llvm/llvmjit.c | 8 +-
src/backend/jit/llvm/llvmjit_deform.c | 52 +-
src/backend/jit/llvm/llvmjit_expr.c | 3 +-
src/backend/jit/llvm/llvmjit_types.c | 4 +-
src/include/access/htup_details.h | 2 -
src/include/executor/executor.h | 1 +
src/include/executor/tuptable.h | 305 ++++-
src/include/jit/llvmjit.h | 7 +-
src/include/nodes/execnodes.h | 1 +
22 files changed, 1562 insertions(+), 796 deletions(-)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 28127b311f5..ccb69bdd616 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -71,6 +71,8 @@
#define VARLENA_ATT_IS_PACKABLE(att) \
((att)->attstorage != 'p')
+static Datum getmissingattr(TupleDesc tupleDesc, int attnum, bool *isnull);
+
/* ----------------------------------------------------------------
* misc support routines
@@ -80,7 +82,7 @@
/*
* Return the missing value of an attribute, or NULL if there isn't one.
*/
-Datum
+static Datum
getmissingattr(TupleDesc tupleDesc,
int attnum, bool *isnull)
{
@@ -1350,186 +1352,6 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
values[attnum] = getmissingattr(tupleDesc, attnum + 1, &isnull[attnum]);
}
-/*
- * slot_deform_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.
- */
-void
-slot_deform_tuple(TupleTableSlot *slot, int natts)
-{
- HeapTuple tuple = slot->tts_tuple;
- 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? */
-
- /*
- * 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 = slot->tts_off;
- 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;
- slot->tts_off = off;
- if (slow)
- slot->tts_flags |= TTS_FLAG_SLOW;
- else
- slot->tts_flags &= ~TTS_FLAG_SLOW;
-}
-
-/*
- * slot_attisnull
- * Detect whether an attribute of the slot is null, without
- * actually fetching it.
- */
-bool
-slot_attisnull(TupleTableSlot *slot, int attnum)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
-
- /*
- * system attributes are handled by heap_attisnull
- */
- 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_attisnull(tuple, attnum, tupleDesc);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- return slot->tts_isnull[attnum - 1];
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- return true;
-
- /*
- * 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");
-
- /* and let the tuple tell it */
- return heap_attisnull(tuple, attnum, tupleDesc);
-}
-
-/*
- * slot_getsysattr
- * This function fetches a system attribute of the slot's current tuple.
- * Unlike slot_getattr, if the slot does not contain system attributes,
- * this will return false (with a NULL attribute value) instead of
- * throwing an error.
- */
-bool
-slot_getsysattr(TupleTableSlot *slot, int attnum,
- Datum *value, bool *isnull)
-{
- HeapTuple tuple = slot->tts_tuple;
-
- Assert(attnum < 0); /* else caller error */
- if (tuple == NULL ||
- tuple == &(slot->tts_minhdr))
- {
- /* No physical tuple, or minimal tuple, so fail */
- *value = (Datum) 0;
- *isnull = true;
- return false;
- }
- *value = heap_getsysattr(tuple, attnum, slot->tts_tupleDescriptor, isnull);
- return true;
-}
-
/*
* heap_freetuple
*/
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 21bdf794da6..a980202a7b1 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2041,7 +2041,9 @@ FormIndexDatum(IndexInfo *indexInfo,
Datum iDatum;
bool isNull;
- if (keycol != 0)
+ if (keycol < 0)
+ iDatum = slot_getsysattr(slot, keycol, &isNull);
+ else if (keycol != 0)
{
/*
* Plain index column; get the value we need directly from the
diff --git a/src/backend/executor/execCurrent.c b/src/backend/executor/execCurrent.c
index aadf7493827..39c462a4e59 100644
--- a/src/backend/executor/execCurrent.c
+++ b/src/backend/executor/execCurrent.c
@@ -218,27 +218,25 @@ execCurrentOf(CurrentOfExpr *cexpr,
ItemPointer tuple_tid;
#ifdef USE_ASSERT_CHECKING
- if (!slot_getsysattr(scanstate->ss_ScanTupleSlot,
- TableOidAttributeNumber,
- &ldatum,
- &lisnull))
+ ldatum = slot_getsysattr(scanstate->ss_ScanTupleSlot,
+ TableOidAttributeNumber,
+ &lisnull);
+ if (lisnull)
ereport(ERROR,
(errcode(ERRCODE_INVALID_CURSOR_STATE),
errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
cursor_name, table_name)));
- Assert(!lisnull);
Assert(DatumGetObjectId(ldatum) == table_oid);
#endif
- if (!slot_getsysattr(scanstate->ss_ScanTupleSlot,
- SelfItemPointerAttributeNumber,
- &ldatum,
- &lisnull))
+ ldatum = slot_getsysattr(scanstate->ss_ScanTupleSlot,
+ SelfItemPointerAttributeNumber,
+ &lisnull);
+ if (lisnull)
ereport(ERROR,
(errcode(ERRCODE_INVALID_CURSOR_STATE),
errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
cursor_name, table_name)));
- Assert(!lisnull);
tuple_tid = (ItemPointer) DatumGetPointer(ldatum);
*current_tid = *tuple_tid;
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index a4099c23176..914d04186bd 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -3313,6 +3313,7 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
*/
ExprState *
ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
+ const TupleTableSlotOps *lops, const TupleTableSlotOps *rops,
int numCols,
AttrNumber *keyColIdx,
Oid *eqfunctions,
@@ -3354,7 +3355,7 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
scratch.d.fetch.last_var = maxatt;
scratch.d.fetch.fixed = false;
scratch.d.fetch.known_desc = ldesc;
- scratch.d.fetch.kind = NULL;
+ scratch.d.fetch.kind = lops;
ExecComputeSlotInfo(state, &scratch);
ExprEvalPushStep(state, &scratch);
@@ -3362,7 +3363,7 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
scratch.d.fetch.last_var = maxatt;
scratch.d.fetch.fixed = false;
scratch.d.fetch.known_desc = rdesc;
- scratch.d.fetch.kind = NULL;
+ scratch.d.fetch.kind = rops;
ExecComputeSlotInfo(state, &scratch);
ExprEvalPushStep(state, &scratch);
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 357eae41cc3..dbf2445a21d 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -428,7 +428,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
{
CheckOpSlotCompatibility(op, innerslot);
- /* XXX: worthwhile to check tts_nvalid inline first? */
slot_getsomeattrs(innerslot, op->d.fetch.last_var);
EEO_NEXT();
@@ -4026,15 +4025,15 @@ void
ExecEvalSysVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
TupleTableSlot *slot)
{
- bool success;
+ Datum d;
/* slot_getsysattr has sufficient defenses against bad attnums */
- success = slot_getsysattr(slot,
- op->d.var.attnum,
- op->resvalue,
- op->resnull);
+ d = slot_getsysattr(slot,
+ op->d.var.attnum,
+ op->resnull);
+ *op->resvalue = d;
/* this ought to be unreachable, but it's cheap enough to check */
- if (unlikely(!success))
+ if (unlikely(*op->resnull))
elog(ERROR, "failed to fetch attribute from slot");
}
diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c
index 5bc200e4dc3..abce1e95cb6 100644
--- a/src/backend/executor/execGrouping.c
+++ b/src/backend/executor/execGrouping.c
@@ -75,7 +75,8 @@ execTuplesMatchPrepare(TupleDesc desc,
eqFunctions[i] = get_opcode(eqOperators[i]);
/* build actual expression */
- expr = ExecBuildGroupingEqual(desc, desc, numCols, keyColIdx, eqFunctions,
+ expr = ExecBuildGroupingEqual(desc, desc, NULL, NULL,
+ numCols, keyColIdx, eqFunctions,
parent);
return expr;
@@ -206,7 +207,9 @@ BuildTupleHashTable(PlanState *parent,
&TTSOpsMinimalTuple);
/* build comparator for all columns */
+ /* XXX: should we support non-minimal tuples for the inputslot? */
hashtable->tab_eq_func = ExecBuildGroupingEqual(inputDesc, inputDesc,
+ &TTSOpsMinimalTuple, &TTSOpsMinimalTuple,
numCols,
keyColIdx, eqfuncoids,
parent);
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 071ba8762d4..727770eab56 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -170,8 +170,11 @@ retry:
HeapUpdateFailureData hufd;
HTSU_Result res;
HeapTupleData locktup;
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *)outslot;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ /* Only a heap tuple has item pointers. */
+ Assert(TTS_IS_HEAPTUPLE(outslot) || TTS_IS_BUFFERTUPLE(outslot));
+ ItemPointerCopy(&hslot->tuple->t_self, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -334,8 +337,12 @@ retry:
HeapUpdateFailureData hufd;
HTSU_Result res;
HeapTupleData locktup;
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *)outslot;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ /* Only a heap tuple has item pointers. */
+ Assert(TTS_IS_HEAPTUPLE(outslot) || TTS_IS_BUFFERTUPLE(outslot));
+
+ ItemPointerCopy(&hslot->tuple->t_self, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -456,6 +463,12 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ HeapTupleTableSlot *hsearchslot = (HeapTupleTableSlot *)searchslot;
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *)slot;
+
+ /* We expect both searchslot and the slot to contain a heap tuple. */
+ Assert(TTS_IS_HEAPTUPLE(searchslot) || TTS_IS_BUFFERTUPLE(searchslot));
+ Assert(TTS_IS_HEAPTUPLE(slot) || TTS_IS_BUFFERTUPLE(slot));
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -467,8 +480,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
- NULL, slot);
+ &hsearchslot->tuple->t_self, NULL, slot);
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
@@ -488,19 +500,18 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/* OK, update the tuple and index entries for it */
- simple_heap_update(rel, &searchslot->tts_tuple->t_self,
- slot->tts_tuple);
+ simple_heap_update(rel, &hsearchslot->tuple->t_self, hslot->tuple);
if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(slot->tts_tuple))
+ !HeapTupleIsHeapOnly(hslot->tuple))
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
estate, false, NULL,
NIL);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
- NULL, tuple, recheckIndexes, NULL);
+ &hsearchslot->tuple->t_self, NULL, tuple,
+ recheckIndexes, NULL);
list_free(recheckIndexes);
}
@@ -519,9 +530,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
bool skip_tuple = false;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ HeapTupleTableSlot *hsearchslot = (HeapTupleTableSlot *)searchslot;
- /* For now we support only tables. */
+ /* For now we support only tables and heap tuples. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
+ Assert(TTS_IS_HEAPTUPLE(searchslot) || TTS_IS_BUFFERTUPLE(searchslot));
CheckCmdReplicaIdentity(rel, CMD_DELETE);
@@ -530,8 +543,8 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_delete_before_row)
{
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
- NULL, NULL);
+ &hsearchslot->tuple->t_self, NULL,
+ NULL);
}
if (!skip_tuple)
@@ -539,11 +552,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
List *recheckIndexes = NIL;
/* OK, delete the tuple */
- simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+ simple_heap_delete(rel, &hsearchslot->tuple->t_self);
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self, NULL, NULL);
+ &hsearchslot->tuple->t_self, NULL, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 233cc280608..d90bb16b570 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -78,8 +78,8 @@ ExecScanFetch(ScanState *node,
return ExecClearTuple(slot);
/* Store test tuple in the plan node's scan slot */
- ExecStoreHeapTuple(estate->es_epqTuple[scanrelid - 1],
- slot, false);
+ ExecForceStoreHeapTuple(estate->es_epqTuple[scanrelid - 1],
+ slot);
/* Check if it meets the access-method conditions */
if (!(*recheckMtd) (node, slot))
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index bb618e94f5d..bf49c4789cb 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -71,12 +71,1024 @@
static TupleDesc ExecTypeFromTLInternal(List *targetList,
bool hasoid, bool skipjunk);
+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;
+/*
+ * 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.
+ */
+static inline void __attribute__((always_inline))
+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;
+}
+
+/*
+ * 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, count the
+ * required size, and allocate enough memory to store all attributes. That
+ * both gains efficiency, and makes it easier to release all the memory at
+ * once.
+ */
+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);
+ }
+
+ /* FIXME: any reason to expand default args? */
+ }
+
+ /* 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;
+ }
+ /* FIXME: any reason to expand default args? */
+ }
+}
+
+/*
+ * A virtual tuple table slot can not copy the datums into its own storage. So
+ * it can not copy contents of the other slot into its own memory context.
+ */
+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);
+}
+
+/*
+ * Materialize the heap tuple contained in the given slot into its own memory
+ * context.
+ */
+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);
+ }
+
+ slot->tts_nvalid = 0;
+ hslot->off = 0;
+
+ MemoryContextSwitchTo(oldContext);
+}
+
+/*
+ * Copy contents of the srcslot into dstslot, on which this callback has been
+ * invoked, in dstslot's memory context. We do this by creating a heap tuple
+ * from the source slot's contents in the memory context of the destination
+ * slot and then storing that heap tuple in the destination slot.
+ */
+static void
+tts_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ HeapTuple tuple;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
+ tuple = ExecCopySlotTuple(srcslot);
+ MemoryContextSwitchTo(oldcontext);
+
+ ExecStoreHeapTuple(tuple, dstslot, true);
+}
+
+/*
+ * Return the heap tuple in the slot as is if it contains one. Otherwise,
+ * materialize a heap tuple using contents of the slot and return it.
+ */
+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;
+}
+
+/*
+ * Return a copy of heap tuple contained in the slot, materialising one if
+ * necessary.
+ */
+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);
+}
+
+/*
+ * Return a minimal tuple constructed from the contents of the slot.
+ *
+ * We always return a new minimal tuple so no copy, per say, is needed.
+ */
+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);
+}
+
+/*
+ * Store the given tuple into the given HeapTupleTableSlot. If the slot
+ * already contains a tuple, we will free the memory for the same before
+ * storing a new one there.
+ */
+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;
+}
+
+
+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");
+}
+
+/*
+ * Materialize the minimal tuple contained in the given slot into its own
+ * memory context.
+ */
+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);
+
+ slot->tts_nvalid = 0;
+ mslot->off = 0;
+}
+
+/*
+ * Copy contents of the srcslot into dstslot, on which this callback has been
+ * invoked, in dstslot's memory context. We do this by creating a minimal tuple
+ * from the source slot's contents in the memory context of the destination
+ * slot and then storing that minimal tuple in the destination slot.
+ */
+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);
+}
+
+/*
+ * Return the minimal tuple if slot contains one. Otherwise materialize the
+ * contents of slot into a minimal tuple and return that.
+ */
+static MinimalTuple
+tts_minimal_get_minimal_tuple(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+
+ if (!mslot->mintuple)
+ tts_minimal_materialize(slot);
+
+ return mslot->mintuple;
+}
+
+/*
+ * Return a heap tuple constructed from the minimal tuple contained in the slot.
+ *
+ * We always construct a new heap tuple, so there is nothing to copy as such.
+ */
+static HeapTuple
+tts_minimal_copy_heap_tuple(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ /* Materialize the minimal tuple if not already present. */
+ if (!mslot->mintuple)
+ tts_minimal_materialize(slot);
+
+ return heap_tuple_from_minimal_tuple(mslot->mintuple);
+}
+
+/*
+ * Return a copy of minimal tuple contained in the slot, materializing one if
+ * necessary.
+ */
+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);
+}
+
+/*
+ * Store the given minimal tuple into the given MinimalTupleTableSlot. If the
+ * slot already contains a tuple, we will free the memory for the same before
+ * storing a new one there.
+ */
+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);
+}
+
+/*
+ * Materialize the heap tuple contained in the given slot into its own memory
+ * context.
+ */
+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;
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ if (!bslot->base.tuple)
+ {
+ /*
+ * A heap tuple stored in a BufferHeapTupleTableSlot should have a
+ * buffer associated with it, unless it's materialized. Thus it will
+ * never happen that there is no heap tuple stored in this slot but
+ * tts_values and tts_isnull is set. IOW, we should never require to construct a
+ * tuple from tts_values and tts_isnull in this slot.
+ */
+ elog(ERROR, "a buffer tuple table slot should never require to construct a tuple from datums");
+ }
+ 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));
+ ReleaseBuffer(bslot->buffer);
+ bslot->buffer = InvalidBuffer;
+
+ bslot->base.off = 0;
+ slot->tts_nvalid = 0;
+}
+
+/*
+ * A buffer tuple table slot is used to store an on-disk tuple in it, at least
+ * to start with. So a need to copy into a buffer tuple table slot should not
+ * arise. May be we could support copying from a buffer tuple table slot, but
+ * it is not clear why it's required to do that.
+ */
+static void
+tts_buffer_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ BufferHeapTupleTableSlot *bsrcslot = (BufferHeapTupleTableSlot *) srcslot;
+ BufferHeapTupleTableSlot *bdstslot = (BufferHeapTupleTableSlot *) dstslot;
+
+ Assert(dstslot->tts_cb == srcslot->tts_cb);
+
+ if (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 = heap_copytuple(bsrcslot->base.tuple);
+ MemoryContextSwitchTo(oldContext);
+ }
+ else
+ {
+ tts_buffer_heap_store_tuple(dstslot, bsrcslot->base.tuple, bsrcslot->buffer);
+ tts_buffer_heap_materialize(dstslot);
+ }
+}
+
+/*
+ * Return the heap tuple in the slot as is if it contains one. Otherwise,
+ * materialize a heap tuple using contents of the slot and return it.
+ */
+static HeapTuple
+tts_buffer_heap_get_heap_tuple(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+
+ /* Is this even possible for a buffer tuple? */
+ if (!bslot->base.tuple)
+ tts_buffer_heap_materialize(slot);
+
+ return bslot->base.tuple;
+}
+
+/*
+ * Return a copy of heap tuple contained in the slot, materialising one if
+ * necessary.
+ */
+static HeapTuple
+tts_buffer_heap_copy_heap_tuple(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+
+ /* Is this even possible for a buffer tuple? */
+ if (!bslot->base.tuple)
+ tts_buffer_heap_materialize(slot);
+
+ return heap_copytuple(bslot->base.tuple);
+}
+
+/*
+ * Return a minimal tuple constructed from the contents of the slot.
+ *
+ * We always return a new minimal tuple so no copy, per say, is needed.
+ */
+static MinimalTuple
+tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!TTS_EMPTY(slot));
+
+ /* Is this even possible for a buffer tuple? */
+ if (!bslot->base.tuple)
+ tts_buffer_heap_materialize(slot);
+
+ return minimal_tuple_from_heap_tuple(bslot->base.tuple);
+}
+
+/*
+ * Store the given tuple into the given BufferHeapTupleTableSlot and pin the
+ * given buffer. If the tuple already contained in the slot can be freed free
+ * it.
+ */
+static void
+tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, Buffer buffer)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ if (TTS_SHOULDFREE(slot))
+ {
+ /*
+ * A heap tuple stored in a BufferHeapTupleTableSlot should have a
+ * buffer associated with it, unless it's materialized.
+ */
+ 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);
+ }
+}
+
+/*
+ * TupleTableSlotOps for each of TupleTableSlotTypes. These are used to
+ * identify the type of slot.
+ */
+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. As mentioned in
+ * the prologue of MinimalTupleData, a minimal tuple may be presented as a
+ * heap tuple, but such a representation does not have memory to hold
+ * system attributes in it and should be used for accessing/manipulating
+ * tuple through a slot. Hence we do not return heap tuple representation
+ * crafted from a minimal tuple to be returned in get_heap_tuple callback
+ * of this TupleTableSlotType.
+ */
+ .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 TTSOpsBufferTuple = {
+ .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
+};
+
/* ----------------------------------------------------------------
* tuple table create/delete functions
* ----------------------------------------------------------------
@@ -85,58 +1097,60 @@ const TupleTableSlotOps TTSOpsBufferTuple;
/* --------------------------------
* 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_cb)
{
- Size sz;
+ Size basesz, allocsz;
TupleTableSlot *slot;
+ basesz = tts_cb->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_cb) = tts_cb;
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_cb->init(slot);
+
return slot;
}
@@ -178,6 +1192,7 @@ ExecResetTupleTable(List *tupleTable, /* tuple table */
/* Always release resources and reset the slot to empty */
ExecClearTuple(slot);
+ slot->tts_cb->release(slot);
if (slot->tts_tupleDescriptor)
{
ReleaseTupleDesc(slot->tts_tupleDescriptor);
@@ -234,6 +1249,7 @@ ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
/* This should match ExecResetTupleTable's processing of one slot */
Assert(IsA(slot, TupleTableSlot));
ExecClearTuple(slot);
+ slot->tts_cb->release(slot);
if (slot->tts_tupleDescriptor)
ReleaseTupleDesc(slot->tts_tupleDescriptor);
if (!TTS_FIXED(slot))
@@ -334,36 +1350,9 @@ ExecStoreHeapTuple(HeapTuple tuple,
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 (!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;
}
@@ -397,46 +1386,9 @@ ExecStoreBufferHeapTuple(HeapTuple tuple,
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);
- }
+ if (!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;
}
@@ -461,93 +1413,9 @@ 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;
-
- 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).
- * --------------------------------
- */
-TupleTableSlot * /* return: slot passed */
-ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
-{
- /*
- * sanity checks
- */
- Assert(slot != NULL);
-
- /*
- * Free the old physical tuple if necessary.
- */
- 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;
- }
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
-
- /*
- * 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 it empty.
- */
- slot->tts_flags |= TTS_FLAG_EMPTY;
- slot->tts_nvalid = 0;
+ if (!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;
}
@@ -611,79 +1479,19 @@ ExecStoreAllNullTuple(TupleTableSlot *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.
+ * ExecFetchSlotTuple
+ * Fetch the slot's regular physical tuple.
*
- * This works even if the slot contains a virtual or minimal tuple;
- * however the "system columns" of the result will not be meaningful.
+ * This is a thin wrapper around TupleTableSlotType specific
+ * get_heap_tuple callback. The callback is expected to return a heap
+ * tuple as is if it holds one and continue to have ownership of the heap
+ * tuple. If materialize is true, the function calls TupleTableSlotType
+ * specific materialize() callback which is expected to materialize the
+ * tuple within the slot so that the slot "owns" it. If materialize is
+ * false, the returned tuple is not guaranteed to have storage local to
+ * the slot and hence should be treated as read-only.
* --------------------------------
*/
-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
@@ -713,89 +1521,67 @@ ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Assert(slot != NULL);
Assert(!TTS_EMPTY(slot));
- if (shouldFree)
- *shouldFree = false;
+ /* Materialize the tuple so that the slot "owns" it, if requested. */
+ if (materialize)
+ slot->tts_cb->materialize(slot);
- /*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
+ if (slot->tts_cb->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_cb->copy_heap_tuple(slot);
+ }
+ else
+ {
+ if (shouldFree)
+ *shouldFree = false;
+ return slot->tts_cb->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).
+ * 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.
*
- * As above, the result must be treated 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));
- 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_cb->get_minimal_tuple)
+ {
+ if (shouldFree)
+ *shouldFree = false;
+ return slot->tts_cb->get_minimal_tuple(slot);
+ }
+ else
+ {
+ if (shouldFree)
+ *shouldFree = true;
+ return slot->tts_cb->copy_minimal_tuple(slot);
+ }
}
/* --------------------------------
@@ -826,103 +1612,58 @@ ExecFetchSlotHeapTupleDatum(TupleTableSlot *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)
+ExecForceStoreHeapTuple(HeapTuple tuple,
+ TupleTableSlot *slot)
{
- MemoryContext oldContext;
+ if (TTS_IS_HEAPTUPLE(slot))
+ {
+ ExecStoreHeapTuple(tuple, slot, false);
+ }
+ else if (TTS_IS_BUFFERTUPLE(slot))
+ {
+ MemoryContext oldContext;
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
- /*
- * 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;
+ 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);
+ }
}
-/* --------------------------------
- * 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)
+void
+ExecForceStoreMinimalTuple(MinimalTuple mtup,
+ TupleTableSlot *slot,
+ bool shouldFree)
{
- HeapTuple newTuple;
- MemoryContext oldContext;
+ if (TTS_IS_MINIMALTUPLE(slot))
+ {
+ tts_minimal_store_tuple(slot, mtup, shouldFree);
+ }
+ else
+ {
+ HeapTupleData htup;
- /*
- * 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);
+ ExecClearTuple(slot);
- return ExecStoreHeapTuple(newTuple, dstslot, true);
+ 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);
+ }
}
-
/* ----------------------------------------------------------------
* convenience initialization routines
* ----------------------------------------------------------------
@@ -1062,7 +1803,6 @@ void
slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum)
{
AttrMissing *attrmiss = NULL;
- int missattnum;
if (slot->tts_tupleDescriptor->constr)
attrmiss = slot->tts_tupleDescriptor->constr->missing;
@@ -1077,6 +1817,8 @@ slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum)
}
else
{
+ int missattnum;
+
/* if there is a missing values array we must process them one by one */
for (missattnum = startAttNum;
missattnum < lastAttNum;
@@ -1085,143 +1827,36 @@ slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int 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.
+ * slot_getsomeattrs_int
*
- * 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];
-}
-
-/*
- * slot_getsomeattrs
- * This function forces the entries of the slot's Datum/isnull
- * arrays to be valid at least up through the attnum'th entry.
*/
void
-slot_getsomeattrs(TupleTableSlot *slot, int attnum)
+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);
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid >= attnum)
- return;
-
- /* Check for caller error */
- if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
+ 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. */
+ slot->tts_cb->getsomeattrs(slot, attnum);
/*
- * load up any slots available from physical tuple
+ * If the underlying tuple doesn't have enough attributes, tuple descriptor
+ * must have the missing attributes.
*/
- attno = HeapTupleHeaderGetNatts(tuple->t_data);
- attno = Min(attno, attnum);
-
- slot_deform_tuple(slot, attno);
-
- attno = slot->tts_nvalid;
-
- /*
- * If tuple doesn't have all the atts indicated by attnum, read the rest
- * as NULLs or missing values
- */
- if (attno < attnum)
- slot_getmissingattrs(slot, attno, attnum);
-
- slot->tts_nvalid = attnum;
+ if (unlikely(slot->tts_nvalid < attnum))
+ {
+ slot_getmissingattrs(slot, slot->tts_nvalid, attnum);
+ slot->tts_nvalid = attnum;
+ }
}
/* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 20d6d8e9cbb..2a1a6123d3b 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1800,9 +1800,8 @@ agg_retrieve_direct(AggState *aggstate)
* reserved for it. The tuple will be deleted when it is
* cleared from the slot.
*/
- ExecStoreHeapTuple(aggstate->grp_firstTuple,
- firstSlot,
- true);
+ ExecForceStoreHeapTuple(aggstate->grp_firstTuple,
+ firstSlot);
aggstate->grp_firstTuple = NULL; /* don't keep two pointers */
/* set up for first advance_aggregates call */
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index d9afd0bdded..8fb477f45c3 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -929,9 +929,10 @@ ExecParallelHashJoinOuterGetTuple(PlanState *outerNode,
hashvalue);
if (tuple != NULL)
{
- slot = ExecStoreMinimalTuple(tuple,
- hjstate->hj_OuterTupleSlot,
- false);
+ ExecForceStoreMinimalTuple(tuple,
+ hjstate->hj_OuterTupleSlot,
+ false);
+ slot = hjstate->hj_OuterTupleSlot;
return slot;
}
else
@@ -1158,9 +1159,10 @@ ExecParallelHashJoinNewBatch(HashJoinState *hjstate)
while ((tuple = sts_parallel_scan_next(inner_tuples,
&hashvalue)))
{
- slot = ExecStoreMinimalTuple(tuple,
- hjstate->hj_HashTupleSlot,
- false);
+ ExecForceStoreMinimalTuple(tuple,
+ hjstate->hj_HashTupleSlot,
+ false);
+ slot = hjstate->hj_HashTupleSlot;
ExecParallelHashTableInsertCurrentBatch(hashtable, slot,
hashvalue);
}
@@ -1294,7 +1296,8 @@ ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read from hash-join temporary file: %m")));
- return ExecStoreMinimalTuple(tuple, tupleSlot, true);
+ ExecForceStoreMinimalTuple(tuple, tupleSlot, true);
+ return tupleSlot;
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 9cc499d6533..d29162bab79 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2059,6 +2059,15 @@ ExecModifyTable(PlanState *pstate)
break;
}
+ /*
+ * Ensure input tuple is the right format for the target relation.
+ */
+ if (node->mt_scans[node->mt_whichplan]->tts_cb != planSlot->tts_cb)
+ {
+ ExecCopySlot(node->mt_scans[node->mt_whichplan], planSlot);
+ planSlot = node->mt_scans[node->mt_whichplan];
+ }
+
/*
* If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
* here is compute the RETURNING expressions.
@@ -2242,6 +2251,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
+ mtstate->mt_scans = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans);
/* If modifying a partitioned table, initialize the root table info */
if (node->rootResultRelIndex >= 0)
@@ -2308,6 +2318,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/* Now init the plan for this result rel */
estate->es_result_relation_info = resultRelInfo;
mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
+ mtstate->mt_scans[i] =
+ ExecInitExtraTupleSlot(mtstate->ps.state, ExecGetResultType(mtstate->mt_plans[i]),
+ &TTSOpsHeapTuple);
/* Also let FDWs init themselves for foreign-table result rels */
if (!resultRelInfo->ri_usesFdwDirectModify &&
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 62811ed0302..b42e60576e3 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -988,6 +988,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
* across-type comparison).
*/
sstate->cur_eq_comp = ExecBuildGroupingEqual(tupDescLeft, tupDescRight,
+ &TTSOpsVirtual, &TTSOpsMinimalTuple,
ncols,
sstate->keyColIdx,
sstate->tab_eq_funcoids,
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index 168072afd2f..162d1be89bd 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -65,6 +65,8 @@ LLVMTypeRef StructFormPgAttribute;
LLVMTypeRef StructTupleConstr;
LLVMTypeRef StructtupleDesc;
LLVMTypeRef StructTupleTableSlot;
+LLVMTypeRef StructHeapTupleTableSlot;
+LLVMTypeRef StructMinimalTupleTableSlot;
LLVMTypeRef StructMemoryContextData;
LLVMTypeRef StructPGFinfoRecord;
LLVMTypeRef StructFmgrInfo;
@@ -79,7 +81,7 @@ LLVMTypeRef StructAggStatePerTransData;
LLVMValueRef AttributeTemplate;
LLVMValueRef FuncStrlen;
LLVMValueRef FuncVarsizeAny;
-LLVMValueRef FuncSlotGetsomeattrs;
+LLVMValueRef FuncSlotGetsomeattrsInt;
LLVMValueRef FuncSlotGetmissingattrs;
LLVMValueRef FuncMakeExpandedObjectReadOnlyInternal;
LLVMValueRef FuncExecEvalArrayRefSubscript;
@@ -811,6 +813,8 @@ llvm_create_types(void)
StructFunctionCallInfoData = load_type(mod, "StructFunctionCallInfoData");
StructMemoryContextData = load_type(mod, "StructMemoryContextData");
StructTupleTableSlot = load_type(mod, "StructTupleTableSlot");
+ StructHeapTupleTableSlot = load_type(mod, "StructHeapTupleTableSlot");
+ StructMinimalTupleTableSlot = load_type(mod, "StructMinimalTupleTableSlot");
StructHeapTupleData = load_type(mod, "StructHeapTupleData");
StructtupleDesc = load_type(mod, "StructtupleDesc");
StructAggState = load_type(mod, "StructAggState");
@@ -820,7 +824,7 @@ llvm_create_types(void)
AttributeTemplate = LLVMGetNamedFunction(mod, "AttributeTemplate");
FuncStrlen = LLVMGetNamedFunction(mod, "strlen");
FuncVarsizeAny = LLVMGetNamedFunction(mod, "varsize_any");
- FuncSlotGetsomeattrs = LLVMGetNamedFunction(mod, "slot_getsomeattrs");
+ FuncSlotGetsomeattrsInt = LLVMGetNamedFunction(mod, "slot_getsomeattrs_int");
FuncSlotGetmissingattrs = LLVMGetNamedFunction(mod, "slot_getmissingattrs");
FuncMakeExpandedObjectReadOnlyInternal = LLVMGetNamedFunction(mod, "MakeExpandedObjectReadOnlyInternal");
FuncExecEvalArrayRefSubscript = LLVMGetNamedFunction(mod, "ExecEvalArrayRefSubscript");
diff --git a/src/backend/jit/llvm/llvmjit_deform.c b/src/backend/jit/llvm/llvmjit_deform.c
index 59e38d2d955..e430cd9eda4 100644
--- a/src/backend/jit/llvm/llvmjit_deform.c
+++ b/src/backend/jit/llvm/llvmjit_deform.c
@@ -31,7 +31,7 @@
* Create a function that deforms a tuple of type desc up to natts columns.
*/
LLVMValueRef
-slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts)
+slot_compile_deform(LLVMJitContext *context, TupleDesc desc, const TupleTableSlotOps *ops, int natts)
{
char *funcname;
@@ -88,6 +88,16 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts)
int attnum;
+ if (ops != &TTSOpsHeapTuple && ops != &TTSOpsBufferTuple &&
+ ops != &TTSOpsMinimalTuple)
+ {
+ /*
+ * Decline to JIT for slot types we don't know to handle, or don't
+ * want to handle (say virtual slots).
+ */
+ return NULL;
+ }
+
mod = llvm_mutable_module(context);
funcname = llvm_expand_funcname(context, "deform");
@@ -166,14 +176,44 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts)
v_tts_nulls =
l_load_struct_gep(b, v_slot, FIELDNO_TUPLETABLESLOT_ISNULL,
"tts_ISNULL");
-
- v_slotoffp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_OFF, "");
v_flagsp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_FLAGS, "");
v_nvalidp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_NVALID, "");
- v_tupleheaderp =
- l_load_struct_gep(b, v_slot, FIELDNO_TUPLETABLESLOT_TUPLE,
- "tupleheader");
+ if (ops == &TTSOpsHeapTuple || ops == &TTSOpsBufferTuple)
+ {
+ LLVMValueRef v_heapslot;
+
+ v_heapslot =
+ LLVMBuildBitCast(b,
+ v_slot,
+ l_ptr(StructHeapTupleTableSlot),
+ "heapslot");
+ v_slotoffp = LLVMBuildStructGEP(b, v_heapslot, FIELDNO_HEAPTUPLETABLESLOT_OFF, "");
+ v_tupleheaderp =
+ l_load_struct_gep(b, v_heapslot, FIELDNO_HEAPTUPLETABLESLOT_TUPLE,
+ "tupleheader");
+
+ }
+ else if (ops == &TTSOpsMinimalTuple)
+ {
+ LLVMValueRef v_minimalslot;
+
+ v_minimalslot =
+ LLVMBuildBitCast(b,
+ v_slot,
+ l_ptr(StructMinimalTupleTableSlot),
+ "minimalslotslot");
+ v_slotoffp = LLVMBuildStructGEP(b, v_minimalslot, FIELDNO_MINIMALTUPLETABLESLOT_OFF, "");
+ v_tupleheaderp =
+ l_load_struct_gep(b, v_minimalslot, FIELDNO_MINIMALTUPLETABLESLOT_TUPLE,
+ "tupleheader");
+ }
+ else
+ {
+ /* should've returned at the start of the function */
+ pg_unreachable();
+ }
+
v_tuplep =
l_load_struct_gep(b, v_tupleheaderp, FIELDNO_HEAPTUPLEDATA_DATA,
"tuple");
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 63b921517c6..f3414ef9caf 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -324,6 +324,7 @@ llvm_compile_expr(ExprState *state)
{
l_jit_deform =
slot_compile_deform(context, desc,
+ tts_cb,
op->d.fetch.last_var);
}
@@ -344,7 +345,7 @@ llvm_compile_expr(ExprState *state)
params[1] = l_int32_const(op->d.fetch.last_var);
LLVMBuildCall(b,
- llvm_get_decl(mod, FuncSlotGetsomeattrs),
+ llvm_get_decl(mod, FuncSlotGetsomeattrsInt),
params, lengthof(params), "");
}
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 855a6977ee5..2df1882b750 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -59,6 +59,8 @@ FunctionCallInfoData StructFunctionCallInfoData;
HeapTupleData StructHeapTupleData;
MemoryContextData StructMemoryContextData;
TupleTableSlot StructTupleTableSlot;
+HeapTupleTableSlot StructHeapTupleTableSlot;
+MinimalTupleTableSlot StructMinimalTupleTableSlot;
struct tupleDesc StructtupleDesc;
@@ -97,7 +99,7 @@ void *referenced_functions[] =
{
strlen,
varsize_any,
- slot_getsomeattrs,
+ slot_getsomeattrs_int,
slot_getmissingattrs,
MakeExpandedObjectReadOnlyInternal,
ExecEvalArrayRefSubscript,
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 97d240fdbb0..1867a70f6f3 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -835,7 +835,5 @@ extern MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup);
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);
-struct TupleTableSlot;
-extern void slot_deform_tuple(struct TupleTableSlot *slot, int natts);
#endif /* HTUP_DETAILS_H */
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 4f156f4a5e7..8b48a23d13e 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -249,6 +249,7 @@ extern List *ExecInitExprList(List *nodes, PlanState *parent);
extern ExprState *ExecBuildAggTrans(AggState *aggstate, struct AggStatePerPhaseData *phase,
bool doSort, bool doHash);
extern ExprState *ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
+ const TupleTableSlotOps *lops, const TupleTableSlotOps *rops,
int numCols,
AttrNumber *keyColIdx,
Oid *eqfunctions,
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index a6f6084f0a9..f78853488a9 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -65,7 +65,7 @@
* 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
+ * 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
@@ -116,25 +116,22 @@
#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)
+#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;
+/* virtual or base type */
typedef struct TupleTableSlot
{
NodeTag type;
@@ -142,26 +139,98 @@ typedef struct TupleTableSlot
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_cb; /* 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
{
+ /* 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
+ * returned heap tuple should be writable. The copy should be palloc'd in
+ * the current memory context. The slot itself is expected to remain
+ * undisturbed. 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 returned minimal tuple should be writable. The copy should be
+ * palloc'd in the current memory context. The slot itself is expected to
+ * remain undisturbed. 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);
};
/*
@@ -173,8 +242,44 @@ extern PGDLLIMPORT const TupleTableSlotOps TTSOpsHeapTuple;
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsMinimalTuple;
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsBufferTuple;
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
+#define TTS_IS_VIRTUAL(slot) ((slot)->tts_cb == &TTSOpsVirtual)
+#define TTS_IS_HEAPTUPLE(slot) ((slot)->tts_cb == &TTSOpsHeapTuple)
+#define TTS_IS_MINIMALTUPLE(slot) ((slot)->tts_cb == &TTSOpsMinimalTuple)
+#define TTS_IS_BUFFERTUPLE(slot) ((slot)->tts_cb == &TTSOpsBufferTuple)
+
+
+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_tuple */
+} HeapTupleTableSlot;
+
+/* heap tuple residing in a buffer */
+typedef struct BufferHeapTupleTableSlot
+{
+ HeapTupleTableSlot base;
+ Buffer buffer; /* tuple's buffer, or InvalidBuffer */
+} BufferHeapTupleTableSlot;
+
+typedef struct MinimalTupleTableSlot
+{
+ TupleTableSlot base;
+#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_tuple */
+} MinimalTupleTableSlot;
/*
* TupIsNull -- is a TupleTableSlot empty?
@@ -195,39 +300,38 @@ extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
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);
-extern void slot_getsomeattrs(TupleTableSlot *slot, int attnum);
-
-/* 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
+/*
+ * This function forces the entries of the slot's Datum/isnull arrays to be
+ * valid at least up through the attnum'th entry.
+ */
+static inline void
+slot_getsomeattrs(TupleTableSlot *slot, int attnum)
+{
+ if (slot->tts_nvalid < attnum)
+ slot_getsomeattrs_int(slot, attnum);
+}
+
/*
* slot_getallattrs
* This function forces all the entries of the slot's Datum/isnull
@@ -240,6 +344,129 @@ slot_getallattrs(TupleTableSlot *slot)
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];
+}
+
+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
+ * This function fetches 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, namely,
+ * heap tuple slot and buffer tuple slot.
+ */
+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_cb->getsysattr(slot, attnum, isnull);
+}
+
+/*
+ * ExecClearTuple
+ *
+ * A thin wrapper around calling TupleTableSlotType specific clear() method.
+ */
+static inline TupleTableSlot *
+ExecClearTuple(TupleTableSlot *slot)
+{
+ slot->tts_cb->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_cb->materialize(slot);
+}
+
+/*
+ * ExecCopySlotTuple
+ *
+ * A thin wrapper calling TupleTableSlotType specific copy_heap_tuple()
+ * method.
+ */
+static inline HeapTuple
+ExecCopySlotTuple(TupleTableSlot *slot)
+{
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(!TTS_EMPTY(slot));
+
+ return slot->tts_cb->copy_heap_tuple(slot);
+}
+
+/*
+ * ExecCopySlotMinimalTuple
+ *
+ * A thin wrapper around calling TupleTableSlotType specific
+ * copy_minimal_tuple() method.
+ */
+static inline MinimalTuple
+ExecCopySlotMinimalTuple(TupleTableSlot *slot)
+{
+ return slot->tts_cb->copy_minimal_tuple(slot);
+}
+
+/*
+ * ExecCopySlot
+ *
+ * A thin wrapper calling TupleTableSlotType specific copyslot callback.
+ *
+ * The caller must ensure the slots have compatible tupdescs.
+ */
+static inline TupleTableSlot *
+ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ dstslot->tts_cb->copyslot(dstslot, srcslot);
+
+ return dstslot;
+}
+
+#endif /* FRONTEND */
#endif /* TUPTABLE_H */
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index f3ea2492835..b4b0a16d1ae 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -65,6 +65,8 @@ extern LLVMTypeRef TypeStorageBool;
extern LLVMTypeRef StructtupleDesc;
extern LLVMTypeRef StructHeapTupleData;
extern LLVMTypeRef StructTupleTableSlot;
+extern LLVMTypeRef StructHeapTupleTableSlot;
+extern LLVMTypeRef StructMinimalTupleTableSlot;
extern LLVMTypeRef StructMemoryContextData;
extern LLVMTypeRef StructFunctionCallInfoData;
extern LLVMTypeRef StructExprContext;
@@ -77,7 +79,7 @@ extern LLVMTypeRef StructAggStatePerGroupData;
extern LLVMValueRef AttributeTemplate;
extern LLVMValueRef FuncStrlen;
extern LLVMValueRef FuncVarsizeAny;
-extern LLVMValueRef FuncSlotGetsomeattrs;
+extern LLVMValueRef FuncSlotGetsomeattrsInt;
extern LLVMValueRef FuncSlotGetmissingattrs;
extern LLVMValueRef FuncMakeExpandedObjectReadOnlyInternal;
extern LLVMValueRef FuncExecEvalArrayRefSubscript;
@@ -111,7 +113,8 @@ extern void llvm_inline(LLVMModuleRef mod);
****************************************************************************
*/
extern bool llvm_compile_expr(struct ExprState *state);
-extern LLVMValueRef slot_compile_deform(struct LLVMJitContext *context, TupleDesc desc, int natts);
+struct TupleTableSlotOps;
+extern LLVMValueRef slot_compile_deform(struct LLVMJitContext *context, TupleDesc desc, const struct TupleTableSlotOps *ops, int natts);
/*
****************************************************************************
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 59fb3c15833..149916093a2 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1100,6 +1100,7 @@ typedef struct ModifyTableState
PlanState **mt_plans; /* subplans (one per target rel) */
int mt_nplans; /* number of plans in the array */
int mt_whichplan; /* which one is being executed (0..n-1) */
+ TupleTableSlot** mt_scans; /* input tuple for underlying plan */
ResultRelInfo *resultRelInfo; /* per-subplan target relations */
ResultRelInfo *rootResultRelInfo; /* root target relation (partitioned
* table root) */
--
2.18.0.rc2.dirty
--ll5lqoiis2grgqzk
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v14-0007-Rationalize-expression-context-reset-in-ExecModi.patch"
view thread (114+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [PATCH v14 6/7] Restructure TupleTableSlot to allow tuples other than HeapTuple
In-Reply-To: <no-message-id-238629@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox