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-2025, 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
746 andrew@dunslane.net 104 :UBC 0 : missing_hash(const void *key, Size keysize)
105 : : {
106 : 0 : const missing_cache_key *entry = (missing_cache_key *) key;
107 : :
29 peter@eisentraut.org 108 :UNC 0 : return hash_bytes((const unsigned char *) DatumGetPointer(entry->value), entry->len);
109 : : }
110 : :
111 : : static int
746 andrew@dunslane.net 112 :UBC 0 : missing_match(const void *key1, const void *key2, Size keysize)
113 : : {
114 : 0 : const missing_cache_key *entry1 = (missing_cache_key *) key1;
115 : 0 : const missing_cache_key *entry2 = (missing_cache_key *) key2;
116 : :
117 [ # # ]: 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
126 : 0 : init_missing_cache()
127 : : {
128 : : HASHCTL hash_ctl;
129 : :
130 : 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
2719 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 : :
260 drowley@postgresql.o 159 : 178 : att = TupleDescCompactAttr(tupleDesc, attnum - 1);
160 : :
2719 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 : :
2628 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 : :
2719 andrew@dunslane.net 177 : 27 : *isnull = false;
178 : :
179 : : /* no need to cache by-value attributes */
746 180 [ + - ]: 27 : if (att->attbyval)
181 : 27 : return attrmiss->am_value;
182 : :
183 : : /* set up cache if required */
746 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
32 peter@eisentraut.org 192 :UNC 0 : key.len = VARSIZE_ANY(DatumGetPointer(attrmiss->am_value));
746 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 : :
2719 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
7479 tgl@sss.pgh.pa.us 219 : 60128143 : heap_compute_data_size(TupleDesc tupleDesc,
220 : : const Datum *values,
221 : : const bool *isnull)
222 : : {
223 : 60128143 : Size data_length = 0;
224 : : int i;
225 : 60128143 : int numberOfAttributes = tupleDesc->natts;
226 : :
227 [ + + ]: 207027153 : for (i = 0; i < numberOfAttributes; i++)
228 : : {
229 : : Datum val;
230 : : CompactAttribute *atti;
231 : :
232 [ + + ]: 146899010 : if (isnull[i])
233 : 13171334 : continue;
234 : :
6728 235 : 133727676 : val = values[i];
260 drowley@postgresql.o 236 : 133727676 : atti = TupleDescCompactAttr(tupleDesc, i);
237 : :
238 [ + + + + : 151666447 : if (COMPACT_ATTR_IS_PACKABLE(atti) &&
+ + ]
6728 tgl@sss.pgh.pa.us 239 [ + + + + ]: 17938771 : 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 : 13836528 : data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
246 : : }
3768 247 [ + + + + ]: 124996786 : else if (atti->attlen == -1 &&
248 [ + + + + ]: 5105638 : 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 : : */
259 drowley@postgresql.o 254 : 1393 : data_length = att_nominal_alignby(data_length, atti->attalignby);
3768 tgl@sss.pgh.pa.us 255 : 1393 : data_length += EOH_get_flat_size(DatumGetEOHP(val));
256 : : }
257 : : else
258 : : {
259 drowley@postgresql.o 259 [ + + + + ]: 119889755 : data_length = att_datum_alignby(data_length, atti->attalignby,
260 : : atti->attlen, val);
3768 tgl@sss.pgh.pa.us 261 [ + + + + : 119889755 : data_length = att_addlength_datum(data_length, atti->attlen,
- + + + +
- - + + +
- + ]
262 : : val);
263 : : }
264 : : }
265 : :
7479 266 : 60128143 : 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
260 drowley@postgresql.o 275 : 135959122 : 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;
2719 andrew@dunslane.net 284 : 135959122 : 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 [ + + ]: 135959122 : if (bit != NULL)
291 : : {
292 [ + + ]: 48800145 : if (*bitmask != HIGHBIT)
293 : 40390798 : *bitmask <<= 1;
294 : : else
295 : : {
296 : 8409347 : *bit += 1;
297 : 8409347 : **bit = 0x0;
298 : 8409347 : *bitmask = 1;
299 : : }
300 : :
301 [ + + ]: 48800145 : if (isnull)
302 : : {
303 : 13043647 : *infomask |= HEAP_HASNULL;
304 : 13043647 : return;
305 : : }
306 : :
307 : 35756498 : **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 [ + + ]: 122915475 : if (att->attbyval)
315 : : {
316 : : /* pass-by-value */
259 drowley@postgresql.o 317 : 92074173 : data = (char *) att_nominal_alignby(data, att->attalignby);
2719 andrew@dunslane.net 318 : 92074173 : store_att_byval(data, datum, att->attlen);
319 : 92074173 : data_length = att->attlen;
320 : : }
321 [ + + ]: 30841302 : else if (att->attlen == -1)
322 : : {
323 : : /* varlena */
324 : 18148701 : Pointer val = DatumGetPointer(datum);
325 : :
326 : 18148701 : *infomask |= HEAP_HASVARWIDTH;
327 [ + + ]: 18148701 : if (VARATT_IS_EXTERNAL(val))
328 : : {
329 [ + + + + ]: 10455 : if (VARATT_IS_EXTERNAL_EXPANDED(val))
2719 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 : : */
2719 andrew@dunslane.net 335 :CBC 1393 : ExpandedObjectHeader *eoh = DatumGetEOHP(datum);
336 : :
259 drowley@postgresql.o 337 : 1393 : data = (char *) att_nominal_alignby(data, att->attalignby);
2719 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 : 9062 : *infomask |= HEAP_HASEXTERNAL;
344 : : /* no alignment, since it's short by definition */
345 [ + + + - : 9062 : data_length = VARSIZE_EXTERNAL(val);
- + ]
346 : 9062 : memcpy(data, val, data_length);
347 : : }
348 : : }
349 [ + + ]: 18138246 : else if (VARATT_IS_SHORT(val))
350 : : {
351 : : /* no alignment for short varlenas */
352 : 3184237 : data_length = VARSIZE_SHORT(val);
353 : 3184237 : memcpy(data, val, data_length);
354 : : }
260 drowley@postgresql.o 355 [ + + + + : 14954009 : else if (att->attispackable && VARATT_CAN_MAKE_SHORT(val))
+ + ]
356 : : {
357 : : /* convert to short varlena -- no alignment */
2719 andrew@dunslane.net 358 : 13371272 : data_length = VARATT_CONVERTED_SHORT_SIZE(val);
359 : 13371272 : SET_VARSIZE_SHORT(data, data_length);
360 : 13371272 : memcpy(data + 1, VARDATA(val), data_length - 1);
361 : : }
362 : : else
363 : : {
364 : : /* full 4-byte header varlena */
259 drowley@postgresql.o 365 : 1582737 : data = (char *) att_nominal_alignby(data, att->attalignby);
2719 andrew@dunslane.net 366 : 1582737 : data_length = VARSIZE(val);
367 : 1582737 : memcpy(data, val, data_length);
368 : : }
369 : : }
370 [ + + ]: 12692601 : else if (att->attlen == -2)
371 : : {
372 : : /* cstring ... never needs alignment */
373 : 1282673 : *infomask |= HEAP_HASVARWIDTH;
259 drowley@postgresql.o 374 [ - + ]: 1282673 : Assert(att->attalignby == sizeof(char));
2719 andrew@dunslane.net 375 : 1282673 : data_length = strlen(DatumGetCString(datum)) + 1;
376 : 1282673 : memcpy(data, DatumGetPointer(datum), data_length);
377 : : }
378 : : else
379 : : {
380 : : /* fixed-length pass-by-reference */
259 drowley@postgresql.o 381 : 11409928 : data = (char *) att_nominal_alignby(data, att->attalignby);
2719 andrew@dunslane.net 382 [ - + ]: 11409928 : Assert(att->attlen > 0);
383 : 11409928 : data_length = att->attlen;
384 : 11409928 : memcpy(data, DatumGetPointer(datum), data_length);
385 : : }
386 : :
387 : 122915475 : data += data_length;
388 : 122915475 : *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
7479 tgl@sss.pgh.pa.us 401 : 50294664 : 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 : 50294664 : int numberOfAttributes = tupleDesc->natts;
410 : :
411 : : #ifdef USE_ASSERT_CHECKING
6728 412 : 50294664 : char *start = data;
413 : : #endif
414 : :
7479 415 [ + + ]: 50294664 : if (bit != NULL)
416 : : {
417 : 3585233 : bitP = &bit[-1];
7195 bruce@momjian.us 418 : 3585233 : bitmask = HIGHBIT;
419 : : }
420 : : else
421 : : {
422 : : /* just to keep compiler quiet */
7479 tgl@sss.pgh.pa.us 423 : 46709431 : bitP = NULL;
424 : 46709431 : bitmask = 0;
425 : : }
426 : :
6728 427 : 50294664 : *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL);
428 : :
7479 429 [ + + ]: 186253786 : for (i = 0; i < numberOfAttributes; i++)
430 : : {
260 drowley@postgresql.o 431 : 135959122 : CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, i);
432 : :
2719 andrew@dunslane.net 433 [ + + ]: 271918244 : fill_val(attr,
434 [ + + ]: 135959122 : bitP ? &bitP : NULL,
435 : : &bitmask,
436 : : &data,
437 : : infomask,
438 : 135959122 : values ? values[i] : PointerGetDatum(NULL),
439 [ + - + + ]: 135959122 : isnull ? isnull[i] : true);
440 : : }
441 : :
6728 tgl@sss.pgh.pa.us 442 [ - + ]: 50294664 : Assert((data - start) == data_size);
7479 443 : 50294664 : }
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
2719 andrew@dunslane.net 456 : 4853990 : 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 [ + + - + ]: 4853990 : Assert(!tupleDesc || attnum <= tupleDesc->natts);
6815 bruce@momjian.us 463 [ - + ]: 4853990 : if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data))
464 : : {
260 drowley@postgresql.o 465 [ # # ]:UBC 0 : if (tupleDesc &&
466 [ # # ]: 0 : TupleDescCompactAttr(tupleDesc, attnum - 1)->atthasmissing)
2719 andrew@dunslane.net 467 : 0 : return false;
468 : : else
469 : 0 : return true;
470 : : }
471 : :
10226 bruce@momjian.us 472 [ + - ]:CBC 4853990 : if (attnum > 0)
473 : : {
7764 tgl@sss.pgh.pa.us 474 [ + + ]: 4853990 : if (HeapTupleNoNulls(tup))
7479 475 : 1480 : return false;
9780 vadim4o@yahoo.com 476 : 4852510 : return att_isnull(attnum - 1, tup->t_data->t_bits);
477 : : }
478 : :
7764 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 : :
7479 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
1083 pg@bowt.ie 521 :CBC 100915273 : nocachegetattr(HeapTuple tup,
522 : : int attnum,
523 : : TupleDesc tupleDesc)
524 : : {
525 : 100915273 : HeapTupleHeader td = tup->t_data;
526 : : char *tp; /* ptr to data part of tuple */
527 : 100915273 : bits8 *bp = td->t_bits; /* ptr to null bitmap in tuple */
6728 tgl@sss.pgh.pa.us 528 : 100915273 : 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 : 100915273 : attnum--;
541 : :
1083 pg@bowt.ie 542 [ + + ]: 100915273 : 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 : : */
5263 bruce@momjian.us 549 : 74658067 : int byte = attnum >> 3;
5718 rhaas@postgresql.org 550 : 74658067 : int finalbit = attnum & 0x07;
551 : :
552 : : /* check for nulls "before" final bit of last byte */
553 [ + + ]: 74658067 : if ((~bp[byte]) & ((1 << finalbit) - 1))
554 : 2914155 : slow = true;
555 : : else
556 : : {
557 : : /* check for nulls in any "earlier" bytes */
558 : : int i;
559 : :
560 [ + + ]: 93659070 : for (i = 0; i < byte; i++)
561 : : {
562 [ + + ]: 22212061 : if (bp[i] != 0xFF)
563 : : {
564 : 296903 : slow = true;
565 : 296903 : break;
566 : : }
567 : : }
568 : : }
569 : : }
570 : :
1083 pg@bowt.ie 571 : 100915273 : tp = (char *) td + td->t_hoff;
572 : :
10226 bruce@momjian.us 573 [ + + ]: 100915273 : 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 : : */
260 drowley@postgresql.o 581 : 97704215 : att = TupleDescCompactAttr(tupleDesc, attnum);
2939 andres@anarazel.de 582 [ + + ]: 97704215 : if (att->attcacheoff >= 0)
583 : 69320158 : 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 : : */
1083 pg@bowt.ie 590 [ + + ]: 28384057 : if (HeapTupleHasVarWidth(tup))
591 : : {
592 : : int j;
593 : :
9722 tgl@sss.pgh.pa.us 594 [ + + ]: 57046273 : for (j = 0; j <= attnum; j++)
595 : : {
260 drowley@postgresql.o 596 [ + + ]: 56955986 : if (TupleDescCompactAttr(tupleDesc, j)->attlen <= 0)
597 : : {
9046 tgl@sss.pgh.pa.us 598 : 28115851 : slow = true;
9722 599 : 28115851 : break;
600 : : }
601 : : }
602 : : }
603 : : }
604 : :
10226 bruce@momjian.us 605 [ + + ]: 31595115 : if (!slow)
606 : : {
6728 tgl@sss.pgh.pa.us 607 : 268206 : int natts = tupleDesc->natts;
10054 bruce@momjian.us 608 : 268206 : 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 : : */
260 drowley@postgresql.o 619 : 268206 : TupleDescCompactAttr(tupleDesc, 0)->attcacheoff = 0;
620 : :
621 : : /* we might have set some offsets in the slow path previously */
622 [ + + + + ]: 270359 : while (j < natts && TupleDescCompactAttr(tupleDesc, j)->attcacheoff > 0)
10226 bruce@momjian.us 623 : 2153 : j++;
624 : :
260 drowley@postgresql.o 625 : 268206 : off = TupleDescCompactAttr(tupleDesc, j - 1)->attcacheoff +
626 : 268206 : TupleDescCompactAttr(tupleDesc, j - 1)->attlen;
627 : :
6728 tgl@sss.pgh.pa.us 628 [ + + ]: 3256190 : for (; j < natts; j++)
629 : : {
260 drowley@postgresql.o 630 : 3160099 : CompactAttribute *att = TupleDescCompactAttr(tupleDesc, j);
631 : :
2939 andres@anarazel.de 632 [ + + ]: 3160099 : if (att->attlen <= 0)
6728 tgl@sss.pgh.pa.us 633 : 172115 : break;
634 : :
259 drowley@postgresql.o 635 : 2987984 : off = att_nominal_alignby(off, att->attalignby);
636 : :
2939 andres@anarazel.de 637 : 2987984 : att->attcacheoff = off;
638 : :
639 : 2987984 : off += att->attlen;
640 : : }
641 : :
6728 tgl@sss.pgh.pa.us 642 [ - + ]: 268206 : Assert(j > attnum);
643 : :
260 drowley@postgresql.o 644 : 268206 : off = TupleDescCompactAttr(tupleDesc, attnum)->attcacheoff;
645 : : }
646 : : else
647 : : {
10054 bruce@momjian.us 648 : 31326909 : 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 : : */
6728 tgl@sss.pgh.pa.us 661 : 31326909 : off = 0;
6505 bruce@momjian.us 662 : 31326909 : for (i = 0;; i++) /* loop exit is at "break" */
10226 663 : 121762814 : {
260 drowley@postgresql.o 664 : 153089723 : CompactAttribute *att = TupleDescCompactAttr(tupleDesc, i);
665 : :
1083 pg@bowt.ie 666 [ + + + + ]: 153089723 : if (HeapTupleHasNulls(tup) && att_isnull(i, bp))
667 : : {
7904 tgl@sss.pgh.pa.us 668 : 8739157 : usecache = false;
6505 bruce@momjian.us 669 : 8739157 : continue; /* this cannot be the target att */
670 : : }
671 : :
672 : : /* If we know the next offset, we can skip the rest */
2939 andres@anarazel.de 673 [ + + + + ]: 144350566 : if (usecache && att->attcacheoff >= 0)
674 : 80885743 : off = att->attcacheoff;
675 [ + + ]: 63464823 : 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 : : */
6728 tgl@sss.pgh.pa.us 683 [ + + ]: 10464287 : if (usecache &&
259 drowley@postgresql.o 684 [ + + ]: 1163059 : off == att_nominal_alignby(off, att->attalignby))
2939 andres@anarazel.de 685 : 46708 : att->attcacheoff = off;
686 : : else
687 : : {
259 drowley@postgresql.o 688 [ + + ]: 10417579 : off = att_pointer_alignby(off, att->attalignby, -1,
689 : : tp + off);
6728 tgl@sss.pgh.pa.us 690 : 10417579 : usecache = false;
691 : : }
692 : : }
693 : : else
694 : : {
695 : : /* not varlena, so safe to use att_nominal_alignby */
259 drowley@postgresql.o 696 : 53000536 : off = att_nominal_alignby(off, att->attalignby);
697 : :
10226 bruce@momjian.us 698 [ + + ]: 53000536 : if (usecache)
2939 andres@anarazel.de 699 : 271253 : att->attcacheoff = off;
700 : : }
701 : :
6728 tgl@sss.pgh.pa.us 702 [ + + ]: 144350566 : if (i == attnum)
703 : 31326909 : break;
704 : :
2939 andres@anarazel.de 705 [ + + + - : 113023657 : off = att_addlength_pointer(off, att->attlen, tp + off);
- - + + +
- - + + +
- - ]
706 : :
707 [ + + + + ]: 113023657 : if (usecache && att->attlen <= 0)
9861 bruce@momjian.us 708 : 28929188 : usecache = false;
709 : : }
710 : : }
711 : :
260 drowley@postgresql.o 712 : 31595115 : 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
7828 tgl@sss.pgh.pa.us 725 : 72484 : heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
726 : : {
727 : : Datum result;
728 : :
9019 729 [ - + ]: 72484 : Assert(tup);
730 : :
731 : : /* Currently, no sys attribute ever reads as NULL. */
5718 rhaas@postgresql.org 732 : 72484 : *isnull = false;
733 : :
9019 tgl@sss.pgh.pa.us 734 [ + + + + : 72484 : switch (attnum)
+ - ]
735 : : {
736 : 2 : case SelfItemPointerAttributeNumber:
737 : : /* pass-by-reference datatype */
738 : 2 : result = PointerGetDatum(&(tup->t_self));
739 : 2 : break;
740 : 72372 : case MinTransactionIdAttributeNumber:
4276 rhaas@postgresql.org 741 : 72372 : result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmin(tup->t_data));
9019 tgl@sss.pgh.pa.us 742 : 72372 : break;
743 : 15 : case MaxTransactionIdAttributeNumber:
4609 alvherre@alvh.no-ip. 744 : 15 : result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmax(tup->t_data));
9019 tgl@sss.pgh.pa.us 745 : 15 : break;
6784 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));
9019 756 : 93 : break;
757 : 2 : case TableOidAttributeNumber:
758 : 2 : result = ObjectIdGetDatum(tup->t_tableOid);
759 : 2 : break;
9019 tgl@sss.pgh.pa.us 760 :UBC 0 : default:
8083 761 [ # # ]: 0 : elog(ERROR, "invalid attnum: %d", attnum);
762 : : result = 0; /* keep compiler quiet */
763 : : break;
764 : : }
9019 tgl@sss.pgh.pa.us 765 :CBC 72484 : 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
10651 scrappy@hub.org 778 : 7232032 : heap_copytuple(HeapTuple tuple)
779 : : {
780 : : HeapTuple newTuple;
781 : :
9780 vadim4o@yahoo.com 782 [ + - - + ]: 7232032 : if (!HeapTupleIsValid(tuple) || tuple->t_data == NULL)
9867 bruce@momjian.us 783 :UBC 0 : return NULL;
784 : :
9780 vadim4o@yahoo.com 785 :CBC 7232032 : newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + tuple->t_len);
786 : 7232032 : newTuple->t_len = tuple->t_len;
787 : 7232032 : newTuple->t_self = tuple->t_self;
9062 tgl@sss.pgh.pa.us 788 : 7232032 : newTuple->t_tableOid = tuple->t_tableOid;
9780 vadim4o@yahoo.com 789 : 7232032 : newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
206 peter@eisentraut.org 790 : 7232032 : memcpy(newTuple->t_data, tuple->t_data, tuple->t_len);
9867 bruce@momjian.us 791 : 7232032 : 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
9780 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;
9062 tgl@sss.pgh.pa.us 814 : 0 : dest->t_tableOid = src->t_tableOid;
9780 vadim4o@yahoo.com 815 : 0 : dest->t_data = (HeapTupleHeader) palloc(src->t_len);
206 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
2719 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 : : {
2628 akapila@postgresql.o 881 [ # # ]: 0 : if (attrmiss[firstmissingnum].am_present)
2719 andrew@dunslane.net 882 : 0 : break;
883 : : else
2539 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 : : {
260 drowley@postgresql.o 897 : 0 : CompactAttribute *att = TupleDescCompactAttr(tupleDesc, attnum);
898 : :
259 899 [ # # # # ]: 0 : targetDataLen = att_datum_alignby(targetDataLen,
900 : : att->attalignby,
901 : : att->attlen,
902 : : attrmiss[attnum].am_value);
903 : :
32 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 */
2539 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 : : */
2719 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;
2509 950 : 0 : (*targetHeapTuple)->t_self = sourceTuple->t_self;
951 : :
2719 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 : : {
260 drowley@postgresql.o 1025 : 0 : CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, attnum);
1026 : :
2628 akapila@postgresql.o 1027 [ # # # # ]: 0 : if (attrmiss && attrmiss[attnum].am_present)
1028 : : {
2719 andrew@dunslane.net 1029 : 0 : fill_val(attr,
1030 : 0 : nullBits ? &nullBits : NULL,
1031 : : &bitMask,
1032 : : &targetData,
1033 : : infoMask,
2628 akapila@postgresql.o 1034 [ # # ]: 0 : attrmiss[attnum].am_value,
1035 : : false);
1036 : : }
1037 : : else
1038 : : {
2719 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
4146 tgl@sss.pgh.pa.us 1081 :CBC 39071 : 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 [ - + ]: 39071 : if (HeapTupleHasExternal(tuple))
4146 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 : : */
4146 tgl@sss.pgh.pa.us 1099 :CBC 39071 : td = (HeapTupleHeader) palloc(tuple->t_len);
206 peter@eisentraut.org 1100 : 39071 : memcpy(td, tuple->t_data, tuple->t_len);
1101 : :
4146 tgl@sss.pgh.pa.us 1102 : 39071 : HeapTupleHeaderSetDatumLength(td, tuple->t_len);
1103 : 39071 : HeapTupleHeaderSetTypeId(td, tupleDesc->tdtypeid);
1104 : 39071 : HeapTupleHeaderSetTypMod(td, tupleDesc->tdtypmod);
1105 : :
1106 : 39071 : 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
7479 1117 : 12613109 : 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 : 12613109 : bool hasnull = false;
1127 : 12613109 : int numberOfAttributes = tupleDescriptor->natts;
1128 : : int i;
1129 : :
1130 [ - + ]: 12613109 : if (numberOfAttributes > MaxTupleAttributeNumber)
7479 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 : : */
7479 tgl@sss.pgh.pa.us 1139 [ + + ]:CBC 62684663 : for (i = 0; i < numberOfAttributes; i++)
1140 : : {
1141 [ + + ]: 53244796 : if (isnull[i])
1142 : : {
4146 1143 : 3173242 : hasnull = true;
1144 : 3173242 : break;
1145 : : }
1146 : : }
1147 : :
1148 : : /*
1149 : : * Determine total space needed
1150 : : */
7479 1151 : 12613109 : len = offsetof(HeapTupleHeaderData, t_bits);
1152 : :
1153 [ + + ]: 12613109 : if (hasnull)
1154 : 3173242 : len += BITMAPLEN(numberOfAttributes);
1155 : :
1156 : 12613109 : hoff = len = MAXALIGN(len); /* align user data safely */
1157 : :
6728 1158 : 12613109 : data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
1159 : :
1160 : 12613109 : 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 : : */
3041 alvherre@alvh.no-ip. 1166 : 12613109 : tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
7479 tgl@sss.pgh.pa.us 1167 : 12613109 : 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 : 12613109 : tuple->t_len = len;
1175 : 12613109 : ItemPointerSetInvalid(&(tuple->t_self));
1176 : 12613109 : tuple->t_tableOid = InvalidOid;
1177 : :
1178 : 12613109 : HeapTupleHeaderSetDatumLength(td, len);
1179 : 12613109 : HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid);
1180 : 12613109 : HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod);
1181 : : /* We also make sure that t_ctid is invalid unless explicitly set */
3769 1182 : 12613109 : ItemPointerSetInvalid(&(td->t_ctid));
1183 : :
6815 bruce@momjian.us 1184 : 12613109 : HeapTupleHeaderSetNatts(td, numberOfAttributes);
7479 tgl@sss.pgh.pa.us 1185 : 12613109 : td->t_hoff = hoff;
1186 : :
1187 [ + + ]: 12613109 : 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 : 12613109 : 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 : 52886 : heap_modify_tuple(HeapTuple tuple,
1211 : : TupleDesc tupleDesc,
1212 : : const Datum *replValues,
1213 : : const bool *replIsnull,
1214 : : const bool *doReplace)
1215 : : {
1216 : 52886 : 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 : : */
1233 : 52886 : values = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
1234 : 52886 : isnull = (bool *) palloc(numberOfAttributes * sizeof(bool));
1235 : :
1236 : 52886 : heap_deform_tuple(tuple, tupleDesc, values, isnull);
1237 : :
1238 [ + + ]: 1605811 : for (attoff = 0; attoff < numberOfAttributes; attoff++)
1239 : : {
1240 [ + + ]: 1552925 : if (doReplace[attoff])
1241 : : {
1242 : 737455 : values[attoff] = replValues[attoff];
1243 : 737455 : isnull[attoff] = replIsnull[attoff];
1244 : : }
1245 : : }
1246 : :
1247 : : /*
1248 : : * create a new tuple from the values and isnull arrays
1249 : : */
1250 : 52886 : newTuple = heap_form_tuple(tupleDesc, values, isnull);
1251 : :
1252 : 52886 : pfree(values);
1253 : 52886 : pfree(isnull);
1254 : :
1255 : : /*
1256 : : * copy the identification info of the old tuple: t_ctid, t_self
1257 : : */
1258 : 52886 : newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
1259 : 52886 : newTuple->t_self = tuple->t_self;
1260 : 52886 : newTuple->t_tableOid = tuple->t_tableOid;
1261 : :
1262 : 52886 : 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
3224 1278 : 655 : 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 : 655 : 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 : : */
1295 : 655 : values = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
1296 : 655 : isnull = (bool *) palloc(numberOfAttributes * sizeof(bool));
1297 : :
1298 : 655 : heap_deform_tuple(tuple, tupleDesc, values, isnull);
1299 : :
1300 [ + + ]: 1901 : for (i = 0; i < nCols; i++)
1301 : : {
1302 : 1246 : int attnum = replCols[i];
1303 : :
1304 [ + - - + ]: 1246 : if (attnum <= 0 || attnum > numberOfAttributes)
3224 tgl@sss.pgh.pa.us 1305 [ # # ]:UBC 0 : elog(ERROR, "invalid column number %d", attnum);
3224 tgl@sss.pgh.pa.us 1306 :CBC 1246 : values[attnum - 1] = replValues[i];
1307 : 1246 : isnull[attnum - 1] = replIsnull[i];
1308 : : }
1309 : :
1310 : : /*
1311 : : * create a new tuple from the values and isnull arrays
1312 : : */
1313 : 655 : newTuple = heap_form_tuple(tupleDesc, values, isnull);
1314 : :
1315 : 655 : pfree(values);
1316 : 655 : pfree(isnull);
1317 : :
1318 : : /*
1319 : : * copy the identification info of the old tuple: t_ctid, t_self
1320 : : */
1321 : 655 : newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
1322 : 655 : newTuple->t_self = tuple->t_self;
1323 : 655 : newTuple->t_tableOid = tuple->t_tableOid;
1324 : :
1325 : 655 : 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
7479 1346 : 2734785 : heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
1347 : : Datum *values, bool *isnull)
1348 : : {
1349 : 2734785 : HeapTupleHeader tup = tuple->t_data;
1350 : 2734785 : bool hasnulls = HeapTupleHasNulls(tuple);
1351 : 2734785 : 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 */
2999 1356 : 2734785 : bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
7479 1357 : 2734785 : bool slow = false; /* can we use/set attcacheoff? */
1358 : :
6815 bruce@momjian.us 1359 : 2734785 : 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 : : */
7479 tgl@sss.pgh.pa.us 1366 : 2734785 : natts = Min(natts, tdesc_natts);
1367 : :
1368 : 2734785 : tp = (char *) tup + tup->t_hoff;
1369 : :
1370 : 2734785 : off = 0;
1371 : :
1372 [ + + ]: 10587951 : for (attnum = 0; attnum < natts; attnum++)
1373 : : {
260 drowley@postgresql.o 1374 : 7853166 : CompactAttribute *thisatt = TupleDescCompactAttr(tupleDesc, attnum);
1375 : :
7479 tgl@sss.pgh.pa.us 1376 [ + + + + ]: 7853166 : if (hasnulls && att_isnull(attnum, bp))
1377 : : {
1378 : 532484 : values[attnum] = (Datum) 0;
1379 : 532484 : isnull[attnum] = true;
1380 : 532484 : slow = true; /* can't use attcacheoff anymore */
1381 : 532484 : continue;
1382 : : }
1383 : :
1384 : 7320682 : isnull[attnum] = false;
1385 : :
1386 [ + + + + ]: 7320682 : if (!slow && thisatt->attcacheoff >= 0)
1387 : 6904489 : off = thisatt->attcacheoff;
6728 1388 [ + + ]: 416193 : 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 [ + + ]: 201692 : if (!slow &&
259 drowley@postgresql.o 1397 [ + + ]: 35007 : off == att_nominal_alignby(off, thisatt->attalignby))
6728 tgl@sss.pgh.pa.us 1398 : 24097 : thisatt->attcacheoff = off;
1399 : : else
1400 : : {
259 drowley@postgresql.o 1401 [ + + ]: 177595 : off = att_pointer_alignby(off, thisatt->attalignby, -1,
1402 : : tp + off);
6728 tgl@sss.pgh.pa.us 1403 : 177595 : slow = true;
1404 : : }
1405 : : }
1406 : : else
1407 : : {
1408 : : /* not varlena, so safe to use att_nominal_alignby */
259 drowley@postgresql.o 1409 : 214501 : off = att_nominal_alignby(off, thisatt->attalignby);
1410 : :
7479 tgl@sss.pgh.pa.us 1411 [ + + ]: 214501 : if (!slow)
1412 : 42284 : thisatt->attcacheoff = off;
1413 : : }
1414 : :
1415 : 7320682 : values[attnum] = fetchatt(thisatt, tp + off);
1416 : :
6728 1417 [ + + + + : 7320682 : off = att_addlength_pointer(off, thisatt->attlen, tp + off);
- + + + +
- - + + +
- + ]
1418 : :
7479 1419 [ + + ]: 7320682 : if (thisatt->attlen <= 0)
1420 : 1302672 : 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 [ + + ]: 2734797 : for (; attnum < tdesc_natts; attnum++)
2719 andrew@dunslane.net 1428 : 12 : values[attnum] = getmissingattr(tupleDesc, attnum + 1, &isnull[attnum]);
7479 tgl@sss.pgh.pa.us 1429 : 2734785 : }
1430 : :
1431 : : /*
1432 : : * heap_freetuple
1433 : : */
1434 : : void
9396 JanWieck@Yahoo.com 1435 : 10593141 : heap_freetuple(HeapTuple htup)
1436 : : {
1437 : 10593141 : pfree(htup);
1438 : 10593141 : }
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
7011 tgl@sss.pgh.pa.us 1453 : 23896994 : 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 : 23896994 : bool hasnull = false;
1464 : 23896994 : int numberOfAttributes = tupleDescriptor->natts;
1465 : : int i;
1466 : :
166 jdavis@postgresql.or 1467 [ - + ]: 23896994 : Assert(extra == MAXALIGN(extra));
1468 : :
7011 tgl@sss.pgh.pa.us 1469 [ - + ]: 23896994 : if (numberOfAttributes > MaxTupleAttributeNumber)
7011 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 : : */
7011 tgl@sss.pgh.pa.us 1478 [ + + ]:CBC 66549236 : for (i = 0; i < numberOfAttributes; i++)
1479 : : {
1480 [ + + ]: 42986669 : if (isnull[i])
1481 : : {
4146 1482 : 334427 : hasnull = true;
1483 : 334427 : break;
1484 : : }
1485 : : }
1486 : :
1487 : : /*
1488 : : * Determine total space needed
1489 : : */
3850 1490 : 23896994 : len = SizeofMinimalTupleHeader;
1491 : :
7011 1492 [ + + ]: 23896994 : if (hasnull)
1493 : 334427 : len += BITMAPLEN(numberOfAttributes);
1494 : :
1495 : 23896994 : hoff = len = MAXALIGN(len); /* align user data safely */
1496 : :
6728 1497 : 23896994 : data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
1498 : :
1499 : 23896994 : len += data_len;
1500 : :
1501 : : /*
1502 : : * Allocate and zero the space needed.
1503 : : */
166 jdavis@postgresql.or 1504 : 23896994 : mem = palloc0(len + extra);
1505 : 23896994 : memset(mem, 0, extra);
1506 : 23896994 : tuple = (MinimalTuple) (mem + extra);
1507 : :
1508 : : /*
1509 : : * And fill in the information.
1510 : : */
7011 tgl@sss.pgh.pa.us 1511 : 23896994 : tuple->t_len = len;
6815 bruce@momjian.us 1512 : 23896994 : HeapTupleHeaderSetNatts(tuple, numberOfAttributes);
7011 tgl@sss.pgh.pa.us 1513 : 23896994 : tuple->t_hoff = hoff + MINIMAL_TUPLE_OFFSET;
1514 : :
1515 [ + + ]: 23896994 : heap_fill_tuple(tupleDescriptor,
1516 : : values,
1517 : : isnull,
1518 : : (char *) tuple + hoff,
1519 : : data_len,
1520 : : &tuple->t_infomask,
1521 : : (hasnull ? tuple->t_bits : NULL));
1522 : :
1523 : 23896994 : return tuple;
1524 : : }
1525 : :
1526 : : /*
1527 : : * heap_free_minimal_tuple
1528 : : */
1529 : : void
1530 : 20212737 : heap_free_minimal_tuple(MinimalTuple mtup)
1531 : : {
1532 : 20212737 : pfree(mtup);
1533 : 20212737 : }
1534 : :
1535 : : /*
1536 : : * heap_copy_minimal_tuple
1537 : : * copy a MinimalTuple
1538 : : *
1539 : : * The result is allocated in the current memory context.
1540 : : */
1541 : : MinimalTuple
166 jdavis@postgresql.or 1542 : 2482364 : heap_copy_minimal_tuple(MinimalTuple mtup, Size extra)
1543 : : {
1544 : : MinimalTuple result;
1545 : : char *mem;
1546 : :
1547 [ - + ]: 2482364 : Assert(extra == MAXALIGN(extra));
1548 : 2482364 : mem = palloc(mtup->t_len + extra);
1549 : 2482364 : memset(mem, 0, extra);
1550 : 2482364 : result = (MinimalTuple) (mem + extra);
7011 tgl@sss.pgh.pa.us 1551 : 2482364 : memcpy(result, mtup, mtup->t_len);
1552 : 2482364 : return result;
1553 : : }
1554 : :
1555 : : /*
1556 : : * heap_tuple_from_minimal_tuple
1557 : : * create a HeapTuple by copying from a MinimalTuple;
1558 : : * system columns are filled with zeroes
1559 : : *
1560 : : * The result is allocated in the current memory context.
1561 : : * The HeapTuple struct, tuple header, and tuple data are all allocated
1562 : : * as a single palloc() block.
1563 : : */
1564 : : HeapTuple
1565 : 384752 : heap_tuple_from_minimal_tuple(MinimalTuple mtup)
1566 : : {
1567 : : HeapTuple result;
1568 : 384752 : uint32 len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
1569 : :
1570 : 384752 : result = (HeapTuple) palloc(HEAPTUPLESIZE + len);
1571 : 384752 : result->t_len = len;
1572 : 384752 : ItemPointerSetInvalid(&(result->t_self));
1573 : 384752 : result->t_tableOid = InvalidOid;
1574 : 384752 : result->t_data = (HeapTupleHeader) ((char *) result + HEAPTUPLESIZE);
1575 : 384752 : memcpy((char *) result->t_data + MINIMAL_TUPLE_OFFSET, mtup, mtup->t_len);
6815 bruce@momjian.us 1576 : 384752 : memset(result->t_data, 0, offsetof(HeapTupleHeaderData, t_infomask2));
7011 tgl@sss.pgh.pa.us 1577 : 384752 : return result;
1578 : : }
1579 : :
1580 : : /*
1581 : : * minimal_tuple_from_heap_tuple
1582 : : * create a MinimalTuple by copying from a HeapTuple
1583 : : *
1584 : : * The result is allocated in the current memory context.
1585 : : */
1586 : : MinimalTuple
166 jdavis@postgresql.or 1587 : 2242735 : minimal_tuple_from_heap_tuple(HeapTuple htup, Size extra)
1588 : : {
1589 : : MinimalTuple result;
1590 : : char *mem;
1591 : : uint32 len;
1592 : :
1593 [ - + ]: 2242735 : Assert(extra == MAXALIGN(extra));
7011 tgl@sss.pgh.pa.us 1594 [ - + ]: 2242735 : Assert(htup->t_len > MINIMAL_TUPLE_OFFSET);
1595 : 2242735 : len = htup->t_len - MINIMAL_TUPLE_OFFSET;
166 jdavis@postgresql.or 1596 : 2242735 : mem = palloc(len + extra);
1597 : 2242735 : memset(mem, 0, extra);
1598 : 2242735 : result = (MinimalTuple) (mem + extra);
7011 tgl@sss.pgh.pa.us 1599 : 2242735 : memcpy(result, (char *) htup->t_data + MINIMAL_TUPLE_OFFSET, len);
1600 : :
1601 : 2242735 : result->t_len = len;
1602 : 2242735 : return result;
1603 : : }
1604 : :
1605 : : /*
1606 : : * This mainly exists so JIT can inline the definition, but it's also
1607 : : * sometimes useful in debugging sessions.
1608 : : */
1609 : : size_t
2721 andres@anarazel.de 1610 :UBC 0 : varsize_any(void *p)
1611 : : {
1612 [ # # # # : 0 : return VARSIZE_ANY(p);
# # # # #
# ]
1613 : : }
|