Age Owner Branch data TLA 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-2024, 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
1976 andres@anarazel.de 98 :CBC 639737 : tts_virtual_init(TupleTableSlot *slot)
99 : : {
100 : 639737 : }
101 : :
102 : : static void
103 : 626469 : tts_virtual_release(TupleTableSlot *slot)
104 : : {
105 : 626469 : }
106 : :
107 : : static void
108 : 39711215 : tts_virtual_clear(TupleTableSlot *slot)
109 : : {
110 [ + + ]: 39711215 : if (unlikely(TTS_SHOULDFREE(slot)))
111 : : {
112 : 872989 : VirtualTupleTableSlot *vslot = (VirtualTupleTableSlot *) slot;
113 : :
114 : 872989 : pfree(vslot->data);
115 : 872989 : vslot->data = NULL;
116 : :
1866 117 : 872989 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
118 : : }
119 : :
1976 120 : 39711215 : slot->tts_nvalid = 0;
121 : 39711215 : slot->tts_flags |= TTS_FLAG_EMPTY;
1874 122 : 39711215 : ItemPointerSetInvalid(&slot->tts_tid);
1976 123 : 39711215 : }
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
1976 andres@anarazel.de 130 :UBC 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 : : }
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
1976 andres@anarazel.de 141 :CBC 6 : tts_virtual_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
142 : : {
1088 tgl@sss.pgh.pa.us 143 [ - + ]: 6 : Assert(!TTS_EMPTY(slot));
144 : :
145 [ + - ]: 6 : ereport(ERROR,
146 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
147 : : errmsg("cannot retrieve a system column in this context")));
148 : :
149 : : 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
24 akorotkov@postgresql 157 :UNC 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 : : 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
1976 andres@anarazel.de 176 :CBC 2141529 : tts_virtual_materialize(TupleTableSlot *slot)
177 : : {
178 : 2141529 : VirtualTupleTableSlot *vslot = (VirtualTupleTableSlot *) slot;
179 : 2141529 : TupleDesc desc = slot->tts_tupleDescriptor;
180 : 2141529 : Size sz = 0;
181 : : char *data;
182 : :
183 : : /* already materialized */
184 [ + + ]: 2141529 : if (TTS_SHOULDFREE(slot))
185 : 192803 : return;
186 : :
187 : : /* compute size of memory required */
188 [ + + ]: 6118720 : for (int natt = 0; natt < desc->natts; natt++)
189 : : {
190 : 4169994 : Form_pg_attribute att = TupleDescAttr(desc, natt);
191 : : Datum val;
192 : :
193 [ + + + + ]: 4169994 : if (att->attbyval || slot->tts_isnull[natt])
194 : 3249580 : continue;
195 : :
196 : 920414 : val = slot->tts_values[natt];
197 : :
198 [ + + ]: 920414 : if (att->attlen == -1 &&
199 [ + + - + ]: 663180 : 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 : : */
1976 andres@anarazel.de 205 [ # # # # :UBC 0 : sz = att_align_nominal(sz, att->attalign);
# # # # ]
206 : 0 : sz += EOH_get_flat_size(DatumGetEOHP(val));
207 : : }
208 : : else
209 : : {
1976 andres@anarazel.de 210 [ + + + + :CBC 920414 : sz = att_align_nominal(sz, att->attalign);
+ + - + ]
211 [ + + + - : 920414 : sz = att_addlength_datum(sz, att->attlen, val);
+ + + - +
- - + + +
- - ]
212 : : }
213 : : }
214 : :
215 : : /* all data is byval */
216 [ + + ]: 1948726 : if (sz == 0)
217 : 1075687 : return;
218 : :
219 : : /* allocate memory */
220 : 873039 : vslot->data = data = MemoryContextAlloc(slot->tts_mcxt, sz);
221 : 873039 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
222 : :
223 : : /* and copy all attributes into the pre-allocated space */
224 [ + + ]: 3418462 : for (int natt = 0; natt < desc->natts; natt++)
225 : : {
226 : 2545423 : Form_pg_attribute att = TupleDescAttr(desc, natt);
227 : : Datum val;
228 : :
229 [ + + + + ]: 2545423 : if (att->attbyval || slot->tts_isnull[natt])
230 : 1625009 : continue;
231 : :
232 : 920414 : val = slot->tts_values[natt];
233 : :
234 [ + + ]: 920414 : if (att->attlen == -1 &&
235 [ + + - + ]: 663180 : VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
1976 andres@anarazel.de 236 :UBC 0 : {
237 : : 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_align_nominal(data,
# # # # ]
246 : : att->attalign);
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 : : }
253 : : else
254 : : {
1789 tgl@sss.pgh.pa.us 255 :CBC 920414 : Size data_length = 0;
256 : :
1976 andres@anarazel.de 257 [ + + + + : 920414 : data = (char *) att_align_nominal(data, att->attalign);
+ + - + ]
258 [ + + + - : 920414 : data_length = att_addlength_datum(data_length, att->attlen, val);
+ + + - +
- - + + +
- - ]
259 : :
260 : 920414 : memcpy(data, DatumGetPointer(val), data_length);
261 : :
262 : 920414 : slot->tts_values[natt] = PointerGetDatum(data);
263 : 920414 : data += data_length;
264 : : }
265 : : }
266 : : }
267 : :
268 : : static void
269 : 72655 : tts_virtual_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
270 : : {
1666 tgl@sss.pgh.pa.us 271 : 72655 : TupleDesc srcdesc = srcslot->tts_tupleDescriptor;
272 : :
1976 andres@anarazel.de 273 : 72655 : tts_virtual_clear(dstslot);
274 : :
275 : 72655 : slot_getallattrs(srcslot);
276 : :
277 [ + + ]: 148772 : for (int natt = 0; natt < srcdesc->natts; natt++)
278 : : {
279 : 76117 : dstslot->tts_values[natt] = srcslot->tts_values[natt];
280 : 76117 : dstslot->tts_isnull[natt] = srcslot->tts_isnull[natt];
281 : : }
282 : :
283 : 72655 : dstslot->tts_nvalid = srcdesc->natts;
284 : 72655 : dstslot->tts_flags &= ~TTS_FLAG_EMPTY;
285 : :
286 : : /* make sure storage doesn't depend on external memory */
287 : 72655 : tts_virtual_materialize(dstslot);
288 : 72655 : }
289 : :
290 : : static HeapTuple
291 : 6786647 : tts_virtual_copy_heap_tuple(TupleTableSlot *slot)
292 : : {
293 [ - + ]: 6786647 : Assert(!TTS_EMPTY(slot));
294 : :
295 : 13573294 : return heap_form_tuple(slot->tts_tupleDescriptor,
1976 andres@anarazel.de 296 :GIC 6786647 : slot->tts_values,
297 : 6786647 : slot->tts_isnull);
298 : : }
299 : :
300 : : static MinimalTuple
1976 andres@anarazel.de 301 :CBC 13224877 : tts_virtual_copy_minimal_tuple(TupleTableSlot *slot)
302 : : {
303 [ - + ]: 13224877 : Assert(!TTS_EMPTY(slot));
304 : :
305 : 26449754 : return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
1976 andres@anarazel.de 306 :GIC 13224877 : slot->tts_values,
307 : 13224877 : slot->tts_isnull);
308 : : }
309 : :
310 : :
311 : : /*
312 : : * TupleTableSlotOps implementation for HeapTupleTableSlot.
313 : : */
314 : :
315 : : static void
1976 andres@anarazel.de 316 :CBC 1830476 : tts_heap_init(TupleTableSlot *slot)
317 : : {
318 : 1830476 : }
319 : :
320 : : static void
321 : 1829973 : tts_heap_release(TupleTableSlot *slot)
322 : : {
323 : 1829973 : }
324 : :
325 : : static void
326 : 4681801 : tts_heap_clear(TupleTableSlot *slot)
327 : : {
328 : 4681801 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
329 : :
330 : : /* Free the memory for the heap tuple if it's allowed. */
331 [ + + ]: 4681801 : if (TTS_SHOULDFREE(slot))
332 : : {
333 : 821372 : heap_freetuple(hslot->tuple);
334 : 821372 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
335 : : }
336 : :
337 : 4681801 : slot->tts_nvalid = 0;
338 : 4681801 : slot->tts_flags |= TTS_FLAG_EMPTY;
1874 339 : 4681801 : ItemPointerSetInvalid(&slot->tts_tid);
1976 340 : 4681801 : hslot->off = 0;
341 : 4681801 : hslot->tuple = NULL;
342 : 4681801 : }
343 : :
344 : : static void
345 : 4616371 : tts_heap_getsomeattrs(TupleTableSlot *slot, int natts)
346 : : {
347 : 4616371 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
348 : :
349 [ - + ]: 4616371 : Assert(!TTS_EMPTY(slot));
350 : :
351 : 4616371 : slot_deform_heap_tuple(slot, hslot->tuple, &hslot->off, natts);
352 : 4616371 : }
353 : :
354 : : static Datum
1976 andres@anarazel.de 355 :UBC 0 : tts_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
356 : : {
357 : 0 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
358 : :
1621 tgl@sss.pgh.pa.us 359 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
360 : :
361 : : /*
362 : : * In some code paths it's possible to get here with a non-materialized
363 : : * slot, in which case we can't retrieve system columns.
364 : : */
1088 365 [ # # ]: 0 : if (!hslot->tuple)
366 [ # # ]: 0 : ereport(ERROR,
367 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
368 : : errmsg("cannot retrieve a system column in this context")));
369 : :
1976 andres@anarazel.de 370 : 0 : return heap_getsysattr(hslot->tuple, attnum,
371 : : slot->tts_tupleDescriptor, isnull);
372 : : }
373 : :
374 : : static bool
24 akorotkov@postgresql 375 :UNC 0 : tts_heap_is_current_xact_tuple(TupleTableSlot *slot)
376 : : {
377 : 0 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
378 : : TransactionId xmin;
379 : :
380 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
381 : :
382 : : /*
383 : : * In some code paths it's possible to get here with a non-materialized
384 : : * slot, in which case we can't check if tuple is created by the current
385 : : * transaction.
386 : : */
387 [ # # ]: 0 : if (!hslot->tuple)
388 [ # # ]: 0 : ereport(ERROR,
389 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
390 : : errmsg("don't have a storage tuple in this context")));
391 : :
392 : 0 : xmin = HeapTupleHeaderGetRawXmin(hslot->tuple->t_data);
393 : :
394 : 0 : return TransactionIdIsCurrentTransactionId(xmin);
395 : : }
396 : :
397 : : static void
1976 andres@anarazel.de 398 :CBC 1521314 : tts_heap_materialize(TupleTableSlot *slot)
399 : : {
400 : 1521314 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
401 : : MemoryContext oldContext;
402 : :
403 [ - + ]: 1521314 : Assert(!TTS_EMPTY(slot));
404 : :
405 : : /* If slot has its tuple already materialized, nothing to do. */
406 [ + + ]: 1521314 : if (TTS_SHOULDFREE(slot))
407 : 761031 : return;
408 : :
409 : 760283 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
410 : :
411 : : /*
412 : : * Have to deform from scratch, otherwise tts_values[] entries could point
413 : : * into the non-materialized tuple (which might be gone when accessed).
414 : : */
1621 tgl@sss.pgh.pa.us 415 : 760283 : slot->tts_nvalid = 0;
416 : 760283 : hslot->off = 0;
417 : :
1976 andres@anarazel.de 418 [ + + ]: 760283 : if (!hslot->tuple)
419 : 760276 : hslot->tuple = heap_form_tuple(slot->tts_tupleDescriptor,
1976 andres@anarazel.de 420 :GIC 760276 : slot->tts_values,
421 : 760276 : slot->tts_isnull);
422 : : else
423 : : {
424 : : /*
425 : : * The tuple contained in this slot is not allocated in the memory
426 : : * context of the given slot (else it would have TTS_FLAG_SHOULDFREE
427 : : * set). Copy the tuple into the given slot's memory context.
428 : : */
1976 andres@anarazel.de 429 :CBC 7 : hslot->tuple = heap_copytuple(hslot->tuple);
430 : : }
431 : :
1621 tgl@sss.pgh.pa.us 432 : 760283 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
433 : :
1976 andres@anarazel.de 434 : 760283 : MemoryContextSwitchTo(oldContext);
435 : : }
436 : :
437 : : static void
438 : 898 : tts_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
439 : : {
440 : : HeapTuple tuple;
441 : : MemoryContext oldcontext;
442 : :
443 : 898 : oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
444 : 898 : tuple = ExecCopySlotHeapTuple(srcslot);
445 : 898 : MemoryContextSwitchTo(oldcontext);
446 : :
447 : 898 : ExecStoreHeapTuple(tuple, dstslot, true);
448 : 898 : }
449 : :
450 : : static HeapTuple
451 : 1520335 : tts_heap_get_heap_tuple(TupleTableSlot *slot)
452 : : {
453 : 1520335 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
454 : :
455 [ - + ]: 1520335 : Assert(!TTS_EMPTY(slot));
456 [ - + ]: 1520335 : if (!hslot->tuple)
1976 andres@anarazel.de 457 :UBC 0 : tts_heap_materialize(slot);
458 : :
1976 andres@anarazel.de 459 :CBC 1520335 : return hslot->tuple;
460 : : }
461 : :
462 : : static HeapTuple
463 : 343 : tts_heap_copy_heap_tuple(TupleTableSlot *slot)
464 : : {
465 : 343 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
466 : :
467 [ - + ]: 343 : Assert(!TTS_EMPTY(slot));
468 [ - + ]: 343 : if (!hslot->tuple)
1976 andres@anarazel.de 469 :UBC 0 : tts_heap_materialize(slot);
470 : :
1976 andres@anarazel.de 471 :CBC 343 : return heap_copytuple(hslot->tuple);
472 : : }
473 : :
474 : : static MinimalTuple
475 : 2665 : tts_heap_copy_minimal_tuple(TupleTableSlot *slot)
476 : : {
477 : 2665 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
478 : :
479 [ + + ]: 2665 : if (!hslot->tuple)
480 : 19 : tts_heap_materialize(slot);
481 : :
482 : 2665 : return minimal_tuple_from_heap_tuple(hslot->tuple);
483 : : }
484 : :
485 : : static void
486 : 2090013 : tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
487 : : {
488 : 2090013 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
489 : :
490 : 2090013 : tts_heap_clear(slot);
491 : :
492 : 2090013 : slot->tts_nvalid = 0;
493 : 2090013 : hslot->tuple = tuple;
494 : 2090013 : hslot->off = 0;
1621 tgl@sss.pgh.pa.us 495 : 2090013 : slot->tts_flags &= ~(TTS_FLAG_EMPTY | TTS_FLAG_SHOULDFREE);
1874 andres@anarazel.de 496 : 2090013 : slot->tts_tid = tuple->t_self;
497 : :
1976 498 [ + + ]: 2090013 : if (shouldFree)
499 : 61098 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
500 : 2090013 : }
501 : :
502 : :
503 : : /*
504 : : * TupleTableSlotOps implementation for MinimalTupleTableSlot.
505 : : */
506 : :
507 : : static void
508 : 170191 : tts_minimal_init(TupleTableSlot *slot)
509 : : {
510 : 170191 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
511 : :
512 : : /*
513 : : * Initialize the heap tuple pointer to access attributes of the minimal
514 : : * tuple contained in the slot as if its a heap tuple.
515 : : */
516 : 170191 : mslot->tuple = &mslot->minhdr;
517 : 170191 : }
518 : :
519 : : static void
520 : 144669 : tts_minimal_release(TupleTableSlot *slot)
521 : : {
522 : 144669 : }
523 : :
524 : : static void
525 : 32932971 : tts_minimal_clear(TupleTableSlot *slot)
526 : : {
527 : 32932971 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
528 : :
529 [ + + ]: 32932971 : if (TTS_SHOULDFREE(slot))
530 : : {
531 : 7855788 : heap_free_minimal_tuple(mslot->mintuple);
532 : 7855788 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
533 : : }
534 : :
535 : 32932971 : slot->tts_nvalid = 0;
536 : 32932971 : slot->tts_flags |= TTS_FLAG_EMPTY;
1874 537 : 32932971 : ItemPointerSetInvalid(&slot->tts_tid);
1976 538 : 32932971 : mslot->off = 0;
539 : 32932971 : mslot->mintuple = NULL;
540 : 32932971 : }
541 : :
542 : : static void
543 : 24119036 : tts_minimal_getsomeattrs(TupleTableSlot *slot, int natts)
544 : : {
545 : 24119036 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
546 : :
547 [ - + ]: 24119036 : Assert(!TTS_EMPTY(slot));
548 : :
549 : 24119036 : slot_deform_heap_tuple(slot, mslot->tuple, &mslot->off, natts);
550 : 24119036 : }
551 : :
552 : : /*
553 : : * MinimalTupleTableSlots never provide system attributes. We generally
554 : : * shouldn't get here, but provide a user-friendly message if we do.
555 : : */
556 : : static Datum
1976 andres@anarazel.de 557 :UBC 0 : tts_minimal_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
558 : : {
1088 tgl@sss.pgh.pa.us 559 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
560 : :
561 [ # # ]: 0 : ereport(ERROR,
562 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
563 : : errmsg("cannot retrieve a system column in this context")));
564 : :
565 : : return 0; /* silence compiler warnings */
566 : : }
567 : :
568 : : /*
569 : : * Within MinimalTuple abstraction transaction information is unavailable.
570 : : * We generally shouldn't get here, but provide a user-friendly message if
571 : : * we do.
572 : : */
573 : : static bool
24 akorotkov@postgresql 574 :UNC 0 : tts_minimal_is_current_xact_tuple(TupleTableSlot *slot)
575 : : {
576 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
577 : :
578 [ # # ]: 0 : ereport(ERROR,
579 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
580 : : errmsg("don't have transaction information for this type of tuple")));
581 : :
582 : : return false; /* silence compiler warnings */
583 : : }
584 : :
585 : : static void
1976 andres@anarazel.de 586 :CBC 789740 : tts_minimal_materialize(TupleTableSlot *slot)
587 : : {
588 : 789740 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
589 : : MemoryContext oldContext;
590 : :
591 [ - + ]: 789740 : Assert(!TTS_EMPTY(slot));
592 : :
593 : : /* If slot has its tuple already materialized, nothing to do. */
594 [ + + ]: 789740 : if (TTS_SHOULDFREE(slot))
595 : 72017 : return;
596 : :
597 : 717723 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
598 : :
599 : : /*
600 : : * Have to deform from scratch, otherwise tts_values[] entries could point
601 : : * into the non-materialized tuple (which might be gone when accessed).
602 : : */
1621 tgl@sss.pgh.pa.us 603 : 717723 : slot->tts_nvalid = 0;
604 : 717723 : mslot->off = 0;
605 : :
1976 andres@anarazel.de 606 [ + + ]: 717723 : if (!mslot->mintuple)
607 : : {
608 : 662234 : mslot->mintuple = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
1976 andres@anarazel.de 609 :GIC 662234 : slot->tts_values,
610 : 662234 : slot->tts_isnull);
611 : : }
612 : : else
613 : : {
614 : : /*
615 : : * The minimal tuple contained in this slot is not allocated in the
616 : : * memory context of the given slot (else it would have
617 : : * TTS_FLAG_SHOULDFREE set). Copy the minimal tuple into the given
618 : : * slot's memory context.
619 : : */
1976 andres@anarazel.de 620 :CBC 55489 : mslot->mintuple = heap_copy_minimal_tuple(mslot->mintuple);
621 : : }
622 : :
1621 tgl@sss.pgh.pa.us 623 : 717723 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
624 : :
1976 andres@anarazel.de 625 [ - + ]: 717723 : Assert(mslot->tuple == &mslot->minhdr);
626 : :
627 : 717723 : mslot->minhdr.t_len = mslot->mintuple->t_len + MINIMAL_TUPLE_OFFSET;
628 : 717723 : mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mslot->mintuple - MINIMAL_TUPLE_OFFSET);
629 : :
630 : 717723 : MemoryContextSwitchTo(oldContext);
631 : : }
632 : :
633 : : static void
634 : 494740 : tts_minimal_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
635 : : {
636 : : MemoryContext oldcontext;
637 : : MinimalTuple mintuple;
638 : :
639 : 494740 : oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
640 : 494740 : mintuple = ExecCopySlotMinimalTuple(srcslot);
641 : 494740 : MemoryContextSwitchTo(oldcontext);
642 : :
643 : 494740 : ExecStoreMinimalTuple(mintuple, dstslot, true);
644 : 494740 : }
645 : :
646 : : static MinimalTuple
647 : 2634946 : tts_minimal_get_minimal_tuple(TupleTableSlot *slot)
648 : : {
649 : 2634946 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
650 : :
651 [ + + ]: 2634946 : if (!mslot->mintuple)
652 : 29058 : tts_minimal_materialize(slot);
653 : :
654 : 2634946 : return mslot->mintuple;
655 : : }
656 : :
657 : : static HeapTuple
658 : 443898 : tts_minimal_copy_heap_tuple(TupleTableSlot *slot)
659 : : {
660 : 443898 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
661 : :
662 [ + + ]: 443898 : if (!mslot->mintuple)
663 : 1245 : tts_minimal_materialize(slot);
664 : :
665 : 443898 : return heap_tuple_from_minimal_tuple(mslot->mintuple);
666 : : }
667 : :
668 : : static MinimalTuple
669 : 1255914 : tts_minimal_copy_minimal_tuple(TupleTableSlot *slot)
670 : : {
671 : 1255914 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
672 : :
673 [ + + ]: 1255914 : if (!mslot->mintuple)
674 : 560231 : tts_minimal_materialize(slot);
675 : :
676 : 1255914 : return heap_copy_minimal_tuple(mslot->mintuple);
677 : : }
678 : :
679 : : static void
680 : 27816054 : tts_minimal_store_tuple(TupleTableSlot *slot, MinimalTuple mtup, bool shouldFree)
681 : : {
682 : 27816054 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
683 : :
684 : 27816054 : tts_minimal_clear(slot);
685 : :
686 [ - + ]: 27816054 : Assert(!TTS_SHOULDFREE(slot));
687 [ - + ]: 27816054 : Assert(TTS_EMPTY(slot));
688 : :
689 : 27816054 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
690 : 27816054 : slot->tts_nvalid = 0;
691 : 27816054 : mslot->off = 0;
692 : :
693 : 27816054 : mslot->mintuple = mtup;
694 [ - + ]: 27816054 : Assert(mslot->tuple == &mslot->minhdr);
695 : 27816054 : mslot->minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
696 : 27816054 : mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
697 : : /* no need to set t_self or t_tableOid since we won't allow access */
698 : :
699 [ + + ]: 27816054 : if (shouldFree)
700 : 7138658 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
701 : 27816054 : }
702 : :
703 : :
704 : : /*
705 : : * TupleTableSlotOps implementation for BufferHeapTupleTableSlot.
706 : : */
707 : :
708 : : static void
709 : 12554810 : tts_buffer_heap_init(TupleTableSlot *slot)
710 : : {
711 : 12554810 : }
712 : :
713 : : static void
714 : 12549369 : tts_buffer_heap_release(TupleTableSlot *slot)
715 : : {
716 : 12549369 : }
717 : :
718 : : static void
719 : 23447710 : tts_buffer_heap_clear(TupleTableSlot *slot)
720 : : {
721 : 23447710 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
722 : :
723 : : /*
724 : : * Free the memory for heap tuple if allowed. A tuple coming from buffer
725 : : * can never be freed. But we may have materialized a tuple from buffer.
726 : : * Such a tuple can be freed.
727 : : */
728 [ + + ]: 23447710 : if (TTS_SHOULDFREE(slot))
729 : : {
730 : : /* We should have unpinned the buffer while materializing the tuple. */
731 [ - + ]: 6601141 : Assert(!BufferIsValid(bslot->buffer));
732 : :
733 : 6601141 : heap_freetuple(bslot->base.tuple);
734 : 6601141 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
735 : : }
736 : :
737 [ + + ]: 23447710 : if (BufferIsValid(bslot->buffer))
738 : 6370615 : ReleaseBuffer(bslot->buffer);
739 : :
740 : 23447710 : slot->tts_nvalid = 0;
741 : 23447710 : slot->tts_flags |= TTS_FLAG_EMPTY;
1874 742 : 23447710 : ItemPointerSetInvalid(&slot->tts_tid);
1976 743 : 23447710 : bslot->base.tuple = NULL;
744 : 23447710 : bslot->base.off = 0;
745 : 23447710 : bslot->buffer = InvalidBuffer;
746 : 23447710 : }
747 : :
748 : : static void
749 : 55976795 : tts_buffer_heap_getsomeattrs(TupleTableSlot *slot, int natts)
750 : : {
751 : 55976795 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
752 : :
753 [ - + ]: 55976795 : Assert(!TTS_EMPTY(slot));
754 : :
755 : 55976795 : slot_deform_heap_tuple(slot, bslot->base.tuple, &bslot->base.off, natts);
756 : 55976795 : }
757 : :
758 : : static Datum
759 : 201 : tts_buffer_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
760 : : {
761 : 201 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
762 : :
1621 tgl@sss.pgh.pa.us 763 [ - + ]: 201 : Assert(!TTS_EMPTY(slot));
764 : :
765 : : /*
766 : : * In some code paths it's possible to get here with a non-materialized
767 : : * slot, in which case we can't retrieve system columns.
768 : : */
1088 769 [ - + ]: 201 : if (!bslot->base.tuple)
1088 tgl@sss.pgh.pa.us 770 [ # # ]:UBC 0 : ereport(ERROR,
771 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
772 : : errmsg("cannot retrieve a system column in this context")));
773 : :
1976 andres@anarazel.de 774 :CBC 201 : return heap_getsysattr(bslot->base.tuple, attnum,
775 : : slot->tts_tupleDescriptor, isnull);
776 : : }
777 : :
778 : : static bool
24 akorotkov@postgresql 779 :GNC 456 : tts_buffer_is_current_xact_tuple(TupleTableSlot *slot)
780 : : {
781 : 456 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
782 : : TransactionId xmin;
783 : :
784 [ - + ]: 456 : Assert(!TTS_EMPTY(slot));
785 : :
786 : : /*
787 : : * In some code paths it's possible to get here with a non-materialized
788 : : * slot, in which case we can't check if tuple is created by the current
789 : : * transaction.
790 : : */
791 [ - + ]: 456 : if (!bslot->base.tuple)
24 akorotkov@postgresql 792 [ # # ]:UNC 0 : ereport(ERROR,
793 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
794 : : errmsg("don't have a storage tuple in this context")));
795 : :
24 akorotkov@postgresql 796 :GNC 456 : xmin = HeapTupleHeaderGetRawXmin(bslot->base.tuple->t_data);
797 : :
798 : 456 : return TransactionIdIsCurrentTransactionId(xmin);
799 : : }
800 : :
801 : : static void
1976 andres@anarazel.de 802 :CBC 13133795 : tts_buffer_heap_materialize(TupleTableSlot *slot)
803 : : {
804 : 13133795 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
805 : : MemoryContext oldContext;
806 : :
807 [ - + ]: 13133795 : Assert(!TTS_EMPTY(slot));
808 : :
809 : : /* If slot has its tuple already materialized, nothing to do. */
810 [ + + ]: 13133795 : if (TTS_SHOULDFREE(slot))
811 : 11772115 : return;
812 : :
813 : 1361680 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
814 : :
815 : : /*
816 : : * Have to deform from scratch, otherwise tts_values[] entries could point
817 : : * into the non-materialized tuple (which might be gone when accessed).
818 : : */
1621 tgl@sss.pgh.pa.us 819 : 1361680 : bslot->base.off = 0;
820 : 1361680 : slot->tts_nvalid = 0;
821 : :
1872 andres@anarazel.de 822 [ + + ]: 1361680 : if (!bslot->base.tuple)
823 : : {
824 : : /*
825 : : * Normally BufferHeapTupleTableSlot should have a tuple + buffer
826 : : * associated with it, unless it's materialized (which would've
827 : : * returned above). But when it's useful to allow storing virtual
828 : : * tuples in a buffer slot, which then also needs to be
829 : : * materializable.
830 : : */
831 : 1164610 : bslot->base.tuple = heap_form_tuple(slot->tts_tupleDescriptor,
1872 andres@anarazel.de 832 :GIC 1164610 : slot->tts_values,
833 : 1164610 : slot->tts_isnull);
834 : : }
835 : : else
836 : : {
1872 andres@anarazel.de 837 :CBC 197070 : bslot->base.tuple = heap_copytuple(bslot->base.tuple);
838 : :
839 : : /*
840 : : * A heap tuple stored in a BufferHeapTupleTableSlot should have a
841 : : * buffer associated with it, unless it's materialized or virtual.
842 : : */
843 [ + - ]: 197070 : if (likely(BufferIsValid(bslot->buffer)))
844 : 197070 : ReleaseBuffer(bslot->buffer);
845 : 197070 : bslot->buffer = InvalidBuffer;
846 : : }
847 : :
848 : : /*
849 : : * We don't set TTS_FLAG_SHOULDFREE until after releasing the buffer, if
850 : : * any. This avoids having a transient state that would fall foul of our
851 : : * assertions that a slot with TTS_FLAG_SHOULDFREE doesn't own a buffer.
852 : : * In the unlikely event that ReleaseBuffer() above errors out, we'd
853 : : * effectively leak the copied tuple, but that seems fairly harmless.
854 : : */
1621 tgl@sss.pgh.pa.us 855 : 1361680 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
856 : :
857 : 1361680 : MemoryContextSwitchTo(oldContext);
858 : : }
859 : :
860 : : static void
1976 andres@anarazel.de 861 : 5406056 : tts_buffer_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
862 : : {
863 : 5406056 : BufferHeapTupleTableSlot *bsrcslot = (BufferHeapTupleTableSlot *) srcslot;
864 : 5406056 : BufferHeapTupleTableSlot *bdstslot = (BufferHeapTupleTableSlot *) dstslot;
865 : :
866 : : /*
867 : : * If the source slot is of a different kind, or is a buffer slot that has
868 : : * been materialized / is virtual, make a new copy of the tuple. Otherwise
869 : : * make a new reference to the in-buffer tuple.
870 : : */
871 [ + + ]: 5406056 : if (dstslot->tts_ops != srcslot->tts_ops ||
1872 872 [ + + ]: 3275 : TTS_SHOULDFREE(srcslot) ||
873 [ - + ]: 3273 : !bsrcslot->base.tuple)
1976 874 : 5402783 : {
875 : : MemoryContext oldContext;
876 : :
877 : 5402783 : ExecClearTuple(dstslot);
878 : 5402783 : dstslot->tts_flags &= ~TTS_FLAG_EMPTY;
879 : 5402783 : oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt);
1945 akapila@postgresql.o 880 : 5402783 : bdstslot->base.tuple = ExecCopySlotHeapTuple(srcslot);
1621 tgl@sss.pgh.pa.us 881 : 5402783 : dstslot->tts_flags |= TTS_FLAG_SHOULDFREE;
1976 andres@anarazel.de 882 : 5402783 : MemoryContextSwitchTo(oldContext);
883 : : }
884 : : else
885 : : {
1872 886 [ - + ]: 3273 : Assert(BufferIsValid(bsrcslot->buffer));
887 : :
1874 888 : 3273 : tts_buffer_heap_store_tuple(dstslot, bsrcslot->base.tuple,
889 : : bsrcslot->buffer, false);
890 : :
891 : : /*
892 : : * The HeapTupleData portion of the source tuple might be shorter
893 : : * lived than the destination slot. Therefore copy the HeapTuple into
894 : : * our slot's tupdata, which is guaranteed to live long enough (but
895 : : * will still point into the buffer).
896 : : */
1872 897 : 3273 : memcpy(&bdstslot->base.tupdata, bdstslot->base.tuple, sizeof(HeapTupleData));
898 : 3273 : bdstslot->base.tuple = &bdstslot->base.tupdata;
899 : : }
1976 900 : 5406056 : }
901 : :
902 : : static HeapTuple
903 : 16638906 : tts_buffer_heap_get_heap_tuple(TupleTableSlot *slot)
904 : : {
905 : 16638906 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
906 : :
907 [ - + ]: 16638906 : Assert(!TTS_EMPTY(slot));
908 : :
909 [ - + ]: 16638906 : if (!bslot->base.tuple)
1976 andres@anarazel.de 910 :UBC 0 : tts_buffer_heap_materialize(slot);
911 : :
1976 andres@anarazel.de 912 :CBC 16638906 : return bslot->base.tuple;
913 : : }
914 : :
915 : : static HeapTuple
916 : 4937911 : tts_buffer_heap_copy_heap_tuple(TupleTableSlot *slot)
917 : : {
918 : 4937911 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
919 : :
920 [ - + ]: 4937911 : Assert(!TTS_EMPTY(slot));
921 : :
922 [ - + ]: 4937911 : if (!bslot->base.tuple)
1976 andres@anarazel.de 923 :UBC 0 : tts_buffer_heap_materialize(slot);
924 : :
1976 andres@anarazel.de 925 :CBC 4937911 : return heap_copytuple(bslot->base.tuple);
926 : : }
927 : :
928 : : static MinimalTuple
929 : 1279039 : tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot)
930 : : {
931 : 1279039 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
932 : :
933 [ - + ]: 1279039 : Assert(!TTS_EMPTY(slot));
934 : :
935 [ - + ]: 1279039 : if (!bslot->base.tuple)
1976 andres@anarazel.de 936 :UBC 0 : tts_buffer_heap_materialize(slot);
937 : :
1976 andres@anarazel.de 938 :CBC 1279039 : return minimal_tuple_from_heap_tuple(bslot->base.tuple);
939 : : }
940 : :
941 : : static inline void
1874 942 : 64637974 : tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
943 : : Buffer buffer, bool transfer_pin)
944 : : {
1976 945 : 64637974 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
946 : :
947 [ + + ]: 64637974 : if (TTS_SHOULDFREE(slot))
948 : : {
949 : : /* materialized slot shouldn't have a buffer to release */
950 [ - + ]: 197881 : Assert(!BufferIsValid(bslot->buffer));
951 : :
952 : 197881 : heap_freetuple(bslot->base.tuple);
953 : 197881 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
954 : : }
955 : :
956 : 64637974 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
957 : 64637974 : slot->tts_nvalid = 0;
958 : 64637974 : bslot->base.tuple = tuple;
959 : 64637974 : bslot->base.off = 0;
1874 960 : 64637974 : slot->tts_tid = tuple->t_self;
961 : :
962 : : /*
963 : : * If tuple is on a disk page, keep the page pinned as long as we hold a
964 : : * pointer into it. We assume the caller already has such a pin. If
965 : : * transfer_pin is true, we'll transfer that pin to this slot, if not
966 : : * we'll pin it again ourselves.
967 : : *
968 : : * This is coded to optimize the case where the slot previously held a
969 : : * tuple on the same disk page: in that case releasing and re-acquiring
970 : : * the pin is a waste of cycles. This is a common situation during
971 : : * seqscans, so it's worth troubling over.
972 : : */
1976 973 [ + + ]: 64637974 : if (bslot->buffer != buffer)
974 : : {
975 [ + + ]: 8631925 : if (BufferIsValid(bslot->buffer))
976 : 2061692 : ReleaseBuffer(bslot->buffer);
977 : :
978 : 8631925 : bslot->buffer = buffer;
979 : :
1874 980 [ + + + - ]: 8631925 : if (!transfer_pin && BufferIsValid(buffer))
981 : 8387687 : IncrBufferRefCount(buffer);
982 : : }
983 [ + + + - ]: 56006049 : else if (transfer_pin && BufferIsValid(buffer))
984 : : {
985 : : /*
986 : : * In transfer_pin mode the caller won't know about the same-page
987 : : * optimization, so we gotta release its pin.
988 : : */
989 : 148794 : ReleaseBuffer(buffer);
990 : : }
1976 991 : 64637974 : }
992 : :
993 : : /*
994 : : * slot_deform_heap_tuple
995 : : * Given a TupleTableSlot, extract data from the slot's physical tuple
996 : : * into its Datum/isnull arrays. Data is extracted up through the
997 : : * natts'th column (caller must ensure this is a legal column number).
998 : : *
999 : : * This is essentially an incremental version of heap_deform_tuple:
1000 : : * on each call we extract attributes up to the one needed, without
1001 : : * re-computing information about previously extracted attributes.
1002 : : * slot->tts_nvalid is the number of attributes already extracted.
1003 : : *
1004 : : * This is marked as always inline, so the different offp for different types
1005 : : * of slots gets optimized away.
1006 : : */
1007 : : static pg_attribute_always_inline void
1008 : 84712202 : slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
1009 : : int natts)
1010 : : {
1011 : 84712202 : TupleDesc tupleDesc = slot->tts_tupleDescriptor;
1012 : 84712202 : Datum *values = slot->tts_values;
1013 : 84712202 : bool *isnull = slot->tts_isnull;
1014 : 84712202 : HeapTupleHeader tup = tuple->t_data;
1015 : 84712202 : bool hasnulls = HeapTupleHasNulls(tuple);
1016 : : int attnum;
1017 : : char *tp; /* ptr to tuple data */
1018 : : uint32 off; /* offset in tuple data */
1019 : 84712202 : bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
1020 : : bool slow; /* can we use/set attcacheoff? */
1021 : :
1022 : : /* We can only fetch as many attributes as the tuple has. */
1023 : 84712202 : natts = Min(HeapTupleHeaderGetNatts(tuple->t_data), natts);
1024 : :
1025 : : /*
1026 : : * Check whether the first call for this tuple, and initialize or restore
1027 : : * loop state.
1028 : : */
1029 : 84712202 : attnum = slot->tts_nvalid;
1030 [ + + ]: 84712202 : if (attnum == 0)
1031 : : {
1032 : : /* Start from the first attribute */
1033 : 73070685 : off = 0;
1034 : 73070685 : slow = false;
1035 : : }
1036 : : else
1037 : : {
1038 : : /* Restore state from previous execution */
1039 : 11641517 : off = *offp;
1040 : 11641517 : slow = TTS_SLOW(slot);
1041 : : }
1042 : :
1043 : 84712202 : tp = (char *) tup + tup->t_hoff;
1044 : :
1045 [ + + ]: 363515782 : for (; attnum < natts; attnum++)
1046 : : {
1047 : 278803580 : Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
1048 : :
1049 [ + + + + ]: 278803580 : if (hasnulls && att_isnull(attnum, bp))
1050 : : {
1051 : 16031789 : values[attnum] = (Datum) 0;
1052 : 16031789 : isnull[attnum] = true;
1053 : 16031789 : slow = true; /* can't use attcacheoff anymore */
1054 : 16031789 : continue;
1055 : : }
1056 : :
1057 : 262771791 : isnull[attnum] = false;
1058 : :
1059 [ + + + + ]: 262771791 : if (!slow && thisatt->attcacheoff >= 0)
1060 : 244694170 : off = thisatt->attcacheoff;
1061 [ + + ]: 18077621 : else if (thisatt->attlen == -1)
1062 : : {
1063 : : /*
1064 : : * We can only cache the offset for a varlena attribute if the
1065 : : * offset is already suitably aligned, so that there would be no
1066 : : * pad bytes in any case: then the offset will be valid for either
1067 : : * an aligned or unaligned value.
1068 : : */
1069 [ + + + + ]: 6438147 : if (!slow &&
1070 [ + + - + : 332057 : off == att_align_nominal(off, thisatt->attalign))
+ - - - ]
1071 : 37525 : thisatt->attcacheoff = off;
1072 : : else
1073 : : {
1074 [ + + + + : 6068565 : off = att_align_pointer(off, thisatt->attalign, -1,
+ - + - -
- ]
1075 : : tp + off);
1076 : 6068565 : slow = true;
1077 : : }
1078 : : }
1079 : : else
1080 : : {
1081 : : /* not varlena, so safe to use att_align_nominal */
1082 [ + + + + : 11971531 : off = att_align_nominal(off, thisatt->attalign);
+ + - + ]
1083 : :
1084 [ + + ]: 11971531 : if (!slow)
1085 : 256085 : thisatt->attcacheoff = off;
1086 : : }
1087 : :
1088 : 262771791 : values[attnum] = fetchatt(thisatt, tp + off);
1089 : :
1090 [ + + + - : 262771791 : off = att_addlength_pointer(off, thisatt->attlen, tp + off);
+ + + + +
- - + + +
- - ]
1091 : :
1092 [ + + ]: 262771791 : if (thisatt->attlen <= 0)
1093 : 21016671 : slow = true; /* can't use attcacheoff anymore */
1094 : : }
1095 : :
1096 : : /*
1097 : : * Save state for next execution
1098 : : */
1099 : 84712202 : slot->tts_nvalid = attnum;
1100 : 84712202 : *offp = off;
1101 [ + + ]: 84712202 : if (slow)
1102 : 17655314 : slot->tts_flags |= TTS_FLAG_SLOW;
1103 : : else
1104 : 67056888 : slot->tts_flags &= ~TTS_FLAG_SLOW;
1105 : 84712202 : }
1106 : :
1107 : :
1108 : : const TupleTableSlotOps TTSOpsVirtual = {
1109 : : .base_slot_size = sizeof(VirtualTupleTableSlot),
1110 : : .init = tts_virtual_init,
1111 : : .release = tts_virtual_release,
1112 : : .clear = tts_virtual_clear,
1113 : : .getsomeattrs = tts_virtual_getsomeattrs,
1114 : : .getsysattr = tts_virtual_getsysattr,
1115 : : .materialize = tts_virtual_materialize,
1116 : : .is_current_xact_tuple = tts_virtual_is_current_xact_tuple,
1117 : : .copyslot = tts_virtual_copyslot,
1118 : :
1119 : : /*
1120 : : * A virtual tuple table slot can not "own" a heap tuple or a minimal
1121 : : * tuple.
1122 : : */
1123 : : .get_heap_tuple = NULL,
1124 : : .get_minimal_tuple = NULL,
1125 : : .copy_heap_tuple = tts_virtual_copy_heap_tuple,
1126 : : .copy_minimal_tuple = tts_virtual_copy_minimal_tuple
1127 : : };
1128 : :
1129 : : const TupleTableSlotOps TTSOpsHeapTuple = {
1130 : : .base_slot_size = sizeof(HeapTupleTableSlot),
1131 : : .init = tts_heap_init,
1132 : : .release = tts_heap_release,
1133 : : .clear = tts_heap_clear,
1134 : : .getsomeattrs = tts_heap_getsomeattrs,
1135 : : .getsysattr = tts_heap_getsysattr,
1136 : : .is_current_xact_tuple = tts_heap_is_current_xact_tuple,
1137 : : .materialize = tts_heap_materialize,
1138 : : .copyslot = tts_heap_copyslot,
1139 : : .get_heap_tuple = tts_heap_get_heap_tuple,
1140 : :
1141 : : /* A heap tuple table slot can not "own" a minimal tuple. */
1142 : : .get_minimal_tuple = NULL,
1143 : : .copy_heap_tuple = tts_heap_copy_heap_tuple,
1144 : : .copy_minimal_tuple = tts_heap_copy_minimal_tuple
1145 : : };
1146 : :
1147 : : const TupleTableSlotOps TTSOpsMinimalTuple = {
1148 : : .base_slot_size = sizeof(MinimalTupleTableSlot),
1149 : : .init = tts_minimal_init,
1150 : : .release = tts_minimal_release,
1151 : : .clear = tts_minimal_clear,
1152 : : .getsomeattrs = tts_minimal_getsomeattrs,
1153 : : .getsysattr = tts_minimal_getsysattr,
1154 : : .is_current_xact_tuple = tts_minimal_is_current_xact_tuple,
1155 : : .materialize = tts_minimal_materialize,
1156 : : .copyslot = tts_minimal_copyslot,
1157 : :
1158 : : /* A minimal tuple table slot can not "own" a heap tuple. */
1159 : : .get_heap_tuple = NULL,
1160 : : .get_minimal_tuple = tts_minimal_get_minimal_tuple,
1161 : : .copy_heap_tuple = tts_minimal_copy_heap_tuple,
1162 : : .copy_minimal_tuple = tts_minimal_copy_minimal_tuple
1163 : : };
1164 : :
1165 : : const TupleTableSlotOps TTSOpsBufferHeapTuple = {
1166 : : .base_slot_size = sizeof(BufferHeapTupleTableSlot),
1167 : : .init = tts_buffer_heap_init,
1168 : : .release = tts_buffer_heap_release,
1169 : : .clear = tts_buffer_heap_clear,
1170 : : .getsomeattrs = tts_buffer_heap_getsomeattrs,
1171 : : .getsysattr = tts_buffer_heap_getsysattr,
1172 : : .is_current_xact_tuple = tts_buffer_is_current_xact_tuple,
1173 : : .materialize = tts_buffer_heap_materialize,
1174 : : .copyslot = tts_buffer_heap_copyslot,
1175 : : .get_heap_tuple = tts_buffer_heap_get_heap_tuple,
1176 : :
1177 : : /* A buffer heap tuple table slot can not "own" a minimal tuple. */
1178 : : .get_minimal_tuple = NULL,
1179 : : .copy_heap_tuple = tts_buffer_heap_copy_heap_tuple,
1180 : : .copy_minimal_tuple = tts_buffer_heap_copy_minimal_tuple
1181 : : };
1182 : :
1183 : :
1184 : : /* ----------------------------------------------------------------
1185 : : * tuple table create/delete functions
1186 : : * ----------------------------------------------------------------
1187 : : */
1188 : :
1189 : : /* --------------------------------
1190 : : * MakeTupleTableSlot
1191 : : *
1192 : : * Basic routine to make an empty TupleTableSlot of given
1193 : : * TupleTableSlotType. If tupleDesc is specified the slot's descriptor is
1194 : : * fixed for its lifetime, gaining some efficiency. If that's
1195 : : * undesirable, pass NULL.
1196 : : * --------------------------------
1197 : : */
1198 : : TupleTableSlot *
1977 1199 : 15195214 : MakeTupleTableSlot(TupleDesc tupleDesc,
1200 : : const TupleTableSlotOps *tts_ops)
1201 : : {
1202 : : Size basesz,
1203 : : allocsz;
1204 : : TupleTableSlot *slot;
1205 : :
1976 1206 : 15195214 : basesz = tts_ops->base_slot_size;
1207 : :
1208 : : /*
1209 : : * When a fixed descriptor is specified, we can reduce overhead by
1210 : : * allocating the entire slot in one go.
1211 : : */
2249 1212 [ + + ]: 15195214 : if (tupleDesc)
1976 1213 : 15165512 : allocsz = MAXALIGN(basesz) +
2249 1214 : 15165512 : MAXALIGN(tupleDesc->natts * sizeof(Datum)) +
1215 : 15165512 : MAXALIGN(tupleDesc->natts * sizeof(bool));
1216 : : else
1976 1217 : 29702 : allocsz = basesz;
1218 : :
1219 : 15195214 : slot = palloc0(allocsz);
1220 : : /* const for optimization purposes, OK to modify at allocation time */
1977 1221 : 15195214 : *((const TupleTableSlotOps **) &slot->tts_ops) = tts_ops;
2249 1222 : 15195214 : slot->type = T_TupleTableSlot;
2008 1223 : 15195214 : slot->tts_flags |= TTS_FLAG_EMPTY;
1224 [ + + ]: 15195214 : if (tupleDesc != NULL)
1225 : 15165512 : slot->tts_flags |= TTS_FLAG_FIXED;
2249 1226 : 15195214 : slot->tts_tupleDescriptor = tupleDesc;
5313 tgl@sss.pgh.pa.us 1227 : 15195214 : slot->tts_mcxt = CurrentMemoryContext;
1228 : 15195214 : slot->tts_nvalid = 0;
1229 : :
2249 andres@anarazel.de 1230 [ + + ]: 15195214 : if (tupleDesc != NULL)
1231 : : {
1232 : 15165512 : slot->tts_values = (Datum *)
1233 : : (((char *) slot)
1976 1234 : 15165512 : + MAXALIGN(basesz));
2249 1235 : 15165512 : slot->tts_isnull = (bool *)
1236 : : (((char *) slot)
1976 1237 : 15165512 : + MAXALIGN(basesz)
2249 1238 : 15165512 : + MAXALIGN(tupleDesc->natts * sizeof(Datum)));
1239 : :
1240 [ + + ]: 15165512 : PinTupleDesc(tupleDesc);
1241 : : }
1242 : :
1243 : : /*
1244 : : * And allow slot type specific initialization.
1245 : : */
1976 1246 : 15195214 : slot->tts_ops->init(slot);
1247 : :
5313 tgl@sss.pgh.pa.us 1248 : 15195214 : return slot;
1249 : : }
1250 : :
1251 : : /* --------------------------------
1252 : : * ExecAllocTableSlot
1253 : : *
1254 : : * Create a tuple table slot within a tuple table (which is just a List).
1255 : : * --------------------------------
1256 : : */
1257 : : TupleTableSlot *
1977 andres@anarazel.de 1258 : 942468 : ExecAllocTableSlot(List **tupleTable, TupleDesc desc,
1259 : : const TupleTableSlotOps *tts_ops)
1260 : : {
1261 : 942468 : TupleTableSlot *slot = MakeTupleTableSlot(desc, tts_ops);
1262 : :
5313 tgl@sss.pgh.pa.us 1263 : 942468 : *tupleTable = lappend(*tupleTable, slot);
1264 : :
1265 : 942468 : return slot;
1266 : : }
1267 : :
1268 : : /* --------------------------------
1269 : : * ExecResetTupleTable
1270 : : *
1271 : : * This releases any resources (buffer pins, tupdesc refcounts)
1272 : : * held by the tuple table, and optionally releases the memory
1273 : : * occupied by the tuple table data structure.
1274 : : * It is expected that this routine be called by ExecEndPlan().
1275 : : * --------------------------------
1276 : : */
1277 : : void
1278 : 440371 : ExecResetTupleTable(List *tupleTable, /* tuple table */
1279 : : bool shouldFree) /* true if we should free memory */
1280 : : {
1281 : : ListCell *lc;
1282 : :
1283 [ + + + + : 1496218 : foreach(lc, tupleTable)
+ + ]
1284 : : {
2561 1285 : 1055847 : TupleTableSlot *slot = lfirst_node(TupleTableSlot, lc);
1286 : :
1287 : : /* Always release resources and reset the slot to empty */
5313 1288 : 1055847 : ExecClearTuple(slot);
1976 andres@anarazel.de 1289 : 1055847 : slot->tts_ops->release(slot);
5313 tgl@sss.pgh.pa.us 1290 [ + + ]: 1055847 : if (slot->tts_tupleDescriptor)
1291 : : {
1292 [ + + ]: 1055820 : ReleaseTupleDesc(slot->tts_tupleDescriptor);
1293 : 1055820 : slot->tts_tupleDescriptor = NULL;
1294 : : }
1295 : :
1296 : : /* If shouldFree, release memory occupied by the slot itself */
1297 [ + + ]: 1055847 : if (shouldFree)
1298 : : {
2008 andres@anarazel.de 1299 [ - + ]: 2857 : if (!TTS_FIXED(slot))
1300 : : {
2249 andres@anarazel.de 1301 [ # # ]:UBC 0 : if (slot->tts_values)
1302 : 0 : pfree(slot->tts_values);
1303 [ # # ]: 0 : if (slot->tts_isnull)
1304 : 0 : pfree(slot->tts_isnull);
1305 : : }
5313 tgl@sss.pgh.pa.us 1306 :CBC 2857 : pfree(slot);
1307 : : }
1308 : : }
1309 : :
1310 : : /* If shouldFree, release the list structure */
1311 [ + + ]: 440371 : if (shouldFree)
1312 : 2792 : list_free(tupleTable);
10141 scrappy@hub.org 1313 : 440371 : }
1314 : :
1315 : : /* --------------------------------
1316 : : * MakeSingleTupleTableSlot
1317 : : *
1318 : : * This is a convenience routine for operations that need a standalone
1319 : : * TupleTableSlot not gotten from the main executor tuple table. It makes
1320 : : * a single slot of given TupleTableSlotType and initializes it to use the
1321 : : * given tuple descriptor.
1322 : : * --------------------------------
1323 : : */
1324 : : TupleTableSlot *
1977 andres@anarazel.de 1325 : 14252639 : MakeSingleTupleTableSlot(TupleDesc tupdesc,
1326 : : const TupleTableSlotOps *tts_ops)
1327 : : {
1328 : 14252639 : TupleTableSlot *slot = MakeTupleTableSlot(tupdesc, tts_ops);
1329 : :
6971 tgl@sss.pgh.pa.us 1330 : 14252639 : return slot;
1331 : : }
1332 : :
1333 : : /* --------------------------------
1334 : : * ExecDropSingleTupleTableSlot
1335 : : *
1336 : : * Release a TupleTableSlot made with MakeSingleTupleTableSlot.
1337 : : * DON'T use this on a slot that's part of a tuple table list!
1338 : : * --------------------------------
1339 : : */
1340 : : void
6969 1341 : 14094633 : ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
1342 : : {
1343 : : /* This should match ExecResetTupleTable's processing of one slot */
5313 1344 [ - + ]: 14094633 : Assert(IsA(slot, TupleTableSlot));
6969 1345 : 14094633 : ExecClearTuple(slot);
1976 andres@anarazel.de 1346 : 14094633 : slot->tts_ops->release(slot);
6512 tgl@sss.pgh.pa.us 1347 [ + - ]: 14094633 : if (slot->tts_tupleDescriptor)
1348 [ + + ]: 14094633 : ReleaseTupleDesc(slot->tts_tupleDescriptor);
2008 andres@anarazel.de 1349 [ - + ]: 14094633 : if (!TTS_FIXED(slot))
1350 : : {
2249 andres@anarazel.de 1351 [ # # ]:UBC 0 : if (slot->tts_values)
1352 : 0 : pfree(slot->tts_values);
1353 [ # # ]: 0 : if (slot->tts_isnull)
1354 : 0 : pfree(slot->tts_isnull);
1355 : : }
6969 tgl@sss.pgh.pa.us 1356 :CBC 14094633 : pfree(slot);
1357 : 14094633 : }
1358 : :
1359 : :
1360 : : /* ----------------------------------------------------------------
1361 : : * tuple table slot accessor functions
1362 : : * ----------------------------------------------------------------
1363 : : */
1364 : :
1365 : : /* --------------------------------
1366 : : * ExecSetSlotDescriptor
1367 : : *
1368 : : * This function is used to set the tuple descriptor associated
1369 : : * with the slot's tuple. The passed descriptor must have lifespan
1370 : : * at least equal to the slot's. If it is a reference-counted descriptor
1371 : : * then the reference count is incremented for as long as the slot holds
1372 : : * a reference.
1373 : : * --------------------------------
1374 : : */
1375 : : void
2489 1376 : 29675 : ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
1377 : : TupleDesc tupdesc) /* new tuple descriptor */
1378 : : {
2008 andres@anarazel.de 1379 [ - + ]: 29675 : Assert(!TTS_FIXED(slot));
1380 : :
1381 : : /* For safety, make sure slot is empty before changing it */
6969 tgl@sss.pgh.pa.us 1382 : 29675 : ExecClearTuple(slot);
1383 : :
1384 : : /*
1385 : : * Release any old descriptor. Also release old Datum/isnull arrays if
1386 : : * present (we don't bother to check if they could be re-used).
1387 : : */
6512 1388 [ - + ]: 29675 : if (slot->tts_tupleDescriptor)
6512 tgl@sss.pgh.pa.us 1389 [ # # ]:UBC 0 : ReleaseTupleDesc(slot->tts_tupleDescriptor);
1390 : :
6969 tgl@sss.pgh.pa.us 1391 [ - + ]:CBC 29675 : if (slot->tts_values)
6969 tgl@sss.pgh.pa.us 1392 :UBC 0 : pfree(slot->tts_values);
6969 tgl@sss.pgh.pa.us 1393 [ - + ]:CBC 29675 : if (slot->tts_isnull)
6969 tgl@sss.pgh.pa.us 1394 :UBC 0 : pfree(slot->tts_isnull);
1395 : :
1396 : : /*
1397 : : * Install the new descriptor; if it's refcounted, bump its refcount.
1398 : : */
6969 tgl@sss.pgh.pa.us 1399 :CBC 29675 : slot->tts_tupleDescriptor = tupdesc;
6512 1400 [ - + ]: 29675 : PinTupleDesc(tupdesc);
1401 : :
1402 : : /*
1403 : : * Allocate Datum/isnull arrays of the appropriate size. These must have
1404 : : * the same lifetime as the slot, so allocate in the slot's own context.
1405 : : */
6969 1406 : 29675 : slot->tts_values = (Datum *)
1407 : 29675 : MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(Datum));
1408 : 29675 : slot->tts_isnull = (bool *)
1409 : 29675 : MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(bool));
1410 : 29675 : }
1411 : :
1412 : : /* --------------------------------
1413 : : * ExecStoreHeapTuple
1414 : : *
1415 : : * This function is used to store an on-the-fly physical tuple into a specified
1416 : : * slot in the tuple table.
1417 : : *
1418 : : * tuple: tuple to store
1419 : : * slot: TTSOpsHeapTuple type slot to store it in
1420 : : * shouldFree: true if ExecClearTuple should pfree() the tuple
1421 : : * when done with it
1422 : : *
1423 : : * shouldFree is normally set 'true' for tuples constructed on-the-fly. But it
1424 : : * can be 'false' when the referenced tuple is held in a tuple table slot
1425 : : * belonging to a lower-level executor Proc node. In this case the lower-level
1426 : : * slot retains ownership and responsibility for eventually releasing the
1427 : : * tuple. When this method is used, we must be certain that the upper-level
1428 : : * Proc node will lose interest in the tuple sooner than the lower-level one
1429 : : * does! If you're not certain, copy the lower-level tuple with heap_copytuple
1430 : : * and let the upper-level table slot assume ownership of the copy!
1431 : : *
1432 : : * Return value is just the passed-in slot pointer.
1433 : : *
1434 : : * If the target slot is not guaranteed to be TTSOpsHeapTuple type slot, use
1435 : : * the, more expensive, ExecForceStoreHeapTuple().
1436 : : * --------------------------------
1437 : : */
1438 : : TupleTableSlot *
2028 andres@anarazel.de 1439 : 2090013 : ExecStoreHeapTuple(HeapTuple tuple,
1440 : : TupleTableSlot *slot,
1441 : : bool shouldFree)
1442 : : {
1443 : : /*
1444 : : * sanity checks
1445 : : */
1446 [ - + ]: 2090013 : Assert(tuple != NULL);
1447 [ - + ]: 2090013 : Assert(slot != NULL);
1448 [ - + ]: 2090013 : Assert(slot->tts_tupleDescriptor != NULL);
1449 : :
1976 1450 [ - + ]: 2090013 : if (unlikely(!TTS_IS_HEAPTUPLE(slot)))
1976 andres@anarazel.de 1451 [ # # ]:UBC 0 : elog(ERROR, "trying to store a heap tuple into wrong type of slot");
1976 andres@anarazel.de 1452 :CBC 2090013 : tts_heap_store_tuple(slot, tuple, shouldFree);
1453 : :
1874 1454 : 2090013 : slot->tts_tableOid = tuple->t_tableOid;
1455 : :
2028 1456 : 2090013 : return slot;
1457 : : }
1458 : :
1459 : : /* --------------------------------
1460 : : * ExecStoreBufferHeapTuple
1461 : : *
1462 : : * This function is used to store an on-disk physical tuple from a buffer
1463 : : * into a specified slot in the tuple table.
1464 : : *
1465 : : * tuple: tuple to store
1466 : : * slot: TTSOpsBufferHeapTuple type slot to store it in
1467 : : * buffer: disk buffer if tuple is in a disk page, else InvalidBuffer
1468 : : *
1469 : : * The tuple table code acquires a pin on the buffer which is held until the
1470 : : * slot is cleared, so that the tuple won't go away on us.
1471 : : *
1472 : : * Return value is just the passed-in slot pointer.
1473 : : *
1474 : : * If the target slot is not guaranteed to be TTSOpsBufferHeapTuple type slot,
1475 : : * use the, more expensive, ExecForceStoreHeapTuple().
1476 : : * --------------------------------
1477 : : */
1478 : : TupleTableSlot *
1479 : 64241669 : ExecStoreBufferHeapTuple(HeapTuple tuple,
1480 : : TupleTableSlot *slot,
1481 : : Buffer buffer)
1482 : : {
1483 : : /*
1484 : : * sanity checks
1485 : : */
1976 1486 [ - + ]: 64241669 : Assert(tuple != NULL);
1487 [ - + ]: 64241669 : Assert(slot != NULL);
1488 [ - + ]: 64241669 : Assert(slot->tts_tupleDescriptor != NULL);
1489 [ - + ]: 64241669 : Assert(BufferIsValid(buffer));
1490 : :
1491 [ - + ]: 64241669 : if (unlikely(!TTS_IS_BUFFERTUPLE(slot)))
1976 andres@anarazel.de 1492 [ # # ]:UBC 0 : elog(ERROR, "trying to store an on-disk heap tuple into wrong type of slot");
1874 andres@anarazel.de 1493 :CBC 64241669 : tts_buffer_heap_store_tuple(slot, tuple, buffer, false);
1494 : :
1495 : 64241669 : slot->tts_tableOid = tuple->t_tableOid;
1496 : :
1497 : 64241669 : return slot;
1498 : : }
1499 : :
1500 : : /*
1501 : : * Like ExecStoreBufferHeapTuple, but transfer an existing pin from the caller
1502 : : * to the slot, i.e. the caller doesn't need to, and may not, release the pin.
1503 : : */
1504 : : TupleTableSlot *
1505 : 393032 : ExecStorePinnedBufferHeapTuple(HeapTuple tuple,
1506 : : TupleTableSlot *slot,
1507 : : Buffer buffer)
1508 : : {
1509 : : /*
1510 : : * sanity checks
1511 : : */
1512 [ - + ]: 393032 : Assert(tuple != NULL);
1513 [ - + ]: 393032 : Assert(slot != NULL);
1514 [ - + ]: 393032 : Assert(slot->tts_tupleDescriptor != NULL);
1515 [ - + ]: 393032 : Assert(BufferIsValid(buffer));
1516 : :
1517 [ - + ]: 393032 : if (unlikely(!TTS_IS_BUFFERTUPLE(slot)))
1874 andres@anarazel.de 1518 [ # # ]:UBC 0 : elog(ERROR, "trying to store an on-disk heap tuple into wrong type of slot");
1874 andres@anarazel.de 1519 :CBC 393032 : tts_buffer_heap_store_tuple(slot, tuple, buffer, true);
1520 : :
1521 : 393032 : slot->tts_tableOid = tuple->t_tableOid;
1522 : :
9716 bruce@momjian.us 1523 : 393032 : return slot;
1524 : : }
1525 : :
1526 : : /*
1527 : : * Store a minimal tuple into TTSOpsMinimalTuple type slot.
1528 : : *
1529 : : * If the target slot is not guaranteed to be TTSOpsMinimalTuple type slot,
1530 : : * use the, more expensive, ExecForceStoreMinimalTuple().
1531 : : */
1532 : : TupleTableSlot *
6501 tgl@sss.pgh.pa.us 1533 : 25457619 : ExecStoreMinimalTuple(MinimalTuple mtup,
1534 : : TupleTableSlot *slot,
1535 : : bool shouldFree)
1536 : : {
1537 : : /*
1538 : : * sanity checks
1539 : : */
1540 [ - + ]: 25457619 : Assert(mtup != NULL);
1541 [ - + ]: 25457619 : Assert(slot != NULL);
1542 [ - + ]: 25457619 : Assert(slot->tts_tupleDescriptor != NULL);
1543 : :
1976 andres@anarazel.de 1544 [ - + ]: 25457619 : if (unlikely(!TTS_IS_MINIMALTUPLE(slot)))
1976 andres@anarazel.de 1545 [ # # ]:UBC 0 : elog(ERROR, "trying to store a minimal tuple into wrong type of slot");
1976 andres@anarazel.de 1546 :CBC 25457619 : tts_minimal_store_tuple(slot, mtup, shouldFree);
1547 : :
6501 tgl@sss.pgh.pa.us 1548 : 25457619 : return slot;
1549 : : }
1550 : :
1551 : : /*
1552 : : * Store a HeapTuple into any kind of slot, performing conversion if
1553 : : * necessary.
1554 : : */
1555 : : void
1976 andres@anarazel.de 1556 : 864986 : ExecForceStoreHeapTuple(HeapTuple tuple,
1557 : : TupleTableSlot *slot,
1558 : : bool shouldFree)
1559 : : {
1560 [ + + ]: 864986 : if (TTS_IS_HEAPTUPLE(slot))
1561 : : {
1822 1562 : 213 : ExecStoreHeapTuple(tuple, slot, shouldFree);
1563 : : }
1976 1564 [ + + ]: 864773 : else if (TTS_IS_BUFFERTUPLE(slot))
1565 : : {
1566 : : MemoryContext oldContext;
1567 : 36170 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
1568 : :
1569 : 36170 : ExecClearTuple(slot);
1570 : 36170 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
1571 : 36170 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
1572 : 36170 : bslot->base.tuple = heap_copytuple(tuple);
1621 tgl@sss.pgh.pa.us 1573 : 36170 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
1976 andres@anarazel.de 1574 : 36170 : MemoryContextSwitchTo(oldContext);
1575 : :
1822 1576 [ + + ]: 36170 : if (shouldFree)
1577 : 35203 : pfree(tuple);
1578 : : }
1579 : : else
1580 : : {
1976 1581 : 828603 : ExecClearTuple(slot);
1582 : 828603 : heap_deform_tuple(tuple, slot->tts_tupleDescriptor,
1583 : : slot->tts_values, slot->tts_isnull);
1584 : 828603 : ExecStoreVirtualTuple(slot);
1585 : :
1822 1586 [ + + ]: 828603 : if (shouldFree)
1587 : : {
1588 : 109446 : ExecMaterializeSlot(slot);
1589 : 109446 : pfree(tuple);
1590 : : }
1591 : : }
1976 1592 : 864986 : }
1593 : :
1594 : : /*
1595 : : * Store a MinimalTuple into any kind of slot, performing conversion if
1596 : : * necessary.
1597 : : */
1598 : : void
1599 : 3693543 : ExecForceStoreMinimalTuple(MinimalTuple mtup,
1600 : : TupleTableSlot *slot,
1601 : : bool shouldFree)
1602 : : {
1603 [ + + ]: 3693543 : if (TTS_IS_MINIMALTUPLE(slot))
1604 : : {
1605 : 2358435 : tts_minimal_store_tuple(slot, mtup, shouldFree);
1606 : : }
1607 : : else
1608 : : {
1609 : : HeapTupleData htup;
1610 : :
1611 : 1335108 : ExecClearTuple(slot);
1612 : :
1613 : 1335108 : htup.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
1614 : 1335108 : htup.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
1615 : 1335108 : heap_deform_tuple(&htup, slot->tts_tupleDescriptor,
1616 : : slot->tts_values, slot->tts_isnull);
1617 : 1335108 : ExecStoreVirtualTuple(slot);
1618 : :
1822 1619 [ + + ]: 1335108 : if (shouldFree)
1620 : : {
1621 : 735096 : ExecMaterializeSlot(slot);
1622 : 735096 : pfree(mtup);
1623 : : }
1624 : : }
10141 scrappy@hub.org 1625 : 3693543 : }
1626 : :
1627 : : /* --------------------------------
1628 : : * ExecStoreVirtualTuple
1629 : : * Mark a slot as containing a virtual tuple.
1630 : : *
1631 : : * The protocol for loading a slot with virtual tuple data is:
1632 : : * * Call ExecClearTuple to mark the slot empty.
1633 : : * * Store data into the Datum/isnull arrays.
1634 : : * * Call ExecStoreVirtualTuple to mark the slot valid.
1635 : : * This is a bit unclean but it avoids one round of data copying.
1636 : : * --------------------------------
1637 : : */
1638 : : TupleTableSlot *
6969 tgl@sss.pgh.pa.us 1639 : 11917944 : ExecStoreVirtualTuple(TupleTableSlot *slot)
1640 : : {
1641 : : /*
1642 : : * sanity checks
1643 : : */
1644 [ - + ]: 11917944 : Assert(slot != NULL);
1645 [ - + ]: 11917944 : Assert(slot->tts_tupleDescriptor != NULL);
2008 andres@anarazel.de 1646 [ - + ]: 11917944 : Assert(TTS_EMPTY(slot));
1647 : :
1648 : 11917944 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
6969 tgl@sss.pgh.pa.us 1649 : 11917944 : slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
1650 : :
1651 : 11917944 : return slot;
1652 : : }
1653 : :
1654 : : /* --------------------------------
1655 : : * ExecStoreAllNullTuple
1656 : : * Set up the slot to contain a null in every column.
1657 : : *
1658 : : * At first glance this might sound just like ExecClearTuple, but it's
1659 : : * entirely different: the slot ends up full, not empty.
1660 : : * --------------------------------
1661 : : */
1662 : : TupleTableSlot *
1663 : 314216 : ExecStoreAllNullTuple(TupleTableSlot *slot)
1664 : : {
1665 : : /*
1666 : : * sanity checks
1667 : : */
1668 [ - + ]: 314216 : Assert(slot != NULL);
1669 [ - + ]: 314216 : Assert(slot->tts_tupleDescriptor != NULL);
1670 : :
1671 : : /* Clear any old contents */
1672 : 314216 : ExecClearTuple(slot);
1673 : :
1674 : : /*
1675 : : * Fill all the columns of the virtual tuple with nulls
1676 : : */
1677 [ + - + - : 5223938 : MemSet(slot->tts_values, 0,
+ - + - +
+ ]
1678 : : slot->tts_tupleDescriptor->natts * sizeof(Datum));
1679 : 314216 : memset(slot->tts_isnull, true,
1680 : 314216 : slot->tts_tupleDescriptor->natts * sizeof(bool));
1681 : :
1682 : 314216 : return ExecStoreVirtualTuple(slot);
1683 : : }
1684 : :
1685 : : /*
1686 : : * Store a HeapTuple in datum form, into a slot. That always requires
1687 : : * deforming it and storing it in virtual form.
1688 : : *
1689 : : * Until the slot is materialized, the contents of the slot depend on the
1690 : : * datum.
1691 : : */
1692 : : void
1871 andres@anarazel.de 1693 : 4 : ExecStoreHeapTupleDatum(Datum data, TupleTableSlot *slot)
1694 : : {
1695 : 4 : HeapTupleData tuple = {0};
1696 : : HeapTupleHeader td;
1697 : :
1698 : 4 : td = DatumGetHeapTupleHeader(data);
1699 : :
1700 : 4 : tuple.t_len = HeapTupleHeaderGetDatumLength(td);
1701 : 4 : tuple.t_self = td->t_ctid;
1702 : 4 : tuple.t_data = td;
1703 : :
1704 : 4 : ExecClearTuple(slot);
1705 : :
1706 : 4 : heap_deform_tuple(&tuple, slot->tts_tupleDescriptor,
1707 : : slot->tts_values, slot->tts_isnull);
1708 : 4 : ExecStoreVirtualTuple(slot);
1709 : 4 : }
1710 : :
1711 : : /*
1712 : : * ExecFetchSlotHeapTuple - fetch HeapTuple representing the slot's content
1713 : : *
1714 : : * The returned HeapTuple represents the slot's content as closely as
1715 : : * possible.
1716 : : *
1717 : : * If materialize is true, the contents of the slots will be made independent
1718 : : * from the underlying storage (i.e. all buffer pins are released, memory is
1719 : : * allocated in the slot's context).
1720 : : *
1721 : : * If shouldFree is not-NULL it'll be set to true if the returned tuple has
1722 : : * been allocated in the calling memory context, and must be freed by the
1723 : : * caller (via explicit pfree() or a memory context reset).
1724 : : *
1725 : : * NB: If materialize is true, modifications of the returned tuple are
1726 : : * allowed. But it depends on the type of the slot whether such modifications
1727 : : * will also affect the slot's contents. While that is not the nicest
1728 : : * behaviour, all such modifications are in the process of being removed.
1729 : : */
1730 : : HeapTuple
1977 1731 : 19618413 : ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
1732 : : {
1733 : : /*
1734 : : * sanity checks
1735 : : */
6969 tgl@sss.pgh.pa.us 1736 [ - + ]: 19618413 : Assert(slot != NULL);
2008 andres@anarazel.de 1737 [ - + ]: 19618413 : Assert(!TTS_EMPTY(slot));
1738 : :
1739 : : /* Materialize the tuple so that the slot "owns" it, if requested. */
1976 1740 [ + + ]: 19618413 : if (materialize)
1741 : 9309459 : slot->tts_ops->materialize(slot);
1742 : :
1743 [ + + ]: 19618413 : if (slot->tts_ops->get_heap_tuple == NULL)
1744 : : {
1745 [ + - ]: 1459172 : if (shouldFree)
1746 : 1459172 : *shouldFree = true;
1747 : 1459172 : return slot->tts_ops->copy_heap_tuple(slot);
1748 : : }
1749 : : else
1750 : : {
1751 [ + + ]: 18159241 : if (shouldFree)
1752 : 16137635 : *shouldFree = false;
1753 : 18159241 : return slot->tts_ops->get_heap_tuple(slot);
1754 : : }
1755 : : }
1756 : :
1757 : : /* --------------------------------
1758 : : * ExecFetchSlotMinimalTuple
1759 : : * Fetch the slot's minimal physical tuple.
1760 : : *
1761 : : * If the given tuple table slot can hold a minimal tuple, indicated by a
1762 : : * non-NULL get_minimal_tuple callback, the function returns the minimal
1763 : : * tuple returned by that callback. It assumes that the minimal tuple
1764 : : * returned by the callback is "owned" by the slot i.e. the slot is
1765 : : * responsible for freeing the memory consumed by the tuple. Hence it sets
1766 : : * *shouldFree to false, indicating that the caller should not free the
1767 : : * memory consumed by the minimal tuple. In this case the returned minimal
1768 : : * tuple should be considered as read-only.
1769 : : *
1770 : : * If that callback is not supported, it calls copy_minimal_tuple callback
1771 : : * which is expected to return a copy of minimal tuple representing the
1772 : : * contents of the slot. In this case *shouldFree is set to true,
1773 : : * indicating the caller that it should free the memory consumed by the
1774 : : * minimal tuple. In this case the returned minimal tuple may be written
1775 : : * up.
1776 : : * --------------------------------
1777 : : */
1778 : : MinimalTuple
1779 : 10684882 : ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
1780 : : bool *shouldFree)
1781 : : {
1782 : : /*
1783 : : * sanity checks
1784 : : */
6501 tgl@sss.pgh.pa.us 1785 [ - + ]: 10684882 : Assert(slot != NULL);
2008 andres@anarazel.de 1786 [ - + ]: 10684882 : Assert(!TTS_EMPTY(slot));
1787 : :
1976 1788 [ + + ]: 10684882 : if (slot->tts_ops->get_minimal_tuple)
1789 : : {
1790 [ + - ]: 2634946 : if (shouldFree)
1791 : 2634946 : *shouldFree = false;
1792 : 2634946 : return slot->tts_ops->get_minimal_tuple(slot);
1793 : : }
1794 : : else
1795 : : {
1796 [ + - ]: 8049936 : if (shouldFree)
1797 : 8049936 : *shouldFree = true;
1798 : 8049936 : return slot->tts_ops->copy_minimal_tuple(slot);
1799 : : }
1800 : : }
1801 : :
1802 : : /* --------------------------------
1803 : : * ExecFetchSlotHeapTupleDatum
1804 : : * Fetch the slot's tuple as a composite-type Datum.
1805 : : *
1806 : : * The result is always freshly palloc'd in the caller's memory context.
1807 : : * --------------------------------
1808 : : */
1809 : : Datum
1977 1810 : 31523 : ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot)
1811 : : {
1812 : : HeapTuple tup;
1813 : : TupleDesc tupdesc;
1814 : : bool shouldFree;
1815 : : Datum ret;
1816 : :
1817 : : /* Fetch slot's contents in regular-physical-tuple form */
1818 : 31523 : tup = ExecFetchSlotHeapTuple(slot, false, &shouldFree);
5647 tgl@sss.pgh.pa.us 1819 : 31523 : tupdesc = slot->tts_tupleDescriptor;
1820 : :
1821 : : /* Convert to Datum form */
1977 andres@anarazel.de 1822 : 31523 : ret = heap_copy_tuple_as_datum(tup, tupdesc);
1823 : :
1824 [ + - ]: 31523 : if (shouldFree)
1825 : 31523 : pfree(tup);
1826 : :
1827 : 31523 : return ret;
1828 : : }
1829 : :
1830 : : /* ----------------------------------------------------------------
1831 : : * convenience initialization routines
1832 : : * ----------------------------------------------------------------
1833 : : */
1834 : :
1835 : : /* ----------------
1836 : : * ExecInitResultTypeTL
1837 : : *
1838 : : * Initialize result type, using the plan node's targetlist.
1839 : : * ----------------
1840 : : */
1841 : : void
1983 1842 : 612868 : ExecInitResultTypeTL(PlanState *planstate)
1843 : : {
1972 1844 : 612868 : TupleDesc tupDesc = ExecTypeFromTL(planstate->plan->targetlist);
1845 : :
1983 1846 : 612868 : planstate->ps_ResultTupleDesc = tupDesc;
1847 : 612868 : }
1848 : :
1849 : : /* --------------------------------
1850 : : * ExecInit{Result,Scan,Extra}TupleSlot[TL]
1851 : : *
1852 : : * These are convenience routines to initialize the specified slot
1853 : : * in nodes inheriting the appropriate state. ExecInitExtraTupleSlot
1854 : : * is used for initializing special-purpose slots.
1855 : : * --------------------------------
1856 : : */
1857 : :
1858 : : /* ----------------
1859 : : * ExecInitResultTupleSlotTL
1860 : : *
1861 : : * Initialize result tuple slot, using the tuple descriptor previously
1862 : : * computed with ExecInitResultTypeTL().
1863 : : * ----------------
1864 : : */
1865 : : void
1977 1866 : 425222 : ExecInitResultSlot(PlanState *planstate, const TupleTableSlotOps *tts_ops)
1867 : : {
1868 : : TupleTableSlot *slot;
1869 : :
1983 1870 : 425222 : slot = ExecAllocTableSlot(&planstate->state->es_tupleTable,
1871 : : planstate->ps_ResultTupleDesc, tts_ops);
1872 : 425222 : planstate->ps_ResultTupleSlot = slot;
1873 : :
1977 1874 : 425222 : planstate->resultopsfixed = planstate->ps_ResultTupleDesc != NULL;
1875 : 425222 : planstate->resultops = tts_ops;
1876 : 425222 : planstate->resultopsset = true;
1983 1877 : 425222 : }
1878 : :
1879 : : /* ----------------
1880 : : * ExecInitResultTupleSlotTL
1881 : : *
1882 : : * Initialize result tuple slot, using the plan node's targetlist.
1883 : : * ----------------
1884 : : */
1885 : : void
1977 1886 : 317427 : ExecInitResultTupleSlotTL(PlanState *planstate,
1887 : : const TupleTableSlotOps *tts_ops)
1888 : : {
1983 1889 : 317427 : ExecInitResultTypeTL(planstate);
1977 1890 : 317427 : ExecInitResultSlot(planstate, tts_ops);
10141 scrappy@hub.org 1891 : 317427 : }
1892 : :
1893 : : /* ----------------
1894 : : * ExecInitScanTupleSlot
1895 : : * ----------------
1896 : : */
1897 : : void
1977 andres@anarazel.de 1898 : 290345 : ExecInitScanTupleSlot(EState *estate, ScanState *scanstate,
1899 : : TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
1900 : : {
2249 1901 : 290345 : scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
1902 : : tupledesc, tts_ops);
2211 1903 : 290345 : scanstate->ps.scandesc = tupledesc;
1977 1904 : 290345 : scanstate->ps.scanopsfixed = tupledesc != NULL;
1905 : 290345 : scanstate->ps.scanops = tts_ops;
1906 : 290345 : scanstate->ps.scanopsset = true;
10141 scrappy@hub.org 1907 : 290345 : }
1908 : :
1909 : : /* ----------------
1910 : : * ExecInitExtraTupleSlot
1911 : : *
1912 : : * Return a newly created slot. If tupledesc is non-NULL the slot will have
1913 : : * that as its fixed tupledesc. Otherwise the caller needs to use
1914 : : * ExecSetSlotDescriptor() to set the descriptor before use.
1915 : : * ----------------
1916 : : */
1917 : : TupleTableSlot *
1977 andres@anarazel.de 1918 : 217167 : ExecInitExtraTupleSlot(EState *estate,
1919 : : TupleDesc tupledesc,
1920 : : const TupleTableSlotOps *tts_ops)
1921 : : {
1922 : 217167 : return ExecAllocTableSlot(&estate->es_tupleTable, tupledesc, tts_ops);
1923 : : }
1924 : :
1925 : : /* ----------------
1926 : : * ExecInitNullTupleSlot
1927 : : *
1928 : : * Build a slot containing an all-nulls tuple of the given type.
1929 : : * This is used as a substitute for an input tuple when performing an
1930 : : * outer join.
1931 : : * ----------------
1932 : : */
1933 : : TupleTableSlot *
1934 : 19458 : ExecInitNullTupleSlot(EState *estate, TupleDesc tupType,
1935 : : const TupleTableSlotOps *tts_ops)
1936 : : {
1937 : 19458 : TupleTableSlot *slot = ExecInitExtraTupleSlot(estate, tupType, tts_ops);
1938 : :
6969 tgl@sss.pgh.pa.us 1939 : 19458 : return ExecStoreAllNullTuple(slot);
1940 : : }
1941 : :
1942 : : /* ---------------------------------------------------------------
1943 : : * Routines for setting/accessing attributes in a slot.
1944 : : * ---------------------------------------------------------------
1945 : : */
1946 : :
1947 : : /*
1948 : : * Fill in missing values for a TupleTableSlot.
1949 : : *
1950 : : * This is only exposed because it's needed for JIT compiled tuple
1951 : : * deforming. That exception aside, there should be no callers outside of this
1952 : : * file.
1953 : : */
1954 : : void
2009 andres@anarazel.de 1955 : 3835 : slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum)
1956 : : {
1957 : 3835 : AttrMissing *attrmiss = NULL;
1958 : :
1959 [ + + ]: 3835 : if (slot->tts_tupleDescriptor->constr)
1960 : 2259 : attrmiss = slot->tts_tupleDescriptor->constr->missing;
1961 : :
1962 [ + + ]: 3835 : if (!attrmiss)
1963 : : {
1964 : : /* no missing values array at all, so just fill everything in as NULL */
1965 : 1615 : memset(slot->tts_values + startAttNum, 0,
1966 : 1615 : (lastAttNum - startAttNum) * sizeof(Datum));
1967 : 1615 : memset(slot->tts_isnull + startAttNum, 1,
1968 : 1615 : (lastAttNum - startAttNum) * sizeof(bool));
1969 : : }
1970 : : else
1971 : : {
1972 : : int missattnum;
1973 : :
1974 : : /* if there is a missing values array we must process them one by one */
1975 : 2220 : for (missattnum = startAttNum;
1976 [ + + ]: 5136 : missattnum < lastAttNum;
1977 : 2916 : missattnum++)
1978 : : {
1979 : 2916 : slot->tts_values[missattnum] = attrmiss[missattnum].am_value;
1980 : 2916 : slot->tts_isnull[missattnum] = !attrmiss[missattnum].am_present;
1981 : : }
1982 : : }
1983 : 3835 : }
1984 : :
1985 : : /*
1986 : : * slot_getsomeattrs_int - workhorse for slot_getsomeattrs()
1987 : : */
1988 : : void
1976 1989 : 84712202 : slot_getsomeattrs_int(TupleTableSlot *slot, int attnum)
1990 : : {
1991 : : /* Check for caller errors */
1772 akapila@postgresql.o 1992 [ - + ]: 84712202 : Assert(slot->tts_nvalid < attnum); /* checked in slot_getsomeattrs */
1976 andres@anarazel.de 1993 [ - + ]: 84712202 : Assert(attnum > 0);
1994 : :
1995 [ - + ]: 84712202 : if (unlikely(attnum > slot->tts_tupleDescriptor->natts))
2009 andres@anarazel.de 1996 [ # # ]:UBC 0 : elog(ERROR, "invalid attribute number %d", attnum);
1997 : :
1998 : : /* Fetch as many attributes as possible from the underlying tuple. */
1976 andres@anarazel.de 1999 :CBC 84712202 : slot->tts_ops->getsomeattrs(slot, attnum);
2000 : :
2001 : : /*
2002 : : * If the underlying tuple doesn't have enough attributes, tuple
2003 : : * descriptor must have the missing attributes.
2004 : : */
2005 [ + + ]: 84712202 : if (unlikely(slot->tts_nvalid < attnum))
2006 : : {
2007 : 3835 : slot_getmissingattrs(slot, slot->tts_nvalid, attnum);
2008 : 3835 : slot->tts_nvalid = attnum;
2009 : : }
2009 2010 : 84712202 : }
2011 : :
2012 : : /* ----------------------------------------------------------------
2013 : : * ExecTypeFromTL
2014 : : *
2015 : : * Generate a tuple descriptor for the result tuple of a targetlist.
2016 : : * (A parse/plan tlist must be passed, not an ExprState tlist.)
2017 : : * Note that resjunk columns, if any, are included in the result.
2018 : : *
2019 : : * Currently there are about 4 different places where we create
2020 : : * TupleDescriptors. They should all be merged, or perhaps
2021 : : * be rewritten to call BuildDesc().
2022 : : * ----------------------------------------------------------------
2023 : : */
2024 : : TupleDesc
1972 2025 : 624580 : ExecTypeFromTL(List *targetList)
2026 : : {
2027 : 624580 : return ExecTypeFromTLInternal(targetList, false);
2028 : : }
2029 : :
2030 : : /* ----------------------------------------------------------------
2031 : : * ExecCleanTypeFromTL
2032 : : *
2033 : : * Same as above, but resjunk columns are omitted from the result.
2034 : : * ----------------------------------------------------------------
2035 : : */
2036 : : TupleDesc
2037 : 57027 : ExecCleanTypeFromTL(List *targetList)
2038 : : {
2039 : 57027 : return ExecTypeFromTLInternal(targetList, true);
2040 : : }
2041 : :
2042 : : static TupleDesc
2043 : 681607 : ExecTypeFromTLInternal(List *targetList, bool skipjunk)
2044 : : {
2045 : : TupleDesc typeInfo;
2046 : : ListCell *l;
2047 : : int len;
7168 bruce@momjian.us 2048 : 681607 : int cur_resno = 1;
2049 : :
7440 2050 [ + + ]: 681607 : if (skipjunk)
2051 : 57027 : len = ExecCleanTargetListLength(targetList);
2052 : : else
2053 : 624580 : len = ExecTargetListLength(targetList);
1972 andres@anarazel.de 2054 : 681607 : typeInfo = CreateTemplateTupleDesc(len);
2055 : :
7440 bruce@momjian.us 2056 [ + + + + : 3340124 : foreach(l, targetList)
+ + ]
2057 : : {
7168 2058 : 2658517 : TargetEntry *tle = lfirst(l);
2059 : :
6948 tgl@sss.pgh.pa.us 2060 [ + + + + ]: 2658517 : if (skipjunk && tle->resjunk)
7649 2061 : 14815 : continue;
2062 : 7931106 : TupleDescInitEntry(typeInfo,
2063 : : cur_resno,
6948 2064 : 2643702 : tle->resname,
2065 : 2643702 : exprType((Node *) tle->expr),
2066 : 2643702 : exprTypmod((Node *) tle->expr),
2067 : : 0);
4814 peter_e@gmx.net 2068 : 2643702 : TupleDescInitEntryCollation(typeInfo,
2069 : : cur_resno,
2070 : 2643702 : exprCollation((Node *) tle->expr));
2071 : 2643702 : cur_resno++;
2072 : : }
2073 : :
7649 tgl@sss.pgh.pa.us 2074 : 681607 : return typeInfo;
2075 : : }
2076 : :
2077 : : /*
2078 : : * ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
2079 : : *
2080 : : * This is roughly like ExecTypeFromTL, but we work from bare expressions
2081 : : * not TargetEntrys. No names are attached to the tupledesc's columns.
2082 : : */
2083 : : TupleDesc
3443 2084 : 6304 : ExecTypeFromExprList(List *exprList)
2085 : : {
2086 : : TupleDesc typeInfo;
2087 : : ListCell *lc;
7168 bruce@momjian.us 2088 : 6304 : int cur_resno = 1;
2089 : :
1972 andres@anarazel.de 2090 : 6304 : typeInfo = CreateTemplateTupleDesc(list_length(exprList));
2091 : :
3443 tgl@sss.pgh.pa.us 2092 [ + + + + : 17350 : foreach(lc, exprList)
+ + ]
2093 : : {
2094 : 11046 : Node *e = lfirst(lc);
2095 : :
7279 2096 : 11046 : TupleDescInitEntry(typeInfo,
2097 : : cur_resno,
2098 : : NULL,
2099 : : exprType(e),
2100 : : exprTypmod(e),
2101 : : 0);
4814 peter_e@gmx.net 2102 : 11046 : TupleDescInitEntryCollation(typeInfo,
2103 : : cur_resno,
2104 : : exprCollation(e));
2105 : 11046 : cur_resno++;
2106 : : }
2107 : :
7279 tgl@sss.pgh.pa.us 2108 : 6304 : return typeInfo;
2109 : : }
2110 : :
2111 : : /*
2112 : : * ExecTypeSetColNames - set column names in a RECORD TupleDesc
2113 : : *
2114 : : * Column names must be provided as an alias list (list of String nodes).
2115 : : */
2116 : : void
3443 2117 : 1894 : ExecTypeSetColNames(TupleDesc typeInfo, List *namesList)
2118 : : {
2119 : 1894 : int colno = 0;
2120 : : ListCell *lc;
2121 : :
2122 : : /* It's only OK to change col names in a not-yet-blessed RECORD type */
759 2123 [ - + ]: 1894 : Assert(typeInfo->tdtypeid == RECORDOID);
2124 [ - + ]: 1894 : Assert(typeInfo->tdtypmod < 0);
2125 : :
3443 2126 [ + + + + : 6529 : foreach(lc, namesList)
+ + ]
2127 : : {
2128 : 4635 : char *cname = strVal(lfirst(lc));
2129 : : Form_pg_attribute attr;
2130 : :
2131 : : /* Guard against too-long names list (probably can't happen) */
2132 [ - + ]: 4635 : if (colno >= typeInfo->natts)
3443 tgl@sss.pgh.pa.us 2133 :UBC 0 : break;
2429 andres@anarazel.de 2134 :CBC 4635 : attr = TupleDescAttr(typeInfo, colno);
2135 : 4635 : colno++;
2136 : :
2137 : : /*
2138 : : * Do nothing for empty aliases or dropped columns (these cases
2139 : : * probably can't arise in RECORD types, either)
2140 : : */
759 tgl@sss.pgh.pa.us 2141 [ + + - + ]: 4635 : if (cname[0] == '\0' || attr->attisdropped)
3443 2142 : 13 : continue;
2143 : :
2144 : : /* OK, assign the column name */
759 2145 : 4622 : namestrcpy(&(attr->attname), cname);
2146 : : }
3443 2147 : 1894 : }
2148 : :
2149 : : /*
2150 : : * BlessTupleDesc - make a completed tuple descriptor useful for SRFs
2151 : : *
2152 : : * Rowtype Datums returned by a function must contain valid type information.
2153 : : * This happens "for free" if the tupdesc came from a relcache entry, but
2154 : : * not if we have manufactured a tupdesc for a transient RECORD datatype.
2155 : : * In that case we have to notify typcache.c of the existence of the type.
2156 : : */
2157 : : TupleDesc
7318 2158 : 32301 : BlessTupleDesc(TupleDesc tupdesc)
2159 : : {
2160 [ + + ]: 32301 : if (tupdesc->tdtypeid == RECORDOID &&
2161 [ + + ]: 30803 : tupdesc->tdtypmod < 0)
2162 : 13991 : assign_record_type_typmod(tupdesc);
2163 : :
2164 : 32301 : return tupdesc; /* just for notational convenience */
2165 : : }
2166 : :
2167 : : /*
2168 : : * TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
2169 : : * supplied TupleDesc. AttInMetadata can be used in conjunction with C strings
2170 : : * to produce a properly formed tuple.
2171 : : */
2172 : : AttInMetadata *
7969 bruce@momjian.us 2173 : 3813 : TupleDescGetAttInMetadata(TupleDesc tupdesc)
2174 : : {
7893 2175 : 3813 : int natts = tupdesc->natts;
2176 : : int i;
2177 : : Oid atttypeid;
2178 : : Oid attinfuncid;
2179 : : FmgrInfo *attinfuncinfo;
2180 : : Oid *attioparams;
2181 : : int32 *atttypmods;
2182 : : AttInMetadata *attinmeta;
2183 : :
7969 2184 : 3813 : attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
2185 : :
2186 : : /* "Bless" the tupledesc so that we can make rowtype datums with it */
7318 tgl@sss.pgh.pa.us 2187 : 3813 : attinmeta->tupdesc = BlessTupleDesc(tupdesc);
2188 : :
2189 : : /*
2190 : : * Gather info needed later to call the "in" function for each attribute
2191 : : */
7503 bruce@momjian.us 2192 : 3813 : attinfuncinfo = (FmgrInfo *) palloc0(natts * sizeof(FmgrInfo));
7252 tgl@sss.pgh.pa.us 2193 : 3813 : attioparams = (Oid *) palloc0(natts * sizeof(Oid));
7503 bruce@momjian.us 2194 : 3813 : atttypmods = (int32 *) palloc0(natts * sizeof(int32));
2195 : :
7969 2196 [ + + ]: 41729 : for (i = 0; i < natts; i++)
2197 : : {
2429 andres@anarazel.de 2198 : 37916 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
2199 : :
2200 : : /* Ignore dropped attributes */
2201 [ + + ]: 37916 : if (!att->attisdropped)
2202 : : {
2203 : 37810 : atttypeid = att->atttypid;
7252 tgl@sss.pgh.pa.us 2204 : 37810 : getTypeInputInfo(atttypeid, &attinfuncid, &attioparams[i]);
7503 bruce@momjian.us 2205 : 37810 : fmgr_info(attinfuncid, &attinfuncinfo[i]);
2429 andres@anarazel.de 2206 : 37810 : atttypmods[i] = att->atttypmod;
2207 : : }
2208 : : }
7969 bruce@momjian.us 2209 : 3813 : attinmeta->attinfuncs = attinfuncinfo;
7252 tgl@sss.pgh.pa.us 2210 : 3813 : attinmeta->attioparams = attioparams;
7969 bruce@momjian.us 2211 : 3813 : attinmeta->atttypmods = atttypmods;
2212 : :
2213 : 3813 : return attinmeta;
2214 : : }
2215 : :
2216 : : /*
2217 : : * BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
2218 : : * values is an array of C strings, one for each attribute of the return tuple.
2219 : : * A NULL string pointer indicates we want to create a NULL field.
2220 : : */
2221 : : HeapTuple
2222 : 733712 : BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
2223 : : {
7893 2224 : 733712 : TupleDesc tupdesc = attinmeta->tupdesc;
2225 : 733712 : int natts = tupdesc->natts;
2226 : : Datum *dvalues;
2227 : : bool *nulls;
2228 : : int i;
2229 : : HeapTuple tuple;
2230 : :
7969 2231 : 733712 : dvalues = (Datum *) palloc(natts * sizeof(Datum));
5642 tgl@sss.pgh.pa.us 2232 : 733712 : nulls = (bool *) palloc(natts * sizeof(bool));
2233 : :
2234 : : /*
2235 : : * Call the "in" function for each non-dropped attribute, even for nulls,
2236 : : * to support domains.
2237 : : */
7969 bruce@momjian.us 2238 [ + + ]: 13066022 : for (i = 0; i < natts; i++)
2239 : : {
2429 andres@anarazel.de 2240 [ + - ]: 12332311 : if (!TupleDescAttr(tupdesc, i)->attisdropped)
2241 : : {
2242 : : /* Non-dropped attributes */
6585 tgl@sss.pgh.pa.us 2243 : 24664621 : dvalues[i] = InputFunctionCall(&attinmeta->attinfuncs[i],
2244 : 12332311 : values[i],
2245 : 12332311 : attinmeta->attioparams[i],
2246 : 12332311 : attinmeta->atttypmods[i]);
7503 bruce@momjian.us 2247 [ + + ]: 12332310 : if (values[i] != NULL)
5642 tgl@sss.pgh.pa.us 2248 : 8359569 : nulls[i] = false;
2249 : : else
2250 : 3972741 : nulls[i] = true;
2251 : : }
2252 : : else
2253 : : {
2254 : : /* Handle dropped attributes by setting to NULL */
7899 tgl@sss.pgh.pa.us 2255 :UBC 0 : dvalues[i] = (Datum) 0;
5642 2256 : 0 : nulls[i] = true;
2257 : : }
2258 : : }
2259 : :
2260 : : /*
2261 : : * Form a tuple
2262 : : */
5642 tgl@sss.pgh.pa.us 2263 :CBC 733711 : tuple = heap_form_tuple(tupdesc, dvalues, nulls);
2264 : :
2265 : : /*
2266 : : * Release locally palloc'd space. XXX would probably be good to pfree
2267 : : * values of pass-by-reference datums, as well.
2268 : : */
7899 2269 : 733711 : pfree(dvalues);
2270 : 733711 : pfree(nulls);
2271 : :
7969 bruce@momjian.us 2272 : 733711 : return tuple;
2273 : : }
2274 : :
2275 : : /*
2276 : : * HeapTupleHeaderGetDatum - convert a HeapTupleHeader pointer to a Datum.
2277 : : *
2278 : : * This must *not* get applied to an on-disk tuple; the tuple should be
2279 : : * freshly made by heap_form_tuple or some wrapper routine for it (such as
2280 : : * BuildTupleFromCStrings). Be sure also that the tupledesc used to build
2281 : : * the tuple has a properly "blessed" rowtype.
2282 : : *
2283 : : * Formerly this was a macro equivalent to PointerGetDatum, relying on the
2284 : : * fact that heap_form_tuple fills in the appropriate tuple header fields
2285 : : * for a composite Datum. However, we now require that composite Datums not
2286 : : * contain any external TOAST pointers. We do not want heap_form_tuple itself
2287 : : * to enforce that; more specifically, the rule applies only to actual Datums
2288 : : * and not to HeapTuple structures. Therefore, HeapTupleHeaderGetDatum is
2289 : : * now a function that detects whether there are externally-toasted fields
2290 : : * and constructs a new tuple with inlined fields if so. We still need
2291 : : * heap_form_tuple to insert the Datum header fields, because otherwise this
2292 : : * code would have no way to obtain a tupledesc for the tuple.
2293 : : *
2294 : : * Note that if we do build a new tuple, it's palloc'd in the current
2295 : : * memory context. Beware of code that changes context between the initial
2296 : : * heap_form_tuple/etc call and calling HeapTuple(Header)GetDatum.
2297 : : *
2298 : : * For performance-critical callers, it could be worthwhile to take extra
2299 : : * steps to ensure that there aren't TOAST pointers in the output of
2300 : : * heap_form_tuple to begin with. It's likely however that the costs of the
2301 : : * typcache lookup and tuple disassembly/reassembly are swamped by TOAST
2302 : : * dereference costs, so that the benefits of such extra effort would be
2303 : : * minimal.
2304 : : *
2305 : : * XXX it would likely be better to create wrapper functions that produce
2306 : : * a composite Datum from the field values in one step. However, there's
2307 : : * enough code using the existing APIs that we couldn't get rid of this
2308 : : * hack anytime soon.
2309 : : */
2310 : : Datum
3636 tgl@sss.pgh.pa.us 2311 : 820228 : HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
2312 : : {
2313 : : Datum result;
2314 : : TupleDesc tupDesc;
2315 : :
2316 : : /* No work if there are no external TOAST pointers in the tuple */
2317 [ + + ]: 820228 : if (!HeapTupleHeaderHasExternal(tuple))
2318 : 820222 : return PointerGetDatum(tuple);
2319 : :
2320 : : /* Use the type data saved by heap_form_tuple to look up the rowtype */
2321 : 6 : tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
2322 : : HeapTupleHeaderGetTypMod(tuple));
2323 : :
2324 : : /* And do the flattening */
2325 : 6 : result = toast_flatten_tuple_to_datum(tuple,
2489 2326 : 6 : HeapTupleHeaderGetDatumLength(tuple),
2327 : : tupDesc);
2328 : :
3636 2329 [ + - ]: 6 : ReleaseTupleDesc(tupDesc);
2330 : :
2331 : 6 : return result;
2332 : : }
2333 : :
2334 : :
2335 : : /*
2336 : : * Functions for sending tuples to the frontend (or other specified destination)
2337 : : * as though it is a SELECT result. These are used by utility commands that
2338 : : * need to project directly to the destination and don't need or want full
2339 : : * table function capability. Currently used by EXPLAIN and SHOW ALL.
2340 : : */
2341 : : TupOutputState *
1977 andres@anarazel.de 2342 : 13530 : begin_tup_output_tupdesc(DestReceiver *dest,
2343 : : TupleDesc tupdesc,
2344 : : const TupleTableSlotOps *tts_ops)
2345 : : {
2346 : : TupOutputState *tstate;
2347 : :
7939 bruce@momjian.us 2348 : 13530 : tstate = (TupOutputState *) palloc(sizeof(TupOutputState));
2349 : :
1977 andres@anarazel.de 2350 : 13530 : tstate->slot = MakeSingleTupleTableSlot(tupdesc, tts_ops);
7649 tgl@sss.pgh.pa.us 2351 : 13530 : tstate->dest = dest;
2352 : :
2411 peter_e@gmx.net 2353 : 13530 : tstate->dest->rStartup(tstate->dest, (int) CMD_SELECT, tupdesc);
2354 : :
7939 bruce@momjian.us 2355 : 13530 : return tstate;
2356 : : }
2357 : :
2358 : : /*
2359 : : * write a single tuple
2360 : : */
2361 : : void
187 peter@eisentraut.org 2362 :GNC 71121 : do_tup_output(TupOutputState *tstate, const Datum *values, const bool *isnull)
2363 : : {
5379 tgl@sss.pgh.pa.us 2364 :CBC 71121 : TupleTableSlot *slot = tstate->slot;
2365 : 71121 : int natts = slot->tts_tupleDescriptor->natts;
2366 : :
2367 : : /* make sure the slot is clear */
2368 : 71121 : ExecClearTuple(slot);
2369 : :
2370 : : /* insert data */
2371 : 71121 : memcpy(slot->tts_values, values, natts * sizeof(Datum));
2372 : 71121 : memcpy(slot->tts_isnull, isnull, natts * sizeof(bool));
2373 : :
2374 : : /* mark slot as containing a virtual tuple */
2375 : 71121 : ExecStoreVirtualTuple(slot);
2376 : :
2377 : : /* send the tuple to the receiver */
2411 peter_e@gmx.net 2378 : 71121 : (void) tstate->dest->receiveSlot(slot, tstate->dest);
2379 : :
2380 : : /* clean up */
5379 tgl@sss.pgh.pa.us 2381 : 71121 : ExecClearTuple(slot);
7939 bruce@momjian.us 2382 : 71121 : }
2383 : :
2384 : : /*
2385 : : * write a chunk of text, breaking at newline characters
2386 : : *
2387 : : * Should only be used with a single-TEXT-attribute tupdesc.
2388 : : */
2389 : : void
2883 tgl@sss.pgh.pa.us 2390 : 10866 : do_text_output_multiline(TupOutputState *tstate, const char *txt)
2391 : : {
2392 : : Datum values[1];
5161 bruce@momjian.us 2393 : 10866 : bool isnull[1] = {false};
2394 : :
2883 tgl@sss.pgh.pa.us 2395 [ + + ]: 78615 : while (*txt)
2396 : : {
2397 : : const char *eol;
2398 : : int len;
2399 : :
2400 : 67749 : eol = strchr(txt, '\n');
7939 bruce@momjian.us 2401 [ + - ]: 67749 : if (eol)
2402 : : {
2883 tgl@sss.pgh.pa.us 2403 : 67749 : len = eol - txt;
5380 2404 : 67749 : eol++;
2405 : : }
2406 : : else
2407 : : {
2883 tgl@sss.pgh.pa.us 2408 :UBC 0 : len = strlen(txt);
2409 : 0 : eol = txt + len;
2410 : : }
2411 : :
2883 tgl@sss.pgh.pa.us 2412 :CBC 67749 : values[0] = PointerGetDatum(cstring_to_text_with_len(txt, len));
5380 2413 : 67749 : do_tup_output(tstate, values, isnull);
2414 : 67749 : pfree(DatumGetPointer(values[0]));
2883 2415 : 67749 : txt = eol;
2416 : : }
7939 bruce@momjian.us 2417 : 10866 : }
2418 : :
2419 : : void
2420 : 13530 : end_tup_output(TupOutputState *tstate)
2421 : : {
2411 peter_e@gmx.net 2422 : 13530 : tstate->dest->rShutdown(tstate->dest);
2423 : : /* note that destroying the dest is not ours to do */
6969 tgl@sss.pgh.pa.us 2424 : 13530 : ExecDropSingleTupleTableSlot(tstate->slot);
7939 bruce@momjian.us 2425 : 13530 : pfree(tstate);
2426 : 13530 : }
|