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-2026, PostgreSQL Global Development Group
50 : : * Portions Copyright (c) 1994, Regents of the University of California
51 : : *
52 : : *
53 : : * IDENTIFICATION
54 : : * src/backend/executor/execTuples.c
55 : : *
56 : : *-------------------------------------------------------------------------
57 : : */
58 : : #include "postgres.h"
59 : :
60 : : #include "access/heaptoast.h"
61 : : #include "access/htup_details.h"
62 : : #include "access/tupdesc_details.h"
63 : : #include "access/xact.h"
64 : : #include "catalog/pg_type.h"
65 : : #include "funcapi.h"
66 : : #include "nodes/nodeFuncs.h"
67 : : #include "storage/bufmgr.h"
68 : : #include "utils/builtins.h"
69 : : #include "utils/expandeddatum.h"
70 : : #include "utils/lsyscache.h"
71 : : #include "utils/typcache.h"
72 : :
73 : : static TupleDesc ExecTypeFromTLInternal(List *targetList,
74 : : bool skipjunk);
75 : : static pg_attribute_always_inline void slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
76 : : int reqnatts, bool support_cstring);
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
2783 andres@anarazel.de 98 :CBC 862922 : tts_virtual_init(TupleTableSlot *slot)
99 : : {
100 : 862922 : }
101 : :
102 : : static void
103 : 842521 : tts_virtual_release(TupleTableSlot *slot)
104 : : {
105 : 842521 : }
106 : :
107 : : static void
108 : 60081864 : tts_virtual_clear(TupleTableSlot *slot)
109 : : {
110 [ + + ]: 60081864 : if (unlikely(TTS_SHOULDFREE(slot)))
111 : : {
112 : 1208616 : VirtualTupleTableSlot *vslot = (VirtualTupleTableSlot *) slot;
113 : :
114 : 1208616 : pfree(vslot->data);
115 : 1208616 : vslot->data = NULL;
116 : :
2673 117 : 1208616 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
118 : : }
119 : :
2783 120 : 60081864 : slot->tts_nvalid = 0;
121 : 60081864 : slot->tts_flags |= TTS_FLAG_EMPTY;
2681 122 : 60081864 : ItemPointerSetInvalid(&slot->tts_tid);
2783 123 : 60081864 : }
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
2783 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
2783 andres@anarazel.de 141 :CBC 8 : tts_virtual_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
142 : : {
1895 tgl@sss.pgh.pa.us 143 [ - + ]: 8 : Assert(!TTS_EMPTY(slot));
144 : :
145 [ + - ]: 8 : 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
831 akorotkov@postgresql 157 :UBC 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
2783 andres@anarazel.de 176 :CBC 3904638 : tts_virtual_materialize(TupleTableSlot *slot)
177 : : {
178 : 3904638 : VirtualTupleTableSlot *vslot = (VirtualTupleTableSlot *) slot;
179 : 3904638 : TupleDesc desc = slot->tts_tupleDescriptor;
180 : 3904638 : Size sz = 0;
181 : : char *data;
182 : :
183 : : /* already materialized */
184 [ + + ]: 3904638 : if (TTS_SHOULDFREE(slot))
185 : 234389 : return;
186 : :
187 : : /* compute size of memory required */
188 [ + + ]: 10410978 : for (int natt = 0; natt < desc->natts; natt++)
189 : : {
557 drowley@postgresql.o 190 : 6740729 : CompactAttribute *att = TupleDescCompactAttr(desc, natt);
191 : : Datum val;
192 : :
2783 andres@anarazel.de 193 [ + + + + ]: 6740729 : if (att->attbyval || slot->tts_isnull[natt])
194 : 5268189 : continue;
195 : :
196 : 1472540 : val = slot->tts_values[natt];
197 : :
198 [ + + - + ]: 2594698 : if (att->attlen == -1 &&
199 [ + + - + ]: 1122158 : 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 : : */
556 drowley@postgresql.o 205 :UBC 0 : sz = att_nominal_alignby(sz, att->attalignby);
2783 andres@anarazel.de 206 : 0 : sz += EOH_get_flat_size(DatumGetEOHP(val));
207 : : }
208 : : else
209 : : {
556 drowley@postgresql.o 210 :CBC 1472540 : sz = att_nominal_alignby(sz, att->attalignby);
2783 andres@anarazel.de 211 [ + + + - : 1472540 : sz = att_addlength_datum(sz, att->attlen, val);
- - + - +
- - + + +
- - ]
212 : : }
213 : : }
214 : :
215 : : /* all data is byval */
216 [ + + ]: 3670249 : if (sz == 0)
217 : 2461522 : return;
218 : :
219 : : /* allocate memory */
220 : 1208727 : vslot->data = data = MemoryContextAlloc(slot->tts_mcxt, sz);
221 : 1208727 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
222 : :
223 : : /* and copy all attributes into the pre-allocated space */
224 [ + + ]: 4695343 : for (int natt = 0; natt < desc->natts; natt++)
225 : : {
557 drowley@postgresql.o 226 : 3486616 : CompactAttribute *att = TupleDescCompactAttr(desc, natt);
227 : : Datum val;
228 : :
2783 andres@anarazel.de 229 [ + + + + ]: 3486616 : if (att->attbyval || slot->tts_isnull[natt])
230 : 2014076 : continue;
231 : :
232 : 1472540 : val = slot->tts_values[natt];
233 : :
234 [ + + - + ]: 2594698 : if (att->attlen == -1 &&
235 [ + + - + ]: 1122158 : VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
2783 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 : :
556 drowley@postgresql.o 245 : 0 : data = (char *) att_nominal_alignby(data,
246 : : att->attalignby);
2783 andres@anarazel.de 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 : : {
2596 tgl@sss.pgh.pa.us 255 :CBC 1472540 : Size data_length = 0;
256 : :
556 drowley@postgresql.o 257 : 1472540 : data = (char *) att_nominal_alignby(data, att->attalignby);
2783 andres@anarazel.de 258 [ + + + - : 1472540 : data_length = att_addlength_datum(data_length, att->attlen, val);
- - + - +
- - + + +
- - ]
259 : :
260 : 1472540 : memcpy(data, DatumGetPointer(val), data_length);
261 : :
262 : 1472540 : slot->tts_values[natt] = PointerGetDatum(data);
263 : 1472540 : data += data_length;
264 : : }
265 : : }
266 : : }
267 : :
268 : : static void
269 : 90018 : tts_virtual_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
270 : : {
2473 tgl@sss.pgh.pa.us 271 : 90018 : TupleDesc srcdesc = srcslot->tts_tupleDescriptor;
272 : :
2783 andres@anarazel.de 273 : 90018 : tts_virtual_clear(dstslot);
274 : :
275 : 90018 : slot_getallattrs(srcslot);
276 : :
277 [ + + ]: 184970 : for (int natt = 0; natt < srcdesc->natts; natt++)
278 : : {
279 : 94952 : dstslot->tts_values[natt] = srcslot->tts_values[natt];
280 : 94952 : dstslot->tts_isnull[natt] = srcslot->tts_isnull[natt];
281 : : }
282 : :
283 : 90018 : dstslot->tts_nvalid = srcdesc->natts;
284 : 90018 : dstslot->tts_flags &= ~TTS_FLAG_EMPTY;
285 : :
286 : : /* make sure storage doesn't depend on external memory */
287 : 90018 : tts_virtual_materialize(dstslot);
288 : 90018 : }
289 : :
290 : : static HeapTuple
291 : 10292099 : tts_virtual_copy_heap_tuple(TupleTableSlot *slot)
292 : : {
293 [ - + ]: 10292099 : Assert(!TTS_EMPTY(slot));
294 : :
295 : 20584198 : return heap_form_tuple(slot->tts_tupleDescriptor,
296 : 10292099 : slot->tts_values,
297 : 10292099 : slot->tts_isnull);
298 : : }
299 : :
300 : : static MinimalTuple
463 jdavis@postgresql.or 301 : 18985728 : tts_virtual_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
302 : : {
2783 andres@anarazel.de 303 [ - + ]: 18985728 : Assert(!TTS_EMPTY(slot));
304 : :
305 : 37971456 : return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
306 : 18985728 : slot->tts_values,
463 jdavis@postgresql.or 307 : 18985728 : slot->tts_isnull,
308 : : extra);
309 : : }
310 : :
311 : :
312 : : /*
313 : : * TupleTableSlotOps implementation for HeapTupleTableSlot.
314 : : */
315 : :
316 : : static void
2783 andres@anarazel.de 317 : 2715542 : tts_heap_init(TupleTableSlot *slot)
318 : : {
319 : 2715542 : }
320 : :
321 : : static void
322 : 2714598 : tts_heap_release(TupleTableSlot *slot)
323 : : {
324 : 2714598 : }
325 : :
326 : : static void
327 : 7337900 : tts_heap_clear(TupleTableSlot *slot)
328 : : {
329 : 7337900 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
330 : :
331 : : /* Free the memory for the heap tuple if it's allowed. */
332 [ + + ]: 7337900 : if (TTS_SHOULDFREE(slot))
333 : : {
334 : 1119931 : heap_freetuple(hslot->tuple);
335 : 1119931 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
336 : : }
337 : :
338 : 7337900 : slot->tts_nvalid = 0;
339 : 7337900 : slot->tts_flags |= TTS_FLAG_EMPTY;
2681 340 : 7337900 : ItemPointerSetInvalid(&slot->tts_tid);
2783 341 : 7337900 : hslot->off = 0;
342 : 7337900 : hslot->tuple = NULL;
343 : 7337900 : }
344 : :
345 : : static void
346 : 7193235 : tts_heap_getsomeattrs(TupleTableSlot *slot, int natts)
347 : : {
348 : 7193235 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
349 : :
350 [ - + ]: 7193235 : Assert(!TTS_EMPTY(slot));
351 : :
102 drowley@postgresql.o 352 :GNC 7193235 : slot_deform_heap_tuple(slot, hslot->tuple, &hslot->off, natts, false);
2783 andres@anarazel.de 353 :CBC 7193235 : }
354 : :
355 : : static Datum
2783 andres@anarazel.de 356 :UBC 0 : tts_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
357 : : {
358 : 0 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
359 : :
2428 tgl@sss.pgh.pa.us 360 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
361 : :
362 : : /*
363 : : * In some code paths it's possible to get here with a non-materialized
364 : : * slot, in which case we can't retrieve system columns.
365 : : */
1895 366 [ # # ]: 0 : if (!hslot->tuple)
367 [ # # ]: 0 : ereport(ERROR,
368 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
369 : : errmsg("cannot retrieve a system column in this context")));
370 : :
2783 andres@anarazel.de 371 : 0 : return heap_getsysattr(hslot->tuple, attnum,
372 : : slot->tts_tupleDescriptor, isnull);
373 : : }
374 : :
375 : : static bool
831 akorotkov@postgresql 376 : 0 : tts_heap_is_current_xact_tuple(TupleTableSlot *slot)
377 : : {
378 : 0 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
379 : : TransactionId xmin;
380 : :
381 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
382 : :
383 : : /*
384 : : * In some code paths it's possible to get here with a non-materialized
385 : : * slot, in which case we can't check if tuple is created by the current
386 : : * transaction.
387 : : */
388 [ # # ]: 0 : if (!hslot->tuple)
389 [ # # ]: 0 : ereport(ERROR,
390 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
391 : : errmsg("don't have a storage tuple in this context")));
392 : :
393 : 0 : xmin = HeapTupleHeaderGetRawXmin(hslot->tuple->t_data);
394 : :
395 : 0 : return TransactionIdIsCurrentTransactionId(xmin);
396 : : }
397 : :
398 : : static void
2783 andres@anarazel.de 399 :CBC 2238604 : tts_heap_materialize(TupleTableSlot *slot)
400 : : {
401 : 2238604 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
402 : : MemoryContext oldContext;
403 : :
404 [ - + ]: 2238604 : Assert(!TTS_EMPTY(slot));
405 : :
406 : : /* If slot has its tuple already materialized, nothing to do. */
407 [ + + ]: 2238604 : if (TTS_SHOULDFREE(slot))
408 : 1119663 : return;
409 : :
410 : 1118941 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
411 : :
412 : : /*
413 : : * Have to deform from scratch, otherwise tts_values[] entries could point
414 : : * into the non-materialized tuple (which might be gone when accessed).
415 : : */
2428 tgl@sss.pgh.pa.us 416 : 1118941 : slot->tts_nvalid = 0;
417 : 1118941 : hslot->off = 0;
418 : :
2783 andres@anarazel.de 419 [ + + ]: 1118941 : if (!hslot->tuple)
420 : 1118934 : hslot->tuple = heap_form_tuple(slot->tts_tupleDescriptor,
421 : 1118934 : slot->tts_values,
422 : 1118934 : slot->tts_isnull);
423 : : else
424 : : {
425 : : /*
426 : : * The tuple contained in this slot is not allocated in the memory
427 : : * context of the given slot (else it would have TTS_FLAG_SHOULDFREE
428 : : * set). Copy the tuple into the given slot's memory context.
429 : : */
430 : 7 : hslot->tuple = heap_copytuple(hslot->tuple);
431 : : }
432 : :
2428 tgl@sss.pgh.pa.us 433 : 1118941 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
434 : :
2783 andres@anarazel.de 435 : 1118941 : MemoryContextSwitchTo(oldContext);
436 : : }
437 : :
438 : : static void
439 : 900 : tts_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
440 : : {
441 : : HeapTuple tuple;
442 : : MemoryContext oldcontext;
443 : :
444 : 900 : oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
445 : 900 : tuple = ExecCopySlotHeapTuple(srcslot);
446 : 900 : MemoryContextSwitchTo(oldcontext);
447 : :
448 : 900 : ExecStoreHeapTuple(tuple, dstslot, true);
449 : 900 : }
450 : :
451 : : static HeapTuple
452 : 2237597 : tts_heap_get_heap_tuple(TupleTableSlot *slot)
453 : : {
454 : 2237597 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
455 : :
456 [ - + ]: 2237597 : Assert(!TTS_EMPTY(slot));
457 [ - + ]: 2237597 : if (!hslot->tuple)
2783 andres@anarazel.de 458 :UBC 0 : tts_heap_materialize(slot);
459 : :
2783 andres@anarazel.de 460 :CBC 2237597 : return hslot->tuple;
461 : : }
462 : :
463 : : static HeapTuple
464 : 344 : tts_heap_copy_heap_tuple(TupleTableSlot *slot)
465 : : {
466 : 344 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
467 : :
468 [ - + ]: 344 : Assert(!TTS_EMPTY(slot));
469 [ - + ]: 344 : if (!hslot->tuple)
2783 andres@anarazel.de 470 :UBC 0 : tts_heap_materialize(slot);
471 : :
2783 andres@anarazel.de 472 :CBC 344 : return heap_copytuple(hslot->tuple);
473 : : }
474 : :
475 : : static MinimalTuple
463 jdavis@postgresql.or 476 : 2722 : tts_heap_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
477 : : {
2783 andres@anarazel.de 478 : 2722 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
479 : :
480 [ + + ]: 2722 : if (!hslot->tuple)
481 : 21 : tts_heap_materialize(slot);
482 : :
463 jdavis@postgresql.or 483 : 2722 : return minimal_tuple_from_heap_tuple(hslot->tuple, extra);
484 : : }
485 : :
486 : : static void
2783 andres@anarazel.de 487 : 3502771 : tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
488 : : {
489 : 3502771 : HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
490 : :
491 : 3502771 : tts_heap_clear(slot);
492 : :
493 : 3502771 : slot->tts_nvalid = 0;
494 : 3502771 : hslot->tuple = tuple;
495 : 3502771 : hslot->off = 0;
2428 tgl@sss.pgh.pa.us 496 : 3502771 : slot->tts_flags &= ~(TTS_FLAG_EMPTY | TTS_FLAG_SHOULDFREE);
2681 andres@anarazel.de 497 : 3502771 : slot->tts_tid = tuple->t_self;
498 : :
2783 499 [ + + ]: 3502771 : if (shouldFree)
500 : 1001 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
501 : 3502771 : }
502 : :
503 : :
504 : : /*
505 : : * TupleTableSlotOps implementation for MinimalTupleTableSlot.
506 : : */
507 : :
508 : : static void
509 : 261760 : tts_minimal_init(TupleTableSlot *slot)
510 : : {
511 : 261760 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
512 : :
513 : : /*
514 : : * Initialize the heap tuple pointer to access attributes of the minimal
515 : : * tuple contained in the slot as if it's a heap tuple.
516 : : */
517 : 261760 : mslot->tuple = &mslot->minhdr;
518 : 261760 : }
519 : :
520 : : static void
521 : 228935 : tts_minimal_release(TupleTableSlot *slot)
522 : : {
523 : 228935 : }
524 : :
525 : : static void
526 : 52297529 : tts_minimal_clear(TupleTableSlot *slot)
527 : : {
528 : 52297529 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
529 : :
530 [ + + ]: 52297529 : if (TTS_SHOULDFREE(slot))
531 : : {
532 : 10765181 : heap_free_minimal_tuple(mslot->mintuple);
533 : 10765181 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
534 : : }
535 : :
536 : 52297529 : slot->tts_nvalid = 0;
537 : 52297529 : slot->tts_flags |= TTS_FLAG_EMPTY;
2681 538 : 52297529 : ItemPointerSetInvalid(&slot->tts_tid);
2783 539 : 52297529 : mslot->off = 0;
540 : 52297529 : mslot->mintuple = NULL;
541 : 52297529 : }
542 : :
543 : : static void
544 : 38796945 : tts_minimal_getsomeattrs(TupleTableSlot *slot, int natts)
545 : : {
546 : 38796945 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
547 : :
548 [ - + ]: 38796945 : Assert(!TTS_EMPTY(slot));
549 : :
102 drowley@postgresql.o 550 :GNC 38796945 : slot_deform_heap_tuple(slot, mslot->tuple, &mslot->off, natts, true);
2783 andres@anarazel.de 551 :CBC 38796945 : }
552 : :
553 : : /*
554 : : * MinimalTupleTableSlots never provide system attributes. We generally
555 : : * shouldn't get here, but provide a user-friendly message if we do.
556 : : */
557 : : static Datum
2783 andres@anarazel.de 558 :UBC 0 : tts_minimal_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
559 : : {
1895 tgl@sss.pgh.pa.us 560 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
561 : :
562 [ # # ]: 0 : ereport(ERROR,
563 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
564 : : errmsg("cannot retrieve a system column in this context")));
565 : :
566 : : return 0; /* silence compiler warnings */
567 : : }
568 : :
569 : : /*
570 : : * Within MinimalTuple abstraction transaction information is unavailable.
571 : : * We generally shouldn't get here, but provide a user-friendly message if
572 : : * we do.
573 : : */
574 : : static bool
831 akorotkov@postgresql 575 : 0 : tts_minimal_is_current_xact_tuple(TupleTableSlot *slot)
576 : : {
577 [ # # ]: 0 : Assert(!TTS_EMPTY(slot));
578 : :
579 [ # # ]: 0 : ereport(ERROR,
580 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
581 : : errmsg("don't have transaction information for this type of tuple")));
582 : :
583 : : return false; /* silence compiler warnings */
584 : : }
585 : :
586 : : static void
2783 andres@anarazel.de 587 :CBC 1058311 : tts_minimal_materialize(TupleTableSlot *slot)
588 : : {
589 : 1058311 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
590 : : MemoryContext oldContext;
591 : :
592 [ - + ]: 1058311 : Assert(!TTS_EMPTY(slot));
593 : :
594 : : /* If slot has its tuple already materialized, nothing to do. */
595 [ + + ]: 1058311 : if (TTS_SHOULDFREE(slot))
596 : 96017 : return;
597 : :
598 : 962294 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
599 : :
600 : : /*
601 : : * Have to deform from scratch, otherwise tts_values[] entries could point
602 : : * into the non-materialized tuple (which might be gone when accessed).
603 : : */
2428 tgl@sss.pgh.pa.us 604 : 962294 : slot->tts_nvalid = 0;
605 : 962294 : mslot->off = 0;
606 : :
2783 andres@anarazel.de 607 [ + + ]: 962294 : if (!mslot->mintuple)
608 : : {
609 : 906633 : mslot->mintuple = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
610 : 906633 : slot->tts_values,
463 jdavis@postgresql.or 611 : 906633 : slot->tts_isnull,
612 : : 0);
613 : : }
614 : : else
615 : : {
616 : : /*
617 : : * The minimal tuple contained in this slot is not allocated in the
618 : : * memory context of the given slot (else it would have
619 : : * TTS_FLAG_SHOULDFREE set). Copy the minimal tuple into the given
620 : : * slot's memory context.
621 : : */
622 : 55661 : mslot->mintuple = heap_copy_minimal_tuple(mslot->mintuple, 0);
623 : : }
624 : :
2428 tgl@sss.pgh.pa.us 625 : 962294 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
626 : :
2783 andres@anarazel.de 627 [ - + ]: 962294 : Assert(mslot->tuple == &mslot->minhdr);
628 : :
629 : 962294 : mslot->minhdr.t_len = mslot->mintuple->t_len + MINIMAL_TUPLE_OFFSET;
630 : 962294 : mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mslot->mintuple - MINIMAL_TUPLE_OFFSET);
631 : :
632 : 962294 : MemoryContextSwitchTo(oldContext);
633 : : }
634 : :
635 : : static void
636 : 783385 : tts_minimal_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
637 : : {
638 : : MemoryContext oldcontext;
639 : : MinimalTuple mintuple;
640 : :
641 : 783385 : oldcontext = MemoryContextSwitchTo(dstslot->tts_mcxt);
642 : 783385 : mintuple = ExecCopySlotMinimalTuple(srcslot);
643 : 783385 : MemoryContextSwitchTo(oldcontext);
644 : :
645 : 783385 : ExecStoreMinimalTuple(mintuple, dstslot, true);
646 : 783385 : }
647 : :
648 : : static MinimalTuple
649 : 3481224 : tts_minimal_get_minimal_tuple(TupleTableSlot *slot)
650 : : {
651 : 3481224 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
652 : :
653 [ + + ]: 3481224 : if (!mslot->mintuple)
654 : 43240 : tts_minimal_materialize(slot);
655 : :
656 : 3481224 : return mslot->mintuple;
657 : : }
658 : :
659 : : static HeapTuple
660 : 640992 : tts_minimal_copy_heap_tuple(TupleTableSlot *slot)
661 : : {
662 : 640992 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
663 : :
664 [ + + ]: 640992 : if (!mslot->mintuple)
665 : 1361 : tts_minimal_materialize(slot);
666 : :
667 : 640992 : return heap_tuple_from_minimal_tuple(mslot->mintuple);
668 : : }
669 : :
670 : : static MinimalTuple
463 jdavis@postgresql.or 671 : 1859865 : tts_minimal_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
672 : : {
2783 andres@anarazel.de 673 : 1859865 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
674 : :
675 [ + + ]: 1859865 : if (!mslot->mintuple)
676 : 737411 : tts_minimal_materialize(slot);
677 : :
463 jdavis@postgresql.or 678 : 1859865 : return heap_copy_minimal_tuple(mslot->mintuple, extra);
679 : : }
680 : :
681 : : static void
2783 andres@anarazel.de 682 : 43886328 : tts_minimal_store_tuple(TupleTableSlot *slot, MinimalTuple mtup, bool shouldFree)
683 : : {
684 : 43886328 : MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
685 : :
686 : 43886328 : tts_minimal_clear(slot);
687 : :
688 [ - + ]: 43886328 : Assert(!TTS_SHOULDFREE(slot));
689 [ - + ]: 43886328 : Assert(TTS_EMPTY(slot));
690 : :
691 : 43886328 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
692 : 43886328 : slot->tts_nvalid = 0;
693 : 43886328 : mslot->off = 0;
694 : :
695 : 43886328 : mslot->mintuple = mtup;
696 [ - + ]: 43886328 : Assert(mslot->tuple == &mslot->minhdr);
697 : 43886328 : mslot->minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
698 : 43886328 : mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
699 : : /* no need to set t_self or t_tableOid since we won't allow access */
700 : :
701 [ + + ]: 43886328 : if (shouldFree)
702 : 9803971 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
703 : 43886328 : }
704 : :
705 : :
706 : : /*
707 : : * TupleTableSlotOps implementation for BufferHeapTupleTableSlot.
708 : : */
709 : :
710 : : static void
711 : 17573671 : tts_buffer_heap_init(TupleTableSlot *slot)
712 : : {
713 : 17573671 : }
714 : :
715 : : static void
716 : 17563258 : tts_buffer_heap_release(TupleTableSlot *slot)
717 : : {
718 : 17563258 : }
719 : :
720 : : static void
721 : 33660247 : tts_buffer_heap_clear(TupleTableSlot *slot)
722 : : {
723 : 33660247 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
724 : :
725 : : /*
726 : : * Free the memory for heap tuple if allowed. A tuple coming from buffer
727 : : * can never be freed. But we may have materialized a tuple from buffer.
728 : : * Such a tuple can be freed.
729 : : */
730 [ + + ]: 33660247 : if (TTS_SHOULDFREE(slot))
731 : : {
732 : : /* We should have unpinned the buffer while materializing the tuple. */
733 [ - + ]: 10953022 : Assert(!BufferIsValid(bslot->buffer));
734 : :
735 : 10953022 : heap_freetuple(bslot->base.tuple);
736 : 10953022 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
737 : : }
738 : :
739 [ + + ]: 33660247 : if (BufferIsValid(bslot->buffer))
740 : 8977528 : ReleaseBuffer(bslot->buffer);
741 : :
742 : 33660247 : slot->tts_nvalid = 0;
743 : 33660247 : slot->tts_flags |= TTS_FLAG_EMPTY;
2681 744 : 33660247 : ItemPointerSetInvalid(&slot->tts_tid);
2783 745 : 33660247 : bslot->base.tuple = NULL;
746 : 33660247 : bslot->base.off = 0;
747 : 33660247 : bslot->buffer = InvalidBuffer;
748 : 33660247 : }
749 : :
750 : : static void
751 : 89857362 : tts_buffer_heap_getsomeattrs(TupleTableSlot *slot, int natts)
752 : : {
753 : 89857362 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
754 : :
755 [ - + ]: 89857362 : Assert(!TTS_EMPTY(slot));
756 : :
102 drowley@postgresql.o 757 :GNC 89857362 : slot_deform_heap_tuple(slot, bslot->base.tuple, &bslot->base.off, natts, false);
2783 andres@anarazel.de 758 :CBC 89857362 : }
759 : :
760 : : static Datum
761 : 72775 : tts_buffer_heap_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
762 : : {
763 : 72775 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
764 : :
2428 tgl@sss.pgh.pa.us 765 [ - + ]: 72775 : Assert(!TTS_EMPTY(slot));
766 : :
767 : : /*
768 : : * In some code paths it's possible to get here with a non-materialized
769 : : * slot, in which case we can't retrieve system columns.
770 : : */
1895 771 [ - + ]: 72775 : if (!bslot->base.tuple)
1895 tgl@sss.pgh.pa.us 772 [ # # ]:UBC 0 : ereport(ERROR,
773 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
774 : : errmsg("cannot retrieve a system column in this context")));
775 : :
2783 andres@anarazel.de 776 :CBC 72775 : return heap_getsysattr(bslot->base.tuple, attnum,
777 : : slot->tts_tupleDescriptor, isnull);
778 : : }
779 : :
780 : : static bool
831 akorotkov@postgresql 781 : 564 : tts_buffer_is_current_xact_tuple(TupleTableSlot *slot)
782 : : {
783 : 564 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
784 : : TransactionId xmin;
785 : :
786 [ - + ]: 564 : Assert(!TTS_EMPTY(slot));
787 : :
788 : : /*
789 : : * In some code paths it's possible to get here with a non-materialized
790 : : * slot, in which case we can't check if tuple is created by the current
791 : : * transaction.
792 : : */
793 [ - + ]: 564 : if (!bslot->base.tuple)
831 akorotkov@postgresql 794 [ # # ]:UBC 0 : ereport(ERROR,
795 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
796 : : errmsg("don't have a storage tuple in this context")));
797 : :
831 akorotkov@postgresql 798 :CBC 564 : xmin = HeapTupleHeaderGetRawXmin(bslot->base.tuple->t_data);
799 : :
800 : 564 : return TransactionIdIsCurrentTransactionId(xmin);
801 : : }
802 : :
803 : : static void
2783 andres@anarazel.de 804 : 23791406 : tts_buffer_heap_materialize(TupleTableSlot *slot)
805 : : {
806 : 23791406 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
807 : : MemoryContext oldContext;
808 : :
809 [ - + ]: 23791406 : Assert(!TTS_EMPTY(slot));
810 : :
811 : : /* If slot has its tuple already materialized, nothing to do. */
812 [ + + ]: 23791406 : if (TTS_SHOULDFREE(slot))
813 : 20166349 : return;
814 : :
815 : 3625057 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
816 : :
817 : : /*
818 : : * Have to deform from scratch, otherwise tts_values[] entries could point
819 : : * into the non-materialized tuple (which might be gone when accessed).
820 : : */
2428 tgl@sss.pgh.pa.us 821 : 3625057 : bslot->base.off = 0;
822 : 3625057 : slot->tts_nvalid = 0;
823 : :
2679 andres@anarazel.de 824 [ + + ]: 3625057 : if (!bslot->base.tuple)
825 : : {
826 : : /*
827 : : * Normally BufferHeapTupleTableSlot should have a tuple + buffer
828 : : * associated with it, unless it's materialized (which would've
829 : : * returned above). But when it's useful to allow storing virtual
830 : : * tuples in a buffer slot, which then also needs to be
831 : : * materializable.
832 : : */
833 : 3386749 : bslot->base.tuple = heap_form_tuple(slot->tts_tupleDescriptor,
834 : 3386749 : slot->tts_values,
835 : 3386749 : slot->tts_isnull);
836 : : }
837 : : else
838 : : {
839 : 238308 : bslot->base.tuple = heap_copytuple(bslot->base.tuple);
840 : :
841 : : /*
842 : : * A heap tuple stored in a BufferHeapTupleTableSlot should have a
843 : : * buffer associated with it, unless it's materialized or virtual.
844 : : */
845 [ + - ]: 238308 : if (likely(BufferIsValid(bslot->buffer)))
846 : 238308 : ReleaseBuffer(bslot->buffer);
847 : 238308 : bslot->buffer = InvalidBuffer;
848 : : }
849 : :
850 : : /*
851 : : * We don't set TTS_FLAG_SHOULDFREE until after releasing the buffer, if
852 : : * any. This avoids having a transient state that would fall foul of our
853 : : * assertions that a slot with TTS_FLAG_SHOULDFREE doesn't own a buffer.
854 : : * In the unlikely event that ReleaseBuffer() above errors out, we'd
855 : : * effectively leak the copied tuple, but that seems fairly harmless.
856 : : */
2428 tgl@sss.pgh.pa.us 857 : 3625057 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
858 : :
859 : 3625057 : MemoryContextSwitchTo(oldContext);
860 : : }
861 : :
862 : : static void
2783 andres@anarazel.de 863 : 7525883 : tts_buffer_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
864 : : {
865 : 7525883 : BufferHeapTupleTableSlot *bsrcslot = (BufferHeapTupleTableSlot *) srcslot;
866 : 7525883 : BufferHeapTupleTableSlot *bdstslot = (BufferHeapTupleTableSlot *) dstslot;
867 : :
868 : : /*
869 : : * If the source slot is of a different kind, or is a buffer slot that has
870 : : * been materialized / is virtual, make a new copy of the tuple. Otherwise
871 : : * make a new reference to the in-buffer tuple.
872 : : */
873 [ + + ]: 7525883 : if (dstslot->tts_ops != srcslot->tts_ops ||
2679 874 [ + + ]: 4362 : TTS_SHOULDFREE(srcslot) ||
875 [ - + ]: 4360 : !bsrcslot->base.tuple)
2783 876 : 7521523 : {
877 : : MemoryContext oldContext;
878 : :
879 : 7521523 : ExecClearTuple(dstslot);
880 : 7521523 : dstslot->tts_flags &= ~TTS_FLAG_EMPTY;
881 : 7521523 : oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt);
2752 akapila@postgresql.o 882 : 7521523 : bdstslot->base.tuple = ExecCopySlotHeapTuple(srcslot);
2428 tgl@sss.pgh.pa.us 883 : 7521523 : dstslot->tts_flags |= TTS_FLAG_SHOULDFREE;
2783 andres@anarazel.de 884 : 7521523 : MemoryContextSwitchTo(oldContext);
885 : : }
886 : : else
887 : : {
2679 888 [ - + ]: 4360 : Assert(BufferIsValid(bsrcslot->buffer));
889 : :
2681 890 : 4360 : tts_buffer_heap_store_tuple(dstslot, bsrcslot->base.tuple,
891 : : bsrcslot->buffer, false);
892 : :
893 : : /*
894 : : * The HeapTupleData portion of the source tuple might be shorter
895 : : * lived than the destination slot. Therefore copy the HeapTuple into
896 : : * our slot's tupdata, which is guaranteed to live long enough (but
897 : : * will still point into the buffer).
898 : : */
2679 899 : 4360 : memcpy(&bdstslot->base.tupdata, bdstslot->base.tuple, sizeof(HeapTupleData));
900 : 4360 : bdstslot->base.tuple = &bdstslot->base.tupdata;
901 : : }
2783 902 : 7525883 : }
903 : :
904 : : static HeapTuple
905 : 26610327 : tts_buffer_heap_get_heap_tuple(TupleTableSlot *slot)
906 : : {
907 : 26610327 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
908 : :
909 [ - + ]: 26610327 : Assert(!TTS_EMPTY(slot));
910 : :
911 [ - + ]: 26610327 : if (!bslot->base.tuple)
2783 andres@anarazel.de 912 :UBC 0 : tts_buffer_heap_materialize(slot);
913 : :
2783 andres@anarazel.de 914 :CBC 26610327 : return bslot->base.tuple;
915 : : }
916 : :
917 : : static HeapTuple
918 : 8549520 : tts_buffer_heap_copy_heap_tuple(TupleTableSlot *slot)
919 : : {
920 : 8549520 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
921 : :
922 [ - + ]: 8549520 : Assert(!TTS_EMPTY(slot));
923 : :
924 [ - + ]: 8549520 : if (!bslot->base.tuple)
2783 andres@anarazel.de 925 :UBC 0 : tts_buffer_heap_materialize(slot);
926 : :
2783 andres@anarazel.de 927 :CBC 8549520 : return heap_copytuple(bslot->base.tuple);
928 : : }
929 : :
930 : : static MinimalTuple
463 jdavis@postgresql.or 931 : 1889965 : tts_buffer_heap_copy_minimal_tuple(TupleTableSlot *slot, Size extra)
932 : : {
2783 andres@anarazel.de 933 : 1889965 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
934 : :
935 [ - + ]: 1889965 : Assert(!TTS_EMPTY(slot));
936 : :
937 [ - + ]: 1889965 : if (!bslot->base.tuple)
2783 andres@anarazel.de 938 :UBC 0 : tts_buffer_heap_materialize(slot);
939 : :
463 jdavis@postgresql.or 940 :CBC 1889965 : return minimal_tuple_from_heap_tuple(bslot->base.tuple, extra);
941 : : }
942 : :
943 : : static inline void
2681 andres@anarazel.de 944 : 107930133 : tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
945 : : Buffer buffer, bool transfer_pin)
946 : : {
2783 947 : 107930133 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
948 : :
949 [ + + ]: 107930133 : if (TTS_SHOULDFREE(slot))
950 : : {
951 : : /* materialized slot shouldn't have a buffer to release */
952 [ - + ]: 239447 : Assert(!BufferIsValid(bslot->buffer));
953 : :
954 : 239447 : heap_freetuple(bslot->base.tuple);
955 : 239447 : slot->tts_flags &= ~TTS_FLAG_SHOULDFREE;
956 : : }
957 : :
958 : 107930133 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
959 : 107930133 : slot->tts_nvalid = 0;
960 : 107930133 : bslot->base.tuple = tuple;
961 : 107930133 : bslot->base.off = 0;
2681 962 : 107930133 : slot->tts_tid = tuple->t_self;
963 : :
964 : : /*
965 : : * If tuple is on a disk page, keep the page pinned as long as we hold a
966 : : * pointer into it. We assume the caller already has such a pin. If
967 : : * transfer_pin is true, we'll transfer that pin to this slot, if not
968 : : * we'll pin it again ourselves.
969 : : *
970 : : * This is coded to optimize the case where the slot previously held a
971 : : * tuple on the same disk page: in that case releasing and re-acquiring
972 : : * the pin is a waste of cycles. This is a common situation during
973 : : * seqscans, so it's worth troubling over.
974 : : */
2783 975 [ + + ]: 107930133 : if (bslot->buffer != buffer)
976 : : {
977 [ + + ]: 12629871 : if (BufferIsValid(bslot->buffer))
978 : 3408650 : ReleaseBuffer(bslot->buffer);
979 : :
980 : 12629871 : bslot->buffer = buffer;
981 : :
2681 982 [ + + + - ]: 12629871 : if (!transfer_pin && BufferIsValid(buffer))
983 : 11832692 : IncrBufferRefCount(buffer);
984 : : }
985 [ + + + - ]: 95300262 : else if (transfer_pin && BufferIsValid(buffer))
986 : : {
987 : : /*
988 : : * In transfer_pin mode the caller won't know about the same-page
989 : : * optimization, so we gotta release its pin.
990 : : */
991 : 2613204 : ReleaseBuffer(buffer);
992 : : }
2783 993 : 107930133 : }
994 : :
995 : : /*
996 : : * slot_deform_heap_tuple
997 : : * Given a TupleTableSlot, extract data from the slot's physical tuple
998 : : * into its Datum/isnull arrays. Data is extracted up through the
999 : : * reqnatts'th column. If there are insufficient attributes in the given
1000 : : * tuple, then slot_getmissingattrs() is called to populate the
1001 : : * remainder. If reqnatts is above the number of attributes in the
1002 : : * slot's TupleDesc, an error is raised.
1003 : : *
1004 : : * This is essentially an incremental version of heap_deform_tuple:
1005 : : * on each call we extract attributes up to the one needed, without
1006 : : * re-computing information about previously extracted attributes.
1007 : : * slot->tts_nvalid is the number of attributes already extracted.
1008 : : *
1009 : : * This is marked as always inline, so the different offp for different types
1010 : : * of slots gets optimized away.
1011 : : *
1012 : : * support_cstring should be passed as a const to allow the compiler only
1013 : : * emit code during inlining for cstring deforming when it's required.
1014 : : * cstrings can exist in MinimalTuples, but not in HeapTuples.
1015 : : */
1016 : : static pg_attribute_always_inline void
549 drowley@postgresql.o 1017 :GNC 135847542 : slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp,
1018 : : int reqnatts, bool support_cstring)
1019 : : {
1020 : : CompactAttribute *cattrs;
1021 : : CompactAttribute *cattr;
106 drowley@postgresql.o 1022 :CBC 135847542 : TupleDesc tupleDesc = slot->tts_tupleDescriptor;
1023 : 135847542 : HeapTupleHeader tup = tuple->t_data;
1024 : : size_t attnum;
1025 : : int firstNonCacheOffsetAttr;
1026 : : int firstNonGuaranteedAttr;
1027 : : int firstNullAttr;
1028 : : int natts;
1029 : : Datum *values;
1030 : : bool *isnull;
1031 : : char *tp; /* ptr to tuple data */
1032 : : uint32 off; /* offset in tuple data */
1033 : :
1034 : : /* Did someone forget to call TupleDescFinalize()? */
106 drowley@postgresql.o 1035 [ - + ]:GNC 135847542 : Assert(tupleDesc->firstNonCachedOffsetAttr >= 0);
1036 : :
1037 : 135847542 : isnull = slot->tts_isnull;
1038 : :
1039 : : /*
1040 : : * Some callers may form and deform tuples prior to NOT NULL constraints
1041 : : * being checked. Here we'd like to optimize the case where we only need
1042 : : * to fetch attributes before or up to the point where the attribute is
1043 : : * guaranteed to exist in the tuple. We rely on the slot flag being set
1044 : : * correctly to only enable this optimization when it's valid to do so.
1045 : : * This optimization allows us to save fetching the number of attributes
1046 : : * from the tuple and saves the additional cost of handling non-byval
1047 : : * attrs.
1048 : : */
1049 : 135847542 : firstNonGuaranteedAttr = Min(reqnatts, slot->tts_first_nonguaranteed);
1050 : :
1051 : 135847542 : firstNonCacheOffsetAttr = tupleDesc->firstNonCachedOffsetAttr;
1052 : :
1053 [ + + ]: 135847542 : if (HeapTupleHasNulls(tuple))
1054 : : {
1055 : 34029786 : natts = HeapTupleHeaderGetNatts(tup);
1056 : 34029786 : tp = (char *) tup + MAXALIGN(offsetof(HeapTupleHeaderData, t_bits) +
1057 : : BITMAPLEN(natts));
1058 : :
1059 : 34029786 : natts = Min(natts, reqnatts);
1060 [ + + ]: 34029786 : if (natts > firstNonGuaranteedAttr)
1061 : : {
92 nathan@postgresql.or 1062 : 31934400 : uint8 *bp = tup->t_bits;
1063 : :
1064 : : /* Find the first NULL attr */
106 drowley@postgresql.o 1065 : 31934400 : firstNullAttr = first_null_attr(bp, natts);
1066 : :
1067 : : /*
1068 : : * And populate the isnull array for all attributes being fetched
1069 : : * from the tuple.
1070 : : */
1071 : 31934400 : populate_isnull_array(bp, natts, isnull);
1072 : : }
1073 : : else
1074 : : {
1075 : : /* Otherwise all required columns are guaranteed to exist */
1076 : 2095386 : firstNullAttr = natts;
1077 : :
1078 : : /*
1079 : : * Check TupleDescFinalize() didn't get confused when setting
1080 : : * firstNonGuaranteedAttr. There should never be a NULL in a
1081 : : * guaranteed column.
1082 : : */
24 1083 [ - + ]: 2095386 : Assert(first_null_attr(tup->t_bits, natts) >= firstNullAttr);
1084 : : }
1085 : : }
1086 : : else
1087 : : {
106 1088 : 101817756 : tp = (char *) tup + MAXALIGN(offsetof(HeapTupleHeaderData, t_bits));
1089 : :
1090 : : /*
1091 : : * We only need to look at the tuple's natts if we need more than the
1092 : : * guaranteed number of columns
1093 : : */
1094 [ + + ]: 101817756 : if (reqnatts > firstNonGuaranteedAttr)
1095 : 97739058 : natts = Min(HeapTupleHeaderGetNatts(tup), reqnatts);
1096 : : else
1097 : : {
1098 : : /* No need to access the number of attributes in the tuple */
1099 : 4078698 : natts = reqnatts;
1100 : : }
1101 : :
1102 : : /* All attrs can be fetched without checking for NULLs */
1103 : 101817756 : firstNullAttr = natts;
1104 : : }
1105 : :
549 1106 : 135847542 : attnum = slot->tts_nvalid;
106 1107 : 135847542 : values = slot->tts_values;
108 1108 : 135847542 : slot->tts_nvalid = reqnatts;
1109 : :
1110 : : /*
1111 : : * We store the tupleDesc's CompactAttribute array in 'cattrs' as gcc
1112 : : * seems to be unwilling to optimize accessing the CompactAttribute
1113 : : * element efficiently when accessing it via TupleDescCompactAttr().
1114 : : */
105 1115 : 135847542 : cattrs = tupleDesc->compact_attrs;
1116 : :
1117 : : /* Ensure we calculated tp correctly */
106 1118 [ - + ]: 135847542 : Assert(tp == (char *) tup + tup->t_hoff);
1119 : :
1120 [ + + ]: 135847542 : if (attnum < firstNonGuaranteedAttr)
1121 : : {
1122 : : int attlen;
1123 : :
1124 : : do
1125 : : {
1126 : 44294567 : isnull[attnum] = false;
105 1127 : 44294567 : cattr = &cattrs[attnum];
106 1128 : 44294567 : attlen = cattr->attlen;
1129 : :
1130 : : /* We don't expect any non-byval types */
1131 [ - + ]: 44294567 : pg_assume(attlen > 0);
1132 [ - + ]: 44294567 : Assert(cattr->attbyval == true);
1133 : :
1134 : 44294567 : off = cattr->attcacheoff;
1135 : 44294567 : values[attnum] = fetch_att_noerr(tp + off, true, attlen);
1136 : 44294567 : attnum++;
1137 [ + + ]: 44294567 : } while (attnum < firstNonGuaranteedAttr);
1138 : :
1139 : 26738597 : off += attlen;
1140 : :
1141 [ + + ]: 26738597 : if (attnum == reqnatts)
1142 : 6174084 : goto done;
1143 : : }
1144 : : else
1145 : : {
1146 : : /*
1147 : : * We may be incrementally deforming the tuple, so set 'off' to the
1148 : : * previously cached value. This may be 0, if the slot has just
1149 : : * received a new tuple.
1150 : : */
549 drowley@postgresql.o 1151 :CBC 109108945 : off = *offp;
1152 : :
1153 : : /* We expect *offp to be set to 0 when attnum == 0 */
106 drowley@postgresql.o 1154 [ + + - + ]:GNC 109108945 : Assert(off == 0 || attnum > 0);
1155 : : }
1156 : :
1157 : : /* We can use attcacheoff up until the first NULL */
1158 : 129673458 : firstNonCacheOffsetAttr = Min(firstNonCacheOffsetAttr, firstNullAttr);
1159 : :
1160 : : /*
1161 : : * Handle the portion of the tuple that we have cached the offset for up
1162 : : * to the first NULL attribute. The offset is effectively fixed for
1163 : : * these, so we can use the CompactAttribute's attcacheoff.
1164 : : */
1165 [ + + ]: 129673458 : if (attnum < firstNonCacheOffsetAttr)
1166 : : {
1167 : : int attlen;
1168 : :
1169 : : do
1170 : : {
1171 : 362922031 : isnull[attnum] = false;
105 1172 : 362922031 : cattr = &cattrs[attnum];
106 1173 : 362922031 : attlen = cattr->attlen;
1174 : 362922031 : off = cattr->attcacheoff;
1175 : 725844062 : values[attnum] = fetch_att_noerr(tp + off,
1176 : 362922031 : cattr->attbyval,
1177 : : attlen);
1178 : 362922031 : attnum++;
1179 [ + + ]: 362922031 : } while (attnum < firstNonCacheOffsetAttr);
1180 : :
1181 : : /*
1182 : : * Point the offset after the end of the last attribute with a cached
1183 : : * offset. We expect the final cached offset attribute to have a
1184 : : * fixed width, so just add the attlen to the attcacheoff
1185 : : */
1186 [ - + ]: 113741025 : Assert(attlen > 0);
1187 : 113741025 : off += attlen;
1188 : : }
1189 : :
1190 : : /*
1191 : : * Handle any portion of the tuple that doesn't have a fixed offset up
1192 : : * until the first NULL attribute. This loop only differs from the one
1193 : : * after it by the NULL checks.
1194 : : */
1195 [ + + ]: 163970657 : for (; attnum < firstNullAttr; attnum++)
1196 : : {
1197 : : int attlen;
1198 : :
1199 : 34297199 : isnull[attnum] = false;
105 1200 : 34297199 : cattr = &cattrs[attnum];
106 1201 : 34297199 : attlen = cattr->attlen;
1202 : :
1203 : : /*
1204 : : * Only emit the cstring-related code in align_fetch_then_add() when
1205 : : * cstring support is needed. We assume support_cstring will be
1206 : : * passed as a const to allow the compiler to eliminate this branch.
1207 : : */
102 1208 [ + + ]: 34297199 : if (!support_cstring)
1209 [ + + - + ]: 20326965 : pg_assume(attlen > 0 || attlen == -1);
1210 : :
1211 : : /* align 'off', fetch the datum, and increment off beyond the datum */
106 1212 : 34297199 : values[attnum] = align_fetch_then_add(tp,
1213 : : &off,
1214 : 34297199 : cattr->attbyval,
1215 : : attlen,
1216 : 34297199 : cattr->attalignby);
1217 : : }
1218 : :
1219 : : /*
1220 : : * Now handle any remaining attributes in the tuple up to the requested
1221 : : * attnum. This time, include NULL checks as we're now at the first NULL
1222 : : * attribute.
1223 : : */
1224 [ + + ]: 177829570 : for (; attnum < natts; attnum++)
1225 : : {
1226 : : int attlen;
1227 : :
1228 [ + + ]: 48156112 : if (isnull[attnum])
1229 : : {
1230 : 33229864 : values[attnum] = (Datum) 0;
1231 : 33229864 : continue;
1232 : : }
1233 : :
105 1234 : 14926248 : cattr = &cattrs[attnum];
106 1235 : 14926248 : attlen = cattr->attlen;
1236 : :
1237 : : /* As above, only emit cstring code when needed. */
102 1238 [ + + ]: 14926248 : if (!support_cstring)
1239 [ + + - + ]: 10472567 : pg_assume(attlen > 0 || attlen == -1);
1240 : :
1241 : : /* align 'off', fetch the datum, and increment off beyond the datum */
106 1242 : 14926248 : values[attnum] = align_fetch_then_add(tp,
1243 : : &off,
1244 : 14926248 : cattr->attbyval,
1245 : : attlen,
1246 : 14926248 : cattr->attalignby);
1247 : : }
1248 : :
1249 : : /* Fetch any missing attrs and raise an error if reqnatts is invalid */
108 1250 [ + + ]: 129673458 : if (unlikely(attnum < reqnatts))
1251 : : {
1252 : : /*
1253 : : * Cache the offset before calling the function to allow the compiler
1254 : : * to implement a tail-call optimization
1255 : : */
106 1256 : 4854 : *offp = off;
108 1257 : 4854 : slot_getmissingattrs(slot, attnum, reqnatts);
106 1258 : 4854 : return;
1259 : : }
1260 : 129668604 : done:
1261 : :
1262 : : /* Save current offset for next execution */
106 drowley@postgresql.o 1263 :CBC 135842688 : *offp = off;
2783 andres@anarazel.de 1264 :ECB (98006589) : }
1265 : :
1266 : : const TupleTableSlotOps TTSOpsVirtual = {
1267 : : .base_slot_size = sizeof(VirtualTupleTableSlot),
1268 : : .init = tts_virtual_init,
1269 : : .release = tts_virtual_release,
1270 : : .clear = tts_virtual_clear,
1271 : : .getsomeattrs = tts_virtual_getsomeattrs,
1272 : : .getsysattr = tts_virtual_getsysattr,
1273 : : .materialize = tts_virtual_materialize,
1274 : : .is_current_xact_tuple = tts_virtual_is_current_xact_tuple,
1275 : : .copyslot = tts_virtual_copyslot,
1276 : :
1277 : : /*
1278 : : * A virtual tuple table slot can not "own" a heap tuple or a minimal
1279 : : * tuple.
1280 : : */
1281 : : .get_heap_tuple = NULL,
1282 : : .get_minimal_tuple = NULL,
1283 : : .copy_heap_tuple = tts_virtual_copy_heap_tuple,
1284 : : .copy_minimal_tuple = tts_virtual_copy_minimal_tuple
1285 : : };
1286 : :
1287 : : const TupleTableSlotOps TTSOpsHeapTuple = {
1288 : : .base_slot_size = sizeof(HeapTupleTableSlot),
1289 : : .init = tts_heap_init,
1290 : : .release = tts_heap_release,
1291 : : .clear = tts_heap_clear,
1292 : : .getsomeattrs = tts_heap_getsomeattrs,
1293 : : .getsysattr = tts_heap_getsysattr,
1294 : : .is_current_xact_tuple = tts_heap_is_current_xact_tuple,
1295 : : .materialize = tts_heap_materialize,
1296 : : .copyslot = tts_heap_copyslot,
1297 : : .get_heap_tuple = tts_heap_get_heap_tuple,
1298 : :
1299 : : /* A heap tuple table slot can not "own" a minimal tuple. */
1300 : : .get_minimal_tuple = NULL,
1301 : : .copy_heap_tuple = tts_heap_copy_heap_tuple,
1302 : : .copy_minimal_tuple = tts_heap_copy_minimal_tuple
1303 : : };
1304 : :
1305 : : const TupleTableSlotOps TTSOpsMinimalTuple = {
1306 : : .base_slot_size = sizeof(MinimalTupleTableSlot),
1307 : : .init = tts_minimal_init,
1308 : : .release = tts_minimal_release,
1309 : : .clear = tts_minimal_clear,
1310 : : .getsomeattrs = tts_minimal_getsomeattrs,
1311 : : .getsysattr = tts_minimal_getsysattr,
1312 : : .is_current_xact_tuple = tts_minimal_is_current_xact_tuple,
1313 : : .materialize = tts_minimal_materialize,
1314 : : .copyslot = tts_minimal_copyslot,
1315 : :
1316 : : /* A minimal tuple table slot can not "own" a heap tuple. */
1317 : : .get_heap_tuple = NULL,
1318 : : .get_minimal_tuple = tts_minimal_get_minimal_tuple,
1319 : : .copy_heap_tuple = tts_minimal_copy_heap_tuple,
1320 : : .copy_minimal_tuple = tts_minimal_copy_minimal_tuple
1321 : : };
1322 : :
1323 : : const TupleTableSlotOps TTSOpsBufferHeapTuple = {
1324 : : .base_slot_size = sizeof(BufferHeapTupleTableSlot),
1325 : : .init = tts_buffer_heap_init,
1326 : : .release = tts_buffer_heap_release,
1327 : : .clear = tts_buffer_heap_clear,
1328 : : .getsomeattrs = tts_buffer_heap_getsomeattrs,
1329 : : .getsysattr = tts_buffer_heap_getsysattr,
1330 : : .is_current_xact_tuple = tts_buffer_is_current_xact_tuple,
1331 : : .materialize = tts_buffer_heap_materialize,
1332 : : .copyslot = tts_buffer_heap_copyslot,
1333 : : .get_heap_tuple = tts_buffer_heap_get_heap_tuple,
1334 : :
1335 : : /* A buffer heap tuple table slot can not "own" a minimal tuple. */
1336 : : .get_minimal_tuple = NULL,
1337 : : .copy_heap_tuple = tts_buffer_heap_copy_heap_tuple,
1338 : : .copy_minimal_tuple = tts_buffer_heap_copy_minimal_tuple
1339 : : };
1340 : :
1341 : :
1342 : : /* ----------------------------------------------------------------
1343 : : * tuple table create/delete functions
1344 : : * ----------------------------------------------------------------
1345 : : */
1346 : :
1347 : : /* --------------------------------
1348 : : * MakeTupleTableSlot
1349 : : *
1350 : : * Basic routine to make an empty TupleTableSlot of given
1351 : : * TupleTableSlotType. If tupleDesc is specified the slot's descriptor is
1352 : : * fixed for its lifetime, gaining some efficiency. If that's
1353 : : * undesirable, pass NULL. 'flags' allows any of non-TTS_FLAGS_TRANSIENT
1354 : : * flags to be set in tts_flags.
1355 : : * --------------------------------
1356 : : */
1357 : : TupleTableSlot *
2784 andres@anarazel.de 1358 :CBC 21413895 : MakeTupleTableSlot(TupleDesc tupleDesc,
1359 : : const TupleTableSlotOps *tts_ops, uint16 flags)
1360 : : {
1361 : : Size basesz,
1362 : : allocsz;
1363 : : TupleTableSlot *slot;
1364 : :
2783 1365 : 21413895 : basesz = tts_ops->base_slot_size;
1366 : :
1367 : : /* Ensure callers don't have any way to set transient flags permanently */
106 drowley@postgresql.o 1368 :GNC 21413895 : flags &= ~TTS_FLAGS_TRANSIENT;
1369 : :
1370 : : /*
1371 : : * When a fixed descriptor is specified, we can reduce overhead by
1372 : : * allocating the entire slot in one go.
1373 : : *
1374 : : * We round the size of tts_isnull up to the next highest multiple of 8.
1375 : : * This is needed as populate_isnull_array() operates on 8 elements at a
1376 : : * time when converting a tuple's NULL bitmap into a boolean array.
1377 : : */
3056 andres@anarazel.de 1378 [ + + ]:CBC 21413895 : if (tupleDesc)
2783 1379 : 21377066 : allocsz = MAXALIGN(basesz) +
3056 1380 : 21377066 : MAXALIGN(tupleDesc->natts * sizeof(Datum)) +
106 drowley@postgresql.o 1381 :GNC 21377066 : TYPEALIGN(8, tupleDesc->natts * sizeof(bool));
1382 : : else
2783 andres@anarazel.de 1383 :CBC 36829 : allocsz = basesz;
1384 : :
1385 : 21413895 : slot = palloc0(allocsz);
1386 : : /* const for optimization purposes, OK to modify at allocation time */
2784 1387 : 21413895 : *((const TupleTableSlotOps **) &slot->tts_ops) = tts_ops;
3056 1388 : 21413895 : slot->type = T_TupleTableSlot;
106 drowley@postgresql.o 1389 :GNC 21413895 : slot->tts_flags = TTS_FLAG_EMPTY | flags;
2815 andres@anarazel.de 1390 [ + + ]:CBC 21413895 : if (tupleDesc != NULL)
1391 : 21377066 : slot->tts_flags |= TTS_FLAG_FIXED;
3056 1392 : 21413895 : slot->tts_tupleDescriptor = tupleDesc;
6120 tgl@sss.pgh.pa.us 1393 : 21413895 : slot->tts_mcxt = CurrentMemoryContext;
1394 : 21413895 : slot->tts_nvalid = 0;
1395 : :
3056 andres@anarazel.de 1396 [ + + ]: 21413895 : if (tupleDesc != NULL)
1397 : : {
1398 : 21377066 : slot->tts_values = (Datum *)
1399 : : (((char *) slot)
2783 1400 : 21377066 : + MAXALIGN(basesz));
1401 : :
3056 1402 : 21377066 : slot->tts_isnull = (bool *)
1403 : : (((char *) slot)
2783 1404 : 21377066 : + MAXALIGN(basesz)
3056 1405 : 21377066 : + MAXALIGN(tupleDesc->natts * sizeof(Datum)));
1406 : :
1407 [ + + ]: 21377066 : PinTupleDesc(tupleDesc);
1408 : :
1409 : : /*
1410 : : * Precalculate the maximum guaranteed attribute that has to exist in
1411 : : * every tuple which gets deformed into this slot. When the
1412 : : * TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS flag is enabled, we simply take
1413 : : * the pre-calculated value from the tupleDesc, otherwise the
1414 : : * optimization is disabled, and we set the value to 0.
1415 : : */
106 drowley@postgresql.o 1416 [ + + ]:GNC 21377066 : if ((flags & TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS) != 0)
1417 : 282226 : slot->tts_first_nonguaranteed = tupleDesc->firstNonGuaranteedAttr;
1418 : : else
1419 : 21094840 : slot->tts_first_nonguaranteed = 0;
1420 : : }
1421 : :
1422 : : /*
1423 : : * And allow slot type specific initialization.
1424 : : */
2783 andres@anarazel.de 1425 :CBC 21413895 : slot->tts_ops->init(slot);
1426 : :
6120 tgl@sss.pgh.pa.us 1427 : 21413895 : return slot;
1428 : : }
1429 : :
1430 : : /* --------------------------------
1431 : : * ExecAllocTableSlot
1432 : : *
1433 : : * Create a tuple table slot within a tuple table (which is just a List).
1434 : : * --------------------------------
1435 : : */
1436 : : TupleTableSlot *
2784 andres@anarazel.de 1437 : 1346117 : ExecAllocTableSlot(List **tupleTable, TupleDesc desc,
1438 : : const TupleTableSlotOps *tts_ops, uint16 flags)
1439 : : {
106 drowley@postgresql.o 1440 :GNC 1346117 : TupleTableSlot *slot = MakeTupleTableSlot(desc, tts_ops, flags);
1441 : :
6120 tgl@sss.pgh.pa.us 1442 :CBC 1346117 : *tupleTable = lappend(*tupleTable, slot);
1443 : :
1444 : 1346117 : return slot;
1445 : : }
1446 : :
1447 : : /* --------------------------------
1448 : : * ExecResetTupleTable
1449 : : *
1450 : : * This releases any resources (buffer pins, tupdesc refcounts)
1451 : : * held by the tuple table, and optionally releases the memory
1452 : : * occupied by the tuple table data structure.
1453 : : * It is expected that this routine be called by ExecEndPlan().
1454 : : * --------------------------------
1455 : : */
1456 : : void
1457 : 514858 : ExecResetTupleTable(List *tupleTable, /* tuple table */
1458 : : bool shouldFree) /* true if we should free memory */
1459 : : {
1460 : : ListCell *lc;
1461 : :
1462 [ + + + + : 1984928 : foreach(lc, tupleTable)
+ + ]
1463 : : {
3368 1464 : 1470070 : TupleTableSlot *slot = lfirst_node(TupleTableSlot, lc);
1465 : :
1466 : : /* Always release resources and reset the slot to empty */
6120 1467 : 1470070 : ExecClearTuple(slot);
2783 andres@anarazel.de 1468 : 1470070 : slot->tts_ops->release(slot);
6120 tgl@sss.pgh.pa.us 1469 [ + + ]: 1470070 : if (slot->tts_tupleDescriptor)
1470 : : {
1471 [ + + ]: 1470034 : ReleaseTupleDesc(slot->tts_tupleDescriptor);
1472 : 1470034 : slot->tts_tupleDescriptor = NULL;
1473 : : }
1474 : :
1475 : : /* If shouldFree, release memory occupied by the slot itself */
1476 [ + + ]: 1470070 : if (shouldFree)
1477 : : {
2815 andres@anarazel.de 1478 [ - + ]: 4893 : if (!TTS_FIXED(slot))
1479 : : {
3056 andres@anarazel.de 1480 [ # # ]:UBC 0 : if (slot->tts_values)
1481 : 0 : pfree(slot->tts_values);
1482 [ # # ]: 0 : if (slot->tts_isnull)
1483 : 0 : pfree(slot->tts_isnull);
1484 : : }
6120 tgl@sss.pgh.pa.us 1485 :CBC 4893 : pfree(slot);
1486 : : }
1487 : : }
1488 : :
1489 : : /* If shouldFree, release the list structure */
1490 [ + + ]: 514858 : if (shouldFree)
1491 : 4807 : list_free(tupleTable);
10948 scrappy@hub.org 1492 : 514858 : }
1493 : :
1494 : : /* --------------------------------
1495 : : * MakeSingleTupleTableSlot
1496 : : *
1497 : : * This is a convenience routine for operations that need a standalone
1498 : : * TupleTableSlot not gotten from the main executor tuple table. It makes
1499 : : * a single slot of given TupleTableSlotType and initializes it to use the
1500 : : * given tuple descriptor.
1501 : : * --------------------------------
1502 : : */
1503 : : TupleTableSlot *
2784 andres@anarazel.de 1504 : 20067664 : MakeSingleTupleTableSlot(TupleDesc tupdesc,
1505 : : const TupleTableSlotOps *tts_ops)
1506 : : {
106 drowley@postgresql.o 1507 :GNC 20067664 : TupleTableSlot *slot = MakeTupleTableSlot(tupdesc, tts_ops, 0);
1508 : :
7778 tgl@sss.pgh.pa.us 1509 :CBC 20067664 : return slot;
1510 : : }
1511 : :
1512 : : /* --------------------------------
1513 : : * ExecDropSingleTupleTableSlot
1514 : : *
1515 : : * Release a TupleTableSlot made with MakeSingleTupleTableSlot.
1516 : : * DON'T use this on a slot that's part of a tuple table list!
1517 : : * --------------------------------
1518 : : */
1519 : : void
7776 1520 : 19879242 : ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
1521 : : {
1522 : : /* This should match ExecResetTupleTable's processing of one slot */
6120 1523 [ - + ]: 19879242 : Assert(IsA(slot, TupleTableSlot));
7776 1524 : 19879242 : ExecClearTuple(slot);
2783 andres@anarazel.de 1525 : 19879242 : slot->tts_ops->release(slot);
7319 tgl@sss.pgh.pa.us 1526 [ + - ]: 19879242 : if (slot->tts_tupleDescriptor)
1527 [ + + ]: 19879242 : ReleaseTupleDesc(slot->tts_tupleDescriptor);
2815 andres@anarazel.de 1528 [ - + ]: 19879242 : if (!TTS_FIXED(slot))
1529 : : {
3056 andres@anarazel.de 1530 [ # # ]:UBC 0 : if (slot->tts_values)
1531 : 0 : pfree(slot->tts_values);
1532 [ # # ]: 0 : if (slot->tts_isnull)
1533 : 0 : pfree(slot->tts_isnull);
1534 : : }
7776 tgl@sss.pgh.pa.us 1535 :CBC 19879242 : pfree(slot);
1536 : 19879242 : }
1537 : :
1538 : :
1539 : : /* ----------------------------------------------------------------
1540 : : * tuple table slot accessor functions
1541 : : * ----------------------------------------------------------------
1542 : : */
1543 : :
1544 : : /* --------------------------------
1545 : : * ExecSetSlotDescriptor
1546 : : *
1547 : : * This function is used to set the tuple descriptor associated
1548 : : * with the slot's tuple. The passed descriptor must have lifespan
1549 : : * at least equal to the slot's. If it is a reference-counted descriptor
1550 : : * then the reference count is incremented for as long as the slot holds
1551 : : * a reference.
1552 : : * --------------------------------
1553 : : */
1554 : : void
3296 1555 : 36793 : ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
1556 : : TupleDesc tupdesc) /* new tuple descriptor */
1557 : : {
2815 andres@anarazel.de 1558 [ - + ]: 36793 : Assert(!TTS_FIXED(slot));
1559 : :
1560 : : /* For safety, make sure slot is empty before changing it */
7776 tgl@sss.pgh.pa.us 1561 : 36793 : ExecClearTuple(slot);
1562 : :
1563 : : /*
1564 : : * Release any old descriptor. Also release old Datum/isnull arrays if
1565 : : * present (we don't bother to check if they could be re-used).
1566 : : */
7319 1567 [ - + ]: 36793 : if (slot->tts_tupleDescriptor)
7319 tgl@sss.pgh.pa.us 1568 [ # # ]:UBC 0 : ReleaseTupleDesc(slot->tts_tupleDescriptor);
1569 : :
7776 tgl@sss.pgh.pa.us 1570 [ - + ]:CBC 36793 : if (slot->tts_values)
7776 tgl@sss.pgh.pa.us 1571 :UBC 0 : pfree(slot->tts_values);
7776 tgl@sss.pgh.pa.us 1572 [ - + ]:CBC 36793 : if (slot->tts_isnull)
7776 tgl@sss.pgh.pa.us 1573 :UBC 0 : pfree(slot->tts_isnull);
1574 : :
1575 : : /*
1576 : : * Install the new descriptor; if it's refcounted, bump its refcount.
1577 : : */
7776 tgl@sss.pgh.pa.us 1578 :CBC 36793 : slot->tts_tupleDescriptor = tupdesc;
7319 1579 [ - + ]: 36793 : PinTupleDesc(tupdesc);
1580 : :
1581 : : /*
1582 : : * Allocate Datum/isnull arrays of the appropriate size. These must have
1583 : : * the same lifetime as the slot, so allocate in the slot's own context.
1584 : : */
7776 1585 : 36793 : slot->tts_values = (Datum *)
1586 : 36793 : MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(Datum));
1587 : :
1588 : : /*
1589 : : * We round the size of tts_isnull up to the next highest multiple of 8.
1590 : : * This is needed as populate_isnull_array() operates on 8 elements at a
1591 : : * time when converting a tuple's NULL bitmap into a boolean array.
1592 : : */
1593 : 36793 : slot->tts_isnull = (bool *)
106 drowley@postgresql.o 1594 :GNC 36793 : MemoryContextAlloc(slot->tts_mcxt, TYPEALIGN(8, tupdesc->natts * sizeof(bool)));
7776 tgl@sss.pgh.pa.us 1595 :CBC 36793 : }
1596 : :
1597 : : /* --------------------------------
1598 : : * ExecStoreHeapTuple
1599 : : *
1600 : : * This function is used to store an on-the-fly physical tuple into a specified
1601 : : * slot in the tuple table.
1602 : : *
1603 : : * tuple: tuple to store
1604 : : * slot: TTSOpsHeapTuple type slot to store it in
1605 : : * shouldFree: true if ExecClearTuple should pfree() the tuple
1606 : : * when done with it
1607 : : *
1608 : : * shouldFree is normally set 'true' for tuples constructed on-the-fly. But it
1609 : : * can be 'false' when the referenced tuple is held in a tuple table slot
1610 : : * belonging to a lower-level executor Proc node. In this case the lower-level
1611 : : * slot retains ownership and responsibility for eventually releasing the
1612 : : * tuple. When this method is used, we must be certain that the upper-level
1613 : : * Proc node will lose interest in the tuple sooner than the lower-level one
1614 : : * does! If you're not certain, copy the lower-level tuple with heap_copytuple
1615 : : * and let the upper-level table slot assume ownership of the copy!
1616 : : *
1617 : : * Return value is just the passed-in slot pointer.
1618 : : *
1619 : : * If the target slot is not guaranteed to be TTSOpsHeapTuple type slot, use
1620 : : * the, more expensive, ExecForceStoreHeapTuple().
1621 : : * --------------------------------
1622 : : */
1623 : : TupleTableSlot *
2835 andres@anarazel.de 1624 : 3502771 : ExecStoreHeapTuple(HeapTuple tuple,
1625 : : TupleTableSlot *slot,
1626 : : bool shouldFree)
1627 : : {
1628 : : /*
1629 : : * sanity checks
1630 : : */
1631 [ - + ]: 3502771 : Assert(tuple != NULL);
1632 [ - + ]: 3502771 : Assert(slot != NULL);
1633 [ - + ]: 3502771 : Assert(slot->tts_tupleDescriptor != NULL);
1634 : :
2783 1635 [ - + ]: 3502771 : if (unlikely(!TTS_IS_HEAPTUPLE(slot)))
2783 andres@anarazel.de 1636 [ # # ]:UBC 0 : elog(ERROR, "trying to store a heap tuple into wrong type of slot");
2783 andres@anarazel.de 1637 :CBC 3502771 : tts_heap_store_tuple(slot, tuple, shouldFree);
1638 : :
2681 1639 : 3502771 : slot->tts_tableOid = tuple->t_tableOid;
1640 : :
2835 1641 : 3502771 : return slot;
1642 : : }
1643 : :
1644 : : /* --------------------------------
1645 : : * ExecStoreBufferHeapTuple
1646 : : *
1647 : : * This function is used to store an on-disk physical tuple from a buffer
1648 : : * into a specified slot in the tuple table.
1649 : : *
1650 : : * tuple: tuple to store
1651 : : * slot: TTSOpsBufferHeapTuple type slot to store it in
1652 : : * buffer: disk buffer if tuple is in a disk page, else InvalidBuffer
1653 : : *
1654 : : * The tuple table code acquires a pin on the buffer which is held until the
1655 : : * slot is cleared, so that the tuple won't go away on us.
1656 : : *
1657 : : * Return value is just the passed-in slot pointer.
1658 : : *
1659 : : * If the target slot is not guaranteed to be TTSOpsBufferHeapTuple type slot,
1660 : : * use the, more expensive, ExecForceStoreHeapTuple().
1661 : : * --------------------------------
1662 : : */
1663 : : TupleTableSlot *
1664 : 104515390 : ExecStoreBufferHeapTuple(HeapTuple tuple,
1665 : : TupleTableSlot *slot,
1666 : : Buffer buffer)
1667 : : {
1668 : : /*
1669 : : * sanity checks
1670 : : */
2783 1671 [ - + ]: 104515390 : Assert(tuple != NULL);
1672 [ - + ]: 104515390 : Assert(slot != NULL);
1673 [ - + ]: 104515390 : Assert(slot->tts_tupleDescriptor != NULL);
1674 [ - + ]: 104515390 : Assert(BufferIsValid(buffer));
1675 : :
1676 [ - + ]: 104515390 : if (unlikely(!TTS_IS_BUFFERTUPLE(slot)))
2783 andres@anarazel.de 1677 [ # # ]:UBC 0 : elog(ERROR, "trying to store an on-disk heap tuple into wrong type of slot");
2681 andres@anarazel.de 1678 :CBC 104515390 : tts_buffer_heap_store_tuple(slot, tuple, buffer, false);
1679 : :
1680 : 104515390 : slot->tts_tableOid = tuple->t_tableOid;
1681 : :
1682 : 104515390 : return slot;
1683 : : }
1684 : :
1685 : : /*
1686 : : * Like ExecStoreBufferHeapTuple, but transfer an existing pin from the caller
1687 : : * to the slot, i.e. the caller doesn't need to, and may not, release the pin.
1688 : : */
1689 : : TupleTableSlot *
1690 : 3410383 : ExecStorePinnedBufferHeapTuple(HeapTuple tuple,
1691 : : TupleTableSlot *slot,
1692 : : Buffer buffer)
1693 : : {
1694 : : /*
1695 : : * sanity checks
1696 : : */
1697 [ - + ]: 3410383 : Assert(tuple != NULL);
1698 [ - + ]: 3410383 : Assert(slot != NULL);
1699 [ - + ]: 3410383 : Assert(slot->tts_tupleDescriptor != NULL);
1700 [ - + ]: 3410383 : Assert(BufferIsValid(buffer));
1701 : :
1702 [ - + ]: 3410383 : if (unlikely(!TTS_IS_BUFFERTUPLE(slot)))
2681 andres@anarazel.de 1703 [ # # ]:UBC 0 : elog(ERROR, "trying to store an on-disk heap tuple into wrong type of slot");
2681 andres@anarazel.de 1704 :CBC 3410383 : tts_buffer_heap_store_tuple(slot, tuple, buffer, true);
1705 : :
1706 : 3410383 : slot->tts_tableOid = tuple->t_tableOid;
1707 : :
10523 bruce@momjian.us 1708 : 3410383 : return slot;
1709 : : }
1710 : :
1711 : : /*
1712 : : * Store a minimal tuple into TTSOpsMinimalTuple type slot.
1713 : : *
1714 : : * If the target slot is not guaranteed to be TTSOpsMinimalTuple type slot,
1715 : : * use the, more expensive, ExecForceStoreMinimalTuple().
1716 : : */
1717 : : TupleTableSlot *
7308 tgl@sss.pgh.pa.us 1718 : 40795100 : ExecStoreMinimalTuple(MinimalTuple mtup,
1719 : : TupleTableSlot *slot,
1720 : : bool shouldFree)
1721 : : {
1722 : : /*
1723 : : * sanity checks
1724 : : */
1725 [ - + ]: 40795100 : Assert(mtup != NULL);
1726 [ - + ]: 40795100 : Assert(slot != NULL);
1727 [ - + ]: 40795100 : Assert(slot->tts_tupleDescriptor != NULL);
1728 : :
2783 andres@anarazel.de 1729 [ - + ]: 40795100 : if (unlikely(!TTS_IS_MINIMALTUPLE(slot)))
2783 andres@anarazel.de 1730 [ # # ]:UBC 0 : elog(ERROR, "trying to store a minimal tuple into wrong type of slot");
2783 andres@anarazel.de 1731 :CBC 40795100 : tts_minimal_store_tuple(slot, mtup, shouldFree);
1732 : :
7308 tgl@sss.pgh.pa.us 1733 : 40795100 : return slot;
1734 : : }
1735 : :
1736 : : /*
1737 : : * Store a HeapTuple into any kind of slot, performing conversion if
1738 : : * necessary.
1739 : : */
1740 : : void
2783 andres@anarazel.de 1741 : 1157651 : ExecForceStoreHeapTuple(HeapTuple tuple,
1742 : : TupleTableSlot *slot,
1743 : : bool shouldFree)
1744 : : {
1745 [ + + ]: 1157651 : if (TTS_IS_HEAPTUPLE(slot))
1746 : : {
2629 1747 : 265 : ExecStoreHeapTuple(tuple, slot, shouldFree);
1748 : : }
2783 1749 [ + + ]: 1157386 : else if (TTS_IS_BUFFERTUPLE(slot))
1750 : : {
1751 : : MemoryContext oldContext;
1752 : 48304 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
1753 : :
1754 : 48304 : ExecClearTuple(slot);
1755 : 48304 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
1756 : 48304 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
1757 : 48304 : bslot->base.tuple = heap_copytuple(tuple);
2428 tgl@sss.pgh.pa.us 1758 : 48304 : slot->tts_flags |= TTS_FLAG_SHOULDFREE;
2783 andres@anarazel.de 1759 : 48304 : MemoryContextSwitchTo(oldContext);
1760 : :
2629 1761 [ + + ]: 48304 : if (shouldFree)
1762 : 47097 : pfree(tuple);
1763 : : }
1764 : : else
1765 : : {
2783 1766 : 1109082 : ExecClearTuple(slot);
1767 : 1109082 : heap_deform_tuple(tuple, slot->tts_tupleDescriptor,
1768 : : slot->tts_values, slot->tts_isnull);
1769 : 1109082 : ExecStoreVirtualTuple(slot);
1770 : :
2629 1771 [ + + ]: 1109082 : if (shouldFree)
1772 : : {
1773 : 138386 : ExecMaterializeSlot(slot);
1774 : 138386 : pfree(tuple);
1775 : : }
1776 : : }
2783 1777 : 1157651 : }
1778 : :
1779 : : /*
1780 : : * Store a MinimalTuple into any kind of slot, performing conversion if
1781 : : * necessary.
1782 : : */
1783 : : void
1784 : 4850241 : ExecForceStoreMinimalTuple(MinimalTuple mtup,
1785 : : TupleTableSlot *slot,
1786 : : bool shouldFree)
1787 : : {
1788 [ + + ]: 4850241 : if (TTS_IS_MINIMALTUPLE(slot))
1789 : : {
1790 : 3091228 : tts_minimal_store_tuple(slot, mtup, shouldFree);
1791 : : }
1792 : : else
1793 : : {
1794 : : HeapTupleData htup;
1795 : :
1796 : 1759013 : ExecClearTuple(slot);
1797 : :
1798 : 1759013 : htup.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
1799 : 1759013 : htup.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
1800 : 1759013 : heap_deform_tuple(&htup, slot->tts_tupleDescriptor,
1801 : : slot->tts_values, slot->tts_isnull);
1802 : 1759013 : ExecStoreVirtualTuple(slot);
1803 : :
2629 1804 [ + + ]: 1759013 : if (shouldFree)
1805 : : {
1806 : 958860 : ExecMaterializeSlot(slot);
1807 : 958860 : pfree(mtup);
1808 : : }
1809 : : }
10948 scrappy@hub.org 1810 : 4850241 : }
1811 : :
1812 : : /* --------------------------------
1813 : : * ExecStoreVirtualTuple
1814 : : * Mark a slot as containing a virtual tuple.
1815 : : *
1816 : : * The protocol for loading a slot with virtual tuple data is:
1817 : : * * Call ExecClearTuple to mark the slot empty.
1818 : : * * Store data into the Datum/isnull arrays.
1819 : : * * Call ExecStoreVirtualTuple to mark the slot valid.
1820 : : * This is a bit unclean but it avoids one round of data copying.
1821 : : * --------------------------------
1822 : : */
1823 : : TupleTableSlot *
7776 tgl@sss.pgh.pa.us 1824 : 18945208 : ExecStoreVirtualTuple(TupleTableSlot *slot)
1825 : : {
1826 : : /*
1827 : : * sanity checks
1828 : : */
1829 [ - + ]: 18945208 : Assert(slot != NULL);
1830 [ - + ]: 18945208 : Assert(slot->tts_tupleDescriptor != NULL);
2815 andres@anarazel.de 1831 [ - + ]: 18945208 : Assert(TTS_EMPTY(slot));
1832 : :
1833 : 18945208 : slot->tts_flags &= ~TTS_FLAG_EMPTY;
7776 tgl@sss.pgh.pa.us 1834 : 18945208 : slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
1835 : :
1836 : 18945208 : return slot;
1837 : : }
1838 : :
1839 : : /* --------------------------------
1840 : : * ExecStoreAllNullTuple
1841 : : * Set up the slot to contain a null in every column.
1842 : : *
1843 : : * At first glance this might sound just like ExecClearTuple, but it's
1844 : : * entirely different: the slot ends up full, not empty.
1845 : : * --------------------------------
1846 : : */
1847 : : TupleTableSlot *
1848 : 28734 : ExecStoreAllNullTuple(TupleTableSlot *slot)
1849 : : {
1850 : : /*
1851 : : * sanity checks
1852 : : */
1853 [ - + ]: 28734 : Assert(slot != NULL);
1854 [ - + ]: 28734 : Assert(slot->tts_tupleDescriptor != NULL);
1855 : :
1856 : : /* Clear any old contents */
1857 : 28734 : ExecClearTuple(slot);
1858 : :
1859 : : /*
1860 : : * Fill all the columns of the virtual tuple with nulls
1861 : : */
1862 [ + - + - : 206465 : MemSet(slot->tts_values, 0,
+ - + - +
+ ]
1863 : : slot->tts_tupleDescriptor->natts * sizeof(Datum));
1864 : 28734 : memset(slot->tts_isnull, true,
1865 : 28734 : slot->tts_tupleDescriptor->natts * sizeof(bool));
1866 : :
1867 : 28734 : return ExecStoreVirtualTuple(slot);
1868 : : }
1869 : :
1870 : : /*
1871 : : * Store a HeapTuple in datum form, into a slot. That always requires
1872 : : * deforming it and storing it in virtual form.
1873 : : *
1874 : : * Until the slot is materialized, the contents of the slot depend on the
1875 : : * datum.
1876 : : */
1877 : : void
2678 andres@anarazel.de 1878 : 9 : ExecStoreHeapTupleDatum(Datum data, TupleTableSlot *slot)
1879 : : {
1880 : 9 : HeapTupleData tuple = {0};
1881 : : HeapTupleHeader td;
1882 : :
1883 : 9 : td = DatumGetHeapTupleHeader(data);
1884 : :
1885 : 9 : tuple.t_len = HeapTupleHeaderGetDatumLength(td);
1886 : 9 : tuple.t_self = td->t_ctid;
1887 : 9 : tuple.t_data = td;
1888 : :
1889 : 9 : ExecClearTuple(slot);
1890 : :
1891 : 9 : heap_deform_tuple(&tuple, slot->tts_tupleDescriptor,
1892 : : slot->tts_values, slot->tts_isnull);
1893 : 9 : ExecStoreVirtualTuple(slot);
1894 : 9 : }
1895 : :
1896 : : /*
1897 : : * ExecFetchSlotHeapTuple - fetch HeapTuple representing the slot's content
1898 : : *
1899 : : * The returned HeapTuple represents the slot's content as closely as
1900 : : * possible.
1901 : : *
1902 : : * If materialize is true, the contents of the slots will be made independent
1903 : : * from the underlying storage (i.e. all buffer pins are released, memory is
1904 : : * allocated in the slot's context).
1905 : : *
1906 : : * If shouldFree is not-NULL it'll be set to true if the returned tuple has
1907 : : * been allocated in the calling memory context, and must be freed by the
1908 : : * caller (via explicit pfree() or a memory context reset).
1909 : : *
1910 : : * NB: If materialize is true, modifications of the returned tuple are
1911 : : * allowed. But it depends on the type of the slot whether such modifications
1912 : : * will also affect the slot's contents. While that is not the nicest
1913 : : * behaviour, all such modifications are in the process of being removed.
1914 : : */
1915 : : HeapTuple
2784 1916 : 31847262 : ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
1917 : : {
1918 : : /*
1919 : : * sanity checks
1920 : : */
7776 tgl@sss.pgh.pa.us 1921 [ - + ]: 31847262 : Assert(slot != NULL);
2815 andres@anarazel.de 1922 [ - + ]: 31847262 : Assert(!TTS_EMPTY(slot));
1923 : :
1924 : : /* Materialize the tuple so that the slot "owns" it, if requested. */
2783 1925 [ + + ]: 31847262 : if (materialize)
1926 : 15829250 : slot->tts_ops->materialize(slot);
1927 : :
1928 [ + + ]: 31847262 : if (slot->tts_ops->get_heap_tuple == NULL)
1929 : : {
1930 [ + - ]: 2999338 : if (shouldFree)
1931 : 2999338 : *shouldFree = true;
1932 : 2999338 : return slot->tts_ops->copy_heap_tuple(slot);
1933 : : }
1934 : : else
1935 : : {
1936 [ + + ]: 28847924 : if (shouldFree)
1937 : 26253473 : *shouldFree = false;
1938 : 28847924 : return slot->tts_ops->get_heap_tuple(slot);
1939 : : }
1940 : : }
1941 : :
1942 : : /* --------------------------------
1943 : : * ExecFetchSlotMinimalTuple
1944 : : * Fetch the slot's minimal physical tuple.
1945 : : *
1946 : : * If the given tuple table slot can hold a minimal tuple, indicated by a
1947 : : * non-NULL get_minimal_tuple callback, the function returns the minimal
1948 : : * tuple returned by that callback. It assumes that the minimal tuple
1949 : : * returned by the callback is "owned" by the slot i.e. the slot is
1950 : : * responsible for freeing the memory consumed by the tuple. Hence it sets
1951 : : * *shouldFree to false, indicating that the caller should not free the
1952 : : * memory consumed by the minimal tuple. In this case the returned minimal
1953 : : * tuple should be considered as read-only.
1954 : : *
1955 : : * If that callback is not supported, it calls copy_minimal_tuple callback
1956 : : * which is expected to return a copy of minimal tuple representing the
1957 : : * contents of the slot. In this case *shouldFree is set to true,
1958 : : * indicating the caller that it should free the memory consumed by the
1959 : : * minimal tuple. In this case the returned minimal tuple may be written
1960 : : * up.
1961 : : * --------------------------------
1962 : : */
1963 : : MinimalTuple
1964 : 15234214 : ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
1965 : : bool *shouldFree)
1966 : : {
1967 : : /*
1968 : : * sanity checks
1969 : : */
7308 tgl@sss.pgh.pa.us 1970 [ - + ]: 15234214 : Assert(slot != NULL);
2815 andres@anarazel.de 1971 [ - + ]: 15234214 : Assert(!TTS_EMPTY(slot));
1972 : :
2783 1973 [ + + ]: 15234214 : if (slot->tts_ops->get_minimal_tuple)
1974 : : {
1975 [ + - ]: 3481224 : if (shouldFree)
1976 : 3481224 : *shouldFree = false;
1977 : 3481224 : return slot->tts_ops->get_minimal_tuple(slot);
1978 : : }
1979 : : else
1980 : : {
1981 [ + - ]: 11752990 : if (shouldFree)
1982 : 11752990 : *shouldFree = true;
463 jdavis@postgresql.or 1983 : 11752990 : return slot->tts_ops->copy_minimal_tuple(slot, 0);
1984 : : }
1985 : : }
1986 : :
1987 : : /* --------------------------------
1988 : : * ExecFetchSlotHeapTupleDatum
1989 : : * Fetch the slot's tuple as a composite-type Datum.
1990 : : *
1991 : : * The result is always freshly palloc'd in the caller's memory context.
1992 : : * --------------------------------
1993 : : */
1994 : : Datum
2784 andres@anarazel.de 1995 : 40644 : ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot)
1996 : : {
1997 : : HeapTuple tup;
1998 : : TupleDesc tupdesc;
1999 : : bool shouldFree;
2000 : : Datum ret;
2001 : :
2002 : : /* Fetch slot's contents in regular-physical-tuple form */
2003 : 40644 : tup = ExecFetchSlotHeapTuple(slot, false, &shouldFree);
6454 tgl@sss.pgh.pa.us 2004 : 40644 : tupdesc = slot->tts_tupleDescriptor;
2005 : :
2006 : : /* Convert to Datum form */
2784 andres@anarazel.de 2007 : 40644 : ret = heap_copy_tuple_as_datum(tup, tupdesc);
2008 : :
2009 [ + + ]: 40644 : if (shouldFree)
2010 : 40500 : pfree(tup);
2011 : :
2012 : 40644 : return ret;
2013 : : }
2014 : :
2015 : : /* ----------------------------------------------------------------
2016 : : * convenience initialization routines
2017 : : * ----------------------------------------------------------------
2018 : : */
2019 : :
2020 : : /* ----------------
2021 : : * ExecInitResultTypeTL
2022 : : *
2023 : : * Initialize result type, using the plan node's targetlist.
2024 : : * ----------------
2025 : : */
2026 : : void
2790 2027 : 866164 : ExecInitResultTypeTL(PlanState *planstate)
2028 : : {
2779 2029 : 866164 : TupleDesc tupDesc = ExecTypeFromTL(planstate->plan->targetlist);
2030 : :
2790 2031 : 866164 : planstate->ps_ResultTupleDesc = tupDesc;
2032 : 866164 : }
2033 : :
2034 : : /* --------------------------------
2035 : : * ExecInit{Result,Scan,Extra}TupleSlot[TL]
2036 : : *
2037 : : * These are convenience routines to initialize the specified slot
2038 : : * in nodes inheriting the appropriate state. ExecInitExtraTupleSlot
2039 : : * is used for initializing special-purpose slots.
2040 : : * --------------------------------
2041 : : */
2042 : :
2043 : : /* ----------------
2044 : : * ExecInitResultTupleSlotTL
2045 : : *
2046 : : * Initialize result tuple slot, using the tuple descriptor previously
2047 : : * computed with ExecInitResultTypeTL().
2048 : : * ----------------
2049 : : */
2050 : : void
2784 2051 : 593585 : ExecInitResultSlot(PlanState *planstate, const TupleTableSlotOps *tts_ops)
2052 : : {
2053 : : TupleTableSlot *slot;
2054 : :
2790 2055 : 593585 : slot = ExecAllocTableSlot(&planstate->state->es_tupleTable,
2056 : : planstate->ps_ResultTupleDesc, tts_ops, 0);
2057 : 593585 : planstate->ps_ResultTupleSlot = slot;
2058 : :
2784 2059 : 593585 : planstate->resultopsfixed = planstate->ps_ResultTupleDesc != NULL;
2060 : 593585 : planstate->resultops = tts_ops;
2061 : 593585 : planstate->resultopsset = true;
2790 2062 : 593585 : }
2063 : :
2064 : : /* ----------------
2065 : : * ExecInitResultTupleSlotTL
2066 : : *
2067 : : * Initialize result tuple slot, using the plan node's targetlist.
2068 : : * ----------------
2069 : : */
2070 : : void
2784 2071 : 422657 : ExecInitResultTupleSlotTL(PlanState *planstate,
2072 : : const TupleTableSlotOps *tts_ops)
2073 : : {
2790 2074 : 422657 : ExecInitResultTypeTL(planstate);
2784 2075 : 422657 : ExecInitResultSlot(planstate, tts_ops);
10948 scrappy@hub.org 2076 : 422657 : }
2077 : :
2078 : : /* ----------------
2079 : : * ExecInitScanTupleSlot
2080 : : * ----------------
2081 : : */
2082 : : void
2784 andres@anarazel.de 2083 : 458244 : ExecInitScanTupleSlot(EState *estate, ScanState *scanstate,
2084 : : TupleDesc tupledesc, const TupleTableSlotOps *tts_ops,
2085 : : uint16 flags)
2086 : : {
3056 2087 : 458244 : scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
2088 : : tupledesc, tts_ops, flags);
3018 2089 : 458244 : scanstate->ps.scandesc = tupledesc;
2784 2090 : 458244 : scanstate->ps.scanopsfixed = tupledesc != NULL;
2091 : 458244 : scanstate->ps.scanops = tts_ops;
2092 : 458244 : scanstate->ps.scanopsset = true;
10948 scrappy@hub.org 2093 : 458244 : }
2094 : :
2095 : : /* ----------------
2096 : : * ExecInitExtraTupleSlot
2097 : : *
2098 : : * Return a newly created slot. If tupledesc is non-NULL the slot will have
2099 : : * that as its fixed tupledesc. Otherwise the caller needs to use
2100 : : * ExecSetSlotDescriptor() to set the descriptor before use.
2101 : : * ----------------
2102 : : */
2103 : : TupleTableSlot *
2784 andres@anarazel.de 2104 : 277761 : ExecInitExtraTupleSlot(EState *estate,
2105 : : TupleDesc tupledesc,
2106 : : const TupleTableSlotOps *tts_ops)
2107 : : {
106 drowley@postgresql.o 2108 :GNC 277761 : return ExecAllocTableSlot(&estate->es_tupleTable, tupledesc, tts_ops, 0);
2109 : : }
2110 : :
2111 : : /* ----------------
2112 : : * ExecInitNullTupleSlot
2113 : : *
2114 : : * Build a slot containing an all-nulls tuple of the given type.
2115 : : * This is used as a substitute for an input tuple when performing an
2116 : : * outer join.
2117 : : * ----------------
2118 : : */
2119 : : TupleTableSlot *
2784 andres@anarazel.de 2120 :CBC 27741 : ExecInitNullTupleSlot(EState *estate, TupleDesc tupType,
2121 : : const TupleTableSlotOps *tts_ops)
2122 : : {
2123 : 27741 : TupleTableSlot *slot = ExecInitExtraTupleSlot(estate, tupType, tts_ops);
2124 : :
7776 tgl@sss.pgh.pa.us 2125 : 27741 : return ExecStoreAllNullTuple(slot);
2126 : : }
2127 : :
2128 : : /* ---------------------------------------------------------------
2129 : : * Routines for setting/accessing attributes in a slot.
2130 : : * ---------------------------------------------------------------
2131 : : */
2132 : :
2133 : : /*
2134 : : * Fill in missing values for a TupleTableSlot.
2135 : : *
2136 : : * This is only exposed because it's needed for JIT compiled tuple
2137 : : * deforming. That exception aside, there should be no callers outside of this
2138 : : * file.
2139 : : */
2140 : : void
2816 andres@anarazel.de 2141 : 4854 : slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum)
2142 : : {
2143 : 4854 : AttrMissing *attrmiss = NULL;
2144 : :
2145 : : /* Check for invalid attnums */
108 drowley@postgresql.o 2146 [ - + ]:GNC 4854 : if (unlikely(lastAttNum > slot->tts_tupleDescriptor->natts))
108 drowley@postgresql.o 2147 [ # # ]:UNC 0 : elog(ERROR, "invalid attribute number %d", lastAttNum);
2148 : :
2816 andres@anarazel.de 2149 [ + + ]:CBC 4854 : if (slot->tts_tupleDescriptor->constr)
2150 : 3153 : attrmiss = slot->tts_tupleDescriptor->constr->missing;
2151 : :
2152 [ + + ]: 4854 : if (!attrmiss)
2153 : : {
2154 : : /* no missing values array at all, so just fill everything in as NULL */
108 drowley@postgresql.o 2155 [ + + ]:GNC 3831 : for (int attnum = startAttNum; attnum < lastAttNum; attnum++)
2156 : : {
2157 : 2020 : slot->tts_values[attnum] = (Datum) 0;
2158 : 2020 : slot->tts_isnull[attnum] = true;
2159 : : }
2160 : : }
2161 : : else
2162 : : {
2163 : : /* use attrmiss to set the missing values */
2164 [ + + ]: 7061 : for (int attnum = startAttNum; attnum < lastAttNum; attnum++)
2165 : : {
2166 : 4018 : slot->tts_values[attnum] = attrmiss[attnum].am_value;
2167 : 4018 : slot->tts_isnull[attnum] = !attrmiss[attnum].am_present;
2168 : : }
2169 : : }
2816 andres@anarazel.de 2170 :CBC 4854 : }
2171 : :
2172 : : /*
2173 : : * slot_getsomeattrs_int
2174 : : * external function to call getsomeattrs() for use in JIT
2175 : : */
2176 : : void
2783 andres@anarazel.de 2177 :LBC (98006589) : slot_getsomeattrs_int(TupleTableSlot *slot, int attnum)
2178 : : {
2179 : : /* Check for caller errors */
2579 akapila@postgresql.o 2180 [ # # ]: (98006589) : Assert(slot->tts_nvalid < attnum); /* checked in slot_getsomeattrs */
2783 andres@anarazel.de 2181 [ # # ]: (98006589) : Assert(attnum > 0);
2182 : :
2183 : : /* Fetch as many attributes as possible from the underlying tuple. */
2184 : (98006589) : slot->tts_ops->getsomeattrs(slot, attnum);
2185 : :
2186 : : /*
2187 : : * Avoid putting new code here as that would prevent the compiler from
2188 : : * using the sibling call optimization for the above function.
2189 : : */
2816 2190 : (98006589) : }
2191 : :
2192 : : /* ----------------------------------------------------------------
2193 : : * ExecTypeFromTL
2194 : : *
2195 : : * Generate a tuple descriptor for the result tuple of a targetlist.
2196 : : * (A parse/plan tlist must be passed, not an ExprState tlist.)
2197 : : * Note that resjunk columns, if any, are included in the result.
2198 : : *
2199 : : * Currently there are about 4 different places where we create
2200 : : * TupleDescriptors. They should all be merged, or perhaps
2201 : : * be rewritten to call BuildDesc().
2202 : : * ----------------------------------------------------------------
2203 : : */
2204 : : TupleDesc
2779 andres@anarazel.de 2205 :CBC 885386 : ExecTypeFromTL(List *targetList)
2206 : : {
2207 : 885386 : return ExecTypeFromTLInternal(targetList, false);
2208 : : }
2209 : :
2210 : : /* ----------------------------------------------------------------
2211 : : * ExecCleanTypeFromTL
2212 : : *
2213 : : * Same as above, but resjunk columns are omitted from the result.
2214 : : * ----------------------------------------------------------------
2215 : : */
2216 : : TupleDesc
2217 : 72643 : ExecCleanTypeFromTL(List *targetList)
2218 : : {
2219 : 72643 : return ExecTypeFromTLInternal(targetList, true);
2220 : : }
2221 : :
2222 : : static TupleDesc
2223 : 958029 : ExecTypeFromTLInternal(List *targetList, bool skipjunk)
2224 : : {
2225 : : TupleDesc typeInfo;
2226 : : ListCell *l;
2227 : : int len;
7975 bruce@momjian.us 2228 : 958029 : int cur_resno = 1;
2229 : :
8247 2230 [ + + ]: 958029 : if (skipjunk)
2231 : 72643 : len = ExecCleanTargetListLength(targetList);
2232 : : else
2233 : 885386 : len = ExecTargetListLength(targetList);
2779 andres@anarazel.de 2234 : 958029 : typeInfo = CreateTemplateTupleDesc(len);
2235 : :
8247 bruce@momjian.us 2236 [ + + + + : 4867575 : foreach(l, targetList)
+ + ]
2237 : : {
7975 2238 : 3909546 : TargetEntry *tle = lfirst(l);
2239 : :
7755 tgl@sss.pgh.pa.us 2240 [ + + + + ]: 3909546 : if (skipjunk && tle->resjunk)
8456 2241 : 19943 : continue;
2242 : 11668809 : TupleDescInitEntry(typeInfo,
2243 : : cur_resno,
7755 2244 : 3889603 : tle->resname,
2245 : 3889603 : exprType((Node *) tle->expr),
2246 : 3889603 : exprTypmod((Node *) tle->expr),
2247 : : 0);
5621 peter_e@gmx.net 2248 : 3889603 : TupleDescInitEntryCollation(typeInfo,
2249 : : cur_resno,
2250 : 3889603 : exprCollation((Node *) tle->expr));
2251 : 3889603 : cur_resno++;
2252 : : }
2253 : :
106 drowley@postgresql.o 2254 :GNC 958029 : TupleDescFinalize(typeInfo);
2255 : :
8456 tgl@sss.pgh.pa.us 2256 :CBC 958029 : return typeInfo;
2257 : : }
2258 : :
2259 : : /*
2260 : : * ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
2261 : : *
2262 : : * This is roughly like ExecTypeFromTL, but we work from bare expressions
2263 : : * not TargetEntrys. No names are attached to the tupledesc's columns.
2264 : : */
2265 : : TupleDesc
4250 2266 : 9778 : ExecTypeFromExprList(List *exprList)
2267 : : {
2268 : : TupleDesc typeInfo;
2269 : : ListCell *lc;
7975 bruce@momjian.us 2270 : 9778 : int cur_resno = 1;
2271 : :
2779 andres@anarazel.de 2272 : 9778 : typeInfo = CreateTemplateTupleDesc(list_length(exprList));
2273 : :
4250 tgl@sss.pgh.pa.us 2274 [ + + + + : 27220 : foreach(lc, exprList)
+ + ]
2275 : : {
2276 : 17442 : Node *e = lfirst(lc);
2277 : :
8086 2278 : 17442 : TupleDescInitEntry(typeInfo,
2279 : : cur_resno,
2280 : : NULL,
2281 : : exprType(e),
2282 : : exprTypmod(e),
2283 : : 0);
5621 peter_e@gmx.net 2284 : 17442 : TupleDescInitEntryCollation(typeInfo,
2285 : : cur_resno,
2286 : : exprCollation(e));
2287 : 17442 : cur_resno++;
2288 : : }
2289 : :
106 drowley@postgresql.o 2290 :GNC 9778 : TupleDescFinalize(typeInfo);
2291 : :
8086 tgl@sss.pgh.pa.us 2292 :CBC 9778 : return typeInfo;
2293 : : }
2294 : :
2295 : : /*
2296 : : * ExecTypeSetColNames - set column names in a RECORD TupleDesc
2297 : : *
2298 : : * Column names must be provided as an alias list (list of String nodes).
2299 : : */
2300 : : void
4250 2301 : 2890 : ExecTypeSetColNames(TupleDesc typeInfo, List *namesList)
2302 : : {
2303 : 2890 : int colno = 0;
2304 : : ListCell *lc;
2305 : :
2306 : : /* It's only OK to change col names in a not-yet-blessed RECORD type */
1566 2307 [ - + ]: 2890 : Assert(typeInfo->tdtypeid == RECORDOID);
2308 [ - + ]: 2890 : Assert(typeInfo->tdtypmod < 0);
2309 : :
4250 2310 [ + + + + : 10052 : foreach(lc, namesList)
+ + ]
2311 : : {
2312 : 7162 : char *cname = strVal(lfirst(lc));
2313 : : Form_pg_attribute attr;
2314 : :
2315 : : /* Guard against too-long names list (probably can't happen) */
2316 [ - + ]: 7162 : if (colno >= typeInfo->natts)
4250 tgl@sss.pgh.pa.us 2317 :UBC 0 : break;
3236 andres@anarazel.de 2318 :CBC 7162 : attr = TupleDescAttr(typeInfo, colno);
2319 : 7162 : colno++;
2320 : :
2321 : : /*
2322 : : * Do nothing for empty aliases or dropped columns (these cases
2323 : : * probably can't arise in RECORD types, either)
2324 : : */
1566 tgl@sss.pgh.pa.us 2325 [ + + - + ]: 7162 : if (cname[0] == '\0' || attr->attisdropped)
4250 2326 : 16 : continue;
2327 : :
2328 : : /* OK, assign the column name */
1566 2329 : 7146 : namestrcpy(&(attr->attname), cname);
2330 : : }
4250 2331 : 2890 : }
2332 : :
2333 : : /*
2334 : : * BlessTupleDesc - make a completed tuple descriptor useful for SRFs
2335 : : *
2336 : : * Rowtype Datums returned by a function must contain valid type information.
2337 : : * This happens "for free" if the tupdesc came from a relcache entry, but
2338 : : * not if we have manufactured a tupdesc for a transient RECORD datatype.
2339 : : * In that case we have to notify typcache.c of the existence of the type.
2340 : : *
2341 : : * TupleDescFinalize() must be called on the TupleDesc before calling this
2342 : : * function.
2343 : : */
2344 : : TupleDesc
8125 2345 : 89033 : BlessTupleDesc(TupleDesc tupdesc)
2346 : : {
2347 : : /* Did someone forget to call TupleDescFinalize()? */
106 drowley@postgresql.o 2348 [ - + ]:GNC 89033 : Assert(tupdesc->firstNonCachedOffsetAttr >= 0);
2349 : :
8125 tgl@sss.pgh.pa.us 2350 [ + + ]:CBC 89033 : if (tupdesc->tdtypeid == RECORDOID &&
2351 [ + + ]: 86291 : tupdesc->tdtypmod < 0)
2352 : 56245 : assign_record_type_typmod(tupdesc);
2353 : :
2354 : 89033 : return tupdesc; /* just for notational convenience */
2355 : : }
2356 : :
2357 : : /*
2358 : : * TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
2359 : : * supplied TupleDesc. AttInMetadata can be used in conjunction with C strings
2360 : : * to produce a properly formed tuple.
2361 : : */
2362 : : AttInMetadata *
8776 bruce@momjian.us 2363 : 14386 : TupleDescGetAttInMetadata(TupleDesc tupdesc)
2364 : : {
8700 2365 : 14386 : int natts = tupdesc->natts;
2366 : : int i;
2367 : : Oid atttypeid;
2368 : : Oid attinfuncid;
2369 : : FmgrInfo *attinfuncinfo;
2370 : : Oid *attioparams;
2371 : : int32 *atttypmods;
2372 : : AttInMetadata *attinmeta;
2373 : :
202 michael@paquier.xyz 2374 :GNC 14386 : attinmeta = palloc_object(AttInMetadata);
2375 : :
2376 : : /* "Bless" the tupledesc so that we can make rowtype datums with it */
8125 tgl@sss.pgh.pa.us 2377 :CBC 14386 : attinmeta->tupdesc = BlessTupleDesc(tupdesc);
2378 : :
2379 : : /*
2380 : : * Gather info needed later to call the "in" function for each attribute
2381 : : */
8310 bruce@momjian.us 2382 : 14386 : attinfuncinfo = (FmgrInfo *) palloc0(natts * sizeof(FmgrInfo));
8059 tgl@sss.pgh.pa.us 2383 : 14386 : attioparams = (Oid *) palloc0(natts * sizeof(Oid));
8310 bruce@momjian.us 2384 : 14386 : atttypmods = (int32 *) palloc0(natts * sizeof(int32));
2385 : :
8776 2386 [ + + ]: 74703 : for (i = 0; i < natts; i++)
2387 : : {
3236 andres@anarazel.de 2388 : 60317 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
2389 : :
2390 : : /* Ignore dropped attributes */
2391 [ + + ]: 60317 : if (!att->attisdropped)
2392 : : {
2393 : 60200 : atttypeid = att->atttypid;
8059 tgl@sss.pgh.pa.us 2394 : 60200 : getTypeInputInfo(atttypeid, &attinfuncid, &attioparams[i]);
8310 bruce@momjian.us 2395 : 60200 : fmgr_info(attinfuncid, &attinfuncinfo[i]);
3236 andres@anarazel.de 2396 : 60200 : atttypmods[i] = att->atttypmod;
2397 : : }
2398 : : }
8776 bruce@momjian.us 2399 : 14386 : attinmeta->attinfuncs = attinfuncinfo;
8059 tgl@sss.pgh.pa.us 2400 : 14386 : attinmeta->attioparams = attioparams;
8776 bruce@momjian.us 2401 : 14386 : attinmeta->atttypmods = atttypmods;
2402 : :
2403 : 14386 : return attinmeta;
2404 : : }
2405 : :
2406 : : /*
2407 : : * BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
2408 : : * values is an array of C strings, one for each attribute of the return tuple.
2409 : : * A NULL string pointer indicates we want to create a NULL field.
2410 : : */
2411 : : HeapTuple
2412 : 1010061 : BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
2413 : : {
8700 2414 : 1010061 : TupleDesc tupdesc = attinmeta->tupdesc;
2415 : 1010061 : int natts = tupdesc->natts;
2416 : : Datum *dvalues;
2417 : : bool *nulls;
2418 : : int i;
2419 : : HeapTuple tuple;
2420 : :
8776 2421 : 1010061 : dvalues = (Datum *) palloc(natts * sizeof(Datum));
6449 tgl@sss.pgh.pa.us 2422 : 1010061 : nulls = (bool *) palloc(natts * sizeof(bool));
2423 : :
2424 : : /*
2425 : : * Call the "in" function for each non-dropped attribute, even for nulls,
2426 : : * to support domains.
2427 : : */
8776 bruce@momjian.us 2428 [ + + ]: 15159469 : for (i = 0; i < natts; i++)
2429 : : {
557 drowley@postgresql.o 2430 [ + - ]: 14149409 : if (!TupleDescCompactAttr(tupdesc, i)->attisdropped)
2431 : : {
2432 : : /* Non-dropped attributes */
7392 tgl@sss.pgh.pa.us 2433 : 28298817 : dvalues[i] = InputFunctionCall(&attinmeta->attinfuncs[i],
2434 : 14149409 : values[i],
2435 : 14149409 : attinmeta->attioparams[i],
2436 : 14149409 : attinmeta->atttypmods[i]);
8310 bruce@momjian.us 2437 [ + + ]: 14149408 : if (values[i] != NULL)
6449 tgl@sss.pgh.pa.us 2438 : 9778648 : nulls[i] = false;
2439 : : else
2440 : 4370760 : nulls[i] = true;
2441 : : }
2442 : : else
2443 : : {
2444 : : /* Handle dropped attributes by setting to NULL */
8706 tgl@sss.pgh.pa.us 2445 :UBC 0 : dvalues[i] = (Datum) 0;
6449 2446 : 0 : nulls[i] = true;
2447 : : }
2448 : : }
2449 : :
2450 : : /*
2451 : : * Form a tuple
2452 : : */
6449 tgl@sss.pgh.pa.us 2453 :CBC 1010060 : tuple = heap_form_tuple(tupdesc, dvalues, nulls);
2454 : :
2455 : : /*
2456 : : * Release locally palloc'd space. XXX would probably be good to pfree
2457 : : * values of pass-by-reference datums, as well.
2458 : : */
8706 2459 : 1010060 : pfree(dvalues);
2460 : 1010060 : pfree(nulls);
2461 : :
8776 bruce@momjian.us 2462 : 1010060 : return tuple;
2463 : : }
2464 : :
2465 : : /*
2466 : : * HeapTupleHeaderGetDatum - convert a HeapTupleHeader pointer to a Datum.
2467 : : *
2468 : : * This must *not* get applied to an on-disk tuple; the tuple should be
2469 : : * freshly made by heap_form_tuple or some wrapper routine for it (such as
2470 : : * BuildTupleFromCStrings). Be sure also that the tupledesc used to build
2471 : : * the tuple has a properly "blessed" rowtype.
2472 : : *
2473 : : * Formerly this was a macro equivalent to PointerGetDatum, relying on the
2474 : : * fact that heap_form_tuple fills in the appropriate tuple header fields
2475 : : * for a composite Datum. However, we now require that composite Datums not
2476 : : * contain any external TOAST pointers. We do not want heap_form_tuple itself
2477 : : * to enforce that; more specifically, the rule applies only to actual Datums
2478 : : * and not to HeapTuple structures. Therefore, HeapTupleHeaderGetDatum is
2479 : : * now a function that detects whether there are externally-toasted fields
2480 : : * and constructs a new tuple with inlined fields if so. We still need
2481 : : * heap_form_tuple to insert the Datum header fields, because otherwise this
2482 : : * code would have no way to obtain a tupledesc for the tuple.
2483 : : *
2484 : : * Note that if we do build a new tuple, it's palloc'd in the current
2485 : : * memory context. Beware of code that changes context between the initial
2486 : : * heap_form_tuple/etc call and calling HeapTuple(Header)GetDatum.
2487 : : *
2488 : : * For performance-critical callers, it could be worthwhile to take extra
2489 : : * steps to ensure that there aren't TOAST pointers in the output of
2490 : : * heap_form_tuple to begin with. It's likely however that the costs of the
2491 : : * typcache lookup and tuple disassembly/reassembly are swamped by TOAST
2492 : : * dereference costs, so that the benefits of such extra effort would be
2493 : : * minimal.
2494 : : *
2495 : : * XXX it would likely be better to create wrapper functions that produce
2496 : : * a composite Datum from the field values in one step. However, there's
2497 : : * enough code using the existing APIs that we couldn't get rid of this
2498 : : * hack anytime soon.
2499 : : */
2500 : : Datum
4443 tgl@sss.pgh.pa.us 2501 : 1496883 : HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
2502 : : {
2503 : : Datum result;
2504 : : TupleDesc tupDesc;
2505 : :
2506 : : /* No work if there are no external TOAST pointers in the tuple */
2507 [ + + ]: 1496883 : if (!HeapTupleHeaderHasExternal(tuple))
2508 : 1496875 : return PointerGetDatum(tuple);
2509 : :
2510 : : /* Use the type data saved by heap_form_tuple to look up the rowtype */
2511 : 8 : tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
2512 : : HeapTupleHeaderGetTypMod(tuple));
2513 : :
2514 : : /* And do the flattening */
2515 : 8 : result = toast_flatten_tuple_to_datum(tuple,
2516 : : HeapTupleHeaderGetDatumLength(tuple),
2517 : : tupDesc);
2518 : :
2519 [ + - ]: 8 : ReleaseTupleDesc(tupDesc);
2520 : :
2521 : 8 : return result;
2522 : : }
2523 : :
2524 : :
2525 : : /*
2526 : : * Functions for sending tuples to the frontend (or other specified destination)
2527 : : * as though it is a SELECT result. These are used by utility commands that
2528 : : * need to project directly to the destination and don't need or want full
2529 : : * table function capability. Currently used by EXPLAIN and SHOW ALL.
2530 : : */
2531 : : TupOutputState *
2784 andres@anarazel.de 2532 : 19822 : begin_tup_output_tupdesc(DestReceiver *dest,
2533 : : TupleDesc tupdesc,
2534 : : const TupleTableSlotOps *tts_ops)
2535 : : {
2536 : : TupOutputState *tstate;
2537 : :
202 michael@paquier.xyz 2538 :GNC 19822 : tstate = palloc_object(TupOutputState);
2539 : :
2784 andres@anarazel.de 2540 :CBC 19822 : tstate->slot = MakeSingleTupleTableSlot(tupdesc, tts_ops);
8456 tgl@sss.pgh.pa.us 2541 : 19822 : tstate->dest = dest;
2542 : :
3218 peter_e@gmx.net 2543 : 19822 : tstate->dest->rStartup(tstate->dest, (int) CMD_SELECT, tupdesc);
2544 : :
8746 bruce@momjian.us 2545 : 19822 : return tstate;
2546 : : }
2547 : :
2548 : : /*
2549 : : * write a single tuple
2550 : : */
2551 : : void
994 peter@eisentraut.org 2552 : 116453 : do_tup_output(TupOutputState *tstate, const Datum *values, const bool *isnull)
2553 : : {
6186 tgl@sss.pgh.pa.us 2554 : 116453 : TupleTableSlot *slot = tstate->slot;
2555 : 116453 : int natts = slot->tts_tupleDescriptor->natts;
2556 : :
2557 : : /* make sure the slot is clear */
2558 : 116453 : ExecClearTuple(slot);
2559 : :
2560 : : /* insert data */
2561 : 116453 : memcpy(slot->tts_values, values, natts * sizeof(Datum));
2562 : 116453 : memcpy(slot->tts_isnull, isnull, natts * sizeof(bool));
2563 : :
2564 : : /* mark slot as containing a virtual tuple */
2565 : 116453 : ExecStoreVirtualTuple(slot);
2566 : :
2567 : : /* send the tuple to the receiver */
3218 peter_e@gmx.net 2568 : 116453 : (void) tstate->dest->receiveSlot(slot, tstate->dest);
2569 : :
2570 : : /* clean up */
6186 tgl@sss.pgh.pa.us 2571 : 116453 : ExecClearTuple(slot);
8746 bruce@momjian.us 2572 : 116453 : }
2573 : :
2574 : : /*
2575 : : * write a chunk of text, breaking at newline characters
2576 : : *
2577 : : * Should only be used with a single-TEXT-attribute tupdesc.
2578 : : */
2579 : : void
3690 tgl@sss.pgh.pa.us 2580 : 16238 : do_text_output_multiline(TupOutputState *tstate, const char *txt)
2581 : : {
2582 : : Datum values[1];
5968 bruce@momjian.us 2583 : 16238 : bool isnull[1] = {false};
2584 : :
3690 tgl@sss.pgh.pa.us 2585 [ + + ]: 128344 : while (*txt)
2586 : : {
2587 : : const char *eol;
2588 : : int len;
2589 : :
2590 : 112106 : eol = strchr(txt, '\n');
8746 bruce@momjian.us 2591 [ + - ]: 112106 : if (eol)
2592 : : {
3690 tgl@sss.pgh.pa.us 2593 : 112106 : len = eol - txt;
6187 2594 : 112106 : eol++;
2595 : : }
2596 : : else
2597 : : {
3690 tgl@sss.pgh.pa.us 2598 :UBC 0 : len = strlen(txt);
2599 : 0 : eol = txt + len;
2600 : : }
2601 : :
3690 tgl@sss.pgh.pa.us 2602 :CBC 112106 : values[0] = PointerGetDatum(cstring_to_text_with_len(txt, len));
6187 2603 : 112106 : do_tup_output(tstate, values, isnull);
2604 : 112106 : pfree(DatumGetPointer(values[0]));
3690 2605 : 112106 : txt = eol;
2606 : : }
8746 bruce@momjian.us 2607 : 16238 : }
2608 : :
2609 : : void
2610 : 19822 : end_tup_output(TupOutputState *tstate)
2611 : : {
3218 peter_e@gmx.net 2612 : 19822 : tstate->dest->rShutdown(tstate->dest);
2613 : : /* note that destroying the dest is not ours to do */
7776 tgl@sss.pgh.pa.us 2614 : 19822 : ExecDropSingleTupleTableSlot(tstate->slot);
8746 bruce@momjian.us 2615 : 19822 : pfree(tstate);
2616 : 19822 : }
|