Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * execTuples.c
4 : : * Routines dealing with TupleTableSlots. These are used for resource
5 : : * management associated with tuples (eg, releasing buffer pins for
6 : : * tuples in disk buffers, or freeing the memory occupied by transient
7 : : * tuples). Slots also provide access abstraction that lets us implement
8 : : * "virtual" tuples to reduce data-copying overhead.
9 : : *
10 : : * Routines dealing with the type information for tuples. Currently,
11 : : * the type information for a tuple is an array of FormData_pg_attribute.
12 : : * This information is needed by routines manipulating tuples
13 : : * (getattribute, formtuple, etc.).
14 : : *
15 : : *
16 : : * EXAMPLE OF HOW TABLE ROUTINES WORK
17 : : * Suppose we have a query such as SELECT emp.name FROM emp and we have
18 : : * a single SeqScan node in the query plan.
19 : : *
20 : : * At ExecutorStart()
21 : : * ----------------
22 : : *
23 : : * - ExecInitSeqScan() calls ExecInitScanTupleSlot() to construct a
24 : : * TupleTableSlots for the tuples returned by the access method, and
25 : : * ExecInitResultTypeTL() to define the node's return
26 : : * type. ExecAssignScanProjectionInfo() will, if necessary, create
27 : : * another TupleTableSlot for the tuples resulting from performing
28 : : * target list projections.
29 : : *
30 : : * During ExecutorRun()
31 : : * ----------------
32 : : * - SeqNext() calls ExecStoreBufferHeapTuple() to place the tuple
33 : : * returned by the access method into the scan tuple slot.
34 : : *
35 : : * - ExecSeqScan() (via ExecScan), if necessary, calls ExecProject(),
36 : : * putting the result of the projection in the result tuple slot. If
37 : : * not necessary, it directly returns the slot returned by SeqNext().
38 : : *
39 : : * - ExecutePlan() calls the output function.
40 : : *
41 : : * The important thing to watch in the executor code is how pointers
42 : : * to the slots containing tuples are passed instead of the tuples
43 : : * themselves. This facilitates the communication of related information
44 : : * (such as whether or not a tuple should be pfreed, what buffer contains
45 : : * this tuple, the tuple's tuple descriptor, etc). It also allows us
46 : : * to avoid physically constructing projection tuples in many cases.
47 : : *
48 : : *
49 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
50 : : * Portions Copyright (c) 1994, Regents of the University of California
51 : : *
52 : : *
53 : : * IDENTIFICATION
54 : : * src/backend/executor/execTuples.c
55 : : *
56 : : *-------------------------------------------------------------------------
57 : : */
58 : : #include "postgres.h"
59 : :
60 : : #include "access/heaptoast.h"
61 : : #include "access/htup_details.h"
62 : : #include "access/tupdesc_details.h"
63 : : #include "access/xact.h"
64 : : #include "catalog/pg_type.h"
65 : : #include "funcapi.h"
66 : : #include "nodes/nodeFuncs.h"
67 : : #include "storage/bufmgr.h"
68 : : #include "utils/builtins.h"
69 : : #include "utils/expandeddatum.h"
70 : : #include "utils/lsyscache.h"
71 : : #include "utils/typcache.h"
72 : :
73 : : static TupleDesc ExecTypeFromTLInternal(List *targetList,
74 : : bool skipjunk);
75 : : static pg_attribute_always_inline void slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
76 : : int natts);
77 : : static inline void tts_buffer_heap_store_tuple(TupleTableSlot *slot,
78 : : HeapTuple tuple,
79 : : Buffer buffer,
80 : : bool transfer_pin);
81 : : static void tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree);
82 : :
83 : :
84 : : const TupleTableSlotOps TTSOpsVirtual;
85 : : const TupleTableSlotOps TTSOpsHeapTuple;
86 : : const TupleTableSlotOps TTSOpsMinimalTuple;
87 : : const TupleTableSlotOps TTSOpsBufferHeapTuple;
88 : :
89 : :
90 : : /*
91 : : * TupleTableSlotOps implementations.
92 : : */
93 : :
94 : : /*
95 : : * TupleTableSlotOps implementation for VirtualTupleTableSlot.
96 : : */
97 : : static void
98 : 906132 : tts_virtual_init(TupleTableSlot *slot)
99 : : {
100 : 906132 : }
101 : :
102 : : static void
103 : 899307 : tts_virtual_release(TupleTableSlot *slot)
104 : : {
105 : 899307 : }
106 : :
107 : : static void
108 : 12101172 : tts_virtual_clear(TupleTableSlot *slot)
109 : : {
110 [ + + ]: 12101172 : if (unlikely(TTS_SHOULDFREE(slot)))
111 : : {
112 : 257309 : VirtualTupleTableSlot *vslot = (VirtualTupleTableSlot *) slot;
113 : :
114 : 257309 : pfree(vslot->data);
115 : 257309 : vslot->data = NULL;
116 : :
117 : 257309 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
118 : 257309 : }
119 : :
120 : 12101172 : slot->tts_nvalid = 0;
121 : 12101172 : slot->tts_flags |= TTS_FLAG_EMPTY;
122 : 12101172 : ItemPointerSetInvalid(&slot->tts_tid);
123 : 12101172 : }
124 : :
125 : : /*
126 : : * VirtualTupleTableSlots always have fully populated tts_values and
127 : : * tts_isnull arrays. So this function should never be called.
128 : : */
129 : : static void
130 : 0 : tts_virtual_getsomeattrs(TupleTableSlot *slot, int natts)
131 : : {
132 [ # # # # ]: 0 : elog(ERROR, "getsomeattrs is not required to be called on a virtual tuple table slot");
133 : 0 : }
134 : :
135 : : /*
136 : : * VirtualTupleTableSlots never provide system attributes (except those
137 : : * handled generically, such as tableoid). We generally shouldn't get
138 : : * here, but provide a user-friendly message if we do.
139 : : */
140 : : static Datum
141 : 2 : tts_virtual_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
142 : : {
143 [ - + ]: 2 : Assert(!TTS_EMPTY(slot));
144 : :
145 [ + - + - ]: 2 : ereport(ERROR,
146 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
147 : : errmsg("cannot retrieve a system column in this context")));
148 : :
149 : 0 : return 0; /* silence compiler warnings */
150 : : }
151 : :
152 : : /*
153 : : * VirtualTupleTableSlots never have storage tuples. We generally
154 : : * shouldn't get here, but provide a user-friendly message if we do.
155 : : */
156 : : static bool
157 : 0 : tts_virtual_is_current_xact_tuple(TupleTableSlot *slot)
158 : : {
159 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
160 : :
161 [ # # # # ]: 0 : ereport(ERROR,
162 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
163 : : errmsg("don't have transaction information for this type of tuple")));
164 : :
165 : 0 : return false; /* silence compiler warnings */
166 : : }
167 : :
168 : : /*
169 : : * To materialize a virtual slot all the datums that aren't passed by value
170 : : * have to be copied into the slot's memory context. To do so, compute the
171 : : * required size, and allocate enough memory to store all attributes. That's
172 : : * good for cache hit ratio, but more importantly requires only memory
173 : : * allocation/deallocation.
174 : : */
175 : : static void
176 : 594503 : tts_virtual_materialize(TupleTableSlot *slot)
177 : : {
178 : 594503 : VirtualTupleTableSlot *vslot = (VirtualTupleTableSlot *) slot;
179 : 594503 : TupleDesc desc = slot->tts_tupleDescriptor;
180 : 594503 : Size sz = 0;
181 : 594503 : char *data;
182 : :
183 : : /* already materialized */
184 [ + + ]: 594503 : if (TTS_SHOULDFREE(slot))
185 : 39060 : return;
186 : :
187 : : /* compute size of memory required */
188 [ + + ]: 1746985 : for (int natt = 0; natt < desc->natts; natt++)
189 : : {
190 : 1191542 : CompactAttribute *att = TupleDescCompactAttr(desc, natt);
191 : 1191542 : Datum val;
192 : :
193 [ + + + + ]: 1191542 : if (att->attbyval || slot->tts_isnull[natt])
194 : 870947 : continue;
195 : :
196 : 320595 : val = slot->tts_values[natt];
197 : :
198 [ + + + - ]: 320595 : if (att->attlen == -1 &&
199 : 235161 : VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
200 : : {
201 : : /*
202 : : * We want to flatten the expanded value so that the materialized
203 : : * slot doesn't depend on it.
204 : : */
205 : 0 : sz = att_nominal_alignby(sz, att->attalignby);
206 : 0 : sz += EOH_get_flat_size(DatumGetEOHP(val));
207 : 0 : }
208 : : else
209 : : {
210 : 320595 : sz = att_nominal_alignby(sz, att->attalignby);
211 [ + + - + : 320595 : sz = att_addlength_datum(sz, att->attlen, val);
# # ]
212 : : }
213 [ + + ]: 1191542 : }
214 : :
215 : : /* all data is byval */
216 [ + + ]: 555443 : if (sz == 0)
217 : 298119 : return;
218 : :
219 : : /* allocate memory */
220 : 257324 : vslot->data = data = MemoryContextAlloc(slot->tts_mcxt, sz);
221 : 257324 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
222 : :
223 : : /* and copy all attributes into the pre-allocated space */
224 [ + + ]: 956003 : for (int natt = 0; natt < desc->natts; natt++)
225 : : {
226 : 698679 : CompactAttribute *att = TupleDescCompactAttr(desc, natt);
227 : 698679 : Datum val;
228 : :
229 [ + + + + ]: 698679 : if (att->attbyval || slot->tts_isnull[natt])
230 : 378084 : continue;
231 : :
232 : 320595 : val = slot->tts_values[natt];
233 : :
234 [ + + + - ]: 320595 : if (att->attlen == -1 &&
235 : 235161 : VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
236 : : {
237 : 0 : Size data_length;
238 : :
239 : : /*
240 : : * We want to flatten the expanded value so that the materialized
241 : : * slot doesn't depend on it.
242 : : */
243 : 0 : ExpandedObjectHeader *eoh = DatumGetEOHP(val);
244 : :
245 : 0 : data = (char *) att_nominal_alignby(data,
246 : : att->attalignby);
247 : 0 : data_length = EOH_get_flat_size(eoh);
248 : 0 : EOH_flatten_into(eoh, data, data_length);
249 : :
250 : 0 : slot->tts_values[natt] = PointerGetDatum(data);
251 : 0 : data += data_length;
252 : 0 : }
253 : : else
254 : : {
255 : 320595 : Size data_length = 0;
256 : :
257 : 320595 : data = (char *) att_nominal_alignby(data, att->attalignby);
258 [ + + - + : 320595 : data_length = att_addlength_datum(data_length, att->attlen, val);
# # ]
259 : :
260 : 320595 : memcpy(data, DatumGetPointer(val), data_length);
261 : :
262 : 320595 : slot->tts_values[natt] = PointerGetDatum(data);
263 : 320595 : data += data_length;
264 : 320595 : }
265 [ + + ]: 698679 : }
266 : 594503 : }
267 : :
268 : : static void
269 : 21170 : tts_virtual_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
270 : : {
271 : 21170 : TupleDesc srcdesc = srcslot->tts_tupleDescriptor;
272 : :
273 : 21170 : tts_virtual_clear(dstslot);
274 : :
275 : 21170 : slot_getallattrs(srcslot);
276 : :
277 [ + + ]: 43411 : for (int natt = 0; natt < srcdesc->natts; natt++)
278 : : {
279 : 22241 : dstslot->tts_values[natt] = srcslot->tts_values[natt];
280 : 22241 : dstslot->tts_isnull[natt] = srcslot->tts_isnull[natt];
281 : 22241 : }
282 : :
283 : 21170 : dstslot->tts_nvalid = srcdesc->natts;
284 : 21170 : dstslot->tts_flags &= ~TTS_FLAG_EMPTY;
285 : :
286 : : /* make sure storage doesn't depend on external memory */
287 : 21170 : tts_virtual_materialize(dstslot);
288 : 21170 : }
289 : :
290 : : static HeapTuple
291 : 1916690 : tts_virtual_copy_heap_tuple(TupleTableSlot *slot)
292 : : {
293 [ + - ]: 1916690 : Assert(!TTS_EMPTY(slot));
294 : :
295 : 3833380 : return heap_form_tuple(slot->tts_tupleDescriptor,
296 : 1916690 : slot->tts_values,
297 : 1916690 : slot->tts_isnull);
298 : : }
299 : :
300 : : static MinimalTuple
301 : 3262227 : tts_virtual_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
302 : : {
303 [ + - ]: 3262227 : Assert(!TTS_EMPTY(slot));
304 : :
305 : 6524454 : return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
306 : 3262227 : slot->tts_values,
307 : 3262227 : slot->tts_isnull,
308 : 3262227 : extra);
309 : : }
310 : :
311 : :
312 : : /*
313 : : * TupleTableSlotOps implementation for HeapTupleTableSlot.
314 : : */
315 : :
316 : : static void
317 : 292743 : tts_heap_init(TupleTableSlot *slot)
318 : : {
319 : 292743 : }
320 : :
321 : : static void
322 : 292739 : tts_heap_release(TupleTableSlot *slot)
323 : : {
324 : 292739 : }
325 : :
326 : : static void
327 : 840841 : tts_heap_clear(TupleTableSlot *slot)
328 : : {
329 : 840841 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
330 : :
331 : : /* Free the memory for the heap tuple if it's allowed. */
332 [ + + ]: 840841 : if (TTS_SHOULDFREE(slot))
333 : : {
334 : 115491 : heap_freetuple(hslot->tuple);
335 : 115491 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
336 : 115491 : }
337 : :
338 : 840841 : slot->tts_nvalid = 0;
339 : 840841 : slot->tts_flags |= TTS_FLAG_EMPTY;
340 : 840841 : ItemPointerSetInvalid(&slot->tts_tid);
341 : 840841 : hslot->off = 0;
342 : 840841 : hslot->tuple = NULL;
343 : 840841 : }
344 : :
345 : : static void
346 : 797733 : tts_heap_getsomeattrs(TupleTableSlot *slot, int natts)
347 : : {
348 : 797733 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
349 : :
350 [ + - ]: 797733 : Assert(!TTS_EMPTY(slot));
351 : :
352 : 797733 : slot_deform_heap_tuple(slot, hslot->tuple, &hslot->off, natts);
353 : 797733 : }
354 : :
355 : : static Datum
356 : 0 : tts_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
357 : : {
358 : 0 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
359 : :
360 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
361 : :
362 : : /*
363 : : * In some code paths it's possible to get here with a non-materialized
364 : : * slot, in which case we can't retrieve system columns.
365 : : */
366 [ # # ]: 0 : if (!hslot->tuple)
367 [ # # # # ]: 0 : ereport(ERROR,
368 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
369 : : errmsg("cannot retrieve a system column in this context")));
370 : :
371 : 0 : return heap_getsysattr(hslot->tuple, attnum,
372 : 0 : slot->tts_tupleDescriptor, isnull);
373 : 0 : }
374 : :
375 : : static bool
376 : 0 : tts_heap_is_current_xact_tuple(TupleTableSlot *slot)
377 : : {
378 : 0 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
379 : 0 : TransactionId xmin;
380 : :
381 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
382 : :
383 : : /*
384 : : * In some code paths it's possible to get here with a non-materialized
385 : : * slot, in which case we can't check if tuple is created by the current
386 : : * transaction.
387 : : */
388 [ # # ]: 0 : if (!hslot->tuple)
389 [ # # # # ]: 0 : ereport(ERROR,
390 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
391 : : errmsg("don't have a storage tuple in this context")));
392 : :
393 : 0 : xmin = HeapTupleHeaderGetRawXmin(hslot->tuple->t_data);
394 : :
395 : 0 : return TransactionIdIsCurrentTransactionId(xmin);
396 : 0 : }
397 : :
398 : : static void
399 : 230982 : tts_heap_materialize(TupleTableSlot *slot)
400 : : {
401 : 230982 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
402 : 230982 : MemoryContext oldContext;
403 : :
404 [ + - ]: 230982 : Assert(!TTS_EMPTY(slot));
405 : :
406 : : /* If slot has its tuple already materialized, nothing to do. */
407 [ + + ]: 230982 : if (TTS_SHOULDFREE(slot))
408 : 115491 : return;
409 : :
410 : 115491 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
411 : :
412 : : /*
413 : : * Have to deform from scratch, otherwise tts_values[] entries could point
414 : : * into the non-materialized tuple (which might be gone when accessed).
415 : : */
416 : 115491 : slot->tts_nvalid = 0;
417 : 115491 : hslot->off = 0;
418 : :
419 [ - + ]: 115491 : if (!hslot->tuple)
420 : 230982 : hslot->tuple = heap_form_tuple(slot->tts_tupleDescriptor,
421 : 115491 : slot->tts_values,
422 : 115491 : slot->tts_isnull);
423 : : else
424 : : {
425 : : /*
426 : : * The tuple contained in this slot is not allocated in the memory
427 : : * context of the given slot (else it would have TTS_FLAG_SHOULDFREE
428 : : * set). Copy the tuple into the given slot's memory context.
429 : : */
430 : 0 : hslot->tuple = heap_copytuple(hslot->tuple);
431 : : }
432 : :
433 : 115491 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
434 : :
435 : 115491 : MemoryContextSwitchTo(oldContext);
436 [ - + ]: 230982 : }
437 : :
438 : : static void
439 : 0 : tts_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
440 : : {
441 : 0 : HeapTuple tuple;
442 : 0 : MemoryContext oldcontext;
443 : :
444 : 0 : oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
445 : 0 : tuple = ExecCopySlotHeapTuple(srcslot);
446 : 0 : MemoryContextSwitchTo(oldcontext);
447 : :
448 : 0 : ExecStoreHeapTuple(tuple, dstslot, true);
449 : 0 : }
450 : :
451 : : static HeapTuple
452 : 230982 : tts_heap_get_heap_tuple(TupleTableSlot *slot)
453 : : {
454 : 230982 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
455 : :
456 [ + - ]: 230982 : Assert(!TTS_EMPTY(slot));
457 [ + - ]: 230982 : if (!hslot->tuple)
458 : 0 : tts_heap_materialize(slot);
459 : :
460 : 461964 : return hslot->tuple;
461 : 230982 : }
462 : :
463 : : static HeapTuple
464 : 0 : tts_heap_copy_heap_tuple(TupleTableSlot *slot)
465 : : {
466 : 0 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
467 : :
468 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
469 [ # # ]: 0 : if (!hslot->tuple)
470 : 0 : tts_heap_materialize(slot);
471 : :
472 : 0 : return heap_copytuple(hslot->tuple);
473 : 0 : }
474 : :
475 : : static MinimalTuple
476 : 16 : tts_heap_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
477 : : {
478 : 16 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
479 : :
480 [ + - ]: 16 : if (!hslot->tuple)
481 : 0 : tts_heap_materialize(slot);
482 : :
483 : 32 : return minimal_tuple_from_heap_tuple(hslot->tuple, extra);
484 : 16 : }
485 : :
486 : : static void
487 : 432609 : tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
488 : : {
489 : 432609 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
490 : :
491 : 432609 : tts_heap_clear(slot);
492 : :
493 : 432609 : slot->tts_nvalid = 0;
494 : 432609 : hslot->tuple = tuple;
495 : 432609 : hslot->off = 0;
496 : 432609 : slot->tts_flags &= ~(TTS_FLAG_EMPTY | TTS_FLAG_SHOULDFREE);
497 : 432609 : slot->tts_tid = tuple->t_self;
498 : :
499 [ + - ]: 432609 : if (shouldFree)
500 : 0 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
501 : 432609 : }
502 : :
503 : :
504 : : /*
505 : : * TupleTableSlotOps implementation for MinimalTupleTableSlot.
506 : : */
507 : :
508 : : static void
509 : 43700 : tts_minimal_init(TupleTableSlot *slot)
510 : : {
511 : 43700 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
512 : :
513 : : /*
514 : : * Initialize the heap tuple pointer to access attributes of the minimal
515 : : * tuple contained in the slot as if its a heap tuple.
516 : : */
517 : 43700 : mslot->tuple = &mslot->minhdr;
518 : 43700 : }
519 : :
520 : : static void
521 : 36373 : tts_minimal_release(TupleTableSlot *slot)
522 : : {
523 : 36373 : }
524 : :
525 : : static void
526 : 9425559 : tts_minimal_clear(TupleTableSlot *slot)
527 : : {
528 : 9425559 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
529 : :
530 [ + + ]: 9425559 : if (TTS_SHOULDFREE(slot))
531 : : {
532 : 2630679 : heap_free_minimal_tuple(mslot->mintuple);
533 : 2630679 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
534 : 2630679 : }
535 : :
536 : 9425559 : slot->tts_nvalid = 0;
537 : 9425559 : slot->tts_flags |= TTS_FLAG_EMPTY;
538 : 9425559 : ItemPointerSetInvalid(&slot->tts_tid);
539 : 9425559 : mslot->off = 0;
540 : 9425559 : mslot->mintuple = NULL;
541 : 9425559 : }
542 : :
543 : : static void
544 : 6671908 : tts_minimal_getsomeattrs(TupleTableSlot *slot, int natts)
545 : : {
546 : 6671908 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
547 : :
548 [ + - ]: 6671908 : Assert(!TTS_EMPTY(slot));
549 : :
550 : 6671908 : slot_deform_heap_tuple(slot, mslot->tuple, &mslot->off, natts);
551 : 6671908 : }
552 : :
553 : : /*
554 : : * MinimalTupleTableSlots never provide system attributes. We generally
555 : : * shouldn't get here, but provide a user-friendly message if we do.
556 : : */
557 : : static Datum
558 : 0 : tts_minimal_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
559 : : {
560 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
561 : :
562 [ # # # # ]: 0 : ereport(ERROR,
563 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
564 : : errmsg("cannot retrieve a system column in this context")));
565 : :
566 : 0 : return 0; /* silence compiler warnings */
567 : : }
568 : :
569 : : /*
570 : : * Within MinimalTuple abstraction transaction information is unavailable.
571 : : * We generally shouldn't get here, but provide a user-friendly message if
572 : : * we do.
573 : : */
574 : : static bool
575 : 0 : tts_minimal_is_current_xact_tuple(TupleTableSlot *slot)
576 : : {
577 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
578 : :
579 [ # # # # ]: 0 : ereport(ERROR,
580 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
581 : : errmsg("don't have transaction information for this type of tuple")));
582 : :
583 : 0 : return false; /* silence compiler warnings */
584 : : }
585 : :
586 : : static void
587 : 209309 : tts_minimal_materialize(TupleTableSlot *slot)
588 : : {
589 : 209309 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
590 : 209309 : MemoryContext oldContext;
591 : :
592 [ + - ]: 209309 : Assert(!TTS_EMPTY(slot));
593 : :
594 : : /* If slot has its tuple already materialized, nothing to do. */
595 [ + + ]: 209309 : if (TTS_SHOULDFREE(slot))
596 : 24000 : return;
597 : :
598 : 185309 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
599 : :
600 : : /*
601 : : * Have to deform from scratch, otherwise tts_values[] entries could point
602 : : * into the non-materialized tuple (which might be gone when accessed).
603 : : */
604 : 185309 : slot->tts_nvalid = 0;
605 : 185309 : mslot->off = 0;
606 : :
607 [ + + ]: 185309 : if (!mslot->mintuple)
608 : : {
609 : 370320 : mslot->mintuple = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
610 : 185160 : slot->tts_values,
611 : 185160 : slot->tts_isnull,
612 : : 0);
613 : 185160 : }
614 : : else
615 : : {
616 : : /*
617 : : * The minimal tuple contained in this slot is not allocated in the
618 : : * memory context of the given slot (else it would have
619 : : * TTS_FLAG_SHOULDFREE set). Copy the minimal tuple into the given
620 : : * slot's memory context.
621 : : */
622 : 149 : mslot->mintuple = heap_copy_minimal_tuple(mslot->mintuple, 0);
623 : : }
624 : :
625 : 185309 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
626 : :
627 [ + - ]: 185309 : Assert(mslot->tuple == &mslot->minhdr);
628 : :
629 : 185309 : mslot->minhdr.t_len = mslot->mintuple->t_len + MINIMAL_TUPLE_OFFSET;
630 : 185309 : mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mslot->mintuple - MINIMAL_TUPLE_OFFSET);
631 : :
632 : 185309 : MemoryContextSwitchTo(oldContext);
633 [ - + ]: 209309 : }
634 : :
635 : : static void
636 : 152580 : tts_minimal_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
637 : : {
638 : 152580 : MemoryContext oldcontext;
639 : 152580 : MinimalTuple mintuple;
640 : :
641 : 152580 : oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
642 : 152580 : mintuple = ExecCopySlotMinimalTuple(srcslot);
643 : 152580 : MemoryContextSwitchTo(oldcontext);
644 : :
645 : 152580 : ExecStoreMinimalTuple(mintuple, dstslot, true);
646 : 152580 : }
647 : :
648 : : static MinimalTuple
649 : 843790 : tts_minimal_get_minimal_tuple(TupleTableSlot *slot)
650 : : {
651 : 843790 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
652 : :
653 [ + - ]: 843790 : if (!mslot->mintuple)
654 : 0 : tts_minimal_materialize(slot);
655 : :
656 : 1687580 : return mslot->mintuple;
657 : 843790 : }
658 : :
659 : : static HeapTuple
660 : 94681 : tts_minimal_copy_heap_tuple(TupleTableSlot *slot)
661 : : {
662 : 94681 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
663 : :
664 [ + + ]: 94681 : if (!mslot->mintuple)
665 : 248 : tts_minimal_materialize(slot);
666 : :
667 : 189362 : return heap_tuple_from_minimal_tuple(mslot->mintuple);
668 : 94681 : }
669 : :
670 : : static MinimalTuple
671 : 420917 : tts_minimal_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
672 : : {
673 : 420917 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
674 : :
675 [ + + ]: 420917 : if (!mslot->mintuple)
676 : 163961 : tts_minimal_materialize(slot);
677 : :
678 : 841834 : return heap_copy_minimal_tuple(mslot->mintuple, extra);
679 : 420917 : }
680 : :
681 : : static void
682 : 7707581 : tts_minimal_store_tuple(TupleTableSlot *slot, MinimalTuple mtup, bool shouldFree)
683 : : {
684 : 7707581 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
685 : :
686 : 7707581 : tts_minimal_clear(slot);
687 : :
688 [ + - ]: 7707581 : Assert(!TTS_SHOULDFREE(slot));
689 [ + - ]: 7707581 : Assert(TTS_EMPTY(slot));
690 : :
691 : 7707581 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
692 : 7707581 : slot->tts_nvalid = 0;
693 : 7707581 : mslot->off = 0;
694 : :
695 : 7707581 : mslot->mintuple = mtup;
696 [ + - ]: 7707581 : Assert(mslot->tuple == &mslot->minhdr);
697 : 7707581 : mslot->minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
698 : 7707581 : mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
699 : : /* no need to set t_self or t_tableOid since we won't allow access */
700 : :
701 [ + + ]: 7707581 : if (shouldFree)
702 : 2445600 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
703 : 7707581 : }
704 : :
705 : :
706 : : /*
707 : : * TupleTableSlotOps implementation for BufferHeapTupleTableSlot.
708 : : */
709 : :
710 : : static void
711 : 3623713 : tts_buffer_heap_init(TupleTableSlot *slot)
712 : : {
713 : 3623713 : }
714 : :
715 : : static void
716 : 3621670 : tts_buffer_heap_release(TupleTableSlot *slot)
717 : : {
718 : 3621670 : }
719 : :
720 : : static void
721 : 6706507 : tts_buffer_heap_clear(TupleTableSlot *slot)
722 : : {
723 : 6706507 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
724 : :
725 : : /*
726 : : * Free the memory for heap tuple if allowed. A tuple coming from buffer
727 : : * can never be freed. But we may have materialized a tuple from buffer.
728 : : * Such a tuple can be freed.
729 : : */
730 [ + + ]: 6706507 : if (TTS_SHOULDFREE(slot))
731 : : {
732 : : /* We should have unpinned the buffer while materializing the tuple. */
733 [ + - ]: 1437885 : Assert(!BufferIsValid(bslot->buffer));
734 : :
735 : 1437885 : heap_freetuple(bslot->base.tuple);
736 : 1437885 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
737 : 1437885 : }
738 : :
739 [ + + ]: 6706507 : if (BufferIsValid(bslot->buffer))
740 : 2287843 : ReleaseBuffer(bslot->buffer);
741 : :
742 : 6706507 : slot->tts_nvalid = 0;
743 : 6706507 : slot->tts_flags |= TTS_FLAG_EMPTY;
744 : 6706507 : ItemPointerSetInvalid(&slot->tts_tid);
745 : 6706507 : bslot->base.tuple = NULL;
746 : 6706507 : bslot->base.off = 0;
747 : 6706507 : bslot->buffer = InvalidBuffer;
748 : 6706507 : }
749 : :
750 : : static void
751 : 20251715 : tts_buffer_heap_getsomeattrs(TupleTableSlot *slot, int natts)
752 : : {
753 : 20251715 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
754 : :
755 [ + - ]: 20251715 : Assert(!TTS_EMPTY(slot));
756 : :
757 : 20251715 : slot_deform_heap_tuple(slot, bslot->base.tuple, &bslot->base.off, natts);
758 : 20251715 : }
759 : :
760 : : static Datum
761 : 52 : tts_buffer_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
762 : : {
763 : 52 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
764 : :
765 [ + - ]: 52 : Assert(!TTS_EMPTY(slot));
766 : :
767 : : /*
768 : : * In some code paths it's possible to get here with a non-materialized
769 : : * slot, in which case we can't retrieve system columns.
770 : : */
771 [ + - ]: 52 : if (!bslot->base.tuple)
772 [ # # # # ]: 0 : ereport(ERROR,
773 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
774 : : errmsg("cannot retrieve a system column in this context")));
775 : :
776 : 156 : return heap_getsysattr(bslot->base.tuple, attnum,
777 : 52 : slot->tts_tupleDescriptor, isnull);
778 : 52 : }
779 : :
780 : : static bool
781 : 99 : tts_buffer_is_current_xact_tuple(TupleTableSlot *slot)
782 : : {
783 : 99 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
784 : 99 : TransactionId xmin;
785 : :
786 [ + - ]: 99 : Assert(!TTS_EMPTY(slot));
787 : :
788 : : /*
789 : : * In some code paths it's possible to get here with a non-materialized
790 : : * slot, in which case we can't check if tuple is created by the current
791 : : * transaction.
792 : : */
793 [ + - ]: 99 : if (!bslot->base.tuple)
794 [ # # # # ]: 0 : ereport(ERROR,
795 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
796 : : errmsg("don't have a storage tuple in this context")));
797 : :
798 : 99 : xmin = HeapTupleHeaderGetRawXmin(bslot->base.tuple->t_data);
799 : :
800 : 198 : return TransactionIdIsCurrentTransactionId(xmin);
801 : 99 : }
802 : :
803 : : static void
804 : 2783419 : tts_buffer_heap_materialize(TupleTableSlot *slot)
805 : : {
806 : 2783419 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
807 : 2783419 : MemoryContext oldContext;
808 : :
809 [ + - ]: 2783419 : Assert(!TTS_EMPTY(slot));
810 : :
811 : : /* If slot has its tuple already materialized, nothing to do. */
812 [ + + ]: 2783419 : if (TTS_SHOULDFREE(slot))
813 : 2491603 : return;
814 : :
815 : 291816 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
816 : :
817 : : /*
818 : : * Have to deform from scratch, otherwise tts_values[] entries could point
819 : : * into the non-materialized tuple (which might be gone when accessed).
820 : : */
821 : 291816 : bslot->base.off = 0;
822 : 291816 : slot->tts_nvalid = 0;
823 : :
824 [ + + ]: 291816 : if (!bslot->base.tuple)
825 : : {
826 : : /*
827 : : * Normally BufferHeapTupleTableSlot should have a tuple + buffer
828 : : * associated with it, unless it's materialized (which would've
829 : : * returned above). But when it's useful to allow storing virtual
830 : : * tuples in a buffer slot, which then also needs to be
831 : : * materializable.
832 : : */
833 : 501492 : bslot->base.tuple = heap_form_tuple(slot->tts_tupleDescriptor,
834 : 250746 : slot->tts_values,
835 : 250746 : slot->tts_isnull);
836 : 250746 : }
837 : : else
838 : : {
839 : 41070 : bslot->base.tuple = heap_copytuple(bslot->base.tuple);
840 : :
841 : : /*
842 : : * A heap tuple stored in a BufferHeapTupleTableSlot should have a
843 : : * buffer associated with it, unless it's materialized or virtual.
844 : : */
845 [ - + ]: 41070 : if (likely(BufferIsValid(bslot->buffer)))
846 : 41070 : ReleaseBuffer(bslot->buffer);
847 : 41070 : bslot->buffer = InvalidBuffer;
848 : : }
849 : :
850 : : /*
851 : : * We don't set TTS_FLAG_SHOULDFREE until after releasing the buffer, if
852 : : * any. This avoids having a transient state that would fall foul of our
853 : : * assertions that a slot with TTS_FLAG_SHOULDFREE doesn't own a buffer.
854 : : * In the unlikely event that ReleaseBuffer() above errors out, we'd
855 : : * effectively leak the copied tuple, but that seems fairly harmless.
856 : : */
857 : 291816 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
858 : :
859 : 291816 : MemoryContextSwitchTo(oldContext);
860 [ - + ]: 2783419 : }
861 : :
862 : : static void
863 : 1177382 : tts_buffer_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
864 : : {
865 : 1177382 : BufferHeapTupleTableSlot *bsrcslot = (BufferHeapTupleTableSlot *) srcslot;
866 : 1177382 : BufferHeapTupleTableSlot *bdstslot = (BufferHeapTupleTableSlot *) dstslot;
867 : :
868 : : /*
869 : : * If the source slot is of a different kind, or is a buffer slot that has
870 : : * been materialized / is virtual, make a new copy of the tuple. Otherwise
871 : : * make a new reference to the in-buffer tuple.
872 : : */
873 [ + + ]: 1177382 : if (dstslot->tts_ops != srcslot->tts_ops ||
874 [ + - - + ]: 1003 : TTS_SHOULDFREE(srcslot) ||
875 : 1003 : !bsrcslot->base.tuple)
876 : : {
877 : 1176379 : MemoryContext oldContext;
878 : :
879 : 1176379 : ExecClearTuple(dstslot);
880 : 1176379 : dstslot->tts_flags &= ~TTS_FLAG_EMPTY;
881 : 1176379 : oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt);
882 : 1176379 : bdstslot->base.tuple = ExecCopySlotHeapTuple(srcslot);
883 : 1176379 : dstslot->tts_flags |= TTS_FLAG_SHOULDFREE;
884 : 1176379 : MemoryContextSwitchTo(oldContext);
885 : 1176379 : }
886 : : else
887 : : {
888 [ + - ]: 1003 : Assert(BufferIsValid(bsrcslot->buffer));
889 : :
890 : 2006 : tts_buffer_heap_store_tuple(dstslot, bsrcslot->base.tuple,
891 : 1003 : bsrcslot->buffer, false);
892 : :
893 : : /*
894 : : * The HeapTupleData portion of the source tuple might be shorter
895 : : * lived than the destination slot. Therefore copy the HeapTuple into
896 : : * our slot's tupdata, which is guaranteed to live long enough (but
897 : : * will still point into the buffer).
898 : : */
899 : 1003 : memcpy(&bdstslot->base.tupdata, bdstslot->base.tuple, sizeof(HeapTupleData));
900 : 1003 : bdstslot->base.tuple = &bdstslot->base.tupdata;
901 : : }
902 : 1177382 : }
903 : :
904 : : static HeapTuple
905 : 3379529 : tts_buffer_heap_get_heap_tuple(TupleTableSlot *slot)
906 : : {
907 : 3379529 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
908 : :
909 [ + - ]: 3379529 : Assert(!TTS_EMPTY(slot));
910 : :
911 [ + - ]: 3379529 : if (!bslot->base.tuple)
912 : 0 : tts_buffer_heap_materialize(slot);
913 : :
914 : 6759058 : return bslot->base.tuple;
915 : 3379529 : }
916 : :
917 : : static HeapTuple
918 : 1241008 : tts_buffer_heap_copy_heap_tuple(TupleTableSlot *slot)
919 : : {
920 : 1241008 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
921 : :
922 [ + - ]: 1241008 : Assert(!TTS_EMPTY(slot));
923 : :
924 [ + - ]: 1241008 : if (!bslot->base.tuple)
925 : 0 : tts_buffer_heap_materialize(slot);
926 : :
927 : 2482016 : return heap_copytuple(bslot->base.tuple);
928 : 1241008 : }
929 : :
930 : : static MinimalTuple
931 : 454552 : tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
932 : : {
933 : 454552 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
934 : :
935 [ + - ]: 454552 : Assert(!TTS_EMPTY(slot));
936 : :
937 [ + - ]: 454552 : if (!bslot->base.tuple)
938 : 0 : tts_buffer_heap_materialize(slot);
939 : :
940 : 909104 : return minimal_tuple_from_heap_tuple(bslot->base.tuple, extra);
941 : 454552 : }
942 : :
943 : : static inline void
944 : 22584690 : tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
945 : : Buffer buffer, bool transfer_pin)
946 : : {
947 : 22584690 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
948 : :
949 [ + + ]: 22584690 : if (TTS_SHOULDFREE(slot))
950 : : {
951 : : /* materialized slot shouldn't have a buffer to release */
952 [ + - ]: 41389 : Assert(!BufferIsValid(bslot->buffer));
953 : :
954 : 41389 : heap_freetuple(bslot->base.tuple);
955 : 41389 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
956 : 41389 : }
957 : :
958 : 22584690 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
959 : 22584690 : slot->tts_nvalid = 0;
960 : 22584690 : bslot->base.tuple = tuple;
961 : 22584690 : bslot->base.off = 0;
962 : 22584690 : slot->tts_tid = tuple->t_self;
963 : :
964 : : /*
965 : : * If tuple is on a disk page, keep the page pinned as long as we hold a
966 : : * pointer into it. We assume the caller already has such a pin. If
967 : : * transfer_pin is true, we'll transfer that pin to this slot, if not
968 : : * we'll pin it again ourselves.
969 : : *
970 : : * This is coded to optimize the case where the slot previously held a
971 : : * tuple on the same disk page: in that case releasing and re-acquiring
972 : : * the pin is a waste of cycles. This is a common situation during
973 : : * seqscans, so it's worth troubling over.
974 : : */
975 [ + + ]: 22584690 : if (bslot->buffer != buffer)
976 : : {
977 [ + + ]: 2876941 : if (BufferIsValid(bslot->buffer))
978 : 546873 : ReleaseBuffer(bslot->buffer);
979 : :
980 : 2876941 : bslot->buffer = buffer;
981 : :
982 [ + + - + ]: 2876941 : if (!transfer_pin && BufferIsValid(buffer))
983 : 2071873 : IncrBufferRefCount(buffer);
984 : 2876941 : }
985 [ + + - + ]: 19707749 : else if (transfer_pin && BufferIsValid(buffer))
986 : : {
987 : : /*
988 : : * In transfer_pin mode the caller won't know about the same-page
989 : : * optimization, so we gotta release its pin.
990 : : */
991 : 7801 : ReleaseBuffer(buffer);
992 : 7801 : }
993 : 22584690 : }
994 : :
995 : : /*
996 : : * slot_deform_heap_tuple_internal
997 : : * An always inline helper function for use in slot_deform_heap_tuple to
998 : : * allow the compiler to emit specialized versions of this function for
999 : : * various combinations of "slow" and "hasnulls". For example, if a
1000 : : * given tuple has no nulls, then we needn't check "hasnulls" for every
1001 : : * attribute that we're deforming. The caller can just call this
1002 : : * function with hasnulls set to constant-false and have the compiler
1003 : : * remove the constant-false branches and emit more optimal code.
1004 : : *
1005 : : * Returns the next attnum to deform, which can be equal to natts when the
1006 : : * function manages to deform all requested attributes. *offp is an input and
1007 : : * output parameter which is the byte offset within the tuple to start deforming
1008 : : * from which, on return, gets set to the offset where the next attribute
1009 : : * should be deformed from. *slowp is set to true when subsequent deforming
1010 : : * of this tuple must use a version of this function with "slow" passed as
1011 : : * true.
1012 : : *
1013 : : * Callers cannot assume when we return "attnum" (i.e. all requested
1014 : : * attributes have been deformed) that slow mode isn't required for any
1015 : : * additional deforming as the final attribute may have caused a switch to
1016 : : * slow mode.
1017 : : */
1018 : : static pg_attribute_always_inline int
1019 : 29801495 : slot_deform_heap_tuple_internal(TupleTableSlot *slot, HeapTuple tuple,
1020 : : int attnum, int natts, bool slow,
1021 : : bool hasnulls, uint32 *offp, bool *slowp)
1022 : : {
1023 : 29801495 : TupleDesc tupleDesc = slot->tts_tupleDescriptor;
1024 : 29801495 : Datum *values = slot->tts_values;
1025 : 29801495 : bool *isnull = slot->tts_isnull;
1026 : 29801495 : HeapTupleHeader tup = tuple->t_data;
1027 : 29801495 : char *tp; /* ptr to tuple data */
1028 : 29801495 : bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
1029 : 29801495 : bool slownext = false;
1030 : :
1031 : 29801495 : tp = (char *) tup + tup->t_hoff;
1032 : :
1033 [ + + ]: 96857775 : for (; attnum < natts; attnum++)
1034 : : {
1035 : 70687888 : CompactAttribute *thisatt = TupleDescCompactAttr(tupleDesc, attnum);
1036 : :
1037 [ + + + + ]: 70687888 : if (hasnulls && att_isnull(attnum, bp))
1038 : : {
1039 : 2491075 : values[attnum] = (Datum) 0;
1040 : 2491075 : isnull[attnum] = true;
1041 [ + + ]: 2491075 : if (!slow)
1042 : : {
1043 : 193594 : *slowp = true;
1044 : 193594 : return attnum + 1;
1045 : : }
1046 : : else
1047 : 2297481 : continue;
1048 : : }
1049 : :
1050 : 68196813 : isnull[attnum] = false;
1051 : :
1052 : : /* calculate the offset of this attribute */
1053 [ + + + + ]: 68196813 : if (!slow && thisatt->attcacheoff >= 0)
1054 : 63545103 : *offp = thisatt->attcacheoff;
1055 [ + + ]: 4651710 : else if (thisatt->attlen == -1)
1056 : : {
1057 : : /*
1058 : : * We can only cache the offset for a varlena attribute if the
1059 : : * offset is already suitably aligned, so that there would be no
1060 : : * pad bytes in any case: then the offset will be valid for either
1061 : : * an aligned or unaligned value.
1062 : : */
1063 [ + + + + ]: 2026693 : if (!slow && *offp == att_nominal_alignby(*offp, thisatt->attalignby))
1064 : 7158 : thisatt->attcacheoff = *offp;
1065 : : else
1066 : : {
1067 [ + + ]: 2019535 : *offp = att_pointer_alignby(*offp,
1068 : : thisatt->attalignby,
1069 : : -1,
1070 : : tp + *offp);
1071 : :
1072 [ + + ]: 2019535 : if (!slow)
1073 : 38557 : slownext = true;
1074 : : }
1075 : 2026693 : }
1076 : : else
1077 : : {
1078 : : /* not varlena, so safe to use att_nominal_alignby */
1079 : 2625017 : *offp = att_nominal_alignby(*offp, thisatt->attalignby);
1080 : :
1081 [ + + ]: 2625017 : if (!slow)
1082 : 38106 : thisatt->attcacheoff = *offp;
1083 : : }
1084 : :
1085 : 68196813 : values[attnum] = fetchatt(thisatt, tp + *offp);
1086 : :
1087 [ + + - + : 68196813 : *offp = att_addlength_pointer(*offp, thisatt->attlen, tp + *offp);
# # ]
1088 : :
1089 : : /* check if we need to switch to slow mode */
1090 [ + + ]: 68196813 : if (!slow)
1091 : : {
1092 : : /*
1093 : : * We're unable to deform any further if the above code set
1094 : : * 'slownext', or if this isn't a fixed-width attribute.
1095 : : */
1096 [ + + + + ]: 63628924 : if (slownext || thisatt->attlen <= 0)
1097 : : {
1098 : 3438014 : *slowp = true;
1099 : 3438014 : return attnum + 1;
1100 : : }
1101 : 60190910 : }
1102 [ + + + ]: 70687888 : }
1103 : :
1104 : 26169887 : return natts;
1105 : 29801495 : }
1106 : :
1107 : : /*
1108 : : * slot_deform_heap_tuple
1109 : : * Given a TupleTableSlot, extract data from the slot's physical tuple
1110 : : * into its Datum/isnull arrays. Data is extracted up through the
1111 : : * natts'th column (caller must ensure this is a legal column number).
1112 : : *
1113 : : * This is essentially an incremental version of heap_deform_tuple:
1114 : : * on each call we extract attributes up to the one needed, without
1115 : : * re-computing information about previously extracted attributes.
1116 : : * slot->tts_nvalid is the number of attributes already extracted.
1117 : : *
1118 : : * This is marked as always inline, so the different offp for different types
1119 : : * of slots gets optimized away.
1120 : : */
1121 : : static pg_attribute_always_inline void
1122 : 27721356 : slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
1123 : : int natts)
1124 : : {
1125 : 27721356 : bool hasnulls = HeapTupleHasNulls(tuple);
1126 : 27721356 : int attnum;
1127 : 27721356 : uint32 off; /* offset in tuple data */
1128 : 27721356 : bool slow; /* can we use/set attcacheoff? */
1129 : :
1130 : : /* We can only fetch as many attributes as the tuple has. */
1131 [ + + ]: 27721356 : natts = Min(HeapTupleHeaderGetNatts(tuple->t_data), natts);
1132 : :
1133 : : /*
1134 : : * Check whether the first call for this tuple, and initialize or restore
1135 : : * loop state.
1136 : : */
1137 : 27721356 : attnum = slot->tts_nvalid;
1138 [ + + ]: 27721356 : if (attnum == 0)
1139 : : {
1140 : : /* Start from the first attribute */
1141 : 25350361 : off = 0;
1142 : 25350361 : slow = false;
1143 : 25350361 : }
1144 : : else
1145 : : {
1146 : : /* Restore state from previous execution */
1147 : 2370995 : off = *offp;
1148 : 2370995 : slow = TTS_SLOW(slot);
1149 : : }
1150 : :
1151 : : /*
1152 : : * If 'slow' isn't set, try deforming using deforming code that does not
1153 : : * contain any of the extra checks required for non-fixed offset
1154 : : * deforming. During deforming, if or when we find a NULL or a variable
1155 : : * length attribute, we'll switch to a deforming method which includes the
1156 : : * extra code required for non-fixed offset deforming, a.k.a slow mode.
1157 : : * Because this is performance critical, we inline
1158 : : * slot_deform_heap_tuple_internal passing the 'slow' and 'hasnull'
1159 : : * parameters as constants to allow the compiler to emit specialized code
1160 : : * with the known-const false comparisons and subsequent branches removed.
1161 : : */
1162 [ + + ]: 27721356 : if (!slow)
1163 : : {
1164 : : /* Tuple without any NULLs? We can skip doing any NULL checking */
1165 [ + + ]: 27538040 : if (!hasnulls)
1166 : 47159614 : attnum = slot_deform_heap_tuple_internal(slot,
1167 : 23579807 : tuple,
1168 : 23579807 : attnum,
1169 : 23579807 : natts,
1170 : : false, /* slow */
1171 : : false, /* hasnulls */
1172 : : &off,
1173 : : &slow);
1174 : : else
1175 : 7916466 : attnum = slot_deform_heap_tuple_internal(slot,
1176 : 3958233 : tuple,
1177 : 3958233 : attnum,
1178 : 3958233 : natts,
1179 : : false, /* slow */
1180 : : true, /* hasnulls */
1181 : : &off,
1182 : : &slow);
1183 : 27538040 : }
1184 : :
1185 : : /* If there's still work to do then we must be in slow mode */
1186 [ + + ]: 27721356 : if (attnum < natts)
1187 : : {
1188 : : /* XXX is it worth adding a separate call when hasnulls is false? */
1189 : 4526910 : attnum = slot_deform_heap_tuple_internal(slot,
1190 : 2263455 : tuple,
1191 : 2263455 : attnum,
1192 : 2263455 : natts,
1193 : : true, /* slow */
1194 : 2263455 : hasnulls,
1195 : : &off,
1196 : : &slow);
1197 : 2263455 : }
1198 : :
1199 : : /*
1200 : : * Save state for next execution
1201 : : */
1202 : 27721356 : slot->tts_nvalid = attnum;
1203 : 27721356 : *offp = off;
1204 [ + + ]: 27721356 : if (slow)
1205 : 3814924 : slot->tts_flags |= TTS_FLAG_SLOW;
1206 : : else
1207 : 23906432 : slot->tts_flags &= ~TTS_FLAG_SLOW;
1208 : 27721356 : }
1209 : :
1210 : : const TupleTableSlotOps TTSOpsVirtual = {
1211 : : .base_slot_size = sizeof(VirtualTupleTableSlot),
1212 : : .init = tts_virtual_init,
1213 : : .release = tts_virtual_release,
1214 : : .clear = tts_virtual_clear,
1215 : : .getsomeattrs = tts_virtual_getsomeattrs,
1216 : : .getsysattr = tts_virtual_getsysattr,
1217 : : .materialize = tts_virtual_materialize,
1218 : : .is_current_xact_tuple = tts_virtual_is_current_xact_tuple,
1219 : : .copyslot = tts_virtual_copyslot,
1220 : :
1221 : : /*
1222 : : * A virtual tuple table slot can not "own" a heap tuple or a minimal
1223 : : * tuple.
1224 : : */
1225 : : .get_heap_tuple = NULL,
1226 : : .get_minimal_tuple = NULL,
1227 : : .copy_heap_tuple = tts_virtual_copy_heap_tuple,
1228 : : .copy_minimal_tuple = tts_virtual_copy_minimal_tuple
1229 : : };
1230 : :
1231 : : const TupleTableSlotOps TTSOpsHeapTuple = {
1232 : : .base_slot_size = sizeof(HeapTupleTableSlot),
1233 : : .init = tts_heap_init,
1234 : : .release = tts_heap_release,
1235 : : .clear = tts_heap_clear,
1236 : : .getsomeattrs = tts_heap_getsomeattrs,
1237 : : .getsysattr = tts_heap_getsysattr,
1238 : : .is_current_xact_tuple = tts_heap_is_current_xact_tuple,
1239 : : .materialize = tts_heap_materialize,
1240 : : .copyslot = tts_heap_copyslot,
1241 : : .get_heap_tuple = tts_heap_get_heap_tuple,
1242 : :
1243 : : /* A heap tuple table slot can not "own" a minimal tuple. */
1244 : : .get_minimal_tuple = NULL,
1245 : : .copy_heap_tuple = tts_heap_copy_heap_tuple,
1246 : : .copy_minimal_tuple = tts_heap_copy_minimal_tuple
1247 : : };
1248 : :
1249 : : const TupleTableSlotOps TTSOpsMinimalTuple = {
1250 : : .base_slot_size = sizeof(MinimalTupleTableSlot),
1251 : : .init = tts_minimal_init,
1252 : : .release = tts_minimal_release,
1253 : : .clear = tts_minimal_clear,
1254 : : .getsomeattrs = tts_minimal_getsomeattrs,
1255 : : .getsysattr = tts_minimal_getsysattr,
1256 : : .is_current_xact_tuple = tts_minimal_is_current_xact_tuple,
1257 : : .materialize = tts_minimal_materialize,
1258 : : .copyslot = tts_minimal_copyslot,
1259 : :
1260 : : /* A minimal tuple table slot can not "own" a heap tuple. */
1261 : : .get_heap_tuple = NULL,
1262 : : .get_minimal_tuple = tts_minimal_get_minimal_tuple,
1263 : : .copy_heap_tuple = tts_minimal_copy_heap_tuple,
1264 : : .copy_minimal_tuple = tts_minimal_copy_minimal_tuple
1265 : : };
1266 : :
1267 : : const TupleTableSlotOps TTSOpsBufferHeapTuple = {
1268 : : .base_slot_size = sizeof(BufferHeapTupleTableSlot),
1269 : : .init = tts_buffer_heap_init,
1270 : : .release = tts_buffer_heap_release,
1271 : : .clear = tts_buffer_heap_clear,
1272 : : .getsomeattrs = tts_buffer_heap_getsomeattrs,
1273 : : .getsysattr = tts_buffer_heap_getsysattr,
1274 : : .is_current_xact_tuple = tts_buffer_is_current_xact_tuple,
1275 : : .materialize = tts_buffer_heap_materialize,
1276 : : .copyslot = tts_buffer_heap_copyslot,
1277 : : .get_heap_tuple = tts_buffer_heap_get_heap_tuple,
1278 : :
1279 : : /* A buffer heap tuple table slot can not "own" a minimal tuple. */
1280 : : .get_minimal_tuple = NULL,
1281 : : .copy_heap_tuple = tts_buffer_heap_copy_heap_tuple,
1282 : : .copy_minimal_tuple = tts_buffer_heap_copy_minimal_tuple
1283 : : };
1284 : :
1285 : :
1286 : : /* ----------------------------------------------------------------
1287 : : * tuple table create/delete functions
1288 : : * ----------------------------------------------------------------
1289 : : */
1290 : :
1291 : : /* --------------------------------
1292 : : * MakeTupleTableSlot
1293 : : *
1294 : : * Basic routine to make an empty TupleTableSlot of given
1295 : : * TupleTableSlotType. If tupleDesc is specified the slot's descriptor is
1296 : : * fixed for its lifetime, gaining some efficiency. If that's
1297 : : * undesirable, pass NULL.
1298 : : * --------------------------------
1299 : : */
1300 : : TupleTableSlot *
1301 : 4866288 : MakeTupleTableSlot(TupleDesc tupleDesc,
1302 : : const TupleTableSlotOps *tts_ops)
1303 : : {
1304 : 4866288 : Size basesz,
1305 : : allocsz;
1306 : 4866288 : TupleTableSlot *slot;
1307 : :
1308 : 4866288 : basesz = tts_ops->base_slot_size;
1309 : :
1310 : : /*
1311 : : * When a fixed descriptor is specified, we can reduce overhead by
1312 : : * allocating the entire slot in one go.
1313 : : */
1314 [ + + ]: 4866288 : if (tupleDesc)
1315 : 13373787 : allocsz = MAXALIGN(basesz) +
1316 : 8915858 : MAXALIGN(tupleDesc->natts * sizeof(Datum)) +
1317 : 4457929 : MAXALIGN(tupleDesc->natts * sizeof(bool));
1318 : : else
1319 : 408359 : allocsz = basesz;
1320 : :
1321 : 4866288 : slot = palloc0(allocsz);
1322 : : /* const for optimization purposes, OK to modify at allocation time */
1323 : 4866288 : *((const TupleTableSlotOps **) &slot->tts_ops) = tts_ops;
1324 : 4866288 : slot->type = T_TupleTableSlot;
1325 : 4866288 : slot->tts_flags |= TTS_FLAG_EMPTY;
1326 [ + + ]: 4866288 : if (tupleDesc != NULL)
1327 : 4457929 : slot->tts_flags |= TTS_FLAG_FIXED;
1328 : 4866288 : slot->tts_tupleDescriptor = tupleDesc;
1329 : 4866288 : slot->tts_mcxt = CurrentMemoryContext;
1330 : 4866288 : slot->tts_nvalid = 0;
1331 : :
1332 [ + + ]: 4866288 : if (tupleDesc != NULL)
1333 : : {
1334 : 4457929 : slot->tts_values = (Datum *)
1335 : 4457929 : (((char *) slot)
1336 : 4457929 : + MAXALIGN(basesz));
1337 : 4457929 : slot->tts_isnull = (bool *)
1338 : 4457929 : (((char *) slot)
1339 : 4457929 : + MAXALIGN(basesz)
1340 : 4457929 : + MAXALIGN(tupleDesc->natts * sizeof(Datum)));
1341 : :
1342 [ + + ]: 4457929 : PinTupleDesc(tupleDesc);
1343 : 4457929 : }
1344 : :
1345 : : /*
1346 : : * And allow slot type specific initialization.
1347 : : */
1348 : 4866288 : slot->tts_ops->init(slot);
1349 : :
1350 : 9732576 : return slot;
1351 : 4866288 : }
1352 : :
1353 : : /* --------------------------------
1354 : : * ExecAllocTableSlot
1355 : : *
1356 : : * Create a tuple table slot within a tuple table (which is just a List).
1357 : : * --------------------------------
1358 : : */
1359 : : TupleTableSlot *
1360 : 1381212 : ExecAllocTableSlot(List **tupleTable, TupleDesc desc,
1361 : : const TupleTableSlotOps *tts_ops)
1362 : : {
1363 : 1381212 : TupleTableSlot *slot = MakeTupleTableSlot(desc, tts_ops);
1364 : :
1365 : 1381212 : *tupleTable = lappend(*tupleTable, slot);
1366 : :
1367 : 2762424 : return slot;
1368 : 1381212 : }
1369 : :
1370 : : /* --------------------------------
1371 : : * ExecResetTupleTable
1372 : : *
1373 : : * This releases any resources (buffer pins, tupdesc refcounts)
1374 : : * held by the tuple table, and optionally releases the memory
1375 : : * occupied by the tuple table data structure.
1376 : : * It is expected that this routine be called by ExecEndPlan().
1377 : : * --------------------------------
1378 : : */
1379 : : void
1380 : 848121 : ExecResetTupleTable(List *tupleTable, /* tuple table */
1381 : : bool shouldFree) /* true if we should free memory */
1382 : : {
1383 : 848121 : ListCell *lc;
1384 : :
1385 [ + + + + : 2630434 : foreach(lc, tupleTable)
+ + ]
1386 : : {
1387 : 1782313 : TupleTableSlot *slot = lfirst_node(TupleTableSlot, lc);
1388 : :
1389 : : /* Always release resources and reset the slot to empty */
1390 : 1782313 : ExecClearTuple(slot);
1391 : 1782313 : slot->tts_ops->release(slot);
1392 [ + + ]: 1782313 : if (slot->tts_tupleDescriptor)
1393 : : {
1394 [ + + ]: 1782304 : ReleaseTupleDesc(slot->tts_tupleDescriptor);
1395 : 1782304 : slot->tts_tupleDescriptor = NULL;
1396 : 1782304 : }
1397 : :
1398 : : /* If shouldFree, release memory occupied by the slot itself */
1399 [ + + ]: 1782313 : if (shouldFree)
1400 : : {
1401 [ + - ]: 400518 : if (!TTS_FIXED(slot))
1402 : : {
1403 [ # # ]: 0 : if (slot->tts_values)
1404 : 0 : pfree(slot->tts_values);
1405 [ # # ]: 0 : if (slot->tts_isnull)
1406 : 0 : pfree(slot->tts_isnull);
1407 : 0 : }
1408 : 400518 : pfree(slot);
1409 : 400518 : }
1410 : 1782313 : }
1411 : :
1412 : : /* If shouldFree, release the list structure */
1413 [ + + ]: 848121 : if (shouldFree)
1414 : 400502 : list_free(tupleTable);
1415 : 848121 : }
1416 : :
1417 : : /* --------------------------------
1418 : : * MakeSingleTupleTableSlot
1419 : : *
1420 : : * This is a convenience routine for operations that need a standalone
1421 : : * TupleTableSlot not gotten from the main executor tuple table. It makes
1422 : : * a single slot of given TupleTableSlotType and initializes it to use the
1423 : : * given tuple descriptor.
1424 : : * --------------------------------
1425 : : */
1426 : : TupleTableSlot *
1427 : 3485054 : MakeSingleTupleTableSlot(TupleDesc tupdesc,
1428 : : const TupleTableSlotOps *tts_ops)
1429 : : {
1430 : 3485054 : TupleTableSlot *slot = MakeTupleTableSlot(tupdesc, tts_ops);
1431 : :
1432 : 6970108 : return slot;
1433 : 3485054 : }
1434 : :
1435 : : /* --------------------------------
1436 : : * ExecDropSingleTupleTableSlot
1437 : : *
1438 : : * Release a TupleTableSlot made with MakeSingleTupleTableSlot.
1439 : : * DON'T use this on a slot that's part of a tuple table list!
1440 : : * --------------------------------
1441 : : */
1442 : : void
1443 : 3067776 : ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
1444 : : {
1445 : : /* This should match ExecResetTupleTable's processing of one slot */
1446 [ + - ]: 3067776 : Assert(IsA(slot, TupleTableSlot));
1447 : 3067776 : ExecClearTuple(slot);
1448 : 3067776 : slot->tts_ops->release(slot);
1449 [ - + ]: 3067776 : if (slot->tts_tupleDescriptor)
1450 [ + + ]: 3067776 : ReleaseTupleDesc(slot->tts_tupleDescriptor);
1451 [ + - ]: 3067776 : if (!TTS_FIXED(slot))
1452 : : {
1453 [ # # ]: 0 : if (slot->tts_values)
1454 : 0 : pfree(slot->tts_values);
1455 [ # # ]: 0 : if (slot->tts_isnull)
1456 : 0 : pfree(slot->tts_isnull);
1457 : 0 : }
1458 : 3067776 : pfree(slot);
1459 : 3067776 : }
1460 : :
1461 : :
1462 : : /* ----------------------------------------------------------------
1463 : : * tuple table slot accessor functions
1464 : : * ----------------------------------------------------------------
1465 : : */
1466 : :
1467 : : /* --------------------------------
1468 : : * ExecSetSlotDescriptor
1469 : : *
1470 : : * This function is used to set the tuple descriptor associated
1471 : : * with the slot's tuple. The passed descriptor must have lifespan
1472 : : * at least equal to the slot's. If it is a reference-counted descriptor
1473 : : * then the reference count is incremented for as long as the slot holds
1474 : : * a reference.
1475 : : * --------------------------------
1476 : : */
1477 : : void
1478 : 408350 : ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
1479 : : TupleDesc tupdesc) /* new tuple descriptor */
1480 : : {
1481 [ + - ]: 408350 : Assert(!TTS_FIXED(slot));
1482 : :
1483 : : /* For safety, make sure slot is empty before changing it */
1484 : 408350 : ExecClearTuple(slot);
1485 : :
1486 : : /*
1487 : : * Release any old descriptor. Also release old Datum/isnull arrays if
1488 : : * present (we don't bother to check if they could be re-used).
1489 : : */
1490 [ + - ]: 408350 : if (slot->tts_tupleDescriptor)
1491 [ # # ]: 0 : ReleaseTupleDesc(slot->tts_tupleDescriptor);
1492 : :
1493 [ + - ]: 408350 : if (slot->tts_values)
1494 : 0 : pfree(slot->tts_values);
1495 [ + - ]: 408350 : if (slot->tts_isnull)
1496 : 0 : pfree(slot->tts_isnull);
1497 : :
1498 : : /*
1499 : : * Install the new descriptor; if it's refcounted, bump its refcount.
1500 : : */
1501 : 408350 : slot->tts_tupleDescriptor = tupdesc;
1502 [ + - ]: 408350 : PinTupleDesc(tupdesc);
1503 : :
1504 : : /*
1505 : : * Allocate Datum/isnull arrays of the appropriate size. These must have
1506 : : * the same lifetime as the slot, so allocate in the slot's own context.
1507 : : */
1508 : 408350 : slot->tts_values = (Datum *)
1509 : 408350 : MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(Datum));
1510 : 408350 : slot->tts_isnull = (bool *)
1511 : 408350 : MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(bool));
1512 : 408350 : }
1513 : :
1514 : : /* --------------------------------
1515 : : * ExecStoreHeapTuple
1516 : : *
1517 : : * This function is used to store an on-the-fly physical tuple into a specified
1518 : : * slot in the tuple table.
1519 : : *
1520 : : * tuple: tuple to store
1521 : : * slot: TTSOpsHeapTuple type slot to store it in
1522 : : * shouldFree: true if ExecClearTuple should pfree() the tuple
1523 : : * when done with it
1524 : : *
1525 : : * shouldFree is normally set 'true' for tuples constructed on-the-fly. But it
1526 : : * can be 'false' when the referenced tuple is held in a tuple table slot
1527 : : * belonging to a lower-level executor Proc node. In this case the lower-level
1528 : : * slot retains ownership and responsibility for eventually releasing the
1529 : : * tuple. When this method is used, we must be certain that the upper-level
1530 : : * Proc node will lose interest in the tuple sooner than the lower-level one
1531 : : * does! If you're not certain, copy the lower-level tuple with heap_copytuple
1532 : : * and let the upper-level table slot assume ownership of the copy!
1533 : : *
1534 : : * Return value is just the passed-in slot pointer.
1535 : : *
1536 : : * If the target slot is not guaranteed to be TTSOpsHeapTuple type slot, use
1537 : : * the, more expensive, ExecForceStoreHeapTuple().
1538 : : * --------------------------------
1539 : : */
1540 : : TupleTableSlot *
1541 : 432609 : ExecStoreHeapTuple(HeapTuple tuple,
1542 : : TupleTableSlot *slot,
1543 : : bool shouldFree)
1544 : : {
1545 : : /*
1546 : : * sanity checks
1547 : : */
1548 [ + - ]: 432609 : Assert(tuple != NULL);
1549 [ + - ]: 432609 : Assert(slot != NULL);
1550 [ + - ]: 432609 : Assert(slot->tts_tupleDescriptor != NULL);
1551 : :
1552 [ + - ]: 432609 : if (unlikely(!TTS_IS_HEAPTUPLE(slot)))
1553 [ # # # # ]: 0 : elog(ERROR, "trying to store a heap tuple into wrong type of slot");
1554 : 432609 : tts_heap_store_tuple(slot, tuple, shouldFree);
1555 : :
1556 : 432609 : slot->tts_tableOid = tuple->t_tableOid;
1557 : :
1558 : 432609 : return slot;
1559 : : }
1560 : :
1561 : : /* --------------------------------
1562 : : * ExecStoreBufferHeapTuple
1563 : : *
1564 : : * This function is used to store an on-disk physical tuple from a buffer
1565 : : * into a specified slot in the tuple table.
1566 : : *
1567 : : * tuple: tuple to store
1568 : : * slot: TTSOpsBufferHeapTuple type slot to store it in
1569 : : * buffer: disk buffer if tuple is in a disk page, else InvalidBuffer
1570 : : *
1571 : : * The tuple table code acquires a pin on the buffer which is held until the
1572 : : * slot is cleared, so that the tuple won't go away on us.
1573 : : *
1574 : : * Return value is just the passed-in slot pointer.
1575 : : *
1576 : : * If the target slot is not guaranteed to be TTSOpsBufferHeapTuple type slot,
1577 : : * use the, more expensive, ExecForceStoreHeapTuple().
1578 : : * --------------------------------
1579 : : */
1580 : : TupleTableSlot *
1581 : 21770818 : ExecStoreBufferHeapTuple(HeapTuple tuple,
1582 : : TupleTableSlot *slot,
1583 : : Buffer buffer)
1584 : : {
1585 : : /*
1586 : : * sanity checks
1587 : : */
1588 [ + - ]: 21770818 : Assert(tuple != NULL);
1589 [ + - ]: 21770818 : Assert(slot != NULL);
1590 [ + - ]: 21770818 : Assert(slot->tts_tupleDescriptor != NULL);
1591 [ + - ]: 21770818 : Assert(BufferIsValid(buffer));
1592 : :
1593 [ + - ]: 21770818 : if (unlikely(!TTS_IS_BUFFERTUPLE(slot)))
1594 [ # # # # ]: 0 : elog(ERROR, "trying to store an on-disk heap tuple into wrong type of slot");
1595 : 21770818 : tts_buffer_heap_store_tuple(slot, tuple, buffer, false);
1596 : :
1597 : 21770818 : slot->tts_tableOid = tuple->t_tableOid;
1598 : :
1599 : 21770818 : return slot;
1600 : : }
1601 : :
1602 : : /*
1603 : : * Like ExecStoreBufferHeapTuple, but transfer an existing pin from the caller
1604 : : * to the slot, i.e. the caller doesn't need to, and may not, release the pin.
1605 : : */
1606 : : TupleTableSlot *
1607 : 812869 : ExecStorePinnedBufferHeapTuple(HeapTuple tuple,
1608 : : TupleTableSlot *slot,
1609 : : Buffer buffer)
1610 : : {
1611 : : /*
1612 : : * sanity checks
1613 : : */
1614 [ + - ]: 812869 : Assert(tuple != NULL);
1615 [ + - ]: 812869 : Assert(slot != NULL);
1616 [ + - ]: 812869 : Assert(slot->tts_tupleDescriptor != NULL);
1617 [ + - ]: 812869 : Assert(BufferIsValid(buffer));
1618 : :
1619 [ + - ]: 812869 : if (unlikely(!TTS_IS_BUFFERTUPLE(slot)))
1620 [ # # # # ]: 0 : elog(ERROR, "trying to store an on-disk heap tuple into wrong type of slot");
1621 : 812869 : tts_buffer_heap_store_tuple(slot, tuple, buffer, true);
1622 : :
1623 : 812869 : slot->tts_tableOid = tuple->t_tableOid;
1624 : :
1625 : 812869 : return slot;
1626 : : }
1627 : :
1628 : : /*
1629 : : * Store a minimal tuple into TTSOpsMinimalTuple type slot.
1630 : : *
1631 : : * If the target slot is not guaranteed to be TTSOpsMinimalTuple type slot,
1632 : : * use the, more expensive, ExecForceStoreMinimalTuple().
1633 : : */
1634 : : TupleTableSlot *
1635 : 6934774 : ExecStoreMinimalTuple(MinimalTuple mtup,
1636 : : TupleTableSlot *slot,
1637 : : bool shouldFree)
1638 : : {
1639 : : /*
1640 : : * sanity checks
1641 : : */
1642 [ + - ]: 6934774 : Assert(mtup != NULL);
1643 [ + - ]: 6934774 : Assert(slot != NULL);
1644 [ + - ]: 6934774 : Assert(slot->tts_tupleDescriptor != NULL);
1645 : :
1646 [ + - ]: 6934774 : if (unlikely(!TTS_IS_MINIMALTUPLE(slot)))
1647 [ # # # # ]: 0 : elog(ERROR, "trying to store a minimal tuple into wrong type of slot");
1648 : 6934774 : tts_minimal_store_tuple(slot, mtup, shouldFree);
1649 : :
1650 : 6934774 : return slot;
1651 : : }
1652 : :
1653 : : /*
1654 : : * Store a HeapTuple into any kind of slot, performing conversion if
1655 : : * necessary.
1656 : : */
1657 : : void
1658 : 266333 : ExecForceStoreHeapTuple(HeapTuple tuple,
1659 : : TupleTableSlot *slot,
1660 : : bool shouldFree)
1661 : : {
1662 [ + - ]: 266333 : if (TTS_IS_HEAPTUPLE(slot))
1663 : : {
1664 : 0 : ExecStoreHeapTuple(tuple, slot, shouldFree);
1665 : 0 : }
1666 [ + + ]: 266333 : else if (TTS_IS_BUFFERTUPLE(slot))
1667 : : {
1668 : 11556 : MemoryContext oldContext;
1669 : 11556 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
1670 : :
1671 : 11556 : ExecClearTuple(slot);
1672 : 11556 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
1673 : 11556 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
1674 : 11556 : bslot->base.tuple = heap_copytuple(tuple);
1675 : 11556 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
1676 : 11556 : MemoryContextSwitchTo(oldContext);
1677 : :
1678 [ + + ]: 11556 : if (shouldFree)
1679 : 11396 : pfree(tuple);
1680 : 11556 : }
1681 : : else
1682 : : {
1683 : 254777 : ExecClearTuple(slot);
1684 : 509554 : heap_deform_tuple(tuple, slot->tts_tupleDescriptor,
1685 : 254777 : slot->tts_values, slot->tts_isnull);
1686 : 254777 : ExecStoreVirtualTuple(slot);
1687 : :
1688 [ + + ]: 254777 : if (shouldFree)
1689 : : {
1690 : 21819 : ExecMaterializeSlot(slot);
1691 : 21819 : pfree(tuple);
1692 : 21819 : }
1693 : : }
1694 : 266333 : }
1695 : :
1696 : : /*
1697 : : * Store a MinimalTuple into any kind of slot, performing conversion if
1698 : : * necessary.
1699 : : */
1700 : : void
1701 : 1212525 : ExecForceStoreMinimalTuple(MinimalTuple mtup,
1702 : : TupleTableSlot *slot,
1703 : : bool shouldFree)
1704 : : {
1705 [ + + ]: 1212525 : if (TTS_IS_MINIMALTUPLE(slot))
1706 : : {
1707 : 772807 : tts_minimal_store_tuple(slot, mtup, shouldFree);
1708 : 772807 : }
1709 : : else
1710 : : {
1711 : 439718 : HeapTupleData htup;
1712 : :
1713 : 439718 : ExecClearTuple(slot);
1714 : :
1715 : 439718 : htup.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
1716 : 439718 : htup.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
1717 : 879436 : heap_deform_tuple(&htup, slot->tts_tupleDescriptor,
1718 : 439718 : slot->tts_values, slot->tts_isnull);
1719 : 439718 : ExecStoreVirtualTuple(slot);
1720 : :
1721 [ + + ]: 439718 : if (shouldFree)
1722 : : {
1723 : 239714 : ExecMaterializeSlot(slot);
1724 : 239714 : pfree(mtup);
1725 : 239714 : }
1726 : 439718 : }
1727 : 1212525 : }
1728 : :
1729 : : /* --------------------------------
1730 : : * ExecStoreVirtualTuple
1731 : : * Mark a slot as containing a virtual tuple.
1732 : : *
1733 : : * The protocol for loading a slot with virtual tuple data is:
1734 : : * * Call ExecClearTuple to mark the slot empty.
1735 : : * * Store data into the Datum/isnull arrays.
1736 : : * * Call ExecStoreVirtualTuple to mark the slot valid.
1737 : : * This is a bit unclean but it avoids one round of data copying.
1738 : : * --------------------------------
1739 : : */
1740 : : TupleTableSlot *
1741 : 3902048 : ExecStoreVirtualTuple(TupleTableSlot *slot)
1742 : : {
1743 : : /*
1744 : : * sanity checks
1745 : : */
1746 [ + - ]: 3902048 : Assert(slot != NULL);
1747 [ + - ]: 3902048 : Assert(slot->tts_tupleDescriptor != NULL);
1748 [ + - ]: 3902048 : Assert(TTS_EMPTY(slot));
1749 : :
1750 : 3902048 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
1751 : 3902048 : slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
1752 : :
1753 : 3902048 : return slot;
1754 : : }
1755 : :
1756 : : /* --------------------------------
1757 : : * ExecStoreAllNullTuple
1758 : : * Set up the slot to contain a null in every column.
1759 : : *
1760 : : * At first glance this might sound just like ExecClearTuple, but it's
1761 : : * entirely different: the slot ends up full, not empty.
1762 : : * --------------------------------
1763 : : */
1764 : : TupleTableSlot *
1765 : 4090 : ExecStoreAllNullTuple(TupleTableSlot *slot)
1766 : : {
1767 : : /*
1768 : : * sanity checks
1769 : : */
1770 [ + - ]: 4090 : Assert(slot != NULL);
1771 [ + - ]: 4090 : Assert(slot->tts_tupleDescriptor != NULL);
1772 : :
1773 : : /* Clear any old contents */
1774 : 4090 : ExecClearTuple(slot);
1775 : :
1776 : : /*
1777 : : * Fill all the columns of the virtual tuple with nulls
1778 : : */
1779 [ + - + - : 32487 : MemSet(slot->tts_values, 0,
+ - - + +
+ ]
1780 : : slot->tts_tupleDescriptor->natts * sizeof(Datum));
1781 : 4090 : memset(slot->tts_isnull, true,
1782 : : slot->tts_tupleDescriptor->natts * sizeof(bool));
1783 : :
1784 : 4090 : return ExecStoreVirtualTuple(slot);
1785 : : }
1786 : :
1787 : : /*
1788 : : * Store a HeapTuple in datum form, into a slot. That always requires
1789 : : * deforming it and storing it in virtual form.
1790 : : *
1791 : : * Until the slot is materialized, the contents of the slot depend on the
1792 : : * datum.
1793 : : */
1794 : : void
1795 : 0 : ExecStoreHeapTupleDatum(Datum data, TupleTableSlot *slot)
1796 : : {
1797 : 0 : HeapTupleData tuple = {0};
1798 : 0 : HeapTupleHeader td;
1799 : :
1800 : 0 : td = DatumGetHeapTupleHeader(data);
1801 : :
1802 : 0 : tuple.t_len = HeapTupleHeaderGetDatumLength(td);
1803 : 0 : tuple.t_self = td->t_ctid;
1804 : 0 : tuple.t_data = td;
1805 : :
1806 : 0 : ExecClearTuple(slot);
1807 : :
1808 : 0 : heap_deform_tuple(&tuple, slot->tts_tupleDescriptor,
1809 : 0 : slot->tts_values, slot->tts_isnull);
1810 : 0 : ExecStoreVirtualTuple(slot);
1811 : 0 : }
1812 : :
1813 : : /*
1814 : : * ExecFetchSlotHeapTuple - fetch HeapTuple representing the slot's content
1815 : : *
1816 : : * The returned HeapTuple represents the slot's content as closely as
1817 : : * possible.
1818 : : *
1819 : : * If materialize is true, the contents of the slots will be made independent
1820 : : * from the underlying storage (i.e. all buffer pins are released, memory is
1821 : : * allocated in the slot's context).
1822 : : *
1823 : : * If shouldFree is not-NULL it'll be set to true if the returned tuple has
1824 : : * been allocated in the calling memory context, and must be freed by the
1825 : : * caller (via explicit pfree() or a memory context reset).
1826 : : *
1827 : : * NB: If materialize is true, modifications of the returned tuple are
1828 : : * allowed. But it depends on the type of the slot whether such modifications
1829 : : * will also affect the slot's contents. While that is not the nicest
1830 : : * behaviour, all such modifications are in the process of being removed.
1831 : : */
1832 : : HeapTuple
1833 : 3997306 : ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
1834 : : {
1835 : : /*
1836 : : * sanity checks
1837 : : */
1838 [ + - ]: 3997306 : Assert(slot != NULL);
1839 [ + - ]: 3997306 : Assert(!TTS_EMPTY(slot));
1840 : :
1841 : : /* Materialize the tuple so that the slot "owns" it, if requested. */
1842 [ + + ]: 3997306 : if (materialize)
1843 : 1965198 : slot->tts_ops->materialize(slot);
1844 : :
1845 [ + + ]: 3997306 : if (slot->tts_ops->get_heap_tuple == NULL)
1846 : : {
1847 [ - + ]: 386795 : if (shouldFree)
1848 : 386795 : *shouldFree = true;
1849 : 386795 : return slot->tts_ops->copy_heap_tuple(slot);
1850 : : }
1851 : : else
1852 : : {
1853 [ + + ]: 3610511 : if (shouldFree)
1854 : 3241626 : *shouldFree = false;
1855 : 3610511 : return slot->tts_ops->get_heap_tuple(slot);
1856 : : }
1857 : 3997306 : }
1858 : :
1859 : : /* --------------------------------
1860 : : * ExecFetchSlotMinimalTuple
1861 : : * Fetch the slot's minimal physical tuple.
1862 : : *
1863 : : * If the given tuple table slot can hold a minimal tuple, indicated by a
1864 : : * non-NULL get_minimal_tuple callback, the function returns the minimal
1865 : : * tuple returned by that callback. It assumes that the minimal tuple
1866 : : * returned by the callback is "owned" by the slot i.e. the slot is
1867 : : * responsible for freeing the memory consumed by the tuple. Hence it sets
1868 : : * *shouldFree to false, indicating that the caller should not free the
1869 : : * memory consumed by the minimal tuple. In this case the returned minimal
1870 : : * tuple should be considered as read-only.
1871 : : *
1872 : : * If that callback is not supported, it calls copy_minimal_tuple callback
1873 : : * which is expected to return a copy of minimal tuple representing the
1874 : : * contents of the slot. In this case *shouldFree is set to true,
1875 : : * indicating the caller that it should free the memory consumed by the
1876 : : * minimal tuple. In this case the returned minimal tuple may be written
1877 : : * up.
1878 : : * --------------------------------
1879 : : */
1880 : : MinimalTuple
1881 : 3258103 : ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
1882 : : bool *shouldFree)
1883 : : {
1884 : : /*
1885 : : * sanity checks
1886 : : */
1887 [ + - ]: 3258103 : Assert(slot != NULL);
1888 [ + - ]: 3258103 : Assert(!TTS_EMPTY(slot));
1889 : :
1890 [ + + ]: 3258103 : if (slot->tts_ops->get_minimal_tuple)
1891 : : {
1892 [ - + ]: 843790 : if (shouldFree)
1893 : 843790 : *shouldFree = false;
1894 : 843790 : return slot->tts_ops->get_minimal_tuple(slot);
1895 : : }
1896 : : else
1897 : : {
1898 [ - + ]: 2414313 : if (shouldFree)
1899 : 2414313 : *shouldFree = true;
1900 : 2414313 : return slot->tts_ops->copy_minimal_tuple(slot, 0);
1901 : : }
1902 : 3258103 : }
1903 : :
1904 : : /* --------------------------------
1905 : : * ExecFetchSlotHeapTupleDatum
1906 : : * Fetch the slot's tuple as a composite-type Datum.
1907 : : *
1908 : : * The result is always freshly palloc'd in the caller's memory context.
1909 : : * --------------------------------
1910 : : */
1911 : : Datum
1912 : 10472 : ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot)
1913 : : {
1914 : 10472 : HeapTuple tup;
1915 : 10472 : TupleDesc tupdesc;
1916 : 10472 : bool shouldFree;
1917 : 10472 : Datum ret;
1918 : :
1919 : : /* Fetch slot's contents in regular-physical-tuple form */
1920 : 10472 : tup = ExecFetchSlotHeapTuple(slot, false, &shouldFree);
1921 : 10472 : tupdesc = slot->tts_tupleDescriptor;
1922 : :
1923 : : /* Convert to Datum form */
1924 : 10472 : ret = heap_copy_tuple_as_datum(tup, tupdesc);
1925 : :
1926 [ - + ]: 10472 : if (shouldFree)
1927 : 10472 : pfree(tup);
1928 : :
1929 : 20944 : return ret;
1930 : 10472 : }
1931 : :
1932 : : /* ----------------------------------------------------------------
1933 : : * convenience initialization routines
1934 : : * ----------------------------------------------------------------
1935 : : */
1936 : :
1937 : : /* ----------------
1938 : : * ExecInitResultTypeTL
1939 : : *
1940 : : * Initialize result type, using the plan node's targetlist.
1941 : : * ----------------
1942 : : */
1943 : : void
1944 : 930431 : ExecInitResultTypeTL(PlanState *planstate)
1945 : : {
1946 : 930431 : TupleDesc tupDesc = ExecTypeFromTL(planstate->plan->targetlist);
1947 : :
1948 : 930431 : planstate->ps_ResultTupleDesc = tupDesc;
1949 : 930431 : }
1950 : :
1951 : : /* --------------------------------
1952 : : * ExecInit{Result,Scan,Extra}TupleSlot[TL]
1953 : : *
1954 : : * These are convenience routines to initialize the specified slot
1955 : : * in nodes inheriting the appropriate state. ExecInitExtraTupleSlot
1956 : : * is used for initializing special-purpose slots.
1957 : : * --------------------------------
1958 : : */
1959 : :
1960 : : /* ----------------
1961 : : * ExecInitResultTupleSlotTL
1962 : : *
1963 : : * Initialize result tuple slot, using the tuple descriptor previously
1964 : : * computed with ExecInitResultTypeTL().
1965 : : * ----------------
1966 : : */
1967 : : void
1968 : 488102 : ExecInitResultSlot(PlanState *planstate, const TupleTableSlotOps *tts_ops)
1969 : : {
1970 : 488102 : TupleTableSlot *slot;
1971 : :
1972 : 976204 : slot = ExecAllocTableSlot(&planstate->state->es_tupleTable,
1973 : 488102 : planstate->ps_ResultTupleDesc, tts_ops);
1974 : 488102 : planstate->ps_ResultTupleSlot = slot;
1975 : :
1976 : 488102 : planstate->resultopsfixed = planstate->ps_ResultTupleDesc != NULL;
1977 : 488102 : planstate->resultops = tts_ops;
1978 : 488102 : planstate->resultopsset = true;
1979 : 488102 : }
1980 : :
1981 : : /* ----------------
1982 : : * ExecInitResultTupleSlotTL
1983 : : *
1984 : : * Initialize result tuple slot, using the plan node's targetlist.
1985 : : * ----------------
1986 : : */
1987 : : void
1988 : 63896 : ExecInitResultTupleSlotTL(PlanState *planstate,
1989 : : const TupleTableSlotOps *tts_ops)
1990 : : {
1991 : 63896 : ExecInitResultTypeTL(planstate);
1992 : 63896 : ExecInitResultSlot(planstate, tts_ops);
1993 : 63896 : }
1994 : :
1995 : : /* ----------------
1996 : : * ExecInitScanTupleSlot
1997 : : * ----------------
1998 : : */
1999 : : void
2000 : 472039 : ExecInitScanTupleSlot(EState *estate, ScanState *scanstate,
2001 : : TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
2002 : : {
2003 : 944078 : scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
2004 : 472039 : tupledesc, tts_ops);
2005 : 472039 : scanstate->ps.scandesc = tupledesc;
2006 : 472039 : scanstate->ps.scanopsfixed = tupledesc != NULL;
2007 : 472039 : scanstate->ps.scanops = tts_ops;
2008 : 472039 : scanstate->ps.scanopsset = true;
2009 : 472039 : }
2010 : :
2011 : : /* ----------------
2012 : : * ExecInitExtraTupleSlot
2013 : : *
2014 : : * Return a newly created slot. If tupledesc is non-NULL the slot will have
2015 : : * that as its fixed tupledesc. Otherwise the caller needs to use
2016 : : * ExecSetSlotDescriptor() to set the descriptor before use.
2017 : : * ----------------
2018 : : */
2019 : : TupleTableSlot *
2020 : 418069 : ExecInitExtraTupleSlot(EState *estate,
2021 : : TupleDesc tupledesc,
2022 : : const TupleTableSlotOps *tts_ops)
2023 : : {
2024 : 418069 : return ExecAllocTableSlot(&estate->es_tupleTable, tupledesc, tts_ops);
2025 : : }
2026 : :
2027 : : /* ----------------
2028 : : * ExecInitNullTupleSlot
2029 : : *
2030 : : * Build a slot containing an all-nulls tuple of the given type.
2031 : : * This is used as a substitute for an input tuple when performing an
2032 : : * outer join.
2033 : : * ----------------
2034 : : */
2035 : : TupleTableSlot *
2036 : 3875 : ExecInitNullTupleSlot(EState *estate, TupleDesc tupType,
2037 : : const TupleTableSlotOps *tts_ops)
2038 : : {
2039 : 3875 : TupleTableSlot *slot = ExecInitExtraTupleSlot(estate, tupType, tts_ops);
2040 : :
2041 : 7750 : return ExecStoreAllNullTuple(slot);
2042 : 3875 : }
2043 : :
2044 : : /* ---------------------------------------------------------------
2045 : : * Routines for setting/accessing attributes in a slot.
2046 : : * ---------------------------------------------------------------
2047 : : */
2048 : :
2049 : : /*
2050 : : * Fill in missing values for a TupleTableSlot.
2051 : : *
2052 : : * This is only exposed because it's needed for JIT compiled tuple
2053 : : * deforming. That exception aside, there should be no callers outside of this
2054 : : * file.
2055 : : */
2056 : : void
2057 : 880 : slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum)
2058 : : {
2059 : 880 : AttrMissing *attrmiss = NULL;
2060 : :
2061 [ + + ]: 880 : if (slot->tts_tupleDescriptor->constr)
2062 : 755 : attrmiss = slot->tts_tupleDescriptor->constr->missing;
2063 : :
2064 [ + + ]: 880 : if (!attrmiss)
2065 : : {
2066 : : /* no missing values array at all, so just fill everything in as NULL */
2067 : 142 : memset(slot->tts_values + startAttNum, 0,
2068 : : (lastAttNum - startAttNum) * sizeof(Datum));
2069 : 142 : memset(slot->tts_isnull + startAttNum, 1,
2070 : : (lastAttNum - startAttNum) * sizeof(bool));
2071 : 142 : }
2072 : : else
2073 : : {
2074 : 738 : int missattnum;
2075 : :
2076 : : /* if there is a missing values array we must process them one by one */
2077 [ + + ]: 1714 : for (missattnum = startAttNum;
2078 : 1714 : missattnum < lastAttNum;
2079 : 976 : missattnum++)
2080 : : {
2081 : 976 : slot->tts_values[missattnum] = attrmiss[missattnum].am_value;
2082 : 976 : slot->tts_isnull[missattnum] = !attrmiss[missattnum].am_present;
2083 : 976 : }
2084 : 738 : }
2085 : 880 : }
2086 : :
2087 : : /*
2088 : : * slot_getsomeattrs_int - workhorse for slot_getsomeattrs()
2089 : : */
2090 : : void
2091 : 27721356 : slot_getsomeattrs_int(TupleTableSlot *slot, int attnum)
2092 : : {
2093 : : /* Check for caller errors */
2094 [ + - ]: 27721356 : Assert(slot->tts_nvalid < attnum); /* checked in slot_getsomeattrs */
2095 [ + - ]: 27721356 : Assert(attnum > 0);
2096 : :
2097 [ + - ]: 27721356 : if (unlikely(attnum > slot->tts_tupleDescriptor->natts))
2098 [ # # # # ]: 0 : elog(ERROR, "invalid attribute number %d", attnum);
2099 : :
2100 : : /* Fetch as many attributes as possible from the underlying tuple. */
2101 : 27721356 : slot->tts_ops->getsomeattrs(slot, attnum);
2102 : :
2103 : : /*
2104 : : * If the underlying tuple doesn't have enough attributes, tuple
2105 : : * descriptor must have the missing attributes.
2106 : : */
2107 [ + + ]: 27721356 : if (unlikely(slot->tts_nvalid < attnum))
2108 : : {
2109 : 880 : slot_getmissingattrs(slot, slot->tts_nvalid, attnum);
2110 : 880 : slot->tts_nvalid = attnum;
2111 : 880 : }
2112 : 27721356 : }
2113 : :
2114 : : /* ----------------------------------------------------------------
2115 : : * ExecTypeFromTL
2116 : : *
2117 : : * Generate a tuple descriptor for the result tuple of a targetlist.
2118 : : * (A parse/plan tlist must be passed, not an ExprState tlist.)
2119 : : * Note that resjunk columns, if any, are included in the result.
2120 : : *
2121 : : * Currently there are about 4 different places where we create
2122 : : * TupleDescriptors. They should all be merged, or perhaps
2123 : : * be rewritten to call BuildDesc().
2124 : : * ----------------------------------------------------------------
2125 : : */
2126 : : TupleDesc
2127 : 933809 : ExecTypeFromTL(List *targetList)
2128 : : {
2129 : 933809 : return ExecTypeFromTLInternal(targetList, false);
2130 : : }
2131 : :
2132 : : /* ----------------------------------------------------------------
2133 : : * ExecCleanTypeFromTL
2134 : : *
2135 : : * Same as above, but resjunk columns are omitted from the result.
2136 : : * ----------------------------------------------------------------
2137 : : */
2138 : : TupleDesc
2139 : 415773 : ExecCleanTypeFromTL(List *targetList)
2140 : : {
2141 : 415773 : return ExecTypeFromTLInternal(targetList, true);
2142 : : }
2143 : :
2144 : : static TupleDesc
2145 : 1349582 : ExecTypeFromTLInternal(List *targetList, bool skipjunk)
2146 : : {
2147 : 1349582 : TupleDesc typeInfo;
2148 : 1349582 : ListCell *l;
2149 : 1349582 : int len;
2150 : 1349582 : int cur_resno = 1;
2151 : :
2152 [ + + ]: 1349582 : if (skipjunk)
2153 : 415773 : len = ExecCleanTargetListLength(targetList);
2154 : : else
2155 : 933809 : len = ExecTargetListLength(targetList);
2156 : 1349582 : typeInfo = CreateTemplateTupleDesc(len);
2157 : :
2158 [ + + + + : 4306156 : foreach(l, targetList)
+ + ]
2159 : : {
2160 : 2956574 : TargetEntry *tle = lfirst(l);
2161 : :
2162 [ + + + + ]: 2956574 : if (skipjunk && tle->resjunk)
2163 : 404339 : continue;
2164 : 5104470 : TupleDescInitEntry(typeInfo,
2165 : 2552235 : cur_resno,
2166 : 2552235 : tle->resname,
2167 : 2552235 : exprType((Node *) tle->expr),
2168 : 2552235 : exprTypmod((Node *) tle->expr),
2169 : : 0);
2170 : 5104470 : TupleDescInitEntryCollation(typeInfo,
2171 : 2552235 : cur_resno,
2172 : 2552235 : exprCollation((Node *) tle->expr));
2173 : 2552235 : cur_resno++;
2174 [ - + + ]: 2956574 : }
2175 : :
2176 : 2699164 : return typeInfo;
2177 : 1349582 : }
2178 : :
2179 : : /*
2180 : : * ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
2181 : : *
2182 : : * This is roughly like ExecTypeFromTL, but we work from bare expressions
2183 : : * not TargetEntrys. No names are attached to the tupledesc's columns.
2184 : : */
2185 : : TupleDesc
2186 : 1848 : ExecTypeFromExprList(List *exprList)
2187 : : {
2188 : 1848 : TupleDesc typeInfo;
2189 : 1848 : ListCell *lc;
2190 : 1848 : int cur_resno = 1;
2191 : :
2192 : 1848 : typeInfo = CreateTemplateTupleDesc(list_length(exprList));
2193 : :
2194 [ + + + + : 4902 : foreach(lc, exprList)
+ + ]
2195 : : {
2196 : 3054 : Node *e = lfirst(lc);
2197 : :
2198 : 6108 : TupleDescInitEntry(typeInfo,
2199 : 3054 : cur_resno,
2200 : : NULL,
2201 : 3054 : exprType(e),
2202 : 3054 : exprTypmod(e),
2203 : : 0);
2204 : 6108 : TupleDescInitEntryCollation(typeInfo,
2205 : 3054 : cur_resno,
2206 : 3054 : exprCollation(e));
2207 : 3054 : cur_resno++;
2208 : 3054 : }
2209 : :
2210 : 3696 : return typeInfo;
2211 : 1848 : }
2212 : :
2213 : : /*
2214 : : * ExecTypeSetColNames - set column names in a RECORD TupleDesc
2215 : : *
2216 : : * Column names must be provided as an alias list (list of String nodes).
2217 : : */
2218 : : void
2219 : 525 : ExecTypeSetColNames(TupleDesc typeInfo, List *namesList)
2220 : : {
2221 : 525 : int colno = 0;
2222 : 525 : ListCell *lc;
2223 : :
2224 : : /* It's only OK to change col names in a not-yet-blessed RECORD type */
2225 [ + - ]: 525 : Assert(typeInfo->tdtypeid == RECORDOID);
2226 [ + - ]: 525 : Assert(typeInfo->tdtypmod < 0);
2227 : :
2228 [ + + + + : 1629 : foreach(lc, namesList)
+ + ]
2229 : : {
2230 : 1104 : char *cname = strVal(lfirst(lc));
2231 : 1104 : Form_pg_attribute attr;
2232 : :
2233 : : /* Guard against too-long names list (probably can't happen) */
2234 [ - + ]: 1104 : if (colno >= typeInfo->natts)
2235 : 0 : break;
2236 : 1104 : attr = TupleDescAttr(typeInfo, colno);
2237 : 1104 : colno++;
2238 : :
2239 : : /*
2240 : : * Do nothing for empty aliases or dropped columns (these cases
2241 : : * probably can't arise in RECORD types, either)
2242 : : */
2243 [ + + - + ]: 1104 : if (cname[0] == '\0' || attr->attisdropped)
2244 : 2 : continue;
2245 : :
2246 : : /* OK, assign the column name */
2247 : 1102 : namestrcpy(&(attr->attname), cname);
2248 [ + - + ]: 1104 : }
2249 : 525 : }
2250 : :
2251 : : /*
2252 : : * BlessTupleDesc - make a completed tuple descriptor useful for SRFs
2253 : : *
2254 : : * Rowtype Datums returned by a function must contain valid type information.
2255 : : * This happens "for free" if the tupdesc came from a relcache entry, but
2256 : : * not if we have manufactured a tupdesc for a transient RECORD datatype.
2257 : : * In that case we have to notify typcache.c of the existence of the type.
2258 : : */
2259 : : TupleDesc
2260 : 7663 : BlessTupleDesc(TupleDesc tupdesc)
2261 : : {
2262 [ + + + + ]: 7663 : if (tupdesc->tdtypeid == RECORDOID &&
2263 : 7202 : tupdesc->tdtypmod < 0)
2264 : 2339 : assign_record_type_typmod(tupdesc);
2265 : :
2266 : 7663 : return tupdesc; /* just for notational convenience */
2267 : : }
2268 : :
2269 : : /*
2270 : : * TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
2271 : : * supplied TupleDesc. AttInMetadata can be used in conjunction with C strings
2272 : : * to produce a properly formed tuple.
2273 : : */
2274 : : AttInMetadata *
2275 : 59 : TupleDescGetAttInMetadata(TupleDesc tupdesc)
2276 : : {
2277 : 59 : int natts = tupdesc->natts;
2278 : 59 : int i;
2279 : 59 : Oid atttypeid;
2280 : 59 : Oid attinfuncid;
2281 : 59 : FmgrInfo *attinfuncinfo;
2282 : 59 : Oid *attioparams;
2283 : 59 : int32 *atttypmods;
2284 : 59 : AttInMetadata *attinmeta;
2285 : :
2286 : 59 : attinmeta = palloc_object(AttInMetadata);
2287 : :
2288 : : /* "Bless" the tupledesc so that we can make rowtype datums with it */
2289 : 59 : attinmeta->tupdesc = BlessTupleDesc(tupdesc);
2290 : :
2291 : : /*
2292 : : * Gather info needed later to call the "in" function for each attribute
2293 : : */
2294 : 59 : attinfuncinfo = (FmgrInfo *) palloc0(natts * sizeof(FmgrInfo));
2295 : 59 : attioparams = (Oid *) palloc0(natts * sizeof(Oid));
2296 : 59 : atttypmods = (int32 *) palloc0(natts * sizeof(int32));
2297 : :
2298 [ + + ]: 335 : for (i = 0; i < natts; i++)
2299 : : {
2300 : 276 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
2301 : :
2302 : : /* Ignore dropped attributes */
2303 [ - + ]: 276 : if (!att->attisdropped)
2304 : : {
2305 : 276 : atttypeid = att->atttypid;
2306 : 276 : getTypeInputInfo(atttypeid, &attinfuncid, &attioparams[i]);
2307 : 276 : fmgr_info(attinfuncid, &attinfuncinfo[i]);
2308 : 276 : atttypmods[i] = att->atttypmod;
2309 : 276 : }
2310 : 276 : }
2311 : 59 : attinmeta->attinfuncs = attinfuncinfo;
2312 : 59 : attinmeta->attioparams = attioparams;
2313 : 59 : attinmeta->atttypmods = atttypmods;
2314 : :
2315 : 118 : return attinmeta;
2316 : 59 : }
2317 : :
2318 : : /*
2319 : : * BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
2320 : : * values is an array of C strings, one for each attribute of the return tuple.
2321 : : * A NULL string pointer indicates we want to create a NULL field.
2322 : : */
2323 : : HeapTuple
2324 : 5125 : BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
2325 : : {
2326 : 5125 : TupleDesc tupdesc = attinmeta->tupdesc;
2327 : 5125 : int natts = tupdesc->natts;
2328 : 5125 : Datum *dvalues;
2329 : 5125 : bool *nulls;
2330 : 5125 : int i;
2331 : 5125 : HeapTuple tuple;
2332 : :
2333 : 5125 : dvalues = (Datum *) palloc(natts * sizeof(Datum));
2334 : 5125 : nulls = (bool *) palloc(natts * sizeof(bool));
2335 : :
2336 : : /*
2337 : : * Call the "in" function for each non-dropped attribute, even for nulls,
2338 : : * to support domains.
2339 : : */
2340 [ + + ]: 61156 : for (i = 0; i < natts; i++)
2341 : : {
2342 [ - + ]: 56031 : if (!TupleDescCompactAttr(tupdesc, i)->attisdropped)
2343 : : {
2344 : : /* Non-dropped attributes */
2345 : 112062 : dvalues[i] = InputFunctionCall(&attinmeta->attinfuncs[i],
2346 : 56031 : values[i],
2347 : 56031 : attinmeta->attioparams[i],
2348 : 56031 : attinmeta->atttypmods[i]);
2349 [ + + ]: 56031 : if (values[i] != NULL)
2350 : 40083 : nulls[i] = false;
2351 : : else
2352 : 15948 : nulls[i] = true;
2353 : 56031 : }
2354 : : else
2355 : : {
2356 : : /* Handle dropped attributes by setting to NULL */
2357 : 0 : dvalues[i] = (Datum) 0;
2358 : 0 : nulls[i] = true;
2359 : : }
2360 : 56031 : }
2361 : :
2362 : : /*
2363 : : * Form a tuple
2364 : : */
2365 : 5125 : tuple = heap_form_tuple(tupdesc, dvalues, nulls);
2366 : :
2367 : : /*
2368 : : * Release locally palloc'd space. XXX would probably be good to pfree
2369 : : * values of pass-by-reference datums, as well.
2370 : : */
2371 : 5125 : pfree(dvalues);
2372 : 5125 : pfree(nulls);
2373 : :
2374 : 10250 : return tuple;
2375 : 5125 : }
2376 : :
2377 : : /*
2378 : : * HeapTupleHeaderGetDatum - convert a HeapTupleHeader pointer to a Datum.
2379 : : *
2380 : : * This must *not* get applied to an on-disk tuple; the tuple should be
2381 : : * freshly made by heap_form_tuple or some wrapper routine for it (such as
2382 : : * BuildTupleFromCStrings). Be sure also that the tupledesc used to build
2383 : : * the tuple has a properly "blessed" rowtype.
2384 : : *
2385 : : * Formerly this was a macro equivalent to PointerGetDatum, relying on the
2386 : : * fact that heap_form_tuple fills in the appropriate tuple header fields
2387 : : * for a composite Datum. However, we now require that composite Datums not
2388 : : * contain any external TOAST pointers. We do not want heap_form_tuple itself
2389 : : * to enforce that; more specifically, the rule applies only to actual Datums
2390 : : * and not to HeapTuple structures. Therefore, HeapTupleHeaderGetDatum is
2391 : : * now a function that detects whether there are externally-toasted fields
2392 : : * and constructs a new tuple with inlined fields if so. We still need
2393 : : * heap_form_tuple to insert the Datum header fields, because otherwise this
2394 : : * code would have no way to obtain a tupledesc for the tuple.
2395 : : *
2396 : : * Note that if we do build a new tuple, it's palloc'd in the current
2397 : : * memory context. Beware of code that changes context between the initial
2398 : : * heap_form_tuple/etc call and calling HeapTuple(Header)GetDatum.
2399 : : *
2400 : : * For performance-critical callers, it could be worthwhile to take extra
2401 : : * steps to ensure that there aren't TOAST pointers in the output of
2402 : : * heap_form_tuple to begin with. It's likely however that the costs of the
2403 : : * typcache lookup and tuple disassembly/reassembly are swamped by TOAST
2404 : : * dereference costs, so that the benefits of such extra effort would be
2405 : : * minimal.
2406 : : *
2407 : : * XXX it would likely be better to create wrapper functions that produce
2408 : : * a composite Datum from the field values in one step. However, there's
2409 : : * enough code using the existing APIs that we couldn't get rid of this
2410 : : * hack anytime soon.
2411 : : */
2412 : : Datum
2413 : 31332 : HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
2414 : : {
2415 : 31332 : Datum result;
2416 : 31332 : TupleDesc tupDesc;
2417 : :
2418 : : /* No work if there are no external TOAST pointers in the tuple */
2419 [ + + ]: 31332 : if (!HeapTupleHeaderHasExternal(tuple))
2420 : 31330 : return PointerGetDatum(tuple);
2421 : :
2422 : : /* Use the type data saved by heap_form_tuple to look up the rowtype */
2423 : 4 : tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
2424 : 2 : HeapTupleHeaderGetTypMod(tuple));
2425 : :
2426 : : /* And do the flattening */
2427 : 4 : result = toast_flatten_tuple_to_datum(tuple,
2428 : 2 : HeapTupleHeaderGetDatumLength(tuple),
2429 : 2 : tupDesc);
2430 : :
2431 [ - + ]: 2 : ReleaseTupleDesc(tupDesc);
2432 : :
2433 : 2 : return result;
2434 : 31332 : }
2435 : :
2436 : :
2437 : : /*
2438 : : * Functions for sending tuples to the frontend (or other specified destination)
2439 : : * as though it is a SELECT result. These are used by utility commands that
2440 : : * need to project directly to the destination and don't need or want full
2441 : : * table function capability. Currently used by EXPLAIN and SHOW ALL.
2442 : : */
2443 : : TupOutputState *
2444 : 3745 : begin_tup_output_tupdesc(DestReceiver *dest,
2445 : : TupleDesc tupdesc,
2446 : : const TupleTableSlotOps *tts_ops)
2447 : : {
2448 : 3745 : TupOutputState *tstate;
2449 : :
2450 : 3745 : tstate = palloc_object(TupOutputState);
2451 : :
2452 : 3745 : tstate->slot = MakeSingleTupleTableSlot(tupdesc, tts_ops);
2453 : 3745 : tstate->dest = dest;
2454 : :
2455 : 3745 : tstate->dest->rStartup(tstate->dest, (int) CMD_SELECT, tupdesc);
2456 : :
2457 : 7490 : return tstate;
2458 : 3745 : }
2459 : :
2460 : : /*
2461 : : * write a single tuple
2462 : : */
2463 : : void
2464 : 26166 : do_tup_output(TupOutputState *tstate, const Datum *values, const bool *isnull)
2465 : : {
2466 : 26166 : TupleTableSlot *slot = tstate->slot;
2467 : 26166 : int natts = slot->tts_tupleDescriptor->natts;
2468 : :
2469 : : /* make sure the slot is clear */
2470 : 26166 : ExecClearTuple(slot);
2471 : :
2472 : : /* insert data */
2473 : 26166 : memcpy(slot->tts_values, values, natts * sizeof(Datum));
2474 : 26166 : memcpy(slot->tts_isnull, isnull, natts * sizeof(bool));
2475 : :
2476 : : /* mark slot as containing a virtual tuple */
2477 : 26166 : ExecStoreVirtualTuple(slot);
2478 : :
2479 : : /* send the tuple to the receiver */
2480 : 26166 : (void) tstate->dest->receiveSlot(slot, tstate->dest);
2481 : :
2482 : : /* clean up */
2483 : 26166 : ExecClearTuple(slot);
2484 : 26166 : }
2485 : :
2486 : : /*
2487 : : * write a chunk of text, breaking at newline characters
2488 : : *
2489 : : * Should only be used with a single-TEXT-attribute tupdesc.
2490 : : */
2491 : : void
2492 : 3569 : do_text_output_multiline(TupOutputState *tstate, const char *txt)
2493 : : {
2494 : 3569 : Datum values[1];
2495 : 3569 : bool isnull[1] = {false};
2496 : :
2497 [ + + ]: 29575 : while (*txt)
2498 : : {
2499 : 26006 : const char *eol;
2500 : 26006 : int len;
2501 : :
2502 : 26006 : eol = strchr(txt, '\n');
2503 [ + - ]: 26006 : if (eol)
2504 : : {
2505 : 26006 : len = eol - txt;
2506 : 26006 : eol++;
2507 : 26006 : }
2508 : : else
2509 : : {
2510 : 0 : len = strlen(txt);
2511 : 0 : eol = txt + len;
2512 : : }
2513 : :
2514 : 26006 : values[0] = PointerGetDatum(cstring_to_text_with_len(txt, len));
2515 : 26006 : do_tup_output(tstate, values, isnull);
2516 : 26006 : pfree(DatumGetPointer(values[0]));
2517 : 26006 : txt = eol;
2518 : 26006 : }
2519 : 3569 : }
2520 : :
2521 : : void
2522 : 3745 : end_tup_output(TupOutputState *tstate)
2523 : : {
2524 : 3745 : tstate->dest->rShutdown(tstate->dest);
2525 : : /* note that destroying the dest is not ours to do */
2526 : 3745 : ExecDropSingleTupleTableSlot(tstate->slot);
2527 : 3745 : pfree(tstate);
2528 : 3745 : }
|