Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * indextuple.c
4 : : * This file contains index tuple accessor and mutator routines,
5 : : * as well as various tuple utilities.
6 : : *
7 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : *
11 : : * IDENTIFICATION
12 : : * src/backend/access/common/indextuple.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : :
17 : : #include "postgres.h"
18 : :
19 : : #include "access/detoast.h"
20 : : #include "access/heaptoast.h"
21 : : #include "access/htup_details.h"
22 : : #include "access/itup.h"
23 : : #include "access/toast_internals.h"
24 : :
25 : : /*
26 : : * This enables de-toasting of index entries. Needed until VACUUM is
27 : : * smart enough to rebuild indexes from scratch.
28 : : */
29 : : #define TOAST_INDEX_HACK
30 : :
31 : : /* ----------------------------------------------------------------
32 : : * index_ tuple interface routines
33 : : * ----------------------------------------------------------------
34 : : */
35 : :
36 : : /* ----------------
37 : : * index_form_tuple
38 : : *
39 : : * As index_form_tuple_context, but allocates the returned tuple in the
40 : : * CurrentMemoryContext.
41 : : * ----------------
42 : : */
43 : : IndexTuple
1157 drowley@postgresql.o 44 :CBC 6410649 : index_form_tuple(TupleDesc tupleDescriptor,
45 : : const Datum *values,
46 : : const bool *isnull)
47 : : {
48 : 6410649 : return index_form_tuple_context(tupleDescriptor, values, isnull,
49 : : CurrentMemoryContext);
50 : : }
51 : :
52 : : /* ----------------
53 : : * index_form_tuple_context
54 : : *
55 : : * This shouldn't leak any memory; otherwise, callers such as
56 : : * tuplesort_putindextuplevalues() will be very unhappy.
57 : : *
58 : : * This shouldn't perform external table access provided caller
59 : : * does not pass values that are stored EXTERNAL.
60 : : *
61 : : * Allocates returned tuple in provided 'context'.
62 : : * ----------------
63 : : */
64 : : IndexTuple
65 : 12980905 : index_form_tuple_context(TupleDesc tupleDescriptor,
66 : : const Datum *values,
67 : : const bool *isnull,
68 : : MemoryContext context)
69 : : {
70 : : char *tp; /* tuple pointer */
71 : : IndexTuple tuple; /* return tuple */
72 : : Size size,
73 : : data_size,
74 : : hoff;
75 : : int i;
10225 bruce@momjian.us 76 : 12980905 : unsigned short infomask = 0;
77 : 12980905 : bool hasnull = false;
10170 vadim4o@yahoo.com 78 : 12980905 : uint16 tupmask = 0;
10225 bruce@momjian.us 79 : 12980905 : int numberOfAttributes = tupleDescriptor->natts;
80 : :
81 : : #ifdef TOAST_INDEX_HACK
697 peter@eisentraut.org 82 : 12980905 : Datum untoasted_values[INDEX_MAX_KEYS] = {0};
83 : 12980905 : bool untoasted_free[INDEX_MAX_KEYS] = {0};
84 : : #endif
85 : :
9370 bruce@momjian.us 86 [ - + ]: 12980905 : if (numberOfAttributes > INDEX_MAX_KEYS)
8083 tgl@sss.pgh.pa.us 87 [ # # ]:UBC 0 : ereport(ERROR,
88 : : (errcode(ERRCODE_TOO_MANY_COLUMNS),
89 : : errmsg("number of index columns (%d) exceeds limit (%d)",
90 : : numberOfAttributes, INDEX_MAX_KEYS)));
91 : :
92 : : #ifdef TOAST_INDEX_HACK
9177 JanWieck@Yahoo.com 93 [ + + ]:CBC 33692398 : for (i = 0; i < numberOfAttributes; i++)
94 : : {
2939 andres@anarazel.de 95 : 20711493 : Form_pg_attribute att = TupleDescAttr(tupleDescriptor, i);
96 : :
7474 tgl@sss.pgh.pa.us 97 : 20711493 : untoasted_values[i] = values[i];
8969 98 : 20711493 : untoasted_free[i] = false;
99 : :
100 : : /* Do nothing if value is NULL or not of varlena type */
7474 101 [ + + + + ]: 20711493 : if (isnull[i] || att->attlen != -1)
8969 102 : 19989487 : continue;
103 : :
104 : : /*
105 : : * If value is stored EXTERNAL, must fetch it so we are not depending
106 : : * on outside storage. This should be improved someday.
107 : : */
6351 alvherre@alvh.no-ip. 108 [ + + ]: 722006 : if (VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
109 : : {
6728 tgl@sss.pgh.pa.us 110 : 181 : untoasted_values[i] =
2164 rhaas@postgresql.org 111 : 181 : PointerGetDatum(detoast_external_attr((struct varlena *)
2999 tgl@sss.pgh.pa.us 112 : 181 : DatumGetPointer(values[i])));
8969 113 : 181 : untoasted_free[i] = true;
114 : : }
115 : :
116 : : /*
117 : : * If value is above size target, and is of a compressible datatype,
118 : : * try to compress it in-line.
119 : : */
6351 alvherre@alvh.no-ip. 120 [ + + + + ]: 1145548 : if (!VARATT_IS_EXTENDED(DatumGetPointer(untoasted_values[i])) &&
2999 tgl@sss.pgh.pa.us 121 [ + + ]: 423542 : VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
2012 122 [ + + ]: 64811 : (att->attstorage == TYPSTORAGE_EXTENDED ||
123 [ - + ]: 58123 : att->attstorage == TYPSTORAGE_MAIN))
124 : : {
125 : : Datum cvalue;
126 : :
1563 127 : 6688 : cvalue = toast_compress_datum(untoasted_values[i],
128 : 6688 : att->attcompression);
129 : :
8969 130 [ + + ]: 6688 : if (DatumGetPointer(cvalue) != NULL)
131 : : {
132 : : /* successful compression */
133 [ - + ]: 1398 : if (untoasted_free[i])
7474 tgl@sss.pgh.pa.us 134 :UBC 0 : pfree(DatumGetPointer(untoasted_values[i]));
7474 tgl@sss.pgh.pa.us 135 :CBC 1398 : untoasted_values[i] = cvalue;
9177 JanWieck@Yahoo.com 136 : 1398 : untoasted_free[i] = true;
137 : : }
138 : : }
139 : : }
140 : : #endif
141 : :
9114 tgl@sss.pgh.pa.us 142 [ + + ]: 33637817 : for (i = 0; i < numberOfAttributes; i++)
143 : : {
7474 144 [ + + ]: 20710322 : if (isnull[i])
145 : : {
10226 bruce@momjian.us 146 : 53410 : hasnull = true;
9114 tgl@sss.pgh.pa.us 147 : 53410 : break;
148 : : }
149 : : }
150 : :
10226 bruce@momjian.us 151 [ + + ]: 12980905 : if (hasnull)
152 : 53410 : infomask |= INDEX_NULL_MASK;
153 : :
154 : 12980905 : hoff = IndexInfoFindDataOffset(infomask);
155 : : #ifdef TOAST_INDEX_HACK
6728 tgl@sss.pgh.pa.us 156 : 12980905 : data_size = heap_compute_data_size(tupleDescriptor,
157 : : untoasted_values, isnull);
158 : : #else
159 : : data_size = heap_compute_data_size(tupleDescriptor,
160 : : values, isnull);
161 : : #endif
162 : 12980905 : size = hoff + data_size;
9278 bruce@momjian.us 163 : 12980905 : size = MAXALIGN(size); /* be conservative */
164 : :
1157 drowley@postgresql.o 165 : 12980905 : tp = (char *) MemoryContextAllocZero(context, size);
10226 bruce@momjian.us 166 : 12980905 : tuple = (IndexTuple) tp;
167 : :
7474 tgl@sss.pgh.pa.us 168 [ + + ]: 12980905 : heap_fill_tuple(tupleDescriptor,
169 : : #ifdef TOAST_INDEX_HACK
170 : : untoasted_values,
171 : : #else
172 : : values,
173 : : #endif
174 : : isnull,
175 : : (char *) tp + hoff,
176 : : data_size,
177 : : &tupmask,
178 : : (hasnull ? (bits8 *) tp + sizeof(IndexTupleData) : NULL));
179 : :
180 : : #ifdef TOAST_INDEX_HACK
9177 JanWieck@Yahoo.com 181 [ + + ]: 33692398 : for (i = 0; i < numberOfAttributes; i++)
182 : : {
183 [ + + ]: 20711493 : if (untoasted_free[i])
7474 tgl@sss.pgh.pa.us 184 : 1579 : pfree(DatumGetPointer(untoasted_values[i]));
185 : : }
186 : : #endif
187 : :
188 : : /*
189 : : * We do this because heap_fill_tuple wants to initialize a "tupmask"
190 : : * which is used for HeapTuples, but we want an indextuple infomask. The
191 : : * only relevant info is the "has variable attributes" field. We have
192 : : * already set the hasnull bit above.
193 : : */
8413 194 [ + + ]: 12980905 : if (tupmask & HEAP_HASVARWIDTH)
10226 bruce@momjian.us 195 : 1732878 : infomask |= INDEX_VAR_MASK;
196 : :
197 : : /* Also assert we got rid of external attributes */
198 : : #ifdef TOAST_INDEX_HACK
4146 tgl@sss.pgh.pa.us 199 [ - + ]: 12980905 : Assert((tupmask & HEAP_HASEXTERNAL) == 0);
200 : : #endif
201 : :
202 : : /*
203 : : * Here we make sure that the size will fit in the field reserved for it
204 : : * in t_info.
205 : : */
9114 206 [ - + ]: 12980905 : if ((size & INDEX_SIZE_MASK) != size)
8083 tgl@sss.pgh.pa.us 207 [ # # ]:UBC 0 : ereport(ERROR,
208 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
209 : : errmsg("index row requires %zu bytes, maximum size is %zu",
210 : : size, (Size) INDEX_SIZE_MASK)));
211 : :
10226 bruce@momjian.us 212 :CBC 12980905 : infomask |= size;
213 : :
214 : : /*
215 : : * initialize metadata
216 : : */
217 : 12980905 : tuple->t_info = infomask;
9867 218 : 12980905 : return tuple;
219 : : }
220 : :
221 : : /* ----------------
222 : : * nocache_index_getattr
223 : : *
224 : : * This gets called from index_getattr() macro, and only in cases
225 : : * where we can't use cacheoffset and the value is not null.
226 : : *
227 : : * This caches attribute offsets in the attribute descriptor.
228 : : *
229 : : * An alternative way to speed things up would be to cache offsets
230 : : * with the tuple, but that seems more difficult unless you take
231 : : * the storage hit of actually putting those offsets into the
232 : : * tuple you send to disk. Yuck.
233 : : *
234 : : * This scheme will be slightly slower than that, but should
235 : : * perform well for queries which hit large #'s of tuples. After
236 : : * you cache the offsets once, examining all the other tuples using
237 : : * the same attribute descriptor will go much quicker. -cim 5/4/91
238 : : * ----------------
239 : : */
240 : : Datum
10080 241 : 3494865 : nocache_index_getattr(IndexTuple tup,
242 : : int attnum,
243 : : TupleDesc tupleDesc)
244 : : {
245 : : char *tp; /* ptr to data part of tuple */
6728 tgl@sss.pgh.pa.us 246 : 3494865 : bits8 *bp = NULL; /* ptr to null bitmap in tuple */
247 : 3494865 : bool slow = false; /* do we have to walk attrs? */
248 : : int data_off; /* tuple data offset */
249 : : int off; /* current offset within data */
250 : :
251 : : /* ----------------
252 : : * Three cases:
253 : : *
254 : : * 1: No nulls and no variable-width attributes.
255 : : * 2: Has a null or a var-width AFTER att.
256 : : * 3: Has nulls or var-widths BEFORE att.
257 : : * ----------------
258 : : */
259 : :
7468 260 : 3494865 : data_off = IndexInfoFindDataOffset(tup->t_info);
261 : :
9046 262 : 3494865 : attnum--;
263 : :
5718 rhaas@postgresql.org 264 [ + + ]: 3494865 : if (IndexTupleHasNulls(tup))
265 : : {
266 : : /*
267 : : * there's a null somewhere in the tuple
268 : : *
269 : : * check to see if desired att is null
270 : : */
271 : :
272 : : /* XXX "knows" t_bits are just after fixed tuple header! */
7468 tgl@sss.pgh.pa.us 273 : 57609 : bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData));
274 : :
275 : : /*
276 : : * Now check to see if any preceding bits are null...
277 : : */
278 : : {
5263 bruce@momjian.us 279 : 57609 : int byte = attnum >> 3;
9046 tgl@sss.pgh.pa.us 280 : 57609 : int finalbit = attnum & 0x07;
281 : :
282 : : /* check for nulls "before" final bit of last byte */
283 [ + + ]: 57609 : if ((~bp[byte]) & ((1 << finalbit) - 1))
284 : 366 : slow = true;
285 : : else
286 : : {
287 : : /* check for nulls in any "earlier" bytes */
288 : : int i;
289 : :
290 [ - + ]: 57243 : for (i = 0; i < byte; i++)
291 : : {
9046 tgl@sss.pgh.pa.us 292 [ # # ]:UBC 0 : if (bp[i] != 0xFF)
293 : : {
294 : 0 : slow = true;
295 : 0 : break;
296 : : }
297 : : }
298 : : }
299 : : }
300 : : }
301 : :
10076 bruce@momjian.us 302 :CBC 3494865 : tp = (char *) tup + data_off;
303 : :
10226 304 [ + + ]: 3494865 : if (!slow)
305 : : {
306 : : CompactAttribute *att;
307 : :
308 : : /*
309 : : * If we get here, there are no nulls up to and including the target
310 : : * attribute. If we have a cached offset, we can use it.
311 : : */
260 drowley@postgresql.o 312 : 3494499 : att = TupleDescCompactAttr(tupleDesc, attnum);
2939 andres@anarazel.de 313 [ + + ]: 3494499 : if (att->attcacheoff >= 0)
314 : 57163 : return fetchatt(att, tp + att->attcacheoff);
315 : :
316 : : /*
317 : : * Otherwise, check for non-fixed-length attrs up to and including
318 : : * target. If there aren't any, it's safe to cheaply initialize the
319 : : * cached offsets for these attrs.
320 : : */
6728 tgl@sss.pgh.pa.us 321 [ + + ]: 3437336 : if (IndexTupleHasVarwidths(tup))
322 : : {
323 : : int j;
324 : :
325 [ + + ]: 3576355 : for (j = 0; j <= attnum; j++)
326 : : {
260 drowley@postgresql.o 327 [ + + ]: 3567839 : if (TupleDescCompactAttr(tupleDesc, j)->attlen <= 0)
328 : : {
9046 tgl@sss.pgh.pa.us 329 : 3256880 : slow = true;
330 : 3256880 : break;
331 : : }
332 : : }
333 : : }
334 : : }
335 : :
10226 bruce@momjian.us 336 [ + + ]: 3437702 : if (!slow)
337 : : {
6728 tgl@sss.pgh.pa.us 338 : 180456 : int natts = tupleDesc->natts;
10054 bruce@momjian.us 339 : 180456 : int j = 1;
340 : :
341 : : /*
342 : : * If we get here, we have a tuple with no nulls or var-widths up to
343 : : * and including the target attribute, so we can use the cached offset
344 : : * ... only we don't have it yet, or we'd not have got here. Since
345 : : * it's cheap to compute offsets for fixed-width columns, we take the
346 : : * opportunity to initialize the cached offsets for *all* the leading
347 : : * fixed-width columns, in hope of avoiding future visits to this
348 : : * routine.
349 : : */
260 drowley@postgresql.o 350 : 180456 : TupleDescCompactAttr(tupleDesc, 0)->attcacheoff = 0;
351 : :
352 : : /* we might have set some offsets in the slow path previously */
353 [ + + - + ]: 180456 : while (j < natts && TupleDescCompactAttr(tupleDesc, j)->attcacheoff > 0)
10226 bruce@momjian.us 354 :UBC 0 : j++;
355 : :
260 drowley@postgresql.o 356 :CBC 180456 : off = TupleDescCompactAttr(tupleDesc, j - 1)->attcacheoff +
357 : 180456 : TupleDescCompactAttr(tupleDesc, j - 1)->attlen;
358 : :
6728 tgl@sss.pgh.pa.us 359 [ + + ]: 301606 : for (; j < natts; j++)
360 : : {
260 drowley@postgresql.o 361 : 130138 : CompactAttribute *att = TupleDescCompactAttr(tupleDesc, j);
362 : :
2939 andres@anarazel.de 363 [ + + ]: 130138 : if (att->attlen <= 0)
6728 tgl@sss.pgh.pa.us 364 : 8988 : break;
365 : :
259 drowley@postgresql.o 366 : 121150 : off = att_nominal_alignby(off, att->attalignby);
367 : :
2939 andres@anarazel.de 368 : 121150 : att->attcacheoff = off;
369 : :
370 : 121150 : off += att->attlen;
371 : : }
372 : :
6728 tgl@sss.pgh.pa.us 373 [ - + ]: 180456 : Assert(j > attnum);
374 : :
260 drowley@postgresql.o 375 : 180456 : off = TupleDescCompactAttr(tupleDesc, attnum)->attcacheoff;
376 : : }
377 : : else
378 : : {
10054 bruce@momjian.us 379 : 3257246 : bool usecache = true;
380 : : int i;
381 : :
382 : : /*
383 : : * Now we know that we have to walk the tuple CAREFULLY. But we still
384 : : * might be able to cache some offsets for next time.
385 : : *
386 : : * Note - This loop is a little tricky. For each non-null attribute,
387 : : * we have to first account for alignment padding before the attr,
388 : : * then advance over the attr based on its length. Nulls have no
389 : : * storage and no alignment padding either. We can use/set
390 : : * attcacheoff until we reach either a null or a var-width attribute.
391 : : */
6728 tgl@sss.pgh.pa.us 392 : 3257246 : off = 0;
6505 bruce@momjian.us 393 : 3257246 : for (i = 0;; i++) /* loop exit is at "break" */
10651 scrappy@hub.org 394 : 3941199 : {
260 drowley@postgresql.o 395 : 7198445 : CompactAttribute *att = TupleDescCompactAttr(tupleDesc, i);
396 : :
6728 tgl@sss.pgh.pa.us 397 [ + + + + ]: 7198445 : if (IndexTupleHasNulls(tup) && att_isnull(i, bp))
398 : : {
399 : 366 : usecache = false;
6505 bruce@momjian.us 400 : 366 : continue; /* this cannot be the target att */
401 : : }
402 : :
403 : : /* If we know the next offset, we can skip the rest */
2939 andres@anarazel.de 404 [ + + + + ]: 7198079 : if (usecache && att->attcacheoff >= 0)
405 : 3493501 : off = att->attcacheoff;
406 [ + + ]: 3704578 : else if (att->attlen == -1)
407 : : {
408 : : /*
409 : : * We can only cache the offset for a varlena attribute if the
410 : : * offset is already suitably aligned, so that there would be
411 : : * no pad bytes in any case: then the offset will be valid for
412 : : * either an aligned or unaligned value.
413 : : */
6728 tgl@sss.pgh.pa.us 414 [ + + ]: 655015 : if (usecache &&
259 drowley@postgresql.o 415 [ + + ]: 5236 : off == att_nominal_alignby(off, att->attalignby))
2939 andres@anarazel.de 416 : 1433 : att->attcacheoff = off;
417 : : else
418 : : {
259 drowley@postgresql.o 419 [ + + ]: 653582 : off = att_pointer_alignby(off, att->attalignby, -1,
420 : : tp + off);
6728 tgl@sss.pgh.pa.us 421 : 653582 : usecache = false;
422 : : }
423 : : }
424 : : else
425 : : {
426 : : /* not varlena, so safe to use att_nominal_alignby */
259 drowley@postgresql.o 427 : 3049563 : off = att_nominal_alignby(off, att->attalignby);
428 : :
10076 bruce@momjian.us 429 [ + + ]: 3049563 : if (usecache)
2939 andres@anarazel.de 430 : 59710 : att->attcacheoff = off;
431 : : }
432 : :
6728 tgl@sss.pgh.pa.us 433 [ + + ]: 7198079 : if (i == attnum)
434 : 3257246 : break;
435 : :
2939 andres@anarazel.de 436 [ + + + + : 3940833 : off = att_addlength_pointer(off, att->attlen, tp + off);
- + - - -
- - - + +
- + ]
437 : :
438 [ + + + + ]: 3940833 : if (usecache && att->attlen <= 0)
9046 tgl@sss.pgh.pa.us 439 : 3191961 : usecache = false;
440 : : }
441 : : }
442 : :
260 drowley@postgresql.o 443 : 3437702 : return fetchatt(TupleDescCompactAttr(tupleDesc, attnum), tp + off);
444 : : }
445 : :
446 : : /*
447 : : * Convert an index tuple into Datum/isnull arrays.
448 : : *
449 : : * The caller must allocate sufficient storage for the output arrays.
450 : : * (INDEX_MAX_KEYS entries should be enough.)
451 : : *
452 : : * This is nearly the same as heap_deform_tuple(), but for IndexTuples.
453 : : * One difference is that the tuple should never have any missing columns.
454 : : */
455 : : void
5880 tgl@sss.pgh.pa.us 456 : 2133587 : index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor,
457 : : Datum *values, bool *isnull)
458 : : {
459 : : char *tp; /* ptr to tuple data */
460 : : bits8 *bp; /* ptr to null bitmap in tuple */
461 : :
462 : : /* XXX "knows" t_bits are just after fixed tuple header! */
2379 463 : 2133587 : bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData));
464 : :
465 : 2133587 : tp = (char *) tup + IndexInfoFindDataOffset(tup->t_info);
466 : :
1615 467 : 2133587 : index_deform_tuple_internal(tupleDescriptor, values, isnull,
468 : 2133587 : tp, bp, IndexTupleHasNulls(tup));
469 : 2133587 : }
470 : :
471 : : /*
472 : : * Convert an index tuple into Datum/isnull arrays,
473 : : * without assuming any specific layout of the index tuple header.
474 : : *
475 : : * Caller must supply pointer to data area, pointer to nulls bitmap
476 : : * (which can be NULL if !hasnulls), and hasnulls flag.
477 : : */
478 : : void
479 : 2164505 : index_deform_tuple_internal(TupleDesc tupleDescriptor,
480 : : Datum *values, bool *isnull,
481 : : char *tp, bits8 *bp, int hasnulls)
482 : : {
483 : 2164505 : int natts = tupleDescriptor->natts; /* number of atts to extract */
484 : : int attnum;
485 : 2164505 : int off = 0; /* offset in tuple data */
486 : 2164505 : bool slow = false; /* can we use/set attcacheoff? */
487 : :
488 : : /* Assert to protect callers who allocate fixed-size arrays */
489 [ - + ]: 2164505 : Assert(natts <= INDEX_MAX_KEYS);
490 : :
2379 491 [ + + ]: 5147910 : for (attnum = 0; attnum < natts; attnum++)
492 : : {
260 drowley@postgresql.o 493 : 2983405 : CompactAttribute *thisatt = TupleDescCompactAttr(tupleDescriptor, attnum);
494 : :
2379 tgl@sss.pgh.pa.us 495 [ + + + + ]: 2983405 : if (hasnulls && att_isnull(attnum, bp))
496 : : {
497 : 2649 : values[attnum] = (Datum) 0;
498 : 2649 : isnull[attnum] = true;
499 : 2649 : slow = true; /* can't use attcacheoff anymore */
500 : 2649 : continue;
501 : : }
502 : :
503 : 2980756 : isnull[attnum] = false;
504 : :
505 [ + + + + ]: 2980756 : if (!slow && thisatt->attcacheoff >= 0)
506 : 2954801 : off = thisatt->attcacheoff;
507 [ + + ]: 25955 : else if (thisatt->attlen == -1)
508 : : {
509 : : /*
510 : : * We can only cache the offset for a varlena attribute if the
511 : : * offset is already suitably aligned, so that there would be no
512 : : * pad bytes in any case: then the offset will be valid for either
513 : : * an aligned or unaligned value.
514 : : */
515 [ + + ]: 5580 : if (!slow &&
259 drowley@postgresql.o 516 [ + - ]: 160 : off == att_nominal_alignby(off, thisatt->attalignby))
2379 tgl@sss.pgh.pa.us 517 : 160 : thisatt->attcacheoff = off;
518 : : else
519 : : {
259 drowley@postgresql.o 520 [ + + ]: 5420 : off = att_pointer_alignby(off, thisatt->attalignby, -1,
521 : : tp + off);
2379 tgl@sss.pgh.pa.us 522 : 5420 : slow = true;
523 : : }
524 : : }
525 : : else
526 : : {
527 : : /* not varlena, so safe to use att_nominal_alignby */
259 drowley@postgresql.o 528 : 20375 : off = att_nominal_alignby(off, thisatt->attalignby);
529 : :
2379 tgl@sss.pgh.pa.us 530 [ + + ]: 20375 : if (!slow)
531 : 16360 : thisatt->attcacheoff = off;
532 : : }
533 : :
534 : 2980756 : values[attnum] = fetchatt(thisatt, tp + off);
535 : :
536 [ + + + + : 2980756 : off = att_addlength_pointer(off, thisatt->attlen, tp + off);
- + - - -
- - - + +
- + ]
537 : :
538 [ + + ]: 2980756 : if (thisatt->attlen <= 0)
539 : 35484 : slow = true; /* can't use attcacheoff anymore */
540 : : }
5880 541 : 2164505 : }
542 : :
543 : : /*
544 : : * Create a palloc'd copy of an index tuple.
545 : : */
546 : : IndexTuple
8231 547 : 2513418 : CopyIndexTuple(IndexTuple source)
548 : : {
549 : : IndexTuple result;
550 : : Size size;
551 : :
10226 bruce@momjian.us 552 : 2513418 : size = IndexTupleSize(source);
8231 tgl@sss.pgh.pa.us 553 : 2513418 : result = (IndexTuple) palloc(size);
554 : 2513418 : memcpy(result, source, size);
555 : 2513418 : return result;
556 : : }
557 : :
558 : : /*
559 : : * Create a palloc'd copy of an index tuple, leaving only the first
560 : : * leavenatts attributes remaining.
561 : : *
562 : : * Truncation is guaranteed to result in an index tuple that is no
563 : : * larger than the original. It is safe to use the IndexTuple with
564 : : * the original tuple descriptor, but caller must avoid actually
565 : : * accessing truncated attributes from returned tuple! In practice
566 : : * this means that index_getattr() must be called with special care,
567 : : * and that the truncated tuple should only ever be accessed by code
568 : : * under caller's direct control.
569 : : *
570 : : * It's safe to call this function with a buffer lock held, since it
571 : : * never performs external table access. If it ever became possible
572 : : * for index tuples to contain EXTERNAL TOAST values, then this would
573 : : * have to be revisited.
574 : : */
575 : : IndexTuple
2697 teodor@sigaev.ru 576 : 31252 : index_truncate_tuple(TupleDesc sourceDescriptor, IndexTuple source,
577 : : int leavenatts)
578 : : {
579 : : TupleDesc truncdesc;
580 : : Datum values[INDEX_MAX_KEYS];
581 : : bool isnull[INDEX_MAX_KEYS];
582 : : IndexTuple truncated;
583 : :
2362 pg@bowt.ie 584 [ - + ]: 31252 : Assert(leavenatts <= sourceDescriptor->natts);
585 : :
586 : : /* Easy case: no truncation actually required */
587 [ + + ]: 31252 : if (leavenatts == sourceDescriptor->natts)
588 : 17888 : return CopyIndexTuple(source);
589 : :
590 : : /* Create temporary truncated tuple descriptor */
260 drowley@postgresql.o 591 : 13364 : truncdesc = CreateTupleDescTruncatedCopy(sourceDescriptor, leavenatts);
592 : :
593 : : /* Deform, form copy of tuple with fewer attributes */
2697 teodor@sigaev.ru 594 : 13364 : index_deform_tuple(source, truncdesc, values, isnull);
595 : 13364 : truncated = index_form_tuple(truncdesc, values, isnull);
596 : 13364 : truncated->t_tid = source->t_tid;
597 [ - + ]: 13364 : Assert(IndexTupleSize(truncated) <= IndexTupleSize(source));
598 : :
599 : : /*
600 : : * Cannot leak memory here, TupleDescCopy() doesn't allocate any inner
601 : : * structure, so, plain pfree() should clean all allocated memory
602 : : */
603 : 13364 : pfree(truncdesc);
604 : :
605 : 13364 : return truncated;
606 : : }
|