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
987 andrew@dunslane.net 104 :UBC 0 : missing_hash(const void *key, Size keysize)
105 : : {
113 peter@eisentraut.org 106 :UNC 0 : const missing_cache_key *entry = key;
107 : :
270 108 : 0 : return hash_bytes((const unsigned char *) DatumGetPointer(entry->value), entry->len);
109 : : }
110 : :
111 : : static int
987 andrew@dunslane.net 112 :UBC 0 : missing_match(const void *key1, const void *key2, Size keysize)
113 : : {
113 peter@eisentraut.org 114 :UNC 0 : const missing_cache_key *entry1 = key1;
115 : 0 : const missing_cache_key *entry2 = key2;
116 : :
987 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
153 nathan@postgresql.or 126 :UNC 0 : init_missing_cache(void)
127 : : {
128 : : HASHCTL hash_ctl;
129 : :
987 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
2960 andrew@dunslane.net 151 :CBC 233 : getmissingattr(TupleDesc tupleDesc,
152 : : int attnum, bool *isnull)
153 : : {
154 : : CompactAttribute *att;
155 : :
156 [ - + ]: 233 : Assert(attnum <= tupleDesc->natts);
157 [ - + ]: 233 : Assert(attnum > 0);
158 : :
501 drowley@postgresql.o 159 : 233 : att = TupleDescCompactAttr(tupleDesc, attnum - 1);
160 : :
2960 andrew@dunslane.net 161 [ + + ]: 233 : if (att->atthasmissing)
162 : : {
163 : : AttrMissing *attrmiss;
164 : :
165 [ - + ]: 36 : Assert(tupleDesc->constr);
166 [ - + ]: 36 : Assert(tupleDesc->constr->missing);
167 : :
168 : 36 : attrmiss = tupleDesc->constr->missing + (attnum - 1);
169 : :
2869 akapila@postgresql.o 170 [ + - ]: 36 : if (attrmiss->am_present)
171 : : {
172 : : missing_cache_key key;
173 : : missing_cache_key *entry;
174 : : bool found;
175 : : MemoryContext oldctx;
176 : :
2960 andrew@dunslane.net 177 : 36 : *isnull = false;
178 : :
179 : : /* no need to cache by-value attributes */
987 180 [ + - ]: 36 : if (att->attbyval)
181 : 36 : return attrmiss->am_value;
182 : :
183 : : /* set up cache if required */
987 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
273 peter@eisentraut.org 192 :UNC 0 : key.len = VARSIZE_ANY(DatumGetPointer(attrmiss->am_value));
987 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 : :
2960 andrew@dunslane.net 210 :CBC 197 : *isnull = true;
211 : 197 : 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
7720 tgl@sss.pgh.pa.us 219 : 80928815 : heap_compute_data_size(TupleDesc tupleDesc,
220 : : const Datum *values,
221 : : const bool *isnull)
222 : : {
223 : 80928815 : Size data_length = 0;
224 : : int i;
225 : 80928815 : int numberOfAttributes = tupleDesc->natts;
226 : :
227 [ + + ]: 274306316 : for (i = 0; i < numberOfAttributes; i++)
228 : : {
229 : : Datum val;
230 : : CompactAttribute *atti;
231 : :
232 [ + + ]: 193377501 : if (isnull[i])
233 : 16278869 : continue;
234 : :
6969 235 : 177098632 : val = values[i];
501 drowley@postgresql.o 236 : 177098632 : atti = TupleDescCompactAttr(tupleDesc, i);
237 : :
238 [ + + + + : 199498990 : if (COMPACT_ATTR_IS_PACKABLE(atti) &&
+ + ]
6969 tgl@sss.pgh.pa.us 239 [ + + + + ]: 22400358 : 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 : 16983259 : data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
246 : : }
4009 247 [ + + + + ]: 166675866 : else if (atti->attlen == -1 &&
248 [ + + + + ]: 6560493 : 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 : : */
500 drowley@postgresql.o 254 : 1855 : data_length = att_nominal_alignby(data_length, atti->attalignby);
4009 tgl@sss.pgh.pa.us 255 : 1855 : data_length += EOH_get_flat_size(DatumGetEOHP(val));
256 : : }
257 : : else
258 : : {
500 drowley@postgresql.o 259 [ + + + + ]: 160113518 : data_length = att_datum_alignby(data_length, atti->attalignby,
260 : : atti->attlen, val);
4009 tgl@sss.pgh.pa.us 261 [ + + + + : 160113518 : data_length = att_addlength_datum(data_length, atti->attlen,
- + + + +
- - + + +
- + ]
262 : : val);
263 : : }
264 : : }
265 : :
7720 266 : 80928815 : 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
501 drowley@postgresql.o 275 : 178851592 : fill_val(CompactAttribute *att,
276 : : uint8 **bit,
277 : : int *bitmask,
278 : : char **dataP,
279 : : uint16 *infomask,
280 : : Datum datum,
281 : : bool isnull)
282 : : {
283 : : Size data_length;
2960 andrew@dunslane.net 284 : 178851592 : 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 [ + + ]: 178851592 : if (bit != NULL)
291 : : {
292 [ + + ]: 60827009 : if (*bitmask != HIGHBIT)
293 : 50491081 : *bitmask <<= 1;
294 : : else
295 : : {
296 : 10335928 : *bit += 1;
297 : 10335928 : **bit = 0x0;
298 : 10335928 : *bitmask = 1;
299 : : }
300 : :
301 [ + + ]: 60827009 : if (isnull)
302 : : {
303 : 16118113 : *infomask |= HEAP_HASNULL;
304 : 16118113 : return;
305 : : }
306 : :
307 : 44708896 : **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 [ + + ]: 162733479 : if (att->attbyval)
315 : : {
316 : : /* pass-by-value */
500 drowley@postgresql.o 317 : 123780595 : data = (char *) att_nominal_alignby(data, att->attalignby);
2960 andrew@dunslane.net 318 : 123780595 : store_att_byval(data, datum, att->attlen);
319 : 123780595 : data_length = att->attlen;
320 : : }
321 [ + + ]: 38952884 : else if (att->attlen == -1)
322 : : {
323 : : /* varlena */
324 : 22508548 : Pointer val = DatumGetPointer(datum);
325 : :
326 : 22508548 : *infomask |= HEAP_HASVARWIDTH;
327 [ + + ]: 22508548 : if (VARATT_IS_EXTERNAL(val))
328 : : {
329 [ + + + + ]: 16307 : if (VARATT_IS_EXTERNAL_EXPANDED(val))
2960 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 : : */
2960 andrew@dunslane.net 335 :CBC 1855 : ExpandedObjectHeader *eoh = DatumGetEOHP(datum);
336 : :
500 drowley@postgresql.o 337 : 1855 : data = (char *) att_nominal_alignby(data, att->attalignby);
2960 andrew@dunslane.net 338 : 1855 : data_length = EOH_get_flat_size(eoh);
339 : 1855 : EOH_flatten_into(eoh, data, data_length);
340 : : }
341 : : else
342 : : {
343 : 14452 : *infomask |= HEAP_HASEXTERNAL;
344 : : /* no alignment, since it's short by definition */
345 [ + + + - : 14452 : data_length = VARSIZE_EXTERNAL(val);
- + ]
346 : 14452 : memcpy(data, val, data_length);
347 : : }
348 : : }
349 [ + + ]: 22492241 : else if (VARATT_IS_SHORT(val))
350 : : {
351 : : /* no alignment for short varlenas */
352 : 4264456 : data_length = VARSIZE_SHORT(val);
353 : 4264456 : memcpy(data, val, data_length);
354 : : }
501 drowley@postgresql.o 355 [ + + + + : 18227785 : else if (att->attispackable && VARATT_CAN_MAKE_SHORT(val))
+ + ]
356 : : {
357 : : /* convert to short varlena -- no alignment */
2960 andrew@dunslane.net 358 : 16368849 : data_length = VARATT_CONVERTED_SHORT_SIZE(val);
359 : 16368849 : SET_VARSIZE_SHORT(data, data_length);
360 : 16368849 : memcpy(data + 1, VARDATA(val), data_length - 1);
361 : : }
362 : : else
363 : : {
364 : : /* full 4-byte header varlena */
500 drowley@postgresql.o 365 : 1858936 : data = (char *) att_nominal_alignby(data, att->attalignby);
2960 andrew@dunslane.net 366 : 1858936 : data_length = VARSIZE(val);
367 : 1858936 : memcpy(data, val, data_length);
368 : : }
369 : : }
370 [ + + ]: 16444336 : else if (att->attlen == -2)
371 : : {
372 : : /* cstring ... never needs alignment */
373 : 1591731 : *infomask |= HEAP_HASVARWIDTH;
500 drowley@postgresql.o 374 [ - + ]: 1591731 : Assert(att->attalignby == sizeof(char));
2960 andrew@dunslane.net 375 : 1591731 : data_length = strlen(DatumGetCString(datum)) + 1;
376 : 1591731 : memcpy(data, DatumGetPointer(datum), data_length);
377 : : }
378 : : else
379 : : {
380 : : /* fixed-length pass-by-reference */
500 drowley@postgresql.o 381 : 14852605 : data = (char *) att_nominal_alignby(data, att->attalignby);
2960 andrew@dunslane.net 382 [ - + ]: 14852605 : Assert(att->attlen > 0);
383 : 14852605 : data_length = att->attlen;
384 : 14852605 : memcpy(data, DatumGetPointer(datum), data_length);
385 : : }
386 : :
387 : 162733479 : data += data_length;
388 : 162733479 : *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
7720 tgl@sss.pgh.pa.us 401 : 67827946 : heap_fill_tuple(TupleDesc tupleDesc,
402 : : const Datum *values, const bool *isnull,
403 : : char *data, Size data_size,
404 : : uint16 *infomask, uint8 *bit)
405 : : {
406 : : uint8 *bitP;
407 : : int bitmask;
408 : : int i;
409 : 67827946 : int numberOfAttributes = tupleDesc->natts;
410 : :
411 : : #ifdef USE_ASSERT_CHECKING
6969 412 : 67827946 : char *start = data;
413 : : #endif
414 : :
7720 415 [ + + ]: 67827946 : if (bit != NULL)
416 : : {
417 : 4289675 : bitP = &bit[-1];
7436 bruce@momjian.us 418 : 4289675 : bitmask = HIGHBIT;
419 : : }
420 : : else
421 : : {
422 : : /* just to keep compiler quiet */
7720 tgl@sss.pgh.pa.us 423 : 63538271 : bitP = NULL;
424 : 63538271 : bitmask = 0;
425 : : }
426 : :
6969 427 : 67827946 : *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL);
428 : :
7720 429 [ + + ]: 246679538 : for (i = 0; i < numberOfAttributes; i++)
430 : : {
501 drowley@postgresql.o 431 : 178851592 : CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, i);
432 : :
2960 andrew@dunslane.net 433 [ + + ]: 357703184 : fill_val(attr,
434 [ + + ]: 178851592 : bitP ? &bitP : NULL,
435 : : &bitmask,
436 : : &data,
437 : : infomask,
438 : 178851279 : values ? values[i] : PointerGetDatum(NULL),
439 [ + - + + ]: 178851592 : isnull ? isnull[i] : true);
440 : : }
441 : :
6969 tgl@sss.pgh.pa.us 442 [ - + ]: 67827946 : Assert((data - start) == data_size);
7720 443 : 67827946 : }
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
2960 andrew@dunslane.net 456 : 6734344 : 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 [ + + - + ]: 6734344 : Assert(!tupleDesc || attnum <= tupleDesc->natts);
7056 bruce@momjian.us 463 [ - + ]: 6734344 : if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data))
464 : : {
501 drowley@postgresql.o 465 [ # # # # ]:UBC 0 : if (tupleDesc &&
466 [ # # ]: 0 : TupleDescCompactAttr(tupleDesc, attnum - 1)->atthasmissing)
2960 andrew@dunslane.net 467 : 0 : return false;
468 : : else
469 : 0 : return true;
470 : : }
471 : :
10467 bruce@momjian.us 472 [ + - ]:CBC 6734344 : if (attnum > 0)
473 : : {
8005 tgl@sss.pgh.pa.us 474 [ + + ]: 6734344 : if (HeapTupleNoNulls(tup))
7720 475 : 1978 : return false;
10021 vadim4o@yahoo.com 476 : 6732366 : return att_isnull(attnum - 1, tup->t_data->t_bits);
477 : : }
478 : :
8005 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 : :
7720 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 the attcacheoff and the value is not null.
502 : : *
503 : : * NOTE: if you need to change this code, see also heap_deform_tuple.
504 : : * Also see nocache_index_getattr, which is the same code for index
505 : : * tuples.
506 : : * ----------------
507 : : */
508 : : Datum
1324 pg@bowt.ie 509 :CBC 144884288 : nocachegetattr(HeapTuple tup,
510 : : int attnum,
511 : : TupleDesc tupleDesc)
512 : : {
513 : : CompactAttribute *cattr;
514 : 144884288 : HeapTupleHeader td = tup->t_data;
515 : : char *tp; /* ptr to data part of tuple */
36 nathan@postgresql.or 516 :GNC 144884288 : uint8 *bp = td->t_bits; /* ptr to null bitmap in tuple */
517 : : int off; /* current offset within data */
518 : : int startAttr;
519 : : int firstNullAttr;
520 : : int i;
50 drowley@postgresql.o 521 : 144884288 : bool hasnulls = HeapTupleHasNulls(tup);
522 : :
523 : : /* Did someone forget to call TupleDescFinalize()? */
524 [ - + ]: 144884288 : Assert(tupleDesc->firstNonCachedOffsetAttr >= 0);
525 : :
6969 tgl@sss.pgh.pa.us 526 :CBC 144884288 : attnum--;
527 : :
528 : : /*
529 : : * To minimize the number of attributes we need to look at, start walking
530 : : * the tuple at the attribute with the highest attcacheoff prior to attnum
531 : : * or the first NULL attribute prior to attnum, whichever comes first.
532 : : */
50 drowley@postgresql.o 533 [ + + ]:GNC 144884288 : if (hasnulls)
534 : 102477920 : firstNullAttr = first_null_attr(bp, attnum);
535 : : else
536 : 42406368 : firstNullAttr = attnum;
537 : :
49 538 [ + + + + ]: 144884288 : if (tupleDesc->firstNonCachedOffsetAttr > 0 && firstNullAttr > 0)
539 : : {
540 : : /*
541 : : * Try to start with the highest attribute with an attcacheoff that's
542 : : * prior to the one we're looking for, or with the attribute prior to
543 : : * the first NULL attribute, if there is one.
544 : : */
545 : 50522907 : startAttr = Min(tupleDesc->firstNonCachedOffsetAttr - 1, firstNullAttr - 1);
50 546 : 50522907 : off = TupleDescCompactAttr(tupleDesc, startAttr)->attcacheoff;
547 : : }
548 : : else
549 : : {
550 : : /* Otherwise, start at the beginning... */
551 : 94361381 : startAttr = 0;
552 : 94361381 : off = 0;
553 : : }
554 : :
1324 pg@bowt.ie 555 :CBC 144884288 : tp = (char *) td + td->t_hoff;
556 : :
557 : : /*
558 : : * Calculate 'off' up to the first NULL attr. We use two cheaper loops
559 : : * when the tuple has no variable-width columns. When variable-width
560 : : * columns exists, we use att_addlength_pointer() to move the offset
561 : : * beyond the current attribute.
562 : : */
50 drowley@postgresql.o 563 [ + + ]:GNC 144884288 : if (!HeapTupleHasVarWidth(tup))
564 : : {
565 [ + + ]: 98493595 : for (i = startAttr; i < firstNullAttr; i++)
566 : : {
567 : 24923699 : cattr = TupleDescCompactAttr(tupleDesc, i);
568 : :
569 : 24923699 : off = att_nominal_alignby(off, cattr->attalignby);
570 : 24923699 : off += cattr->attlen;
571 : : }
572 : :
573 [ + + ]: 78789256 : for (; i < attnum; i++)
574 : : {
575 [ + + ]: 5219360 : if (att_isnull(i, bp))
576 : 4274409 : continue;
577 : :
578 : 944951 : cattr = TupleDescCompactAttr(tupleDesc, i);
579 : :
580 : 944951 : off = att_nominal_alignby(off, cattr->attalignby);
581 : 944951 : off += cattr->attlen;
582 : : }
583 : : }
584 : : else
585 : : {
586 [ + + ]: 179756141 : for (i = startAttr; i < firstNullAttr; i++)
10467 bruce@momjian.us 587 :ECB (122064031) : {
588 : : int attlen;
589 : :
50 drowley@postgresql.o 590 :GNC 108441749 : cattr = TupleDescCompactAttr(tupleDesc, i);
591 : 108441749 : attlen = cattr->attlen;
592 [ + + + + ]: 108441749 : off = att_pointer_alignby(off,
593 : : cattr->attalignby,
594 : : attlen,
595 : : tp + off);
596 [ + + + - : 108441749 : off = att_addlength_pointer(off, attlen, tp + off);
- - ]
597 : : }
598 : :
599 [ + + ]: 82586282 : for (; i < attnum; i++)
600 : : {
601 : : int attlen;
602 : :
603 [ + + ]: 11271890 : if (att_isnull(i, bp))
604 : 8372495 : continue;
605 : :
606 : 2899395 : cattr = TupleDescCompactAttr(tupleDesc, i);
607 : 2899395 : attlen = cattr->attlen;
608 [ + + + + ]: 2899395 : off = att_pointer_alignby(off, cattr->attalignby, attlen,
609 : : tp + off);
610 [ + + + - : 2899395 : off = att_addlength_pointer(off, attlen, tp + off);
- - ]
611 : : }
612 : : }
613 : :
614 : 144884288 : cattr = TupleDescCompactAttr(tupleDesc, attnum);
615 [ + + + + ]: 144884288 : off = att_pointer_alignby(off,
616 : : cattr->attalignby,
617 : : cattr->attlen,
618 : : tp + off);
619 : :
620 : 144884288 : return fetchatt(cattr, tp + off);
621 : : }
622 : :
623 : : /* ----------------
624 : : * heap_getsysattr
625 : : *
626 : : * Fetch the value of a system attribute for a tuple.
627 : : *
628 : : * This is a support routine for heap_getattr(). The function has already
629 : : * determined that the attnum refers to a system attribute.
630 : : * ----------------
631 : : */
632 : : Datum
8069 tgl@sss.pgh.pa.us 633 :CBC 72787 : heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
634 : : {
635 : : Datum result;
636 : :
9260 637 [ - + ]: 72787 : Assert(tup);
638 : :
639 : : /* Currently, no sys attribute ever reads as NULL. */
5959 rhaas@postgresql.org 640 : 72787 : *isnull = false;
641 : :
9260 tgl@sss.pgh.pa.us 642 [ + + + + : 72787 : switch (attnum)
+ - ]
643 : : {
644 : 2 : case SelfItemPointerAttributeNumber:
645 : : /* pass-by-reference datatype */
646 : 2 : result = PointerGetDatum(&(tup->t_self));
647 : 2 : break;
648 : 72540 : case MinTransactionIdAttributeNumber:
4517 rhaas@postgresql.org 649 : 72540 : result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmin(tup->t_data));
9260 tgl@sss.pgh.pa.us 650 : 72540 : break;
651 : 119 : case MaxTransactionIdAttributeNumber:
4850 alvherre@alvh.no-ip. 652 : 119 : result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmax(tup->t_data));
9260 tgl@sss.pgh.pa.us 653 : 119 : break;
7025 654 : 124 : case MinCommandIdAttributeNumber:
655 : : case MaxCommandIdAttributeNumber:
656 : :
657 : : /*
658 : : * cmin and cmax are now both aliases for the same field, which
659 : : * can in fact also be a combo command id. XXX perhaps we should
660 : : * return the "real" cmin or cmax if possible, that is if we are
661 : : * inside the originating transaction?
662 : : */
663 : 124 : result = CommandIdGetDatum(HeapTupleHeaderGetRawCommandId(tup->t_data));
9260 664 : 124 : break;
665 : 2 : case TableOidAttributeNumber:
666 : 2 : result = ObjectIdGetDatum(tup->t_tableOid);
667 : 2 : break;
9260 tgl@sss.pgh.pa.us 668 :UBC 0 : default:
8324 669 [ # # ]: 0 : elog(ERROR, "invalid attnum: %d", attnum);
670 : : result = 0; /* keep compiler quiet */
671 : : break;
672 : : }
9260 tgl@sss.pgh.pa.us 673 :CBC 72787 : return result;
674 : : }
675 : :
676 : : /* ----------------
677 : : * heap_copytuple
678 : : *
679 : : * returns a copy of an entire tuple
680 : : *
681 : : * The HeapTuple struct, tuple header, and tuple data are all allocated
682 : : * as a single palloc() block.
683 : : * ----------------
684 : : */
685 : : HeapTuple
10892 scrappy@hub.org 686 : 11104224 : heap_copytuple(HeapTuple tuple)
687 : : {
688 : : HeapTuple newTuple;
689 : :
10021 vadim4o@yahoo.com 690 [ + - - + ]: 11104224 : if (!HeapTupleIsValid(tuple) || tuple->t_data == NULL)
10108 bruce@momjian.us 691 :UBC 0 : return NULL;
692 : :
10021 vadim4o@yahoo.com 693 :CBC 11104224 : newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + tuple->t_len);
694 : 11104224 : newTuple->t_len = tuple->t_len;
695 : 11104224 : newTuple->t_self = tuple->t_self;
9303 tgl@sss.pgh.pa.us 696 : 11104224 : newTuple->t_tableOid = tuple->t_tableOid;
10021 vadim4o@yahoo.com 697 : 11104224 : newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
447 peter@eisentraut.org 698 : 11104224 : memcpy(newTuple->t_data, tuple->t_data, tuple->t_len);
10108 bruce@momjian.us 699 : 11104224 : return newTuple;
700 : : }
701 : :
702 : : /* ----------------
703 : : * heap_copytuple_with_tuple
704 : : *
705 : : * copy a tuple into a caller-supplied HeapTuple management struct
706 : : *
707 : : * Note that after calling this function, the "dest" HeapTuple will not be
708 : : * allocated as a single palloc() block (unlike with heap_copytuple()).
709 : : * ----------------
710 : : */
711 : : void
10021 vadim4o@yahoo.com 712 :UBC 0 : heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest)
713 : : {
714 [ # # # # ]: 0 : if (!HeapTupleIsValid(src) || src->t_data == NULL)
715 : : {
716 : 0 : dest->t_data = NULL;
717 : 0 : return;
718 : : }
719 : :
720 : 0 : dest->t_len = src->t_len;
721 : 0 : dest->t_self = src->t_self;
9303 tgl@sss.pgh.pa.us 722 : 0 : dest->t_tableOid = src->t_tableOid;
10021 vadim4o@yahoo.com 723 : 0 : dest->t_data = (HeapTupleHeader) palloc(src->t_len);
447 peter@eisentraut.org 724 : 0 : memcpy(dest->t_data, src->t_data, src->t_len);
725 : : }
726 : :
727 : : /*
728 : : * Expand a tuple which has fewer attributes than required. For each attribute
729 : : * not present in the sourceTuple, if there is a missing value that will be
730 : : * used. Otherwise the attribute will be set to NULL.
731 : : *
732 : : * The source tuple must have fewer attributes than the required number.
733 : : *
734 : : * Only one of targetHeapTuple and targetMinimalTuple may be supplied. The
735 : : * other argument must be NULL.
736 : : */
737 : : static void
2960 andrew@dunslane.net 738 : 0 : expand_tuple(HeapTuple *targetHeapTuple,
739 : : MinimalTuple *targetMinimalTuple,
740 : : HeapTuple sourceTuple,
741 : : TupleDesc tupleDesc)
742 : : {
743 : 0 : AttrMissing *attrmiss = NULL;
744 : : int attnum;
745 : : int firstmissingnum;
746 : 0 : bool hasNulls = HeapTupleHasNulls(sourceTuple);
747 : : HeapTupleHeader targetTHeader;
748 : 0 : HeapTupleHeader sourceTHeader = sourceTuple->t_data;
749 : 0 : int sourceNatts = HeapTupleHeaderGetNatts(sourceTHeader);
750 : 0 : int natts = tupleDesc->natts;
751 : : int sourceNullLen;
752 : : int targetNullLen;
753 : 0 : Size sourceDataLen = sourceTuple->t_len - sourceTHeader->t_hoff;
754 : : Size targetDataLen;
755 : : Size len;
756 : : int hoff;
36 nathan@postgresql.or 757 :UNC 0 : uint8 *nullBits = NULL;
2960 andrew@dunslane.net 758 :UBC 0 : int bitMask = 0;
759 : : char *targetData;
760 : : uint16 *infoMask;
761 : :
762 [ # # # # : 0 : Assert((targetHeapTuple && !targetMinimalTuple)
# # # # ]
763 : : || (!targetHeapTuple && targetMinimalTuple));
764 : :
765 [ # # ]: 0 : Assert(sourceNatts < natts);
766 : :
767 [ # # ]: 0 : sourceNullLen = (hasNulls ? BITMAPLEN(sourceNatts) : 0);
768 : :
769 : 0 : targetDataLen = sourceDataLen;
770 : :
771 [ # # ]: 0 : if (tupleDesc->constr &&
772 [ # # ]: 0 : tupleDesc->constr->missing)
773 : : {
774 : : /*
775 : : * If there are missing values we want to put them into the tuple.
776 : : * Before that we have to compute the extra length for the values
777 : : * array and the variable length data.
778 : : */
779 : 0 : attrmiss = tupleDesc->constr->missing;
780 : :
781 : : /*
782 : : * Find the first item in attrmiss for which we don't have a value in
783 : : * the source. We can ignore all the missing entries before that.
784 : : */
785 : 0 : for (firstmissingnum = sourceNatts;
786 [ # # ]: 0 : firstmissingnum < natts;
787 : 0 : firstmissingnum++)
788 : : {
2869 akapila@postgresql.o 789 [ # # ]: 0 : if (attrmiss[firstmissingnum].am_present)
2960 andrew@dunslane.net 790 : 0 : break;
791 : : else
2780 792 : 0 : hasNulls = true;
793 : : }
794 : :
795 : : /*
796 : : * Now walk the missing attributes. If there is a missing value make
797 : : * space for it. Otherwise, it's going to be NULL.
798 : : */
799 : 0 : for (attnum = firstmissingnum;
800 [ # # ]: 0 : attnum < natts;
801 : 0 : attnum++)
802 : : {
803 [ # # ]: 0 : if (attrmiss[attnum].am_present)
804 : : {
501 drowley@postgresql.o 805 : 0 : CompactAttribute *att = TupleDescCompactAttr(tupleDesc, attnum);
806 : :
500 807 [ # # # # ]: 0 : targetDataLen = att_datum_alignby(targetDataLen,
808 : : att->attalignby,
809 : : att->attlen,
810 : : attrmiss[attnum].am_value);
811 : :
273 peter@eisentraut.org 812 [ # # # # :UNC 0 : targetDataLen = att_addlength_datum(targetDataLen,
# # ]
813 : : att->attlen,
814 : : attrmiss[attnum].am_value);
815 : : }
816 : : else
817 : : {
818 : : /* no missing value, so it must be null */
2780 andrew@dunslane.net 819 :UBC 0 : hasNulls = true;
820 : : }
821 : : }
822 : : } /* end if have missing values */
823 : : else
824 : : {
825 : : /*
826 : : * If there are no missing values at all then NULLS must be allowed,
827 : : * since some of the attributes are known to be absent.
828 : : */
2960 829 : 0 : hasNulls = true;
830 : : }
831 : :
832 : 0 : len = 0;
833 : :
834 [ # # ]: 0 : if (hasNulls)
835 : : {
836 : 0 : targetNullLen = BITMAPLEN(natts);
837 : 0 : len += targetNullLen;
838 : : }
839 : : else
840 : 0 : targetNullLen = 0;
841 : :
842 : : /*
843 : : * Allocate and zero the space needed. Note that the tuple body and
844 : : * HeapTupleData management structure are allocated in one chunk.
845 : : */
846 [ # # ]: 0 : if (targetHeapTuple)
847 : : {
848 : 0 : len += offsetof(HeapTupleHeaderData, t_bits);
849 : 0 : hoff = len = MAXALIGN(len); /* align user data safely */
850 : 0 : len += targetDataLen;
851 : :
852 : 0 : *targetHeapTuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
853 : 0 : (*targetHeapTuple)->t_data
854 : 0 : = targetTHeader
855 : 0 : = (HeapTupleHeader) ((char *) *targetHeapTuple + HEAPTUPLESIZE);
856 : 0 : (*targetHeapTuple)->t_len = len;
857 : 0 : (*targetHeapTuple)->t_tableOid = sourceTuple->t_tableOid;
2750 858 : 0 : (*targetHeapTuple)->t_self = sourceTuple->t_self;
859 : :
2960 860 : 0 : targetTHeader->t_infomask = sourceTHeader->t_infomask;
861 : 0 : targetTHeader->t_hoff = hoff;
862 : 0 : HeapTupleHeaderSetNatts(targetTHeader, natts);
863 : 0 : HeapTupleHeaderSetDatumLength(targetTHeader, len);
864 : 0 : HeapTupleHeaderSetTypeId(targetTHeader, tupleDesc->tdtypeid);
865 : 0 : HeapTupleHeaderSetTypMod(targetTHeader, tupleDesc->tdtypmod);
866 : : /* We also make sure that t_ctid is invalid unless explicitly set */
867 : 0 : ItemPointerSetInvalid(&(targetTHeader->t_ctid));
868 [ # # ]: 0 : if (targetNullLen > 0)
36 nathan@postgresql.or 869 :UNC 0 : nullBits = (uint8 *) ((char *) (*targetHeapTuple)->t_data
870 : : + offsetof(HeapTupleHeaderData, t_bits));
2960 andrew@dunslane.net 871 :UBC 0 : targetData = (char *) (*targetHeapTuple)->t_data + hoff;
872 : 0 : infoMask = &(targetTHeader->t_infomask);
873 : : }
874 : : else
875 : : {
876 : 0 : len += SizeofMinimalTupleHeader;
877 : 0 : hoff = len = MAXALIGN(len); /* align user data safely */
878 : 0 : len += targetDataLen;
879 : :
880 : 0 : *targetMinimalTuple = (MinimalTuple) palloc0(len);
881 : 0 : (*targetMinimalTuple)->t_len = len;
882 : 0 : (*targetMinimalTuple)->t_hoff = hoff + MINIMAL_TUPLE_OFFSET;
883 : 0 : (*targetMinimalTuple)->t_infomask = sourceTHeader->t_infomask;
884 : : /* Same macro works for MinimalTuples */
885 : 0 : HeapTupleHeaderSetNatts(*targetMinimalTuple, natts);
886 [ # # ]: 0 : if (targetNullLen > 0)
36 nathan@postgresql.or 887 :UNC 0 : nullBits = (uint8 *) ((char *) *targetMinimalTuple
2960 andrew@dunslane.net 888 :UBC 0 : + offsetof(MinimalTupleData, t_bits));
889 : 0 : targetData = (char *) *targetMinimalTuple + hoff;
890 : 0 : infoMask = &((*targetMinimalTuple)->t_infomask);
891 : : }
892 : :
893 [ # # ]: 0 : if (targetNullLen > 0)
894 : : {
895 [ # # ]: 0 : if (sourceNullLen > 0)
896 : : {
897 : : /* if bitmap pre-existed copy in - all is set */
898 : 0 : memcpy(nullBits,
899 : : ((char *) sourceTHeader)
900 : : + offsetof(HeapTupleHeaderData, t_bits),
901 : : sourceNullLen);
902 : 0 : nullBits += sourceNullLen - 1;
903 : : }
904 : : else
905 : : {
906 : 0 : sourceNullLen = BITMAPLEN(sourceNatts);
907 : : /* Set NOT NULL for all existing attributes */
908 : 0 : memset(nullBits, 0xff, sourceNullLen);
909 : :
910 : 0 : nullBits += sourceNullLen - 1;
911 : :
912 [ # # ]: 0 : if (sourceNatts & 0x07)
913 : : {
914 : : /* build the mask (inverted!) */
915 : 0 : bitMask = 0xff << (sourceNatts & 0x07);
916 : : /* Voila */
917 : 0 : *nullBits = ~bitMask;
918 : : }
919 : : }
920 : :
921 : 0 : bitMask = (1 << ((sourceNatts - 1) & 0x07));
922 : : } /* End if have null bitmap */
923 : :
924 : 0 : memcpy(targetData,
925 : 0 : ((char *) sourceTuple->t_data) + sourceTHeader->t_hoff,
926 : : sourceDataLen);
927 : :
928 : 0 : targetData += sourceDataLen;
929 : :
930 : : /* Now fill in the missing values */
931 [ # # ]: 0 : for (attnum = sourceNatts; attnum < natts; attnum++)
932 : : {
501 drowley@postgresql.o 933 : 0 : CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, attnum);
934 : :
2869 akapila@postgresql.o 935 [ # # # # ]: 0 : if (attrmiss && attrmiss[attnum].am_present)
936 : : {
2960 andrew@dunslane.net 937 : 0 : fill_val(attr,
938 : 0 : nullBits ? &nullBits : NULL,
939 : : &bitMask,
940 : : &targetData,
941 : : infoMask,
2869 akapila@postgresql.o 942 [ # # ]: 0 : attrmiss[attnum].am_value,
943 : : false);
944 : : }
945 : : else
946 : : {
2960 andrew@dunslane.net 947 : 0 : fill_val(attr,
948 : : &nullBits,
949 : : &bitMask,
950 : : &targetData,
951 : : infoMask,
952 : : (Datum) 0,
953 : : true);
954 : : }
955 : : } /* end loop over missing attributes */
956 : 0 : }
957 : :
958 : : /*
959 : : * Fill in the missing values for a minimal HeapTuple
960 : : */
961 : : MinimalTuple
962 : 0 : minimal_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc)
963 : : {
964 : : MinimalTuple minimalTuple;
965 : :
966 : 0 : expand_tuple(NULL, &minimalTuple, sourceTuple, tupleDesc);
967 : 0 : return minimalTuple;
968 : : }
969 : :
970 : : /*
971 : : * Fill in the missing values for an ordinary HeapTuple
972 : : */
973 : : HeapTuple
974 : 0 : heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc)
975 : : {
976 : : HeapTuple heapTuple;
977 : :
978 : 0 : expand_tuple(&heapTuple, NULL, sourceTuple, tupleDesc);
979 : 0 : return heapTuple;
980 : : }
981 : :
982 : : /* ----------------
983 : : * heap_copy_tuple_as_datum
984 : : *
985 : : * copy a tuple as a composite-type Datum
986 : : * ----------------
987 : : */
988 : : Datum
4387 tgl@sss.pgh.pa.us 989 :CBC 52327 : heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
990 : : {
991 : : HeapTupleHeader td;
992 : :
993 : : /*
994 : : * If the tuple contains any external TOAST pointers, we have to inline
995 : : * those fields to meet the conventions for composite-type Datums.
996 : : */
997 [ - + ]: 52327 : if (HeapTupleHasExternal(tuple))
4387 tgl@sss.pgh.pa.us 998 :UBC 0 : return toast_flatten_tuple_to_datum(tuple->t_data,
999 : : tuple->t_len,
1000 : : tupleDesc);
1001 : :
1002 : : /*
1003 : : * Fast path for easy case: just make a palloc'd copy and insert the
1004 : : * correct composite-Datum header fields (since those may not be set if
1005 : : * the given tuple came from disk, rather than from heap_form_tuple).
1006 : : */
4387 tgl@sss.pgh.pa.us 1007 :CBC 52327 : td = (HeapTupleHeader) palloc(tuple->t_len);
447 peter@eisentraut.org 1008 : 52327 : memcpy(td, tuple->t_data, tuple->t_len);
1009 : :
4387 tgl@sss.pgh.pa.us 1010 : 52327 : HeapTupleHeaderSetDatumLength(td, tuple->t_len);
1011 : 52327 : HeapTupleHeaderSetTypeId(td, tupleDesc->tdtypeid);
1012 : 52327 : HeapTupleHeaderSetTypMod(td, tupleDesc->tdtypmod);
1013 : :
1014 : 52327 : return PointerGetDatum(td);
1015 : : }
1016 : :
1017 : : /*
1018 : : * heap_form_tuple
1019 : : * construct a tuple from the given values[] and isnull[] arrays,
1020 : : * which are of the length indicated by tupleDescriptor->natts
1021 : : *
1022 : : * The result is allocated in the current memory context.
1023 : : */
1024 : : HeapTuple
7720 1025 : 19066027 : heap_form_tuple(TupleDesc tupleDescriptor,
1026 : : const Datum *values,
1027 : : const bool *isnull)
1028 : : {
1029 : : HeapTuple tuple; /* return tuple */
1030 : : HeapTupleHeader td; /* tuple data */
1031 : : Size len,
1032 : : data_len;
1033 : : int hoff;
1034 : 19066027 : bool hasnull = false;
1035 : 19066027 : int numberOfAttributes = tupleDescriptor->natts;
1036 : : int i;
1037 : :
1038 [ - + ]: 19066027 : if (numberOfAttributes > MaxTupleAttributeNumber)
7720 tgl@sss.pgh.pa.us 1039 [ # # ]:UBC 0 : ereport(ERROR,
1040 : : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1041 : : errmsg("number of columns (%d) exceeds limit (%d)",
1042 : : numberOfAttributes, MaxTupleAttributeNumber)));
1043 : :
1044 : : /*
1045 : : * Check for nulls
1046 : : */
7720 tgl@sss.pgh.pa.us 1047 [ + + ]:CBC 84610080 : for (i = 0; i < numberOfAttributes; i++)
1048 : : {
1049 [ + + ]: 69246850 : if (isnull[i])
1050 : : {
4387 1051 : 3702797 : hasnull = true;
1052 : 3702797 : break;
1053 : : }
1054 : : }
1055 : :
1056 : : /*
1057 : : * Determine total space needed
1058 : : */
7720 1059 : 19066027 : len = offsetof(HeapTupleHeaderData, t_bits);
1060 : :
1061 [ + + ]: 19066027 : if (hasnull)
1062 : 3702797 : len += BITMAPLEN(numberOfAttributes);
1063 : :
1064 : 19066027 : hoff = len = MAXALIGN(len); /* align user data safely */
1065 : :
6969 1066 : 19066027 : data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
1067 : :
1068 : 19066027 : len += data_len;
1069 : :
1070 : : /*
1071 : : * Allocate and zero the space needed. Note that the tuple body and
1072 : : * HeapTupleData management structure are allocated in one chunk.
1073 : : */
3282 alvherre@alvh.no-ip. 1074 : 19066027 : tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
7720 tgl@sss.pgh.pa.us 1075 : 19066027 : tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
1076 : :
1077 : : /*
1078 : : * And fill in the information. Note we fill the Datum fields even though
1079 : : * this tuple may never become a Datum. This lets HeapTupleHeaderGetDatum
1080 : : * identify the tuple type if needed.
1081 : : */
1082 : 19066027 : tuple->t_len = len;
1083 : 19066027 : ItemPointerSetInvalid(&(tuple->t_self));
1084 : 19066027 : tuple->t_tableOid = InvalidOid;
1085 : :
1086 : 19066027 : HeapTupleHeaderSetDatumLength(td, len);
1087 : 19066027 : HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid);
1088 : 19066027 : HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod);
1089 : : /* We also make sure that t_ctid is invalid unless explicitly set */
4010 1090 : 19066027 : ItemPointerSetInvalid(&(td->t_ctid));
1091 : :
7056 bruce@momjian.us 1092 : 19066027 : HeapTupleHeaderSetNatts(td, numberOfAttributes);
7720 tgl@sss.pgh.pa.us 1093 : 19066027 : td->t_hoff = hoff;
1094 : :
1095 [ + + ]: 19066027 : heap_fill_tuple(tupleDescriptor,
1096 : : values,
1097 : : isnull,
1098 : : (char *) td + hoff,
1099 : : data_len,
1100 : : &td->t_infomask,
1101 : : (hasnull ? td->t_bits : NULL));
1102 : :
1103 : 19066027 : return tuple;
1104 : : }
1105 : :
1106 : : /*
1107 : : * heap_modify_tuple
1108 : : * form a new tuple from an old tuple and a set of replacement values.
1109 : : *
1110 : : * The replValues, replIsnull, and doReplace arrays must be of the length
1111 : : * indicated by tupleDesc->natts. The new tuple is constructed using the data
1112 : : * from replValues/replIsnull at columns where doReplace is true, and using
1113 : : * the data from the old tuple at columns where doReplace is false.
1114 : : *
1115 : : * The result is allocated in the current memory context.
1116 : : */
1117 : : HeapTuple
1118 : 60841 : heap_modify_tuple(HeapTuple tuple,
1119 : : TupleDesc tupleDesc,
1120 : : const Datum *replValues,
1121 : : const bool *replIsnull,
1122 : : const bool *doReplace)
1123 : : {
1124 : 60841 : int numberOfAttributes = tupleDesc->natts;
1125 : : int attoff;
1126 : : Datum *values;
1127 : : bool *isnull;
1128 : : HeapTuple newTuple;
1129 : :
1130 : : /*
1131 : : * allocate and fill values and isnull arrays from either the tuple or the
1132 : : * repl information, as appropriate.
1133 : : *
1134 : : * NOTE: it's debatable whether to use heap_deform_tuple() here or just
1135 : : * heap_getattr() only the non-replaced columns. The latter could win if
1136 : : * there are many replaced columns and few non-replaced ones. However,
1137 : : * heap_deform_tuple costs only O(N) while the heap_getattr way would cost
1138 : : * O(N^2) if there are many non-replaced columns, so it seems better to
1139 : : * err on the side of linear cost.
1140 : : */
146 michael@paquier.xyz 1141 :GNC 60841 : values = palloc_array(Datum, numberOfAttributes);
1142 : 60841 : isnull = palloc_array(bool, numberOfAttributes);
1143 : :
7720 tgl@sss.pgh.pa.us 1144 :CBC 60841 : heap_deform_tuple(tuple, tupleDesc, values, isnull);
1145 : :
1146 [ + + ]: 1845288 : for (attoff = 0; attoff < numberOfAttributes; attoff++)
1147 : : {
1148 [ + + ]: 1784447 : if (doReplace[attoff])
1149 : : {
1150 : 839940 : values[attoff] = replValues[attoff];
1151 : 839940 : isnull[attoff] = replIsnull[attoff];
1152 : : }
1153 : : }
1154 : :
1155 : : /*
1156 : : * create a new tuple from the values and isnull arrays
1157 : : */
1158 : 60841 : newTuple = heap_form_tuple(tupleDesc, values, isnull);
1159 : :
1160 : 60841 : pfree(values);
1161 : 60841 : pfree(isnull);
1162 : :
1163 : : /*
1164 : : * copy the identification info of the old tuple: t_ctid, t_self
1165 : : */
1166 : 60841 : newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
1167 : 60841 : newTuple->t_self = tuple->t_self;
1168 : 60841 : newTuple->t_tableOid = tuple->t_tableOid;
1169 : :
1170 : 60841 : return newTuple;
1171 : : }
1172 : :
1173 : : /*
1174 : : * heap_modify_tuple_by_cols
1175 : : * form a new tuple from an old tuple and a set of replacement values.
1176 : : *
1177 : : * This is like heap_modify_tuple, except that instead of specifying which
1178 : : * column(s) to replace by a boolean map, an array of target column numbers
1179 : : * is used. This is often more convenient when a fixed number of columns
1180 : : * are to be replaced. The replCols, replValues, and replIsnull arrays must
1181 : : * be of length nCols. Target column numbers are indexed from 1.
1182 : : *
1183 : : * The result is allocated in the current memory context.
1184 : : */
1185 : : HeapTuple
3465 1186 : 723 : heap_modify_tuple_by_cols(HeapTuple tuple,
1187 : : TupleDesc tupleDesc,
1188 : : int nCols,
1189 : : const int *replCols,
1190 : : const Datum *replValues,
1191 : : const bool *replIsnull)
1192 : : {
1193 : 723 : int numberOfAttributes = tupleDesc->natts;
1194 : : Datum *values;
1195 : : bool *isnull;
1196 : : HeapTuple newTuple;
1197 : : int i;
1198 : :
1199 : : /*
1200 : : * allocate and fill values and isnull arrays from the tuple, then replace
1201 : : * selected columns from the input arrays.
1202 : : */
146 michael@paquier.xyz 1203 :GNC 723 : values = palloc_array(Datum, numberOfAttributes);
1204 : 723 : isnull = palloc_array(bool, numberOfAttributes);
1205 : :
3465 tgl@sss.pgh.pa.us 1206 :CBC 723 : heap_deform_tuple(tuple, tupleDesc, values, isnull);
1207 : :
1208 [ + + ]: 2147 : for (i = 0; i < nCols; i++)
1209 : : {
1210 : 1424 : int attnum = replCols[i];
1211 : :
1212 [ + - - + ]: 1424 : if (attnum <= 0 || attnum > numberOfAttributes)
3465 tgl@sss.pgh.pa.us 1213 [ # # ]:UBC 0 : elog(ERROR, "invalid column number %d", attnum);
3465 tgl@sss.pgh.pa.us 1214 :CBC 1424 : values[attnum - 1] = replValues[i];
1215 : 1424 : isnull[attnum - 1] = replIsnull[i];
1216 : : }
1217 : :
1218 : : /*
1219 : : * create a new tuple from the values and isnull arrays
1220 : : */
1221 : 723 : newTuple = heap_form_tuple(tupleDesc, values, isnull);
1222 : :
1223 : 723 : pfree(values);
1224 : 723 : pfree(isnull);
1225 : :
1226 : : /*
1227 : : * copy the identification info of the old tuple: t_ctid, t_self
1228 : : */
1229 : 723 : newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
1230 : 723 : newTuple->t_self = tuple->t_self;
1231 : 723 : newTuple->t_tableOid = tuple->t_tableOid;
1232 : :
1233 : 723 : return newTuple;
1234 : : }
1235 : :
1236 : : /*
1237 : : * heap_deform_tuple
1238 : : * Given a tuple, extract data into values/isnull arrays; this is
1239 : : * the inverse of heap_form_tuple.
1240 : : *
1241 : : * Storage for the values/isnull arrays is provided by the caller;
1242 : : * it should be sized according to tupleDesc->natts not
1243 : : * HeapTupleHeaderGetNatts(tuple->t_data).
1244 : : *
1245 : : * Note that for pass-by-reference datatypes, the pointer placed
1246 : : * in the Datum will point into the given tuple.
1247 : : *
1248 : : * When all or most of a tuple's fields need to be extracted,
1249 : : * this routine will be significantly quicker than a loop around
1250 : : * heap_getattr; the loop will become O(N^2) as soon as any
1251 : : * noncacheable attribute offsets are involved.
1252 : : */
1253 : : void
7720 1254 : 3171076 : heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
1255 : : Datum *values, bool *isnull)
1256 : : {
1257 : 3171076 : HeapTupleHeader tup = tuple->t_data;
1258 : : CompactAttribute *cattr;
1259 : 3171076 : bool hasnulls = HeapTupleHasNulls(tuple);
1260 : 3171076 : int tdesc_natts = tupleDesc->natts;
1261 : : int natts; /* number of atts to extract */
1262 : : int attnum;
1263 : : char *tp; /* ptr to tuple data */
1264 : : uint32 off; /* offset in tuple data */
36 nathan@postgresql.or 1265 :GNC 3171076 : uint8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
1266 : : int firstNonCacheOffsetAttr;
1267 : : int firstNullAttr;
1268 : :
7056 bruce@momjian.us 1269 :CBC 3171076 : natts = HeapTupleHeaderGetNatts(tup);
1270 : :
1271 : : /* Did someone forget to call TupleDescFinalize()? */
50 drowley@postgresql.o 1272 [ - + ]:GNC 3171076 : Assert(tupleDesc->firstNonCachedOffsetAttr >= 0);
1273 : :
1274 : : /*
1275 : : * In inheritance situations, it is possible that the given tuple actually
1276 : : * has more fields than the caller is expecting. Don't run off the end of
1277 : : * the caller's arrays.
1278 : : */
7720 tgl@sss.pgh.pa.us 1279 :CBC 3171076 : natts = Min(natts, tdesc_natts);
50 drowley@postgresql.o 1280 :GNC 3171076 : firstNonCacheOffsetAttr = Min(tupleDesc->firstNonCachedOffsetAttr, natts);
1281 : :
1282 [ + + ]: 3171076 : if (hasnulls)
1283 : : {
1284 : 120347 : firstNullAttr = first_null_attr(bp, natts);
1285 : :
1286 : : /*
1287 : : * XXX: it'd be nice to use populate_isnull_array() here, but that
1288 : : * requires that the isnull array's size is rounded up to the next
1289 : : * multiple of 8. Doing that would require adjusting many locations
1290 : : * that allocate the array.
1291 : : */
1292 : 120347 : firstNonCacheOffsetAttr = Min(firstNonCacheOffsetAttr, firstNullAttr);
1293 : : }
1294 : : else
1295 : 3050729 : firstNullAttr = natts;
1296 : :
7720 tgl@sss.pgh.pa.us 1297 :CBC 3171076 : tp = (char *) tup + tup->t_hoff;
50 drowley@postgresql.o 1298 :GNC 3171076 : attnum = 0;
1299 : :
1300 [ + + ]: 3171076 : if (firstNonCacheOffsetAttr > 0)
1301 : : {
1302 : : #ifdef USE_ASSERT_CHECKING
1303 : : /* In Assert enabled builds, verify attcacheoff is correct */
1304 : 2575821 : int offcheck = 0;
1305 : : #endif
1306 : : do
1307 : : {
1308 : 4372994 : isnull[attnum] = false;
1309 : 4372994 : cattr = TupleDescCompactAttr(tupleDesc, attnum);
1310 : 4372994 : off = cattr->attcacheoff;
1311 : :
1312 : : #ifdef USE_ASSERT_CHECKING
1313 : 4372994 : offcheck = att_nominal_alignby(offcheck, cattr->attalignby);
1314 [ - + ]: 4372994 : Assert(offcheck == cattr->attcacheoff);
1315 : 4372994 : offcheck += cattr->attlen;
1316 : : #endif
1317 : :
1318 : 8745988 : values[attnum] = fetch_att_noerr(tp + off,
1319 : 4372994 : cattr->attbyval,
1320 : 4372994 : cattr->attlen);
1321 [ + + ]: 4372994 : } while (++attnum < firstNonCacheOffsetAttr);
1322 : 2575821 : off += cattr->attlen;
1323 : : }
1324 : : else
1325 : 595255 : off = 0;
1326 : :
1327 [ + + ]: 4884087 : for (; attnum < firstNullAttr; attnum++)
1328 : : {
1329 : 1713011 : isnull[attnum] = false;
1330 : 1713011 : cattr = TupleDescCompactAttr(tupleDesc, attnum);
1331 : 1713011 : values[attnum] = align_fetch_then_add(tp,
1332 : : &off,
1333 : 1713011 : cattr->attbyval,
1334 : 1713011 : cattr->attlen,
1335 : 1713011 : cattr->attalignby);
1336 : : }
1337 : :
1338 [ + + ]: 3711924 : for (; attnum < natts; attnum++)
1339 : : {
1340 [ - + ]: 540848 : Assert(hasnulls);
1341 : :
1342 [ + + ]: 540848 : if (att_isnull(attnum, bp))
1343 : : {
7720 tgl@sss.pgh.pa.us 1344 :CBC 425424 : values[attnum] = (Datum) 0;
1345 : 425424 : isnull[attnum] = true;
1346 : 425424 : continue;
1347 : : }
1348 : :
1349 : 115424 : isnull[attnum] = false;
50 drowley@postgresql.o 1350 :GNC 115424 : cattr = TupleDescCompactAttr(tupleDesc, attnum);
1351 : :
1352 : : /* align 'off', fetch the attr's value, and increment off beyond it */
1353 : 115424 : values[attnum] = align_fetch_then_add(tp,
1354 : : &off,
1355 : 115424 : cattr->attbyval,
1356 : 115424 : cattr->attlen,
1357 : 115424 : cattr->attalignby);
1358 : : }
1359 : :
1360 : : /*
1361 : : * If tuple doesn't have all the atts indicated by tupleDesc, read the
1362 : : * rest as nulls or missing values as appropriate.
1363 : : */
7720 tgl@sss.pgh.pa.us 1364 [ + + ]:CBC 3171088 : for (; attnum < tdesc_natts; attnum++)
2960 andrew@dunslane.net 1365 : 12 : values[attnum] = getmissingattr(tupleDesc, attnum + 1, &isnull[attnum]);
7720 tgl@sss.pgh.pa.us 1366 : 3171076 : }
1367 : :
1368 : : /*
1369 : : * heap_freetuple
1370 : : */
1371 : : void
9637 JanWieck@Yahoo.com 1372 : 15913835 : heap_freetuple(HeapTuple htup)
1373 : : {
1374 : 15913835 : pfree(htup);
1375 : 15913835 : }
1376 : :
1377 : :
1378 : : /*
1379 : : * heap_form_minimal_tuple
1380 : : * construct a MinimalTuple from the given values[] and isnull[] arrays,
1381 : : * which are of the length indicated by tupleDescriptor->natts
1382 : : *
1383 : : * This is exactly like heap_form_tuple() except that the result is a
1384 : : * "minimal" tuple lacking a HeapTupleData header as well as room for system
1385 : : * columns.
1386 : : *
1387 : : * The result is allocated in the current memory context.
1388 : : */
1389 : : MinimalTuple
7252 tgl@sss.pgh.pa.us 1390 : 30121805 : heap_form_minimal_tuple(TupleDesc tupleDescriptor,
1391 : : const Datum *values,
1392 : : const bool *isnull,
1393 : : Size extra)
1394 : : {
1395 : : MinimalTuple tuple; /* return tuple */
1396 : : char *mem;
1397 : : Size len,
1398 : : data_len;
1399 : : int hoff;
1400 : 30121805 : bool hasnull = false;
1401 : 30121805 : int numberOfAttributes = tupleDescriptor->natts;
1402 : : int i;
1403 : :
407 jdavis@postgresql.or 1404 [ - + ]: 30121805 : Assert(extra == MAXALIGN(extra));
1405 : :
7252 tgl@sss.pgh.pa.us 1406 [ - + ]: 30121805 : if (numberOfAttributes > MaxTupleAttributeNumber)
7252 tgl@sss.pgh.pa.us 1407 [ # # ]:UBC 0 : ereport(ERROR,
1408 : : (errcode(ERRCODE_TOO_MANY_COLUMNS),
1409 : : errmsg("number of columns (%d) exceeds limit (%d)",
1410 : : numberOfAttributes, MaxTupleAttributeNumber)));
1411 : :
1412 : : /*
1413 : : * Check for nulls
1414 : : */
7252 tgl@sss.pgh.pa.us 1415 [ + + ]:CBC 88826081 : for (i = 0; i < numberOfAttributes; i++)
1416 : : {
1417 [ + + ]: 59208374 : if (isnull[i])
1418 : : {
4387 1419 : 504098 : hasnull = true;
1420 : 504098 : break;
1421 : : }
1422 : : }
1423 : :
1424 : : /*
1425 : : * Determine total space needed
1426 : : */
4091 1427 : 30121805 : len = SizeofMinimalTupleHeader;
1428 : :
7252 1429 [ + + ]: 30121805 : if (hasnull)
1430 : 504098 : len += BITMAPLEN(numberOfAttributes);
1431 : :
1432 : 30121805 : hoff = len = MAXALIGN(len); /* align user data safely */
1433 : :
6969 1434 : 30121805 : data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
1435 : :
1436 : 30121805 : len += data_len;
1437 : :
1438 : : /*
1439 : : * Allocate and zero the space needed.
1440 : : */
407 jdavis@postgresql.or 1441 : 30121805 : mem = palloc0(len + extra);
1442 : 30121805 : tuple = (MinimalTuple) (mem + extra);
1443 : :
1444 : : /*
1445 : : * And fill in the information.
1446 : : */
7252 tgl@sss.pgh.pa.us 1447 : 30121805 : tuple->t_len = len;
7056 bruce@momjian.us 1448 : 30121805 : HeapTupleHeaderSetNatts(tuple, numberOfAttributes);
7252 tgl@sss.pgh.pa.us 1449 : 30121805 : tuple->t_hoff = hoff + MINIMAL_TUPLE_OFFSET;
1450 : :
1451 [ + + ]: 30121805 : heap_fill_tuple(tupleDescriptor,
1452 : : values,
1453 : : isnull,
1454 : : (char *) tuple + hoff,
1455 : : data_len,
1456 : : &tuple->t_infomask,
1457 : : (hasnull ? tuple->t_bits : NULL));
1458 : :
1459 : 30121805 : return tuple;
1460 : : }
1461 : :
1462 : : /*
1463 : : * heap_free_minimal_tuple
1464 : : */
1465 : : void
1466 : 25067371 : heap_free_minimal_tuple(MinimalTuple mtup)
1467 : : {
1468 : 25067371 : pfree(mtup);
1469 : 25067371 : }
1470 : :
1471 : : /*
1472 : : * heap_copy_minimal_tuple
1473 : : * copy a MinimalTuple
1474 : : *
1475 : : * The result is allocated in the current memory context.
1476 : : */
1477 : : MinimalTuple
407 jdavis@postgresql.or 1478 : 3249563 : heap_copy_minimal_tuple(MinimalTuple mtup, Size extra)
1479 : : {
1480 : : MinimalTuple result;
1481 : : char *mem;
1482 : :
1483 [ - + ]: 3249563 : Assert(extra == MAXALIGN(extra));
1484 : 3249563 : mem = palloc(mtup->t_len + extra);
1485 : 3249563 : memset(mem, 0, extra);
1486 : 3249563 : result = (MinimalTuple) (mem + extra);
7252 tgl@sss.pgh.pa.us 1487 : 3249563 : memcpy(result, mtup, mtup->t_len);
1488 : 3249563 : return result;
1489 : : }
1490 : :
1491 : : /*
1492 : : * heap_tuple_from_minimal_tuple
1493 : : * create a HeapTuple by copying from a MinimalTuple;
1494 : : * system columns are filled with zeroes
1495 : : *
1496 : : * The result is allocated in the current memory context.
1497 : : * The HeapTuple struct, tuple header, and tuple data are all allocated
1498 : : * as a single palloc() block.
1499 : : */
1500 : : HeapTuple
1501 : 641572 : heap_tuple_from_minimal_tuple(MinimalTuple mtup)
1502 : : {
1503 : : HeapTuple result;
1504 : 641572 : uint32 len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
1505 : :
1506 : 641572 : result = (HeapTuple) palloc(HEAPTUPLESIZE + len);
1507 : 641572 : result->t_len = len;
1508 : 641572 : ItemPointerSetInvalid(&(result->t_self));
1509 : 641572 : result->t_tableOid = InvalidOid;
1510 : 641572 : result->t_data = (HeapTupleHeader) ((char *) result + HEAPTUPLESIZE);
1511 : 641572 : memcpy((char *) result->t_data + MINIMAL_TUPLE_OFFSET, mtup, mtup->t_len);
7056 bruce@momjian.us 1512 : 641572 : memset(result->t_data, 0, offsetof(HeapTupleHeaderData, t_infomask2));
7252 tgl@sss.pgh.pa.us 1513 : 641572 : return result;
1514 : : }
1515 : :
1516 : : /*
1517 : : * minimal_tuple_from_heap_tuple
1518 : : * create a MinimalTuple by copying from a HeapTuple
1519 : : *
1520 : : * The result is allocated in the current memory context.
1521 : : */
1522 : : MinimalTuple
407 jdavis@postgresql.or 1523 : 3041220 : minimal_tuple_from_heap_tuple(HeapTuple htup, Size extra)
1524 : : {
1525 : : MinimalTuple result;
1526 : : char *mem;
1527 : : uint32 len;
1528 : :
1529 [ - + ]: 3041220 : Assert(extra == MAXALIGN(extra));
7252 tgl@sss.pgh.pa.us 1530 [ - + ]: 3041220 : Assert(htup->t_len > MINIMAL_TUPLE_OFFSET);
1531 : 3041220 : len = htup->t_len - MINIMAL_TUPLE_OFFSET;
407 jdavis@postgresql.or 1532 : 3041220 : mem = palloc(len + extra);
1533 : 3041220 : memset(mem, 0, extra);
1534 : 3041220 : result = (MinimalTuple) (mem + extra);
7252 tgl@sss.pgh.pa.us 1535 : 3041220 : memcpy(result, (char *) htup->t_data + MINIMAL_TUPLE_OFFSET, len);
1536 : :
1537 : 3041220 : result->t_len = len;
1538 : 3041220 : return result;
1539 : : }
1540 : :
1541 : : /*
1542 : : * This mainly exists so JIT can inline the definition, but it's also
1543 : : * sometimes useful in debugging sessions.
1544 : : */
1545 : : size_t
2962 andres@anarazel.de 1546 :UBC 0 : varsize_any(void *p)
1547 : : {
1548 [ # # # # : 0 : return VARSIZE_ANY(p);
# # # # #
# ]
1549 : : }
|