| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * tuptable.h |
| 4 | * tuple table support stuff |
| 5 | * |
| 6 | * |
| 7 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 8 | * Portions Copyright (c) 1994, Regents of the University of California |
| 9 | * |
| 10 | * src/include/executor/tuptable.h |
| 11 | * |
| 12 | *------------------------------------------------------------------------- |
| 13 | */ |
| 14 | #ifndef TUPTABLE_H |
| 15 | #define TUPTABLE_H |
| 16 | |
| 17 | #include "access/htup.h" |
| 18 | #include "access/sysattr.h" |
| 19 | #include "access/tupdesc.h" |
| 20 | #include "access/htup_details.h" |
| 21 | #include "storage/buf.h" |
| 22 | |
| 23 | /*---------- |
| 24 | * The executor stores tuples in a "tuple table" which is a List of |
| 25 | * independent TupleTableSlots. |
| 26 | * |
| 27 | * There's various different types of tuple table slots, each being able to |
| 28 | * store different types of tuples. Additional types of slots can be added |
| 29 | * without modifying core code. The type of a slot is determined by the |
| 30 | * TupleTableSlotOps* passed to the slot creation routine. The builtin types |
| 31 | * of slots are |
| 32 | * |
| 33 | * 1. physical tuple in a disk buffer page (TTSOpsBufferHeapTuple) |
| 34 | * 2. physical tuple constructed in palloc'ed memory (TTSOpsHeapTuple) |
| 35 | * 3. "minimal" physical tuple constructed in palloc'ed memory |
| 36 | * (TTSOpsMinimalTuple) |
| 37 | * 4. "virtual" tuple consisting of Datum/isnull arrays (TTSOpsVirtual) |
| 38 | * |
| 39 | * |
| 40 | * The first two cases are similar in that they both deal with "materialized" |
| 41 | * tuples, but resource management is different. For a tuple in a disk page |
| 42 | * we need to hold a pin on the buffer until the TupleTableSlot's reference |
| 43 | * to the tuple is dropped; while for a palloc'd tuple we usually want the |
| 44 | * tuple pfree'd when the TupleTableSlot's reference is dropped. |
| 45 | * |
| 46 | * A "minimal" tuple is handled similarly to a palloc'd regular tuple. |
| 47 | * At present, minimal tuples never are stored in buffers, so there is no |
| 48 | * parallel to case 1. Note that a minimal tuple has no "system columns". |
| 49 | * (Actually, it could have an OID, but we have no need to access the OID.) |
| 50 | * |
| 51 | * A "virtual" tuple is an optimization used to minimize physical data copying |
| 52 | * in a nest of plan nodes. Until materialized pass-by-reference Datums in |
| 53 | * the slot point to storage that is not directly associated with the |
| 54 | * TupleTableSlot; generally they will point to part of a tuple stored in a |
| 55 | * lower plan node's output TupleTableSlot, or to a function result |
| 56 | * constructed in a plan node's per-tuple econtext. It is the responsibility |
| 57 | * of the generating plan node to be sure these resources are not released for |
| 58 | * as long as the virtual tuple needs to be valid or is materialized. Note |
| 59 | * also that a virtual tuple does not have any "system columns". |
| 60 | * |
| 61 | * The Datum/isnull arrays of a TupleTableSlot serve double duty. For virtual |
| 62 | * slots they are the authoritative data. For the other builtin slots, |
| 63 | * the arrays contain data extracted from the tuple. (In this state, any |
| 64 | * pass-by-reference Datums point into the physical tuple.) The extracted |
| 65 | * information is built "lazily", ie, only as needed. This serves to avoid |
| 66 | * repeated extraction of data from the physical tuple. |
| 67 | * |
| 68 | * A TupleTableSlot can also be "empty", indicated by flag TTS_FLAG_EMPTY set |
| 69 | * in tts_flags, holding no valid data. This is the only valid state for a |
| 70 | * freshly-created slot that has not yet had a tuple descriptor assigned to |
| 71 | * it. In this state, TTS_SHOULDFREE should not be set in tts_flags, tts_tuple |
| 72 | * must be NULL and tts_nvalid zero. |
| 73 | * |
| 74 | * The tupleDescriptor is simply referenced, not copied, by the TupleTableSlot |
| 75 | * code. The caller of ExecSetSlotDescriptor() is responsible for providing |
| 76 | * a descriptor that will live as long as the slot does. (Typically, both |
| 77 | * slots and descriptors are in per-query memory and are freed by memory |
| 78 | * context deallocation at query end; so it's not worth providing any extra |
| 79 | * mechanism to do more. However, the slot will increment the tupdesc |
| 80 | * reference count if a reference-counted tupdesc is supplied.) |
| 81 | * |
| 82 | * When TTS_SHOULDFREE is set in tts_flags, the physical tuple is "owned" by |
| 83 | * the slot and should be freed when the slot's reference to the tuple is |
| 84 | * dropped. |
| 85 | * |
| 86 | * tts_values/tts_isnull are allocated either when the slot is created (when |
| 87 | * the descriptor is provided), or when a descriptor is assigned to the slot; |
| 88 | * they are of length equal to the descriptor's natts. |
| 89 | * |
| 90 | * The TTS_FLAG_SLOW flag is saved state for |
| 91 | * slot_deform_heap_tuple, and should not be touched by any other code. |
| 92 | *---------- |
| 93 | */ |
| 94 | |
| 95 | /* true = slot is empty */ |
| 96 | #define TTS_FLAG_EMPTY (1 << 1) |
| 97 | #define TTS_EMPTY(slot) (((slot)->tts_flags & TTS_FLAG_EMPTY) != 0) |
| 98 | |
| 99 | /* should pfree tuple "owned" by the slot? */ |
| 100 | #define TTS_FLAG_SHOULDFREE (1 << 2) |
| 101 | #define TTS_SHOULDFREE(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREE) != 0) |
| 102 | |
| 103 | /* saved state for slot_deform_heap_tuple */ |
| 104 | #define TTS_FLAG_SLOW (1 << 3) |
| 105 | #define TTS_SLOW(slot) (((slot)->tts_flags & TTS_FLAG_SLOW) != 0) |
| 106 | |
| 107 | /* fixed tuple descriptor */ |
| 108 | #define TTS_FLAG_FIXED (1 << 4) |
| 109 | #define TTS_FIXED(slot) (((slot)->tts_flags & TTS_FLAG_FIXED) != 0) |
| 110 | |
| 111 | struct TupleTableSlotOps; |
| 112 | typedef struct TupleTableSlotOps TupleTableSlotOps; |
| 113 | |
| 114 | /* base tuple table slot type */ |
| 115 | typedef struct TupleTableSlot |
| 116 | { |
| 117 | NodeTag type; |
| 118 | #define FIELDNO_TUPLETABLESLOT_FLAGS 1 |
| 119 | uint16 tts_flags; /* Boolean states */ |
| 120 | #define FIELDNO_TUPLETABLESLOT_NVALID 2 |
| 121 | AttrNumber tts_nvalid; /* # of valid values in tts_values */ |
| 122 | const TupleTableSlotOps *const tts_ops; /* implementation of slot */ |
| 123 | #define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4 |
| 124 | TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */ |
| 125 | #define FIELDNO_TUPLETABLESLOT_VALUES 5 |
| 126 | Datum *tts_values; /* current per-attribute values */ |
| 127 | #define FIELDNO_TUPLETABLESLOT_ISNULL 6 |
| 128 | bool *tts_isnull; /* current per-attribute isnull flags */ |
| 129 | MemoryContext tts_mcxt; /* slot itself is in this context */ |
| 130 | ItemPointerData tts_tid; /* stored tuple's tid */ |
| 131 | Oid tts_tableOid; /* table oid of tuple */ |
| 132 | } TupleTableSlot; |
| 133 | |
| 134 | /* routines for a TupleTableSlot implementation */ |
| 135 | struct TupleTableSlotOps |
| 136 | { |
| 137 | /* Minimum size of the slot */ |
| 138 | size_t base_slot_size; |
| 139 | |
| 140 | /* Initialization. */ |
| 141 | void (*init) (TupleTableSlot *slot); |
| 142 | |
| 143 | /* Destruction. */ |
| 144 | void (*release) (TupleTableSlot *slot); |
| 145 | |
| 146 | /* |
| 147 | * Clear the contents of the slot. Only the contents are expected to be |
| 148 | * cleared and not the tuple descriptor. Typically an implementation of |
| 149 | * this callback should free the memory allocated for the tuple contained |
| 150 | * in the slot. |
| 151 | */ |
| 152 | void (*clear) (TupleTableSlot *slot); |
| 153 | |
| 154 | /* |
| 155 | * Fill up first natts entries of tts_values and tts_isnull arrays with |
| 156 | * values from the tuple contained in the slot. The function may be called |
| 157 | * with natts more than the number of attributes available in the tuple, |
| 158 | * in which case it should set tts_nvalid to the number of returned |
| 159 | * columns. |
| 160 | */ |
| 161 | void (*getsomeattrs) (TupleTableSlot *slot, int natts); |
| 162 | |
| 163 | /* |
| 164 | * Returns value of the given system attribute as a datum and sets isnull |
| 165 | * to false, if it's not NULL. Throws an error if the slot type does not |
| 166 | * support system attributes. |
| 167 | */ |
| 168 | Datum (*getsysattr) (TupleTableSlot *slot, int attnum, bool *isnull); |
| 169 | |
| 170 | /* |
| 171 | * Make the contents of the slot solely depend on the slot, and not on |
| 172 | * underlying resources (like another memory context, buffers, etc). |
| 173 | */ |
| 174 | void (*materialize) (TupleTableSlot *slot); |
| 175 | |
| 176 | /* |
| 177 | * Copy the contents of the source slot into the destination slot's own |
| 178 | * context. Invoked using callback of the destination slot. |
| 179 | */ |
| 180 | void (*copyslot) (TupleTableSlot *dstslot, TupleTableSlot *srcslot); |
| 181 | |
| 182 | /* |
| 183 | * Return a heap tuple "owned" by the slot. It is slot's responsibility to |
| 184 | * free the memory consumed by the heap tuple. If the slot can not "own" a |
| 185 | * heap tuple, it should not implement this callback and should set it as |
| 186 | * NULL. |
| 187 | */ |
| 188 | HeapTuple (*get_heap_tuple) (TupleTableSlot *slot); |
| 189 | |
| 190 | /* |
| 191 | * Return a minimal tuple "owned" by the slot. It is slot's responsibility |
| 192 | * to free the memory consumed by the minimal tuple. If the slot can not |
| 193 | * "own" a minimal tuple, it should not implement this callback and should |
| 194 | * set it as NULL. |
| 195 | */ |
| 196 | MinimalTuple (*get_minimal_tuple) (TupleTableSlot *slot); |
| 197 | |
| 198 | /* |
| 199 | * Return a copy of heap tuple representing the contents of the slot. The |
| 200 | * copy needs to be palloc'd in the current memory context. The slot |
| 201 | * itself is expected to remain unaffected. It is *not* expected to have |
| 202 | * meaningful "system columns" in the copy. The copy is not be "owned" by |
| 203 | * the slot i.e. the caller has to take responsibility to free memory |
| 204 | * consumed by the slot. |
| 205 | */ |
| 206 | HeapTuple (*copy_heap_tuple) (TupleTableSlot *slot); |
| 207 | |
| 208 | /* |
| 209 | * Return a copy of minimal tuple representing the contents of the slot. |
| 210 | * The copy needs to be palloc'd in the current memory context. The slot |
| 211 | * itself is expected to remain unaffected. It is *not* expected to have |
| 212 | * meaningful "system columns" in the copy. The copy is not be "owned" by |
| 213 | * the slot i.e. the caller has to take responsibility to free memory |
| 214 | * consumed by the slot. |
| 215 | */ |
| 216 | MinimalTuple (*copy_minimal_tuple) (TupleTableSlot *slot); |
| 217 | }; |
| 218 | |
| 219 | /* |
| 220 | * Predefined TupleTableSlotOps for various types of TupleTableSlotOps. The |
| 221 | * same are used to identify the type of a given slot. |
| 222 | */ |
| 223 | extern PGDLLIMPORT const TupleTableSlotOps TTSOpsVirtual; |
| 224 | extern PGDLLIMPORT const TupleTableSlotOps TTSOpsHeapTuple; |
| 225 | extern PGDLLIMPORT const TupleTableSlotOps TTSOpsMinimalTuple; |
| 226 | extern PGDLLIMPORT const TupleTableSlotOps TTSOpsBufferHeapTuple; |
| 227 | |
| 228 | #define TTS_IS_VIRTUAL(slot) ((slot)->tts_ops == &TTSOpsVirtual) |
| 229 | #define TTS_IS_HEAPTUPLE(slot) ((slot)->tts_ops == &TTSOpsHeapTuple) |
| 230 | #define TTS_IS_MINIMALTUPLE(slot) ((slot)->tts_ops == &TTSOpsMinimalTuple) |
| 231 | #define TTS_IS_BUFFERTUPLE(slot) ((slot)->tts_ops == &TTSOpsBufferHeapTuple) |
| 232 | |
| 233 | |
| 234 | /* |
| 235 | * Tuple table slot implementations. |
| 236 | */ |
| 237 | |
| 238 | typedef struct VirtualTupleTableSlot |
| 239 | { |
| 240 | TupleTableSlot base; |
| 241 | |
| 242 | char *data; /* data for materialized slots */ |
| 243 | } VirtualTupleTableSlot; |
| 244 | |
| 245 | typedef struct HeapTupleTableSlot |
| 246 | { |
| 247 | TupleTableSlot base; |
| 248 | |
| 249 | #define FIELDNO_HEAPTUPLETABLESLOT_TUPLE 1 |
| 250 | HeapTuple tuple; /* physical tuple */ |
| 251 | #define FIELDNO_HEAPTUPLETABLESLOT_OFF 2 |
| 252 | uint32 off; /* saved state for slot_deform_heap_tuple */ |
| 253 | HeapTupleData tupdata; /* optional workspace for storing tuple */ |
| 254 | } HeapTupleTableSlot; |
| 255 | |
| 256 | /* heap tuple residing in a buffer */ |
| 257 | typedef struct BufferHeapTupleTableSlot |
| 258 | { |
| 259 | HeapTupleTableSlot base; |
| 260 | |
| 261 | /* |
| 262 | * If buffer is not InvalidBuffer, then the slot is holding a pin on the |
| 263 | * indicated buffer page; drop the pin when we release the slot's |
| 264 | * reference to that buffer. (TTS_FLAG_SHOULDFREE should not be set be |
| 265 | * false in such a case, since presumably tts_tuple is pointing at the |
| 266 | * buffer page.) |
| 267 | */ |
| 268 | Buffer buffer; /* tuple's buffer, or InvalidBuffer */ |
| 269 | } BufferHeapTupleTableSlot; |
| 270 | |
| 271 | typedef struct MinimalTupleTableSlot |
| 272 | { |
| 273 | TupleTableSlot base; |
| 274 | |
| 275 | /* |
| 276 | * In a minimal slot tuple points at minhdr and the fields of that struct |
| 277 | * are set correctly for access to the minimal tuple; in particular, |
| 278 | * minhdr.t_data points MINIMAL_TUPLE_OFFSET bytes before mintuple. This |
| 279 | * allows column extraction to treat the case identically to regular |
| 280 | * physical tuples. |
| 281 | */ |
| 282 | #define FIELDNO_MINIMALTUPLETABLESLOT_TUPLE 1 |
| 283 | HeapTuple tuple; /* tuple wrapper */ |
| 284 | MinimalTuple mintuple; /* minimal tuple, or NULL if none */ |
| 285 | HeapTupleData minhdr; /* workspace for minimal-tuple-only case */ |
| 286 | #define FIELDNO_MINIMALTUPLETABLESLOT_OFF 4 |
| 287 | uint32 off; /* saved state for slot_deform_heap_tuple */ |
| 288 | } MinimalTupleTableSlot; |
| 289 | |
| 290 | /* |
| 291 | * TupIsNull -- is a TupleTableSlot empty? |
| 292 | */ |
| 293 | #define TupIsNull(slot) \ |
| 294 | ((slot) == NULL || TTS_EMPTY(slot)) |
| 295 | |
| 296 | /* in executor/execTuples.c */ |
| 297 | extern TupleTableSlot *MakeTupleTableSlot(TupleDesc tupleDesc, |
| 298 | const TupleTableSlotOps *tts_ops); |
| 299 | extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable, TupleDesc desc, |
| 300 | const TupleTableSlotOps *tts_ops); |
| 301 | extern void ExecResetTupleTable(List *tupleTable, bool shouldFree); |
| 302 | extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc, |
| 303 | const TupleTableSlotOps *tts_ops); |
| 304 | extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot); |
| 305 | extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc); |
| 306 | extern TupleTableSlot *ExecStoreHeapTuple(HeapTuple tuple, |
| 307 | TupleTableSlot *slot, |
| 308 | bool shouldFree); |
| 309 | extern void ExecForceStoreHeapTuple(HeapTuple tuple, |
| 310 | TupleTableSlot *slot, |
| 311 | bool shouldFree); |
| 312 | extern TupleTableSlot *ExecStoreBufferHeapTuple(HeapTuple tuple, |
| 313 | TupleTableSlot *slot, |
| 314 | Buffer buffer); |
| 315 | extern TupleTableSlot *ExecStorePinnedBufferHeapTuple(HeapTuple tuple, |
| 316 | TupleTableSlot *slot, |
| 317 | Buffer buffer); |
| 318 | extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup, |
| 319 | TupleTableSlot *slot, |
| 320 | bool shouldFree); |
| 321 | extern void ExecForceStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot, |
| 322 | bool shouldFree); |
| 323 | extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot); |
| 324 | extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot); |
| 325 | extern void ExecStoreHeapTupleDatum(Datum data, TupleTableSlot *slot); |
| 326 | extern HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree); |
| 327 | extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot, |
| 328 | bool *shouldFree); |
| 329 | extern Datum ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot); |
| 330 | extern void slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, |
| 331 | int lastAttNum); |
| 332 | extern void slot_getsomeattrs_int(TupleTableSlot *slot, int attnum); |
| 333 | |
| 334 | |
| 335 | #ifndef FRONTEND |
| 336 | |
| 337 | /* |
| 338 | * This function forces the entries of the slot's Datum/isnull arrays to be |
| 339 | * valid at least up through the attnum'th entry. |
| 340 | */ |
| 341 | static inline void |
| 342 | slot_getsomeattrs(TupleTableSlot *slot, int attnum) |
| 343 | { |
| 344 | if (slot->tts_nvalid < attnum) |
| 345 | slot_getsomeattrs_int(slot, attnum); |
| 346 | } |
| 347 | |
| 348 | /* |
| 349 | * slot_getallattrs |
| 350 | * This function forces all the entries of the slot's Datum/isnull |
| 351 | * arrays to be valid. The caller may then extract data directly |
| 352 | * from those arrays instead of using slot_getattr. |
| 353 | */ |
| 354 | static inline void |
| 355 | slot_getallattrs(TupleTableSlot *slot) |
| 356 | { |
| 357 | slot_getsomeattrs(slot, slot->tts_tupleDescriptor->natts); |
| 358 | } |
| 359 | |
| 360 | |
| 361 | /* |
| 362 | * slot_attisnull |
| 363 | * |
| 364 | * Detect whether an attribute of the slot is null, without actually fetching |
| 365 | * it. |
| 366 | */ |
| 367 | static inline bool |
| 368 | slot_attisnull(TupleTableSlot *slot, int attnum) |
| 369 | { |
| 370 | AssertArg(attnum > 0); |
| 371 | |
| 372 | if (attnum > slot->tts_nvalid) |
| 373 | slot_getsomeattrs(slot, attnum); |
| 374 | |
| 375 | return slot->tts_isnull[attnum - 1]; |
| 376 | } |
| 377 | |
| 378 | /* |
| 379 | * slot_getattr - fetch one attribute of the slot's contents. |
| 380 | */ |
| 381 | static inline Datum |
| 382 | slot_getattr(TupleTableSlot *slot, int attnum, |
| 383 | bool *isnull) |
| 384 | { |
| 385 | AssertArg(attnum > 0); |
| 386 | |
| 387 | if (attnum > slot->tts_nvalid) |
| 388 | slot_getsomeattrs(slot, attnum); |
| 389 | |
| 390 | *isnull = slot->tts_isnull[attnum - 1]; |
| 391 | |
| 392 | return slot->tts_values[attnum - 1]; |
| 393 | } |
| 394 | |
| 395 | /* |
| 396 | * slot_getsysattr - fetch a system attribute of the slot's current tuple. |
| 397 | * |
| 398 | * If the slot type does not contain system attributes, this will throw an |
| 399 | * error. Hence before calling this function, callers should make sure that |
| 400 | * the slot type is the one that supports system attributes. |
| 401 | */ |
| 402 | static inline Datum |
| 403 | slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull) |
| 404 | { |
| 405 | AssertArg(attnum < 0); /* caller error */ |
| 406 | |
| 407 | if (attnum == TableOidAttributeNumber) |
| 408 | { |
| 409 | *isnull = false; |
| 410 | return ObjectIdGetDatum(slot->tts_tableOid); |
| 411 | } |
| 412 | else if (attnum == SelfItemPointerAttributeNumber) |
| 413 | { |
| 414 | *isnull = false; |
| 415 | return PointerGetDatum(&slot->tts_tid); |
| 416 | } |
| 417 | |
| 418 | /* Fetch the system attribute from the underlying tuple. */ |
| 419 | return slot->tts_ops->getsysattr(slot, attnum, isnull); |
| 420 | } |
| 421 | |
| 422 | /* |
| 423 | * ExecClearTuple - clear the slot's contents |
| 424 | */ |
| 425 | static inline TupleTableSlot * |
| 426 | ExecClearTuple(TupleTableSlot *slot) |
| 427 | { |
| 428 | slot->tts_ops->clear(slot); |
| 429 | |
| 430 | return slot; |
| 431 | } |
| 432 | |
| 433 | /* ExecMaterializeSlot - force a slot into the "materialized" state. |
| 434 | * |
| 435 | * This causes the slot's tuple to be a local copy not dependent on any |
| 436 | * external storage (i.e. pointing into a Buffer, or having allocations in |
| 437 | * another memory context). |
| 438 | * |
| 439 | * A typical use for this operation is to prepare a computed tuple for being |
| 440 | * stored on disk. The original data may or may not be virtual, but in any |
| 441 | * case we need a private copy for heap_insert to scribble on. |
| 442 | */ |
| 443 | static inline void |
| 444 | ExecMaterializeSlot(TupleTableSlot *slot) |
| 445 | { |
| 446 | slot->tts_ops->materialize(slot); |
| 447 | } |
| 448 | |
| 449 | /* |
| 450 | * ExecCopySlotHeapTuple - return HeapTuple allocated in caller's context |
| 451 | */ |
| 452 | static inline HeapTuple |
| 453 | ExecCopySlotHeapTuple(TupleTableSlot *slot) |
| 454 | { |
| 455 | Assert(!TTS_EMPTY(slot)); |
| 456 | |
| 457 | return slot->tts_ops->copy_heap_tuple(slot); |
| 458 | } |
| 459 | |
| 460 | /* |
| 461 | * ExecCopySlotMinimalTuple - return MinimalTuple allocated in caller's context |
| 462 | */ |
| 463 | static inline MinimalTuple |
| 464 | ExecCopySlotMinimalTuple(TupleTableSlot *slot) |
| 465 | { |
| 466 | return slot->tts_ops->copy_minimal_tuple(slot); |
| 467 | } |
| 468 | |
| 469 | /* |
| 470 | * ExecCopySlot - copy one slot's contents into another. |
| 471 | * |
| 472 | * If a source's system attributes are supposed to be accessed in the target |
| 473 | * slot, the target slot and source slot types need to match. |
| 474 | */ |
| 475 | static inline TupleTableSlot * |
| 476 | ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot) |
| 477 | { |
| 478 | Assert(!TTS_EMPTY(srcslot)); |
| 479 | |
| 480 | dstslot->tts_ops->copyslot(dstslot, srcslot); |
| 481 | |
| 482 | return dstslot; |
| 483 | } |
| 484 | |
| 485 | #endif /* FRONTEND */ |
| 486 | |
| 487 | #endif /* TUPTABLE_H */ |
| 488 | |