summaryrefslogtreecommitdiff
path: root/src/include
diff options
context:
space:
mode:
authorAndres Freund2018-11-17 00:35:11 +0000
committerAndres Freund2018-11-17 00:35:15 +0000
commit4da597edf1bae0cf0453b5ed6fc4347b6334dfe1 (patch)
tree1311627bcb3c21c15d3bed719b16bf5254289e93 /src/include
parent0201d79a5549e3375ea5e5aee351378399453f15 (diff)
Make TupleTableSlots extensible, finish split of existing slot type.
This commit completes the work prepared in 1a0586de36, splitting the old TupleTableSlot implementation (which could store buffer, heap, minimal and virtual slots) into four different slot types. As described in the aforementioned commit, this is done with the goal of making tuple table slots extensible, to allow for pluggable table access methods. To achieve runtime extensibility for TupleTableSlots, operations on slots that can differ between types of slots are performed using the TupleTableSlotOps struct provided at slot creation time. That includes information from the size of TupleTableSlot struct to be allocated, initialization, deforming etc. See the struct's definition for more detailed information about callbacks TupleTableSlotOps. I decided to rename TTSOpsBufferTuple to TTSOpsBufferHeapTuple and ExecCopySlotTuple to ExecCopySlotHeapTuple, as that seems more consistent with other naming introduced in recent patches. There's plenty optimization potential in the slot implementation, but according to benchmarking the state after this commit has similar performance characteristics to before this set of changes, which seems sufficient. There's a few changes in execReplication.c that currently need to poke through the slot abstraction, that'll be repaired once the pluggable storage patchset provides the necessary infrastructure. Author: Andres Freund and Ashutosh Bapat, with changes by Amit Khandekar Discussion: https://postgr.es/m/[email protected]
Diffstat (limited to 'src/include')
-rw-r--r--src/include/access/htup_details.h2
-rw-r--r--src/include/executor/tuptable.h409
-rw-r--r--src/include/jit/llvmjit.h2
3 files changed, 310 insertions, 103 deletions
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/tuptable.h b/src/include/executor/tuptable.h
index 9470d385309..db5031f5746 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -20,11 +20,20 @@
/*----------
* The executor stores tuples in a "tuple table" which is a List of
- * independent TupleTableSlots. There are several cases we need to handle:
- * 1. physical tuple in a disk buffer page
- * 2. physical tuple constructed in palloc'ed memory
- * 3. "minimal" physical tuple constructed in palloc'ed memory
- * 4. "virtual" tuple consisting of Datum/isnull arrays
+ * independent TupleTableSlots.
+ *
+ * There's various different types of tuple table slots, each being able to
+ * store different types of tuples. Additional types of slots can be added
+ * without modifying core code. The type of a slot is determined by the
+ * TupleTableSlotOps* passed to the slot creation routine. The builtin types
+ * of slots are
+ *
+ * 1. physical tuple in a disk buffer page (TTSOpsBufferHeapTuple)
+ * 2. physical tuple constructed in palloc'ed memory (TTSOpsHeapTuple)
+ * 3. "minimal" physical tuple constructed in palloc'ed memory
+ * (TTSOpsMinimalTuple)
+ * 4. "virtual" tuple consisting of Datum/isnull arrays (TTSOpsVirtual)
+ *
*
* The first two cases are similar in that they both deal with "materialized"
* tuples, but resource management is different. For a tuple in a disk page
@@ -37,39 +46,28 @@
* parallel to case 1. Note that a minimal tuple has no "system columns".
* (Actually, it could have an OID, but we have no need to access the OID.)
*
- * A "virtual" tuple is an optimization used to minimize physical data
- * copying in a nest of plan nodes. Any pass-by-reference Datums in the
- * tuple point to storage that is not directly associated with the
- * TupleTableSlot; generally they will point to part of a tuple stored in
- * a lower plan node's output TupleTableSlot, or to a function result
+ * A "virtual" tuple is an optimization used to minimize physical data copying
+ * in a nest of plan nodes. Until materialized pass-by-reference Datums in
+ * the slot point to storage that is not directly associated with the
+ * TupleTableSlot; generally they will point to part of a tuple stored in a
+ * lower plan node's output TupleTableSlot, or to a function result
* constructed in a plan node's per-tuple econtext. It is the responsibility
- * of the generating plan node to be sure these resources are not released
- * for as long as the virtual tuple needs to be valid. We only use virtual
- * tuples in the result slots of plan nodes --- tuples to be copied anywhere
- * else need to be "materialized" into physical tuples. Note also that a
- * virtual tuple does not have any "system columns".
+ * of the generating plan node to be sure these resources are not released for
+ * as long as the virtual tuple needs to be valid or is materialized. Note
+ * also that a virtual tuple does not have any "system columns".
*
- * It is also possible for a TupleTableSlot to hold both physical and minimal
- * copies of a tuple. This is done when the slot is requested to provide
- * the format other than the one it currently holds. (Originally we attempted
- * to handle such requests by replacing one format with the other, but that
- * had the fatal defect of invalidating any pass-by-reference Datums pointing
- * into the existing slot contents.) Both copies must contain identical data
- * payloads when this is the case.
+ * The Datum/isnull arrays of a TupleTableSlot serve double duty. For virtual
+ * slots they they are the authoritative data. For the other builtin slots,
+ * the arrays contain data extracted from the tuple. (In this state, any
+ * pass-by-reference Datums point into the physical tuple.) The extracted
+ * information is built "lazily", ie, only as needed. This serves to avoid
+ * repeated extraction of data from the physical tuple.
*
- * The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
- * slot contains a virtual tuple, they are the authoritative data. When the
- * slot contains a physical tuple, the arrays contain data extracted from
- * the tuple. (In this state, any pass-by-reference Datums point into
- * the physical tuple.) The extracted information is built "lazily",
- * ie, only as needed. This serves to avoid repeated extraction of data
- * from the physical tuple.
- *
- * A TupleTableSlot can also be "empty", indicated by flag TTS_EMPTY set in
- * tts_flags, holding no valid data. This is the only valid state for a
- * freshly-created slot that has not yet had a tuple descriptor assigned to it.
- * In this state, TTS_SHOULDFREE should not be set in tts_flag, tts_tuple must
- * be NULL, tts_buffer InvalidBuffer, and tts_nvalid zero.
+ * A TupleTableSlot can also be "empty", indicated by flag TTS_FLAG_EMPTY set
+ * in tts_flags, holding no valid data. This is the only valid state for a
+ * freshly-created slot that has not yet had a tuple descriptor assigned to
+ * it. In this state, TTS_SHOULDFREE should not be set in tts_flag, tts_tuple
+ * must be NULL, tts_buffer InvalidBuffer, and tts_nvalid zero.
*
* The tupleDescriptor is simply referenced, not copied, by the TupleTableSlot
* code. The caller of ExecSetSlotDescriptor() is responsible for providing
@@ -83,32 +81,12 @@
* the slot and should be freed when the slot's reference to the tuple is
* dropped.
*
- * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
- * on the indicated buffer page; drop the pin when we release the
- * slot's reference to that buffer. (tts_shouldFree should always be
- * false in such a case, since presumably tts_tuple is pointing at the
- * buffer page.)
- *
- * tts_nvalid indicates the number of valid columns in the tts_values/isnull
- * arrays. When the slot is holding a "virtual" tuple this must be equal
- * to the descriptor's natts. When the slot is holding a physical tuple
- * this is equal to the number of columns we have extracted (we always
- * extract columns from left to right, so there are no holes).
- *
- * tts_values/tts_isnull are allocated when a descriptor is assigned to the
- * slot; they are of length equal to the descriptor's natts.
+ * tts_values/tts_isnull are allocated either when the slot is created (when
+ * the descriptor is provided), or when a descriptor is assigned to the slot;
+ * they are of length equal to the descriptor's natts.
*
- * tts_mintuple must always be NULL if the slot does not hold a "minimal"
- * tuple. When it does, tts_mintuple points to the actual MinimalTupleData
- * object (the thing to be pfree'd if tts_shouldFreeMin is true). If the slot
- * has only a minimal and not also a regular physical tuple, then tts_tuple
- * points at tts_minhdr and the fields of that struct are set correctly
- * for access to the minimal tuple; in particular, tts_minhdr.t_data points
- * MINIMAL_TUPLE_OFFSET bytes before tts_mintuple. This allows column
- * extraction to treat the case identically to regular physical tuples.
- *
- * TTS_SLOW flag in tts_flags and tts_off are saved state for
- * slot_deform_tuple, and should not be touched by any other code.
+ * The TTS_FLAG_SLOW flag and tts_off are saved state for
+ * slot_deform_heap_tuple, and should not be touched by any other code.
*----------
*/
@@ -116,25 +94,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)
+/* saved state for slot_deform_heap_tuple */
+#define TTS_FLAG_SLOW (1 << 3)
#define TTS_SLOW(slot) (((slot)->tts_flags & TTS_FLAG_SLOW) != 0)
/* fixed tuple descriptor */
-#define TTS_FLAG_FIXED (1 << 5)
+#define TTS_FLAG_FIXED (1 << 4)
#define TTS_FIXED(slot) (((slot)->tts_flags & TTS_FLAG_FIXED) != 0)
struct TupleTableSlotOps;
typedef struct TupleTableSlotOps TupleTableSlotOps;
+/* base tuple table slot type */
typedef struct TupleTableSlot
{
NodeTag type;
@@ -142,28 +117,99 @@ 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_ops; /* implementation of slot */
-#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 5
+#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
- MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
-#define FIELDNO_TUPLETABLESLOT_OFF 8
- uint32 tts_off; /* saved state for slot_deform_tuple */
-#define FIELDNO_TUPLETABLESLOT_VALUES 9
+#define FIELDNO_TUPLETABLESLOT_VALUES 5
Datum *tts_values; /* current per-attribute values */
-#define FIELDNO_TUPLETABLESLOT_ISNULL 10
+#define FIELDNO_TUPLETABLESLOT_ISNULL 6
bool *tts_isnull; /* current per-attribute isnull flags */
- MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
- HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
+ MemoryContext tts_mcxt; /* slot itself is in this context */
} TupleTableSlot;
/* routines for a TupleTableSlot implementation */
struct TupleTableSlotOps
{
- /* body will be replaced in later commit */
- int dummy;
+ /* Minimum size of the slot */
+ size_t base_slot_size;
+
+ /* Initialization. */
+ void (*init)(TupleTableSlot *slot);
+
+ /* Destruction. */
+ void (*release)(TupleTableSlot *slot);
+
+ /*
+ * Clear the contents of the slot. Only the contents are expected to be
+ * cleared and not the tuple descriptor. Typically an implementation of
+ * this callback should free the memory allocated for the tuple contained
+ * in the slot.
+ */
+ void (*clear)(TupleTableSlot *slot);
+
+ /*
+ * Fill up first natts entries of tts_values and tts_isnull arrays with
+ * values from the tuple contained in the slot. The function may be called
+ * with natts more than the number of attributes available in the tuple,
+ * in which case it should set tts_nvalid to the number of returned
+ * columns.
+ */
+ void (*getsomeattrs)(TupleTableSlot *slot, int natts);
+
+ /*
+ * Returns value of the given system attribute as a datum and sets isnull
+ * to false, if it's not NULL. Throws an error if the slot type does not
+ * support system attributes.
+ */
+ Datum (*getsysattr)(TupleTableSlot *slot, int attnum, bool *isnull);
+
+ /*
+ * Make the contents of the slot solely depend on the slot, and not on
+ * underlying resources (like another memory context, buffers, etc).
+ */
+ void (*materialize)(TupleTableSlot *slot);
+
+ /*
+ * Copy the contents of the source slot into the destination slot's own
+ * context. Invoked using callback of the destination slot.
+ */
+ void (*copyslot) (TupleTableSlot *dstslot, TupleTableSlot *srcslot);
+
+ /*
+ * Return a heap tuple "owned" by the slot. It is slot's responsibility to
+ * free the memory consumed by the heap tuple. If the slot can not "own" a
+ * heap tuple, it should not implement this callback and should set it as
+ * NULL.
+ */
+ HeapTuple (*get_heap_tuple)(TupleTableSlot *slot);
+
+ /*
+ * Return a minimal tuple "owned" by the slot. It is slot's responsibility
+ * to free the memory consumed by the minimal tuple. If the slot can not
+ * "own" a minimal tuple, it should not implement this callback and should
+ * set it as NULL.
+ */
+ MinimalTuple (*get_minimal_tuple)(TupleTableSlot *slot);
+
+ /*
+ * Return a copy of heap tuple representing the contents of the slot. The
+ * copy needs to be palloc'd in the current memory context. The slot
+ * itself is expected to remain unaffected. It is *not* expected to have
+ * meaningful "system columns" in the copy. The copy is not be "owned" by
+ * the slot i.e. the caller has to take responsibilty to free memory
+ * consumed by the slot.
+ */
+ HeapTuple (*copy_heap_tuple)(TupleTableSlot *slot);
+
+ /*
+ * Return a copy of minimal tuple representing the contents of the slot. The
+ * copy needs to be palloc'd in the current memory context. The slot
+ * itself is expected to remain unaffected. It is *not* expected to have
+ * meaningful "system columns" in the copy. The copy is not be "owned" by
+ * the slot i.e. the caller has to take responsibilty to free memory
+ * consumed by the slot.
+ */
+ MinimalTuple (*copy_minimal_tuple)(TupleTableSlot *slot);
};
/*
@@ -173,10 +219,68 @@ struct TupleTableSlotOps
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsVirtual;
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsHeapTuple;
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsMinimalTuple;
-extern PGDLLIMPORT const TupleTableSlotOps TTSOpsBufferTuple;
+extern PGDLLIMPORT const TupleTableSlotOps TTSOpsBufferHeapTuple;
+
+#define TTS_IS_VIRTUAL(slot) ((slot)->tts_ops == &TTSOpsVirtual)
+#define TTS_IS_HEAPTUPLE(slot) ((slot)->tts_ops == &TTSOpsHeapTuple)
+#define TTS_IS_MINIMALTUPLE(slot) ((slot)->tts_ops == &TTSOpsMinimalTuple)
+#define TTS_IS_BUFFERTUPLE(slot) ((slot)->tts_ops == &TTSOpsBufferHeapTuple)
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
+
+/*
+ * Tuple table slot implementations.
+ */
+
+typedef struct VirtualTupleTableSlot
+{
+ TupleTableSlot base;
+
+ char *data; /* data for materialized slots */
+} VirtualTupleTableSlot;
+
+typedef struct HeapTupleTableSlot
+{
+ TupleTableSlot base;
+
+#define FIELDNO_HEAPTUPLETABLESLOT_TUPLE 1
+ HeapTuple tuple; /* physical tuple */
+#define FIELDNO_HEAPTUPLETABLESLOT_OFF 2
+ uint32 off; /* saved state for slot_deform_heap_tuple */
+} HeapTupleTableSlot;
+
+/* heap tuple residing in a buffer */
+typedef struct BufferHeapTupleTableSlot
+{
+ HeapTupleTableSlot base;
+
+ /*
+ * If buffer is not InvalidBuffer, then the slot is holding a pin on the
+ * indicated buffer page; drop the pin when we release the slot's
+ * reference to that buffer. (TTS_FLAG_SHOULDFREE should not be set be
+ * false in such a case, since presumably tts_tuple is pointing at the
+ * buffer page.)
+ */
+ Buffer buffer; /* tuple's buffer, or InvalidBuffer */
+} BufferHeapTupleTableSlot;
+
+typedef struct MinimalTupleTableSlot
+{
+ TupleTableSlot base;
+
+ /*
+ * In a minimal slot tuple points at minhdr and the fields of that struct
+ * are set correctly for access to the minimal tuple; in particular,
+ * minhdr.t_data points MINIMAL_TUPLE_OFFSET bytes before mintuple. This
+ * allows column extraction to treat the case identically to regular
+ * physical tuples.
+ */
+#define FIELDNO_MINIMALTUPLETABLESLOT_TUPLE 1
+ HeapTuple tuple; /* tuple wrapper */
+ MinimalTuple mintuple; /* minimal tuple, or NULL if none */
+ HeapTupleData minhdr; /* workspace for minimal-tuple-only case */
+#define FIELDNO_MINIMALTUPLETABLESLOT_OFF 4
+ uint32 off; /* saved state for slot_deform_heap_tuple */
+} MinimalTupleTableSlot;
/*
* TupIsNull -- is a TupleTableSlot empty?
@@ -197,37 +301,26 @@ 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);
-
-/* 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
/*
@@ -253,6 +346,120 @@ 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];
+}
+
+/*
+ * slot_getattr - fetch one attribute of the slot's contents.
+ */
+static inline Datum
+slot_getattr(TupleTableSlot *slot, int attnum,
+ bool *isnull)
+{
+ AssertArg(attnum > 0);
+
+ if (attnum > slot->tts_nvalid)
+ slot_getsomeattrs(slot, attnum);
+
+ *isnull = slot->tts_isnull[attnum - 1];
+
+ return slot->tts_values[attnum - 1];
+}
+
+/*
+ * slot_getsysattr - fetch a system attribute of the slot's current tuple.
+ *
+ * If the slot type does not contain system attributes, this will throw an
+ * error. Hence before calling this function, callers should make sure that
+ * the slot type is the one that supports system attributes.
+ */
+static inline Datum
+slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ AssertArg(attnum < 0); /* caller error */
+
+ /* Fetch the system attribute from the underlying tuple. */
+ return slot->tts_ops->getsysattr(slot, attnum, isnull);
+}
+
+/*
+ * ExecClearTuple - clear the slot's contents
+ */
+static inline TupleTableSlot *
+ExecClearTuple(TupleTableSlot *slot)
+{
+ slot->tts_ops->clear(slot);
+
+ return slot;
+}
+
+/* ExecMaterializeSlot - force a slot into the "materialized" state.
+ *
+ * This causes the slot's tuple to be a local copy not dependent on any
+ * external storage (i.e. pointing into a Buffer, or having allocations in
+ * another memory context).
+ *
+ * A typical use for this operation is to prepare a computed tuple for being
+ * stored on disk. The original data may or may not be virtual, but in any
+ * case we need a private copy for heap_insert to scribble on.
+ */
+static inline void
+ExecMaterializeSlot(TupleTableSlot *slot)
+{
+ slot->tts_ops->materialize(slot);
+}
+
+/*
+ * ExecCopySlotHeapTuple - return HeapTuple allocated in caller's context
+ */
+static inline HeapTuple
+ExecCopySlotHeapTuple(TupleTableSlot *slot)
+{
+ Assert(!TTS_EMPTY(slot));
+
+ return slot->tts_ops->copy_heap_tuple(slot);
+}
+
+/*
+ * ExecCopySlotMinimalTuple - return MinimalTuple allocated in caller's context
+ */
+static inline MinimalTuple
+ExecCopySlotMinimalTuple(TupleTableSlot *slot)
+{
+ return slot->tts_ops->copy_minimal_tuple(slot);
+}
+
+/*
+ * ExecCopySlot - copy one slot's contents into another.
+ *
+ * If a source's system attributes are supposed to be accessed in the target
+ * slot, the target slot and source slot types need to match.
+ */
+static inline TupleTableSlot *
+ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ Assert(!TTS_EMPTY(srcslot));
+
+ dstslot->tts_ops->copyslot(dstslot, srcslot);
+
+ return dstslot;
+}
+
+#endif /* FRONTEND */
#endif /* TUPTABLE_H */
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index 05c9740bc5e..1639846d8b5 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;