Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * heaptuple.c
4 : : * This file contains heap tuple accessor and mutator routines, as well
5 : : * as various tuple utilities.
6 : : *
7 : : * Some notes about varlenas and this code:
8 : : *
9 : : * Before Postgres 8.3 varlenas always had a 4-byte length header, and
10 : : * therefore always needed 4-byte alignment (at least). This wasted space
11 : : * for short varlenas, for example CHAR(1) took 5 bytes and could need up to
12 : : * 3 additional padding bytes for alignment.
13 : : *
14 : : * Now, a short varlena (up to 126 data bytes) is reduced to a 1-byte header
15 : : * and we don't align it. To hide this from datatype-specific functions that
16 : : * don't want to deal with it, such a datum is considered "toasted" and will
17 : : * be expanded back to the normal 4-byte-header format by pg_detoast_datum.
18 : : * (In performance-critical code paths we can use pg_detoast_datum_packed
19 : : * and the appropriate access macros to avoid that overhead.) Note that this
20 : : * conversion is performed directly in heap_form_tuple, without invoking
21 : : * heaptoast.c.
22 : : *
23 : : * This change will break any code that assumes it needn't detoast values
24 : : * that have been put into a tuple but never sent to disk. Hopefully there
25 : : * are few such places.
26 : : *
27 : : * Varlenas still have alignment INT (or DOUBLE) in pg_type/pg_attribute, since
28 : : * that's the normal requirement for the untoasted format. But we ignore that
29 : : * for the 1-byte-header format. This means that the actual start position
30 : : * of a varlena datum may vary depending on which format it has. To determine
31 : : * what is stored, we have to require that alignment padding bytes be zero.
32 : : * (Postgres actually has always zeroed them, but now it's required!) Since
33 : : * the first byte of a 1-byte-header varlena can never be zero, we can examine
34 : : * the first byte after the previous datum to tell if it's a pad byte or the
35 : : * start of a 1-byte-header varlena.
36 : : *
37 : : * Note that while formerly we could rely on the first varlena column of a
38 : : * system catalog to be at the offset suggested by the C struct for the
39 : : * catalog, this is now risky: it's only safe if the preceding field is
40 : : * word-aligned, so that there will never be any padding.
41 : : *
42 : : * We don't pack varlenas whose attstorage is PLAIN, since the data type
43 : : * isn't expecting to have to detoast values. This is used in particular
44 : : * by oidvector and int2vector, which are used in the system catalogs
45 : : * and we'd like to still refer to them via C struct offsets.
46 : : *
47 : : *
48 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
49 : : * Portions Copyright (c) 1994, Regents of the University of California
50 : : *
51 : : *
52 : : * IDENTIFICATION
53 : : * src/backend/access/common/heaptuple.c
54 : : *
55 : : *-------------------------------------------------------------------------
56 : : */
57 : :
58 : : #include "postgres.h"
59 : :
60 : : #include "access/heaptoast.h"
61 : : #include "access/sysattr.h"
62 : : #include "access/tupdesc_details.h"
63 : : #include "common/hashfn.h"
64 : : #include "utils/datum.h"
65 : : #include "utils/expandeddatum.h"
66 : : #include "utils/hsearch.h"
67 : : #include "utils/memutils.h"
68 : :
69 : :
70 : : /*
71 : : * Does att's datatype allow packing into the 1-byte-header varlena format?
72 : : * While functions that use TupleDescAttr() and assign attstorage =
73 : : * TYPSTORAGE_PLAIN cannot use packed varlena headers, functions that call
74 : : * TupleDescInitEntry() use typeForm->typstorage (TYPSTORAGE_EXTENDED) and
75 : : * can use packed varlena headers, e.g.:
76 : : * CREATE TABLE test(a VARCHAR(10000) STORAGE PLAIN);
77 : : * INSERT INTO test VALUES (repeat('A',10));
78 : : * This can be verified with pageinspect.
79 : : */
80 : : #define ATT_IS_PACKABLE(att) \
81 : : ((att)->attlen == -1 && (att)->attstorage != TYPSTORAGE_PLAIN)
82 : : /* Use this if it's already known varlena */
83 : : #define VARLENA_ATT_IS_PACKABLE(att) \
84 : : ((att)->attstorage != TYPSTORAGE_PLAIN)
85 : :
86 : : /* FormData_pg_attribute.attstorage != TYPSTORAGE_PLAIN and an attlen of -1 */
87 : : #define COMPACT_ATTR_IS_PACKABLE(att) \
88 : : ((att)->attlen == -1 && (att)->attispackable)
89 : :
90 : : /*
91 : : * Setup for caching pass-by-ref missing attributes in a way that survives
92 : : * tupleDesc destruction.
93 : : */
94 : :
95 : : typedef struct
96 : : {
97 : : int len;
98 : : Datum value;
99 : : } missing_cache_key;
100 : :
101 : : static HTAB *missing_cache = NULL;
102 : :
103 : : static uint32
936 andrew@dunslane.net 104 :UBC 0 : missing_hash(const void *key, Size keysize)
105 : : {
62 peter@eisentraut.org 106 :UNC 0 : const missing_cache_key *entry = key;
107 : :
219 108 : 0 : return hash_bytes((const unsigned char *) DatumGetPointer(entry->value), entry->len);
109 : : }
110 : :
111 : : static int
936 andrew@dunslane.net 112 :UBC 0 : missing_match(const void *key1, const void *key2, Size keysize)
113 : : {
62 peter@eisentraut.org 114 :UNC 0 : const missing_cache_key *entry1 = key1;
115 : 0 : const missing_cache_key *entry2 = key2;
116 : :
936 andrew@dunslane.net 117 [ # # ]:UBC 0 : if (entry1->len != entry2->len)
118 [ # # ]: 0 : return entry1->len > entry2->len ? 1 : -1;
119 : :
120 : 0 : return memcmp(DatumGetPointer(entry1->value),
121 : 0 : DatumGetPointer(entry2->value),
122 : 0 : entry1->len);
123 : : }
124 : :
125 : : static void
102 nathan@postgresql.or 126 :UNC 0 : init_missing_cache(void)
127 : : {
128 : : HASHCTL hash_ctl;
129 : :
936 andrew@dunslane.net 130 :UBC 0 : hash_ctl.keysize = sizeof(missing_cache_key);
131 : 0 : hash_ctl.entrysize = sizeof(missing_cache_key);
132 : 0 : hash_ctl.hcxt = TopMemoryContext;
133 : 0 : hash_ctl.hash = missing_hash;
134 : 0 : hash_ctl.match = missing_match;
135 : 0 : missing_cache =
136 : 0 : hash_create("Missing Values Cache",
137 : : 32,
138 : : &hash_ctl,
139 : : HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
140 : 0 : }
141 : :
142 : : /* ----------------------------------------------------------------
143 : : * misc support routines
144 : : * ----------------------------------------------------------------
145 : : */
146 : :
147 : : /*
148 : : * Return the missing value of an attribute, or NULL if there isn't one.
149 : : */
150 : : Datum
2909 andrew@dunslane.net 151 :CBC 178 : getmissingattr(TupleDesc tupleDesc,
152 : : int attnum, bool *isnull)
153 : : {
154 : : CompactAttribute *att;
155 : :
156 [ - + ]: 178 : Assert(attnum <= tupleDesc->natts);
157 [ - + ]: 178 : Assert(attnum > 0);
158 : :
450 drowley@postgresql.o 159 : 178 : att = TupleDescCompactAttr(tupleDesc, attnum - 1);
160 : :
2909 andrew@dunslane.net 161 [ + + ]: 178 : if (att->atthasmissing)
162 : : {
163 : : AttrMissing *attrmiss;
164 : :
165 [ - + ]: 27 : Assert(tupleDesc->constr);
166 [ - + ]: 27 : Assert(tupleDesc->constr->missing);
167 : :
168 : 27 : attrmiss = tupleDesc->constr->missing + (attnum - 1);
169 : :
2818 akapila@postgresql.o 170 [ + - ]: 27 : if (attrmiss->am_present)
171 : : {
172 : : missing_cache_key key;
173 : : missing_cache_key *entry;
174 : : bool found;
175 : : MemoryContext oldctx;
176 : :
2909 andrew@dunslane.net 177 : 27 : *isnull = false;
178 : :
179 : : /* no need to cache by-value attributes */
936 180 [ + - ]: 27 : if (att->attbyval)
181 : 27 : return attrmiss->am_value;
182 : :
183 : : /* set up cache if required */
936 andrew@dunslane.net 184 [ # # ]:UBC 0 : if (missing_cache == NULL)
185 : 0 : init_missing_cache();
186 : :
187 : : /* check if there's a cache entry */
188 [ # # # # ]: 0 : Assert(att->attlen > 0 || att->attlen == -1);
189 [ # # ]: 0 : if (att->attlen > 0)
190 : 0 : key.len = att->attlen;
191 : : else
222 peter@eisentraut.org 192 :UNC 0 : key.len = VARSIZE_ANY(DatumGetPointer(attrmiss->am_value));
936 andrew@dunslane.net 193 :UBC 0 : key.value = attrmiss->am_value;
194 : :
195 : 0 : entry = hash_search(missing_cache, &key, HASH_ENTER, &found);
196 : :
197 [ # # ]: 0 : if (!found)
198 : : {
199 : : /* cache miss, so we need a non-transient copy of the datum */
200 : 0 : oldctx = MemoryContextSwitchTo(TopMemoryContext);
201 : 0 : entry->value =
202 : 0 : datumCopy(attrmiss->am_value, false, att->attlen);
203 : 0 : MemoryContextSwitchTo(oldctx);
204 : : }
205 : :
206 : 0 : return entry->value;
207 : : }
208 : : }
209 : :
2909 andrew@dunslane.net 210 :CBC 151 : *isnull = true;
211 : 151 : return PointerGetDatum(NULL);
212 : : }
213 : :
214 : : /*
215 : : * heap_compute_data_size
216 : : * Determine size of the data area of a tuple to be constructed
217 : : */
218 : : Size
7669 tgl@sss.pgh.pa.us 219 : 64575902 : heap_compute_data_size(TupleDesc tupleDesc,
220 : : const Datum *values,
221 : : const bool *isnull)
222 : : {
223 : 64575902 : Size data_length = 0;
224 : : int i;
225 : 64575902 : int numberOfAttributes = tupleDesc->natts;
226 : :
227 [ + + ]: 223039875 : for (i = 0; i < numberOfAttributes; i++)
228 : : {
229 : : Datum val;
230 : : CompactAttribute *atti;
231 : :
232 [ + + ]: 158463973 : if (isnull[i])
233 : 13870090 : continue;
234 : :
6918 235 : 144593883 : val = values[i];
450 drowley@postgresql.o 236 : 144593883 : atti = TupleDescCompactAttr(tupleDesc, i);
237 : :
238 [ + + + + : 163877247 : if (COMPACT_ATTR_IS_PACKABLE(atti) &&
+ + ]
6918 tgl@sss.pgh.pa.us 239 [ + + + + ]: 19283364 : VARATT_CAN_MAKE_SHORT(DatumGetPointer(val)))
240 : : {
241 : : /*
242 : : * we're anticipating converting to a short varlena header, so
243 : : * adjust length and don't count any alignment
244 : : */
245 : 14689312 : data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
246 : : }
3958 247 [ + + + + ]: 135494146 : else if (atti->attlen == -1 &&
248 [ + + + + ]: 5589575 : VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
249 : : {
250 : : /*
251 : : * we want to flatten the expanded value so that the constructed
252 : : * tuple doesn't depend on it
253 : : */
449 drowley@postgresql.o 254 : 1393 : data_length = att_nominal_alignby(data_length, atti->attalignby);
3958 tgl@sss.pgh.pa.us 255 : 1393 : data_length += EOH_get_flat_size(DatumGetEOHP(val));
256 : : }
257 : : else
258 : : {
449 drowley@postgresql.o 259 [ + + + + ]: 129903178 : data_length = att_datum_alignby(data_length, atti->attalignby,
260 : : atti->attlen, val);
3958 tgl@sss.pgh.pa.us 261 [ + + + + : 129903178 : data_length = att_addlength_datum(data_length, atti->attlen,
- + + + +
- - + + +
- + ]
262 : : val);
263 : : }
264 : : }
265 : :
7669 266 : 64575902 : return data_length;
267 : : }
268 : :
269 : : /*
270 : : * Per-attribute helper for heap_fill_tuple and other routines building tuples.
271 : : *
272 : : * Fill in either a data value or a bit in the null bitmask
273 : : */
274 : : static inline void
450 drowley@postgresql.o 275 : 147442054 : fill_val(CompactAttribute *att,
276 : : bits8 **bit,
277 : : int *bitmask,
278 : : char **dataP,
279 : : uint16 *infomask,
280 : : Datum datum,
281 : : bool isnull)
282 : : {
283 : : Size data_length;
2909 andrew@dunslane.net 284 : 147442054 : char *data = *dataP;
285 : :
286 : : /*
287 : : * If we're building a null bitmap, set the appropriate bit for the
288 : : * current column value here.
289 : : */
290 [ + + ]: 147442054 : if (bit != NULL)
291 : : {
292 [ + + ]: 51787943 : if (*bitmask != HIGHBIT)
293 : 42883658 : *bitmask <<= 1;
294 : : else
295 : : {
296 : 8904285 : *bit += 1;
297 : 8904285 : **bit = 0x0;
298 : 8904285 : *bitmask = 1;
299 : : }
300 : :
301 [ + + ]: 51787943 : if (isnull)
302 : : {
303 : 13733084 : *infomask |= HEAP_HASNULL;
304 : 13733084 : return;
305 : : }
306 : :
307 : 38054859 : **bit |= *bitmask;
308 : : }
309 : :
310 : : /*
311 : : * XXX we use the att_nominal_alignby macro on the pointer value itself,
312 : : * not on an offset. This is a bit of a hack.
313 : : */
314 [ + + ]: 133708970 : if (att->attbyval)
315 : : {
316 : : /* pass-by-value */
449 drowley@postgresql.o 317 : 101377153 : data = (char *) att_nominal_alignby(data, att->attalignby);
2909 andrew@dunslane.net 318 : 101377153 : store_att_byval(data, datum, att->attlen);
319 : 101377153 : data_length = att->attlen;
320 : : }
321 [ + + ]: 32331817 : else if (att->attlen == -1)
322 : : {
323 : : /* varlena */
324 : 19471564 : Pointer val = DatumGetPointer(datum);
325 : :
326 : 19471564 : *infomask |= HEAP_HASVARWIDTH;
327 [ + + ]: 19471564 : if (VARATT_IS_EXTERNAL(val))
328 : : {
329 [ + + + + ]: 11882 : if (VARATT_IS_EXTERNAL_EXPANDED(val))
2909 andrew@dunslane.net 330 :ECB (1393) : {
331 : : /*
332 : : * we want to flatten the expanded value so that the
333 : : * constructed tuple doesn't depend on it
334 : : */
2909 andrew@dunslane.net 335 :CBC 1393 : ExpandedObjectHeader *eoh = DatumGetEOHP(datum);
336 : :
449 drowley@postgresql.o 337 : 1393 : data = (char *) att_nominal_alignby(data, att->attalignby);
2909 andrew@dunslane.net 338 : 1393 : data_length = EOH_get_flat_size(eoh);
339 : 1393 : EOH_flatten_into(eoh, data, data_length);
340 : : }
341 : : else
342 : : {
343 : 10489 : *infomask |= HEAP_HASEXTERNAL;
344 : : /* no alignment, since it's short by definition */
345 [ + + + - : 10489 : data_length = VARSIZE_EXTERNAL(val);
- + ]
346 : 10489 : memcpy(data, val, data_length);
347 : : }
348 : : }
349 [ + + ]: 19459682 : else if (VARATT_IS_SHORT(val))
350 : : {
351 : : /* no alignment for short varlenas */
352 : 3647026 : data_length = VARSIZE_SHORT(val);
353 : 3647026 : memcpy(data, val, data_length);
354 : : }
450 drowley@postgresql.o 355 [ + + + + : 15812656 : else if (att->attispackable && VARATT_CAN_MAKE_SHORT(val))
+ + ]
356 : : {
357 : : /* convert to short varlena -- no alignment */
2909 andrew@dunslane.net 358 : 14223819 : data_length = VARATT_CONVERTED_SHORT_SIZE(val);
359 : 14223819 : SET_VARSIZE_SHORT(data, data_length);
360 : 14223819 : memcpy(data + 1, VARDATA(val), data_length - 1);
361 : : }
362 : : else
363 : : {
364 : : /* full 4-byte header varlena */
449 drowley@postgresql.o 365 : 1588837 : data = (char *) att_nominal_alignby(data, att->attalignby);
2909 andrew@dunslane.net 366 : 1588837 : data_length = VARSIZE(val);
367 : 1588837 : memcpy(data, val, data_length);
368 : : }
369 : : }
370 [ + + ]: 12860253 : else if (att->attlen == -2)
371 : : {
372 : : /* cstring ... never needs alignment */
373 : 1348274 : *infomask |= HEAP_HASVARWIDTH;
449 drowley@postgresql.o 374 [ - + ]: 1348274 : Assert(att->attalignby == sizeof(char));
2909 andrew@dunslane.net 375 : 1348274 : data_length = strlen(DatumGetCString(datum)) + 1;
376 : 1348274 : memcpy(data, DatumGetPointer(datum), data_length);
377 : : }
378 : : else
379 : : {
380 : : /* fixed-length pass-by-reference */
449 drowley@postgresql.o 381 : 11511979 : data = (char *) att_nominal_alignby(data, att->attalignby);
2909 andrew@dunslane.net 382 [ - + ]: 11511979 : Assert(att->attlen > 0);
383 : 11511979 : data_length = att->attlen;
384 : 11511979 : memcpy(data, DatumGetPointer(datum), data_length);
385 : : }
386 : :
387 : 133708970 : data += data_length;
388 : 133708970 : *dataP = data;
389 : : }
390 : :
391 : : /*
392 : : * heap_fill_tuple
393 : : * Load data portion of a tuple from values/isnull arrays
394 : : *
395 : : * We also fill the null bitmap (if any) and set the infomask bits
396 : : * that reflect the tuple's data contents.
397 : : *
398 : : * NOTE: it is now REQUIRED that the caller have pre-zeroed the data area.
399 : : */
400 : : void
7669 tgl@sss.pgh.pa.us 401 : 54733865 : heap_fill_tuple(TupleDesc tupleDesc,
402 : : const Datum *values, const bool *isnull,
403 : : char *data, Size data_size,
404 : : uint16 *infomask, bits8 *bit)
405 : : {
406 : : bits8 *bitP;
407 : : int bitmask;
408 : : int i;
409 : 54733865 : int numberOfAttributes = tupleDesc->natts;
410 : :
411 : : #ifdef USE_ASSERT_CHECKING
6918 412 : 54733865 : char *start = data;
413 : : #endif
414 : :
7669 415 [ + + ]: 54733865 : if (bit != NULL)
416 : : {
417 : 3758971 : bitP = &bit[-1];
7385 bruce@momjian.us 418 : 3758971 : bitmask = HIGHBIT;
419 : : }
420 : : else
421 : : {
422 : : /* just to keep compiler quiet */
7669 tgl@sss.pgh.pa.us 423 : 50974894 : bitP = NULL;
424 : 50974894 : bitmask = 0;
425 : : }
426 : :
6918 427 : 54733865 : *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL);
428 : :
7669 429 [ + + ]: 202175919 : for (i = 0; i < numberOfAttributes; i++)
430 : : {
450 drowley@postgresql.o 431 : 147442054 : CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, i);
432 : :
2909 andrew@dunslane.net 433 [ + + ]: 294884108 : fill_val(attr,
434 [ + + ]: 147442054 : bitP ? &bitP : NULL,
435 : : &bitmask,
436 : : &data,
437 : : infomask,
438 : 147442054 : values ? values[i] : PointerGetDatum(NULL),
439 [ + - + + ]: 147442054 : isnull ? isnull[i] : true);
440 : : }
441 : :
6918 tgl@sss.pgh.pa.us 442 [ - + ]: 54733865 : Assert((data - start) == data_size);
7669 443 : 54733865 : }
444 : :
445 : :
446 : : /* ----------------------------------------------------------------
447 : : * heap tuple interface
448 : : * ----------------------------------------------------------------
449 : : */
450 : :
451 : : /* ----------------
452 : : * heap_attisnull - returns true iff tuple attribute is not present
453 : : * ----------------
454 : : */
455 : : bool
2909 andrew@dunslane.net 456 : 5386286 : heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
457 : : {
458 : : /*
459 : : * We allow a NULL tupledesc for relations not expected to have missing
460 : : * values, such as catalog relations and indexes.
461 : : */
462 [ + + - + ]: 5386286 : Assert(!tupleDesc || attnum <= tupleDesc->natts);
7005 bruce@momjian.us 463 [ - + ]: 5386286 : if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data))
464 : : {
450 drowley@postgresql.o 465 [ # # ]:UBC 0 : if (tupleDesc &&
466 [ # # ]: 0 : TupleDescCompactAttr(tupleDesc, attnum - 1)->atthasmissing)
2909 andrew@dunslane.net 467 : 0 : return false;
468 : : else
469 : 0 : return true;
470 : : }
471 : :
10416 bruce@momjian.us 472 [ + - ]:CBC 5386286 : if (attnum > 0)
473 : : {
7954 tgl@sss.pgh.pa.us 474 [ + + ]: 5386286 : if (HeapTupleNoNulls(tup))
7669 475 : 1533 : return false;
9970 vadim4o@yahoo.com 476 : 5384753 : return att_isnull(attnum - 1, tup->t_data->t_bits);
477 : : }
478 : :
7954 tgl@sss.pgh.pa.us 479 [ # # ]:UBC 0 : switch (attnum)
480 : : {
481 : 0 : case TableOidAttributeNumber:
482 : : case SelfItemPointerAttributeNumber:
483 : : case MinTransactionIdAttributeNumber:
484 : : case MinCommandIdAttributeNumber:
485 : : case MaxTransactionIdAttributeNumber:
486 : : case MaxCommandIdAttributeNumber:
487 : : /* these are never null */
488 : 0 : break;
489 : :
490 : 0 : default:
491 [ # # ]: 0 : elog(ERROR, "invalid attnum: %d", attnum);
492 : : }
493 : :
7669 494 : 0 : return false;
495 : : }
496 : :
497 : : /* ----------------
498 : : * nocachegetattr
499 : : *
500 : : * This only gets called from fastgetattr(), in cases where we
501 : : * can't use a cacheoffset and the value is not null.
502 : : *
503 : : * This caches attribute offsets in the attribute descriptor.
504 : : *
505 : : * An alternative way to speed things up would be to cache offsets
506 : : * with the tuple, but that seems more difficult unless you take
507 : : * the storage hit of actually putting those offsets into the
508 : : * tuple you send to disk. Yuck.
509 : : *
510 : : * This scheme will be slightly slower than that, but should
511 : : * perform well for queries which hit large #'s of tuples. After
512 : : * you cache the offsets once, examining all the other tuples using
513 : : * the same attribute descriptor will go much quicker. -cim 5/4/91
514 : : *
515 : : * NOTE: if you need to change this code, see also heap_deform_tuple.
516 : : * Also see nocache_index_getattr, which is the same code for index
517 : : * tuples.
518 : : * ----------------
519 : : */
520 : : Datum
1273 pg@bowt.ie 521 :CBC 117627604 : nocachegetattr(HeapTuple tup,
522 : : int attnum,
523 : : TupleDesc tupleDesc)
524 : : {
525 : 117627604 : HeapTupleHeader td = tup->t_data;
526 : : char *tp; /* ptr to data part of tuple */
527 : 117627604 : bits8 *bp = td->t_bits; /* ptr to null bitmap in tuple */
6918 tgl@sss.pgh.pa.us 528 : 117627604 : bool slow = false; /* do we have to walk attrs? */
529 : : int off; /* current offset within data */
530 : :
531 : : /* ----------------
532 : : * Three cases:
533 : : *
534 : : * 1: No nulls and no variable-width attributes.
535 : : * 2: Has a null or a var-width AFTER att.
536 : : * 3: Has nulls or var-widths BEFORE att.
537 : : * ----------------
538 : : */
539 : :
540 : 117627604 : attnum--;
541 : :
1273 pg@bowt.ie 542 [ + + ]: 117627604 : if (!HeapTupleNoNulls(tup))
543 : : {
544 : : /*
545 : : * there's a null somewhere in the tuple
546 : : *
547 : : * check to see if any preceding bits are null...
548 : : */
5453 bruce@momjian.us 549 : 81122243 : int byte = attnum >> 3;
5908 rhaas@postgresql.org 550 : 81122243 : int finalbit = attnum & 0x07;
551 : :
552 : : /* check for nulls "before" final bit of last byte */
553 [ + + ]: 81122243 : if ((~bp[byte]) & ((1 << finalbit) - 1))
554 : 3108220 : slow = true;
555 : : else
556 : : {
557 : : /* check for nulls in any "earlier" bytes */
558 : : int i;
559 : :
560 [ + + ]: 102837270 : for (i = 0; i < byte; i++)
561 : : {
562 [ + + ]: 25229780 : if (bp[i] != 0xFF)
563 : : {
564 : 406533 : slow = true;
565 : 406533 : break;
566 : : }
567 : : }
568 : : }
569 : : }
570 : :
1273 pg@bowt.ie 571 : 117627604 : tp = (char *) td + td->t_hoff;
572 : :
10416 bruce@momjian.us 573 [ + + ]: 117627604 : if (!slow)
574 : : {
575 : : CompactAttribute *att;
576 : :
577 : : /*
578 : : * If we get here, there are no nulls up to and including the target
579 : : * attribute. If we have a cached offset, we can use it.
580 : : */
450 drowley@postgresql.o 581 : 114112851 : att = TupleDescCompactAttr(tupleDesc, attnum);
3129 andres@anarazel.de 582 [ + + ]: 114112851 : if (att->attcacheoff >= 0)
583 : 75191587 : return fetchatt(att, tp + att->attcacheoff);
584 : :
585 : : /*
586 : : * Otherwise, check for non-fixed-length attrs up to and including
587 : : * target. If there aren't any, it's safe to cheaply initialize the
588 : : * cached offsets for these attrs.
589 : : */
1273 pg@bowt.ie 590 [ + + ]: 38921264 : if (HeapTupleHasVarWidth(tup))
591 : : {
592 : : int j;
593 : :
9912 tgl@sss.pgh.pa.us 594 [ + + ]: 72261941 : for (j = 0; j <= attnum; j++)
595 : : {
450 drowley@postgresql.o 596 [ + + ]: 72160904 : if (TupleDescCompactAttr(tupleDesc, j)->attlen <= 0)
597 : : {
9236 tgl@sss.pgh.pa.us 598 : 38618957 : slow = true;
9912 599 : 38618957 : break;
600 : : }
601 : : }
602 : : }
603 : : }
604 : :
10416 bruce@momjian.us 605 [ + + ]: 42436017 : if (!slow)
606 : : {
6918 tgl@sss.pgh.pa.us 607 : 302307 : int natts = tupleDesc->natts;
10244 bruce@momjian.us 608 : 302307 : int j = 1;
609 : :
610 : : /*
611 : : * If we get here, we have a tuple with no nulls or var-widths up to
612 : : * and including the target attribute, so we can use the cached offset
613 : : * ... only we don't have it yet, or we'd not have got here. Since
614 : : * it's cheap to compute offsets for fixed-width columns, we take the
615 : : * opportunity to initialize the cached offsets for *all* the leading
616 : : * fixed-width columns, in hope of avoiding future visits to this
617 : : * routine.
618 : : */
450 drowley@postgresql.o 619 : 302307 : TupleDescCompactAttr(tupleDesc, 0)->attcacheoff = 0;
620 : :
621 : : /* we might have set some offsets in the slow path previously */
622 [ + + + + ]: 304346 : while (j < natts && TupleDescCompactAttr(tupleDesc, j)->attcacheoff > 0)
10416 bruce@momjian.us 623 : 2039 : j++;
624 : :
450 drowley@postgresql.o 625 : 302307 : off = TupleDescCompactAttr(tupleDesc, j - 1)->attcacheoff +
626 : 302307 : TupleDescCompactAttr(tupleDesc, j - 1)->attlen;
627 : :
6918 tgl@sss.pgh.pa.us 628 [ + + ]: 3682112 : for (; j < natts; j++)
629 : : {
450 drowley@postgresql.o 630 : 3572877 : CompactAttribute *att = TupleDescCompactAttr(tupleDesc, j);
631 : :
3129 andres@anarazel.de 632 [ + + ]: 3572877 : if (att->attlen <= 0)
6918 tgl@sss.pgh.pa.us 633 : 193072 : break;
634 : :
449 drowley@postgresql.o 635 : 3379805 : off = att_nominal_alignby(off, att->attalignby);
636 : :
3129 andres@anarazel.de 637 : 3379805 : att->attcacheoff = off;
638 : :
639 : 3379805 : off += att->attlen;
640 : : }
641 : :
6918 tgl@sss.pgh.pa.us 642 [ - + ]: 302307 : Assert(j > attnum);
643 : :
450 drowley@postgresql.o 644 : 302307 : off = TupleDescCompactAttr(tupleDesc, attnum)->attcacheoff;
645 : : }
646 : : else
647 : : {
10244 bruce@momjian.us 648 : 42133710 : bool usecache = true;
649 : : int i;
650 : :
651 : : /*
652 : : * Now we know that we have to walk the tuple CAREFULLY. But we still
653 : : * might be able to cache some offsets for next time.
654 : : *
655 : : * Note - This loop is a little tricky. For each non-null attribute,
656 : : * we have to first account for alignment padding before the attr,
657 : : * then advance over the attr based on its length. Nulls have no
658 : : * storage and no alignment padding either. We can use/set
659 : : * attcacheoff until we reach either a null or a var-width attribute.
660 : : */
6918 tgl@sss.pgh.pa.us 661 : 42133710 : off = 0;
6695 bruce@momjian.us 662 : 42133710 : for (i = 0;; i++) /* loop exit is at "break" */
10416 663 : 152312986 : {
450 drowley@postgresql.o 664 : 194446696 : CompactAttribute *att = TupleDescCompactAttr(tupleDesc, i);
665 : :
1273 pg@bowt.ie 666 [ + + + + ]: 194446696 : if (HeapTupleHasNulls(tup) && att_isnull(i, bp))
667 : : {
8094 tgl@sss.pgh.pa.us 668 : 9942092 : usecache = false;
6695 bruce@momjian.us 669 : 9942092 : continue; /* this cannot be the target att */
670 : : }
671 : :
672 : : /* If we know the next offset, we can skip the rest */
3129 andres@anarazel.de 673 [ + + + + ]: 184504604 : if (usecache && att->attcacheoff >= 0)
674 : 100145067 : off = att->attcacheoff;
675 [ + + ]: 84359537 : else if (att->attlen == -1)
676 : : {
677 : : /*
678 : : * We can only cache the offset for a varlena attribute if the
679 : : * offset is already suitably aligned, so that there would be
680 : : * no pad bytes in any case: then the offset will be valid for
681 : : * either an aligned or unaligned value.
682 : : */
6918 tgl@sss.pgh.pa.us 683 [ + + ]: 11878769 : if (usecache &&
449 drowley@postgresql.o 684 [ + + ]: 1260919 : off == att_nominal_alignby(off, att->attalignby))
3129 andres@anarazel.de 685 : 51793 : att->attcacheoff = off;
686 : : else
687 : : {
449 drowley@postgresql.o 688 [ + + ]: 11826976 : off = att_pointer_alignby(off, att->attalignby, -1,
689 : : tp + off);
6918 tgl@sss.pgh.pa.us 690 : 11826976 : usecache = false;
691 : : }
692 : : }
693 : : else
694 : : {
695 : : /* not varlena, so safe to use att_nominal_alignby */
449 drowley@postgresql.o 696 : 72480768 : off = att_nominal_alignby(off, att->attalignby);
697 : :
10416 bruce@momjian.us 698 [ + + ]: 72480768 : if (usecache)
3129 andres@anarazel.de 699 : 309006 : att->attcacheoff = off;
700 : : }
701 : :
6918 tgl@sss.pgh.pa.us 702 [ + + ]: 184504604 : if (i == attnum)
703 : 42133710 : break;
704 : :
3129 andres@anarazel.de 705 [ + + + - : 142370894 : off = att_addlength_pointer(off, att->attlen, tp + off);
- - + + +
- - + + +
- - ]
706 : :
707 [ + + + + ]: 142370894 : if (usecache && att->attlen <= 0)
10051 bruce@momjian.us 708 : 39490913 : usecache = false;
709 : : }
710 : : }
711 : :
450 drowley@postgresql.o 712 : 42436017 : return fetchatt(TupleDescCompactAttr(tupleDesc, attnum), tp + off);
713 : : }
714 : :
715 : : /* ----------------
716 : : * heap_getsysattr
717 : : *
718 : : * Fetch the value of a system attribute for a tuple.
719 : : *
720 : : * This is a support routine for heap_getattr(). The function has already
721 : : * determined that the attnum refers to a system attribute.
722 : : * ----------------
723 : : */
724 : : Datum
8018 tgl@sss.pgh.pa.us 725 : 72715 : heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
726 : : {
727 : : Datum result;
728 : :
9209 729 [ - + ]: 72715 : Assert(tup);
730 : :
731 : : /* Currently, no sys attribute ever reads as NULL. */
5908 rhaas@postgresql.org 732 : 72715 : *isnull = false;
733 : :
9209 tgl@sss.pgh.pa.us 734 [ + + + + : 72715 : switch (attnum)
+ - ]
735 : : {
736 : 2 : case SelfItemPointerAttributeNumber:
737 : : /* pass-by-reference datatype */
738 : 2 : result = PointerGetDatum(&(tup->t_self));
739 : 2 : break;
740 : 72503 : case MinTransactionIdAttributeNumber:
4466 rhaas@postgresql.org 741 : 72503 : result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmin(tup->t_data));
9209 tgl@sss.pgh.pa.us 742 : 72503 : break;
743 : 115 : case MaxTransactionIdAttributeNumber:
4799 alvherre@alvh.no-ip. 744 : 115 : result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmax(tup->t_data));
9209 tgl@sss.pgh.pa.us 745 : 115 : break;
6974 746 : 93 : case MinCommandIdAttributeNumber:
747 : : case MaxCommandIdAttributeNumber:
748 : :
749 : : /*
750 : : * cmin and cmax are now both aliases for the same field, which
751 : : * can in fact also be a combo command id. XXX perhaps we should
752 : : * return the "real" cmin or cmax if possible, that is if we are
753 : : * inside the originating transaction?
754 : : */
755 : 93 : result = CommandIdGetDatum(HeapTupleHeaderGetRawCommandId(tup->t_data));
9209 756 : 93 : break;
757 : 2 : case TableOidAttributeNumber:
758 : 2 : result = ObjectIdGetDatum(tup->t_tableOid);
759 : 2 : break;
9209 tgl@sss.pgh.pa.us 760 :UBC 0 : default:
8273 761 [ # # ]: 0 : elog(ERROR, "invalid attnum: %d", attnum);
762 : : result = 0; /* keep compiler quiet */
763 : : break;
764 : : }
9209 tgl@sss.pgh.pa.us 765 :CBC 72715 : return result;
766 : : }
767 : :
768 : : /* ----------------
769 : : * heap_copytuple
770 : : *
771 : : * returns a copy of an entire tuple
772 : : *
773 : : * The HeapTuple struct, tuple header, and tuple data are all allocated
774 : : * as a single palloc() block.
775 : : * ----------------
776 : : */
777 : : HeapTuple
10841 scrappy@hub.org 778 : 7917112 : heap_copytuple(HeapTuple tuple)
779 : : {
780 : : HeapTuple newTuple;
781 : :
9970 vadim4o@yahoo.com 782 [ + - - + ]: 7917112 : if (!HeapTupleIsValid(tuple) || tuple->t_data == NULL)
10057 bruce@momjian.us 783 :UBC 0 : return NULL;
784 : :
9970 vadim4o@yahoo.com 785 :CBC 7917112 : newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + tuple->t_len);
786 : 7917112 : newTuple->t_len = tuple->t_len;
787 : 7917112 : newTuple->t_self = tuple->t_self;
9252 tgl@sss.pgh.pa.us 788 : 7917112 : newTuple->t_tableOid = tuple->t_tableOid;
9970 vadim4o@yahoo.com 789 : 7917112 : newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
396 peter@eisentraut.org 790 : 7917112 : memcpy(newTuple->t_data, tuple->t_data, tuple->t_len);
10057 bruce@momjian.us 791 : 7917112 : return newTuple;
792 : : }
793 : :
794 : : /* ----------------
795 : : * heap_copytuple_with_tuple
796 : : *
797 : : * copy a tuple into a caller-supplied HeapTuple management struct
798 : : *
799 : : * Note that after calling this function, the "dest" HeapTuple will not be
800 : : * allocated as a single palloc() block (unlike with heap_copytuple()).
801 : : * ----------------
802 : : */
803 : : void
9970 vadim4o@yahoo.com 804 :UBC 0 : heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest)
805 : : {
806 [ # # # # ]: 0 : if (!HeapTupleIsValid(src) || src->t_data == NULL)
807 : : {
808 : 0 : dest->t_data = NULL;
809 : 0 : return;
810 : : }
811 : :
812 : 0 : dest->t_len = src->t_len;
813 : 0 : dest->t_self = src->t_self;
9252 tgl@sss.pgh.pa.us 814 : 0 : dest->t_tableOid = src->t_tableOid;
9970 vadim4o@yahoo.com 815 : 0 : dest->t_data = (HeapTupleHeader) palloc(src->t_len);
396 peter@eisentraut.org 816 : 0 : memcpy(dest->t_data, src->t_data, src->t_len);
817 : : }
818 : :
819 : : /*
820 : : * Expand a tuple which has fewer attributes than required. For each attribute
821 : : * not present in the sourceTuple, if there is a missing value that will be
822 : : * used. Otherwise the attribute will be set to NULL.
823 : : *
824 : : * The source tuple must have fewer attributes than the required number.
825 : : *
826 : : * Only one of targetHeapTuple and targetMinimalTuple may be supplied. The
827 : : * other argument must be NULL.
828 : : */
829 : : static void
2909 andrew@dunslane.net 830 : 0 : expand_tuple(HeapTuple *targetHeapTuple,
831 : : MinimalTuple *targetMinimalTuple,
832 : : HeapTuple sourceTuple,
833 : : TupleDesc tupleDesc)
834 : : {
835 : 0 : AttrMissing *attrmiss = NULL;
836 : : int attnum;
837 : : int firstmissingnum;
838 : 0 : bool hasNulls = HeapTupleHasNulls(sourceTuple);
839 : : HeapTupleHeader targetTHeader;
840 : 0 : HeapTupleHeader sourceTHeader = sourceTuple->t_data;
841 : 0 : int sourceNatts = HeapTupleHeaderGetNatts(sourceTHeader);
842 : 0 : int natts = tupleDesc->natts;
843 : : int sourceNullLen;
844 : : int targetNullLen;
845 : 0 : Size sourceDataLen = sourceTuple->t_len - sourceTHeader->t_hoff;
846 : : Size targetDataLen;
847 : : Size len;
848 : : int hoff;
849 : 0 : bits8 *nullBits = NULL;
850 : 0 : int bitMask = 0;
851 : : char *targetData;
852 : : uint16 *infoMask;
853 : :
854 [ # # # # : 0 : Assert((targetHeapTuple && !targetMinimalTuple)
# # # # ]
855 : : || (!targetHeapTuple && targetMinimalTuple));
856 : :
857 [ # # ]: 0 : Assert(sourceNatts < natts);
858 : :
859 [ # # ]: 0 : sourceNullLen = (hasNulls ? BITMAPLEN(sourceNatts) : 0);
860 : :
861 : 0 : targetDataLen = sourceDataLen;
862 : :
863 [ # # ]: 0 : if (tupleDesc->constr &&
864 [ # # ]: 0 : tupleDesc->constr->missing)
865 : : {
866 : : /*
867 : : * If there are missing values we want to put them into the tuple.
868 : : * Before that we have to compute the extra length for the values
869 : : * array and the variable length data.
870 : : */
871 : 0 : attrmiss = tupleDesc->constr->missing;
872 : :
873 : : /*
874 : : * Find the first item in attrmiss for which we don't have a value in
875 : : * the source. We can ignore all the missing entries before that.
876 : : */
877 : 0 : for (firstmissingnum = sourceNatts;
878 [ # # ]: 0 : firstmissingnum < natts;
879 : 0 : firstmissingnum++)
880 : : {
2818 akapila@postgresql.o 881 [ # # ]: 0 : if (attrmiss[firstmissingnum].am_present)
2909 andrew@dunslane.net 882 : 0 : break;
883 : : else
2729 884 : 0 : hasNulls = true;
885 : : }
886 : :
887 : : /*
888 : : * Now walk the missing attributes. If there is a missing value make
889 : : * space for it. Otherwise, it's going to be NULL.
890 : : */
891 : 0 : for (attnum = firstmissingnum;
892 [ # # ]: 0 : attnum < natts;
893 : 0 : attnum++)
894 : : {
895 [ # # ]: 0 : if (attrmiss[attnum].am_present)
896 : : {
450 drowley@postgresql.o 897 : 0 : CompactAttribute *att = TupleDescCompactAttr(tupleDesc, attnum);
898 : :
449 899 [ # # # # ]: 0 : targetDataLen = att_datum_alignby(targetDataLen,
900 : : att->attalignby,
901 : : att->attlen,
902 : : attrmiss[attnum].am_value);
903 : :
222 peter@eisentraut.org 904 [ # # # # :UNC 0 : targetDataLen = att_addlength_datum(targetDataLen,
# # ]
905 : : att->attlen,
906 : : attrmiss[attnum].am_value);
907 : : }
908 : : else
909 : : {
910 : : /* no missing value, so it must be null */
2729 andrew@dunslane.net 911 :UBC 0 : hasNulls = true;
912 : : }
913 : : }
914 : : } /* end if have missing values */
915 : : else
916 : : {
917 : : /*
918 : : * If there are no missing values at all then NULLS must be allowed,
919 : : * since some of the attributes are known to be absent.
920 : : */
2909 921 : 0 : hasNulls = true;
922 : : }
923 : :
924 : 0 : len = 0;
925 : :
926 [ # # ]: 0 : if (hasNulls)
927 : : {
928 : 0 : targetNullLen = BITMAPLEN(natts);
929 : 0 : len += targetNullLen;
930 : : }
931 : : else
932 : 0 : targetNullLen = 0;
933 : :
934 : : /*
935 : : * Allocate and zero the space needed. Note that the tuple body and
936 : : * HeapTupleData management structure are allocated in one chunk.
937 : : */
938 [ # # ]: 0 : if (targetHeapTuple)
939 : : {
940 : 0 : len += offsetof(HeapTupleHeaderData, t_bits);
941 : 0 : hoff = len = MAXALIGN(len); /* align user data safely */
942 : 0 : len += targetDataLen;
943 : :
944 : 0 : *targetHeapTuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
945 : 0 : (*targetHeapTuple)->t_data
946 : 0 : = targetTHeader
947 : 0 : = (HeapTupleHeader) ((char *) *targetHeapTuple + HEAPTUPLESIZE);
948 : 0 : (*targetHeapTuple)->t_len = len;
949 : 0 : (*targetHeapTuple)->t_tableOid = sourceTuple->t_tableOid;
2699 950 : 0 : (*targetHeapTuple)->t_self = sourceTuple->t_self;
951 : :
2909 952 : 0 : targetTHeader->t_infomask = sourceTHeader->t_infomask;
953 : 0 : targetTHeader->t_hoff = hoff;
954 : 0 : HeapTupleHeaderSetNatts(targetTHeader, natts);
955 : 0 : HeapTupleHeaderSetDatumLength(targetTHeader, len);
956 : 0 : HeapTupleHeaderSetTypeId(targetTHeader, tupleDesc->tdtypeid);
957 : 0 : HeapTupleHeaderSetTypMod(targetTHeader, tupleDesc->tdtypmod);
958 : : /* We also make sure that t_ctid is invalid unless explicitly set */
959 : 0 : ItemPointerSetInvalid(&(targetTHeader->t_ctid));
960 [ # # ]: 0 : if (targetNullLen > 0)
961 : 0 : nullBits = (bits8 *) ((char *) (*targetHeapTuple)->t_data
962 : : + offsetof(HeapTupleHeaderData, t_bits));
963 : 0 : targetData = (char *) (*targetHeapTuple)->t_data + hoff;
964 : 0 : infoMask = &(targetTHeader->t_infomask);
965 : : }
966 : : else
967 : : {
968 : 0 : len += SizeofMinimalTupleHeader;
969 : 0 : hoff = len = MAXALIGN(len); /* align user data safely */
970 : 0 : len += targetDataLen;
971 : :
972 : 0 : *targetMinimalTuple = (MinimalTuple) palloc0(len);
973 : 0 : (*targetMinimalTuple)->t_len = len;
974 : 0 : (*targetMinimalTuple)->t_hoff = hoff + MINIMAL_TUPLE_OFFSET;
975 : 0 : (*targetMinimalTuple)->t_infomask = sourceTHeader->t_infomask;
976 : : /* Same macro works for MinimalTuples */
977 : 0 : HeapTupleHeaderSetNatts(*targetMinimalTuple, natts);
978 [ # # ]: 0 : if (targetNullLen > 0)
979 : 0 : nullBits = (bits8 *) ((char *) *targetMinimalTuple
980 : 0 : + offsetof(MinimalTupleData, t_bits));
981 : 0 : targetData = (char *) *targetMinimalTuple + hoff;
982 : 0 : infoMask = &((*targetMinimalTuple)->t_infomask);
983 : : }
984 : :
985 [ # # ]: 0 : if (targetNullLen > 0)
986 : : {
987 [ # # ]: 0 : if (sourceNullLen > 0)
988 : : {
989 : : /* if bitmap pre-existed copy in - all is set */
990 : 0 : memcpy(nullBits,
991 : : ((char *) sourceTHeader)
992 : : + offsetof(HeapTupleHeaderData, t_bits),
993 : : sourceNullLen);
994 : 0 : nullBits += sourceNullLen - 1;
995 : : }
996 : : else
997 : : {
998 : 0 : sourceNullLen = BITMAPLEN(sourceNatts);
999 : : /* Set NOT NULL for all existing attributes */
1000 : 0 : memset(nullBits, 0xff, sourceNullLen);
1001 : :
1002 : 0 : nullBits += sourceNullLen - 1;
1003 : :
1004 [ # # ]: 0 : if (sourceNatts & 0x07)
1005 : : {
1006 : : /* build the mask (inverted!) */
1007 : 0 : bitMask = 0xff << (sourceNatts & 0x07);
1008 : : /* Voila */
1009 : 0 : *nullBits = ~bitMask;
1010 : : }
1011 : : }
1012 : :
1013 : 0 : bitMask = (1 << ((sourceNatts - 1) & 0x07));
1014 : : } /* End if have null bitmap */
1015 : :
1016 : 0 : memcpy(targetData,
1017 : 0 : ((char *) sourceTuple->t_data) + sourceTHeader->t_hoff,
1018 : : sourceDataLen);
1019 : :
1020 : 0 : targetData += sourceDataLen;
1021 : :
1022 : : /* Now fill in the missing values */
1023 [ # # ]: 0 : for (attnum = sourceNatts; attnum < natts; attnum++)
1024 : : {
450 drowley@postgresql.o 1025 : 0 : CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, attnum);
1026 : :
2818 akapila@postgresql.o 1027 [ # # # # ]: 0 : if (attrmiss && attrmiss[attnum].am_present)
1028 : : {
2909 andrew@dunslane.net 1029 : 0 : fill_val(attr,
1030 : 0 : nullBits ? &nullBits : NULL,
1031 : : &bitMask,
1032 : : &targetData,
1033 : : infoMask,
2818 akapila@postgresql.o 1034 [ # # ]: 0 : attrmiss[attnum].am_value,
1035 : : false);
1036 : : }
1037 : : else
1038 : : {
2909 andrew@dunslane.net 1039 : 0 : fill_val(attr,
1040 : : &nullBits,
1041 : : &bitMask,
1042 : : &targetData,
1043 : : infoMask,
1044 : : (Datum) 0,
1045 : : true);
1046 : : }
1047 : : } /* end loop over missing attributes */
1048 : 0 : }
1049 : :
1050 : : /*
1051 : : * Fill in the missing values for a minimal HeapTuple
1052 : : */
1053 : : MinimalTuple
1054 : 0 : minimal_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc)
1055 : : {
1056 : : MinimalTuple minimalTuple;
1057 : :
1058 : 0 : expand_tuple(NULL, &minimalTuple, sourceTuple, tupleDesc);
1059 : 0 : return minimalTuple;
1060 : : }
1061 : :
1062 : : /*
1063 : : * Fill in the missing values for an ordinary HeapTuple
1064 : : */
1065 : : HeapTuple
1066 : 0 : heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc)
1067 : : {
1068 : : HeapTuple heapTuple;
1069 : :
1070 : 0 : expand_tuple(&heapTuple, NULL, sourceTuple, tupleDesc);
1071 : 0 : return heapTuple;
1072 : : }
1073 : :
1074 : : /* ----------------
1075 : : * heap_copy_tuple_as_datum
1076 : : *
1077 : : * copy a tuple as a composite-type Datum
1078 : : * ----------------
1079 : : */
1080 : : Datum
4336 tgl@sss.pgh.pa.us 1081 :CBC 40971 : heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
1082 : : {
1083 : : HeapTupleHeader td;
1084 : :
1085 : : /*
1086 : : * If the tuple contains any external TOAST pointers, we have to inline
1087 : : * those fields to meet the conventions for composite-type Datums.
1088 : : */
1089 [ - + ]: 40971 : if (HeapTupleHasExternal(tuple))
4336 tgl@sss.pgh.pa.us 1090 :UBC 0 : return toast_flatten_tuple_to_datum(tuple->t_data,
1091 : : tuple->t_len,
1092 : : tupleDesc);
1093 : :
1094 : : /*
1095 : : * Fast path for easy case: just make a palloc'd copy and insert the
1096 : : * correct composite-Datum header fields (since those may not be set if
1097 : : * the given tuple came from disk, rather than from heap_form_tuple).
1098 : : */
4336 tgl@sss.pgh.pa.us 1099 :CBC 40971 : td = (HeapTupleHeader) palloc(tuple->t_len);
396 peter@eisentraut.org 1100 : 40971 : memcpy(td, tuple->t_data, tuple->t_len);
1101 : :
4336 tgl@sss.pgh.pa.us 1102 : 40971 : HeapTupleHeaderSetDatumLength(td, tuple->t_len);
1103 : 40971 : HeapTupleHeaderSetTypeId(td, tupleDesc->tdtypeid);
1104 : 40971 : HeapTupleHeaderSetTypMod(td, tupleDesc->tdtypmod);
1105 : :
1106 : 40971 : return PointerGetDatum(td);
1107 : : }
1108 : :
1109 : : /*
1110 : : * heap_form_tuple
1111 : : * construct a tuple from the given values[] and isnull[] arrays,
1112 : : * which are of the length indicated by tupleDescriptor->natts
1113 : : *
1114 : : * The result is allocated in the current memory context.
1115 : : */
1116 : : HeapTuple
7669 1117 : 14066383 : heap_form_tuple(TupleDesc tupleDescriptor,
1118 : : const Datum *values,
1119 : : const bool *isnull)
1120 : : {
1121 : : HeapTuple tuple; /* return tuple */
1122 : : HeapTupleHeader td; /* tuple data */
1123 : : Size len,
1124 : : data_len;
1125 : : int hoff;
1126 : 14066383 : bool hasnull = false;
1127 : 14066383 : int numberOfAttributes = tupleDescriptor->natts;
1128 : : int i;
1129 : :
1130 [ - + ]: 14066383 : if (numberOfAttributes > MaxTupleAttributeNumber)
7669 tgl@sss.pgh.pa.us 1131 [ # # ]:UBC 0 : ereport(ERROR,
1132 : : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1133 : : errmsg("number of columns (%d) exceeds limit (%d)",
1134 : : numberOfAttributes, MaxTupleAttributeNumber)));
1135 : :
1136 : : /*
1137 : : * Check for nulls
1138 : : */
7669 tgl@sss.pgh.pa.us 1139 [ + + ]:CBC 68111961 : for (i = 0; i < numberOfAttributes; i++)
1140 : : {
1141 [ + + ]: 57392398 : if (isnull[i])
1142 : : {
4336 1143 : 3346820 : hasnull = true;
1144 : 3346820 : break;
1145 : : }
1146 : : }
1147 : :
1148 : : /*
1149 : : * Determine total space needed
1150 : : */
7669 1151 : 14066383 : len = offsetof(HeapTupleHeaderData, t_bits);
1152 : :
1153 [ + + ]: 14066383 : if (hasnull)
1154 : 3346820 : len += BITMAPLEN(numberOfAttributes);
1155 : :
1156 : 14066383 : hoff = len = MAXALIGN(len); /* align user data safely */
1157 : :
6918 1158 : 14066383 : data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
1159 : :
1160 : 14066383 : len += data_len;
1161 : :
1162 : : /*
1163 : : * Allocate and zero the space needed. Note that the tuple body and
1164 : : * HeapTupleData management structure are allocated in one chunk.
1165 : : */
3231 alvherre@alvh.no-ip. 1166 : 14066383 : tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
7669 tgl@sss.pgh.pa.us 1167 : 14066383 : tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
1168 : :
1169 : : /*
1170 : : * And fill in the information. Note we fill the Datum fields even though
1171 : : * this tuple may never become a Datum. This lets HeapTupleHeaderGetDatum
1172 : : * identify the tuple type if needed.
1173 : : */
1174 : 14066383 : tuple->t_len = len;
1175 : 14066383 : ItemPointerSetInvalid(&(tuple->t_self));
1176 : 14066383 : tuple->t_tableOid = InvalidOid;
1177 : :
1178 : 14066383 : HeapTupleHeaderSetDatumLength(td, len);
1179 : 14066383 : HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid);
1180 : 14066383 : HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod);
1181 : : /* We also make sure that t_ctid is invalid unless explicitly set */
3959 1182 : 14066383 : ItemPointerSetInvalid(&(td->t_ctid));
1183 : :
7005 bruce@momjian.us 1184 : 14066383 : HeapTupleHeaderSetNatts(td, numberOfAttributes);
7669 tgl@sss.pgh.pa.us 1185 : 14066383 : td->t_hoff = hoff;
1186 : :
1187 [ + + ]: 14066383 : heap_fill_tuple(tupleDescriptor,
1188 : : values,
1189 : : isnull,
1190 : : (char *) td + hoff,
1191 : : data_len,
1192 : : &td->t_infomask,
1193 : : (hasnull ? td->t_bits : NULL));
1194 : :
1195 : 14066383 : return tuple;
1196 : : }
1197 : :
1198 : : /*
1199 : : * heap_modify_tuple
1200 : : * form a new tuple from an old tuple and a set of replacement values.
1201 : : *
1202 : : * The replValues, replIsnull, and doReplace arrays must be of the length
1203 : : * indicated by tupleDesc->natts. The new tuple is constructed using the data
1204 : : * from replValues/replIsnull at columns where doReplace is true, and using
1205 : : * the data from the old tuple at columns where doReplace is false.
1206 : : *
1207 : : * The result is allocated in the current memory context.
1208 : : */
1209 : : HeapTuple
1210 : 51151 : heap_modify_tuple(HeapTuple tuple,
1211 : : TupleDesc tupleDesc,
1212 : : const Datum *replValues,
1213 : : const bool *replIsnull,
1214 : : const bool *doReplace)
1215 : : {
1216 : 51151 : int numberOfAttributes = tupleDesc->natts;
1217 : : int attoff;
1218 : : Datum *values;
1219 : : bool *isnull;
1220 : : HeapTuple newTuple;
1221 : :
1222 : : /*
1223 : : * allocate and fill values and isnull arrays from either the tuple or the
1224 : : * repl information, as appropriate.
1225 : : *
1226 : : * NOTE: it's debatable whether to use heap_deform_tuple() here or just
1227 : : * heap_getattr() only the non-replaced columns. The latter could win if
1228 : : * there are many replaced columns and few non-replaced ones. However,
1229 : : * heap_deform_tuple costs only O(N) while the heap_getattr way would cost
1230 : : * O(N^2) if there are many non-replaced columns, so it seems better to
1231 : : * err on the side of linear cost.
1232 : : */
95 michael@paquier.xyz 1233 :GNC 51151 : values = palloc_array(Datum, numberOfAttributes);
1234 : 51151 : isnull = palloc_array(bool, numberOfAttributes);
1235 : :
7669 tgl@sss.pgh.pa.us 1236 :CBC 51151 : heap_deform_tuple(tuple, tupleDesc, values, isnull);
1237 : :
1238 [ + + ]: 1546946 : for (attoff = 0; attoff < numberOfAttributes; attoff++)
1239 : : {
1240 [ + + ]: 1495795 : if (doReplace[attoff])
1241 : : {
1242 : 738874 : values[attoff] = replValues[attoff];
1243 : 738874 : isnull[attoff] = replIsnull[attoff];
1244 : : }
1245 : : }
1246 : :
1247 : : /*
1248 : : * create a new tuple from the values and isnull arrays
1249 : : */
1250 : 51151 : newTuple = heap_form_tuple(tupleDesc, values, isnull);
1251 : :
1252 : 51151 : pfree(values);
1253 : 51151 : pfree(isnull);
1254 : :
1255 : : /*
1256 : : * copy the identification info of the old tuple: t_ctid, t_self
1257 : : */
1258 : 51151 : newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
1259 : 51151 : newTuple->t_self = tuple->t_self;
1260 : 51151 : newTuple->t_tableOid = tuple->t_tableOid;
1261 : :
1262 : 51151 : return newTuple;
1263 : : }
1264 : :
1265 : : /*
1266 : : * heap_modify_tuple_by_cols
1267 : : * form a new tuple from an old tuple and a set of replacement values.
1268 : : *
1269 : : * This is like heap_modify_tuple, except that instead of specifying which
1270 : : * column(s) to replace by a boolean map, an array of target column numbers
1271 : : * is used. This is often more convenient when a fixed number of columns
1272 : : * are to be replaced. The replCols, replValues, and replIsnull arrays must
1273 : : * be of length nCols. Target column numbers are indexed from 1.
1274 : : *
1275 : : * The result is allocated in the current memory context.
1276 : : */
1277 : : HeapTuple
3414 1278 : 661 : heap_modify_tuple_by_cols(HeapTuple tuple,
1279 : : TupleDesc tupleDesc,
1280 : : int nCols,
1281 : : const int *replCols,
1282 : : const Datum *replValues,
1283 : : const bool *replIsnull)
1284 : : {
1285 : 661 : int numberOfAttributes = tupleDesc->natts;
1286 : : Datum *values;
1287 : : bool *isnull;
1288 : : HeapTuple newTuple;
1289 : : int i;
1290 : :
1291 : : /*
1292 : : * allocate and fill values and isnull arrays from the tuple, then replace
1293 : : * selected columns from the input arrays.
1294 : : */
95 michael@paquier.xyz 1295 :GNC 661 : values = palloc_array(Datum, numberOfAttributes);
1296 : 661 : isnull = palloc_array(bool, numberOfAttributes);
1297 : :
3414 tgl@sss.pgh.pa.us 1298 :CBC 661 : heap_deform_tuple(tuple, tupleDesc, values, isnull);
1299 : :
1300 [ + + ]: 1924 : for (i = 0; i < nCols; i++)
1301 : : {
1302 : 1263 : int attnum = replCols[i];
1303 : :
1304 [ + - - + ]: 1263 : if (attnum <= 0 || attnum > numberOfAttributes)
3414 tgl@sss.pgh.pa.us 1305 [ # # ]:UBC 0 : elog(ERROR, "invalid column number %d", attnum);
3414 tgl@sss.pgh.pa.us 1306 :CBC 1263 : values[attnum - 1] = replValues[i];
1307 : 1263 : isnull[attnum - 1] = replIsnull[i];
1308 : : }
1309 : :
1310 : : /*
1311 : : * create a new tuple from the values and isnull arrays
1312 : : */
1313 : 661 : newTuple = heap_form_tuple(tupleDesc, values, isnull);
1314 : :
1315 : 661 : pfree(values);
1316 : 661 : pfree(isnull);
1317 : :
1318 : : /*
1319 : : * copy the identification info of the old tuple: t_ctid, t_self
1320 : : */
1321 : 661 : newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
1322 : 661 : newTuple->t_self = tuple->t_self;
1323 : 661 : newTuple->t_tableOid = tuple->t_tableOid;
1324 : :
1325 : 661 : return newTuple;
1326 : : }
1327 : :
1328 : : /*
1329 : : * heap_deform_tuple
1330 : : * Given a tuple, extract data into values/isnull arrays; this is
1331 : : * the inverse of heap_form_tuple.
1332 : : *
1333 : : * Storage for the values/isnull arrays is provided by the caller;
1334 : : * it should be sized according to tupleDesc->natts not
1335 : : * HeapTupleHeaderGetNatts(tuple->t_data).
1336 : : *
1337 : : * Note that for pass-by-reference datatypes, the pointer placed
1338 : : * in the Datum will point into the given tuple.
1339 : : *
1340 : : * When all or most of a tuple's fields need to be extracted,
1341 : : * this routine will be significantly quicker than a loop around
1342 : : * heap_getattr; the loop will become O(N^2) as soon as any
1343 : : * noncacheable attribute offsets are involved.
1344 : : */
1345 : : void
7669 1346 : 2769086 : heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
1347 : : Datum *values, bool *isnull)
1348 : : {
1349 : 2769086 : HeapTupleHeader tup = tuple->t_data;
1350 : 2769086 : bool hasnulls = HeapTupleHasNulls(tuple);
1351 : 2769086 : int tdesc_natts = tupleDesc->natts;
1352 : : int natts; /* number of atts to extract */
1353 : : int attnum;
1354 : : char *tp; /* ptr to tuple data */
1355 : : uint32 off; /* offset in tuple data */
3189 1356 : 2769086 : bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
7669 1357 : 2769086 : bool slow = false; /* can we use/set attcacheoff? */
1358 : :
7005 bruce@momjian.us 1359 : 2769086 : natts = HeapTupleHeaderGetNatts(tup);
1360 : :
1361 : : /*
1362 : : * In inheritance situations, it is possible that the given tuple actually
1363 : : * has more fields than the caller is expecting. Don't run off the end of
1364 : : * the caller's arrays.
1365 : : */
7669 tgl@sss.pgh.pa.us 1366 : 2769086 : natts = Min(natts, tdesc_natts);
1367 : :
1368 : 2769086 : tp = (char *) tup + tup->t_hoff;
1369 : :
1370 : 2769086 : off = 0;
1371 : :
1372 [ + + ]: 10830001 : for (attnum = 0; attnum < natts; attnum++)
1373 : : {
450 drowley@postgresql.o 1374 : 8060915 : CompactAttribute *thisatt = TupleDescCompactAttr(tupleDesc, attnum);
1375 : :
7669 tgl@sss.pgh.pa.us 1376 [ + + + + ]: 8060915 : if (hasnulls && att_isnull(attnum, bp))
1377 : : {
1378 : 539382 : values[attnum] = (Datum) 0;
1379 : 539382 : isnull[attnum] = true;
1380 : 539382 : slow = true; /* can't use attcacheoff anymore */
1381 : 539382 : continue;
1382 : : }
1383 : :
1384 : 7521533 : isnull[attnum] = false;
1385 : :
1386 [ + + + + ]: 7521533 : if (!slow && thisatt->attcacheoff >= 0)
1387 : 7096342 : off = thisatt->attcacheoff;
6918 1388 [ + + ]: 425191 : else if (thisatt->attlen == -1)
1389 : : {
1390 : : /*
1391 : : * We can only cache the offset for a varlena attribute if the
1392 : : * offset is already suitably aligned, so that there would be no
1393 : : * pad bytes in any case: then the offset will be valid for either
1394 : : * an aligned or unaligned value.
1395 : : */
1396 [ + + ]: 201166 : if (!slow &&
449 drowley@postgresql.o 1397 [ + + ]: 36209 : off == att_nominal_alignby(off, thisatt->attalignby))
6918 tgl@sss.pgh.pa.us 1398 : 24625 : thisatt->attcacheoff = off;
1399 : : else
1400 : : {
449 drowley@postgresql.o 1401 [ + + ]: 176541 : off = att_pointer_alignby(off, thisatt->attalignby, -1,
1402 : : tp + off);
6918 tgl@sss.pgh.pa.us 1403 : 176541 : slow = true;
1404 : : }
1405 : : }
1406 : : else
1407 : : {
1408 : : /* not varlena, so safe to use att_nominal_alignby */
449 drowley@postgresql.o 1409 : 224025 : off = att_nominal_alignby(off, thisatt->attalignby);
1410 : :
7669 tgl@sss.pgh.pa.us 1411 [ + + ]: 224025 : if (!slow)
1412 : 49771 : thisatt->attcacheoff = off;
1413 : : }
1414 : :
1415 : 7521533 : values[attnum] = fetchatt(thisatt, tp + off);
1416 : :
6918 1417 [ + + + + : 7521533 : off = att_addlength_pointer(off, thisatt->attlen, tp + off);
- + + + +
- - + + +
- + ]
1418 : :
7669 1419 [ + + ]: 7521533 : if (thisatt->attlen <= 0)
1420 : 1304011 : slow = true; /* can't use attcacheoff anymore */
1421 : : }
1422 : :
1423 : : /*
1424 : : * If tuple doesn't have all the atts indicated by tupleDesc, read the
1425 : : * rest as nulls or missing values as appropriate.
1426 : : */
1427 [ + + ]: 2769098 : for (; attnum < tdesc_natts; attnum++)
2909 andrew@dunslane.net 1428 : 12 : values[attnum] = getmissingattr(tupleDesc, attnum + 1, &isnull[attnum]);
7669 tgl@sss.pgh.pa.us 1429 : 2769086 : }
1430 : :
1431 : : /*
1432 : : * heap_freetuple
1433 : : */
1434 : : void
9586 JanWieck@Yahoo.com 1435 : 11685607 : heap_freetuple(HeapTuple htup)
1436 : : {
1437 : 11685607 : pfree(htup);
1438 : 11685607 : }
1439 : :
1440 : :
1441 : : /*
1442 : : * heap_form_minimal_tuple
1443 : : * construct a MinimalTuple from the given values[] and isnull[] arrays,
1444 : : * which are of the length indicated by tupleDescriptor->natts
1445 : : *
1446 : : * This is exactly like heap_form_tuple() except that the result is a
1447 : : * "minimal" tuple lacking a HeapTupleData header as well as room for system
1448 : : * columns.
1449 : : *
1450 : : * The result is allocated in the current memory context.
1451 : : */
1452 : : MinimalTuple
7201 tgl@sss.pgh.pa.us 1453 : 25275252 : heap_form_minimal_tuple(TupleDesc tupleDescriptor,
1454 : : const Datum *values,
1455 : : const bool *isnull,
1456 : : Size extra)
1457 : : {
1458 : : MinimalTuple tuple; /* return tuple */
1459 : : char *mem;
1460 : : Size len,
1461 : : data_len;
1462 : : int hoff;
1463 : 25275252 : bool hasnull = false;
1464 : 25275252 : int numberOfAttributes = tupleDescriptor->natts;
1465 : : int i;
1466 : :
356 jdavis@postgresql.or 1467 [ - + ]: 25275252 : Assert(extra == MAXALIGN(extra));
1468 : :
7201 tgl@sss.pgh.pa.us 1469 [ - + ]: 25275252 : if (numberOfAttributes > MaxTupleAttributeNumber)
7201 tgl@sss.pgh.pa.us 1470 [ # # ]:UBC 0 : ereport(ERROR,
1471 : : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1472 : : errmsg("number of columns (%d) exceeds limit (%d)",
1473 : : numberOfAttributes, MaxTupleAttributeNumber)));
1474 : :
1475 : : /*
1476 : : * Check for nulls
1477 : : */
7201 tgl@sss.pgh.pa.us 1478 [ + + ]:CBC 72518797 : for (i = 0; i < numberOfAttributes; i++)
1479 : : {
1480 [ + + ]: 47577879 : if (isnull[i])
1481 : : {
4336 1482 : 334334 : hasnull = true;
1483 : 334334 : break;
1484 : : }
1485 : : }
1486 : :
1487 : : /*
1488 : : * Determine total space needed
1489 : : */
4040 1490 : 25275252 : len = SizeofMinimalTupleHeader;
1491 : :
7201 1492 [ + + ]: 25275252 : if (hasnull)
1493 : 334334 : len += BITMAPLEN(numberOfAttributes);
1494 : :
1495 : 25275252 : hoff = len = MAXALIGN(len); /* align user data safely */
1496 : :
6918 1497 : 25275252 : data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
1498 : :
1499 : 25275252 : len += data_len;
1500 : :
1501 : : /*
1502 : : * Allocate and zero the space needed.
1503 : : */
356 jdavis@postgresql.or 1504 : 25275252 : mem = palloc0(len + extra);
1505 : 25275252 : tuple = (MinimalTuple) (mem + extra);
1506 : :
1507 : : /*
1508 : : * And fill in the information.
1509 : : */
7201 tgl@sss.pgh.pa.us 1510 : 25275252 : tuple->t_len = len;
7005 bruce@momjian.us 1511 : 25275252 : HeapTupleHeaderSetNatts(tuple, numberOfAttributes);
7201 tgl@sss.pgh.pa.us 1512 : 25275252 : tuple->t_hoff = hoff + MINIMAL_TUPLE_OFFSET;
1513 : :
1514 [ + + ]: 25275252 : heap_fill_tuple(tupleDescriptor,
1515 : : values,
1516 : : isnull,
1517 : : (char *) tuple + hoff,
1518 : : data_len,
1519 : : &tuple->t_infomask,
1520 : : (hasnull ? tuple->t_bits : NULL));
1521 : :
1522 : 25275252 : return tuple;
1523 : : }
1524 : :
1525 : : /*
1526 : : * heap_free_minimal_tuple
1527 : : */
1528 : : void
1529 : 20909990 : heap_free_minimal_tuple(MinimalTuple mtup)
1530 : : {
1531 : 20909990 : pfree(mtup);
1532 : 20909990 : }
1533 : :
1534 : : /*
1535 : : * heap_copy_minimal_tuple
1536 : : * copy a MinimalTuple
1537 : : *
1538 : : * The result is allocated in the current memory context.
1539 : : */
1540 : : MinimalTuple
356 jdavis@postgresql.or 1541 : 2507028 : heap_copy_minimal_tuple(MinimalTuple mtup, Size extra)
1542 : : {
1543 : : MinimalTuple result;
1544 : : char *mem;
1545 : :
1546 [ - + ]: 2507028 : Assert(extra == MAXALIGN(extra));
1547 : 2507028 : mem = palloc(mtup->t_len + extra);
1548 : 2507028 : memset(mem, 0, extra);
1549 : 2507028 : result = (MinimalTuple) (mem + extra);
7201 tgl@sss.pgh.pa.us 1550 : 2507028 : memcpy(result, mtup, mtup->t_len);
1551 : 2507028 : return result;
1552 : : }
1553 : :
1554 : : /*
1555 : : * heap_tuple_from_minimal_tuple
1556 : : * create a HeapTuple by copying from a MinimalTuple;
1557 : : * system columns are filled with zeroes
1558 : : *
1559 : : * The result is allocated in the current memory context.
1560 : : * The HeapTuple struct, tuple header, and tuple data are all allocated
1561 : : * as a single palloc() block.
1562 : : */
1563 : : HeapTuple
1564 : 547779 : heap_tuple_from_minimal_tuple(MinimalTuple mtup)
1565 : : {
1566 : : HeapTuple result;
1567 : 547779 : uint32 len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
1568 : :
1569 : 547779 : result = (HeapTuple) palloc(HEAPTUPLESIZE + len);
1570 : 547779 : result->t_len = len;
1571 : 547779 : ItemPointerSetInvalid(&(result->t_self));
1572 : 547779 : result->t_tableOid = InvalidOid;
1573 : 547779 : result->t_data = (HeapTupleHeader) ((char *) result + HEAPTUPLESIZE);
1574 : 547779 : memcpy((char *) result->t_data + MINIMAL_TUPLE_OFFSET, mtup, mtup->t_len);
7005 bruce@momjian.us 1575 : 547779 : memset(result->t_data, 0, offsetof(HeapTupleHeaderData, t_infomask2));
7201 tgl@sss.pgh.pa.us 1576 : 547779 : return result;
1577 : : }
1578 : :
1579 : : /*
1580 : : * minimal_tuple_from_heap_tuple
1581 : : * create a MinimalTuple by copying from a HeapTuple
1582 : : *
1583 : : * The result is allocated in the current memory context.
1584 : : */
1585 : : MinimalTuple
356 jdavis@postgresql.or 1586 : 2555099 : minimal_tuple_from_heap_tuple(HeapTuple htup, Size extra)
1587 : : {
1588 : : MinimalTuple result;
1589 : : char *mem;
1590 : : uint32 len;
1591 : :
1592 [ - + ]: 2555099 : Assert(extra == MAXALIGN(extra));
7201 tgl@sss.pgh.pa.us 1593 [ - + ]: 2555099 : Assert(htup->t_len > MINIMAL_TUPLE_OFFSET);
1594 : 2555099 : len = htup->t_len - MINIMAL_TUPLE_OFFSET;
356 jdavis@postgresql.or 1595 : 2555099 : mem = palloc(len + extra);
1596 : 2555099 : memset(mem, 0, extra);
1597 : 2555099 : result = (MinimalTuple) (mem + extra);
7201 tgl@sss.pgh.pa.us 1598 : 2555099 : memcpy(result, (char *) htup->t_data + MINIMAL_TUPLE_OFFSET, len);
1599 : :
1600 : 2555099 : result->t_len = len;
1601 : 2555099 : return result;
1602 : : }
1603 : :
1604 : : /*
1605 : : * This mainly exists so JIT can inline the definition, but it's also
1606 : : * sometimes useful in debugging sessions.
1607 : : */
1608 : : size_t
2911 andres@anarazel.de 1609 :UBC 0 : varsize_any(void *p)
1610 : : {
1611 [ # # # # : 0 : return VARSIZE_ANY(p);
# # # # #
# ]
1612 : : }
|