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-2024, 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
647 drowley@postgresql.o 44 :CBC 6155033 : index_form_tuple(TupleDesc tupleDescriptor,
45 : : const Datum *values,
46 : : const bool *isnull)
47 : : {
48 : 6155033 : 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 : 12273304 : 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;
9715 bruce@momjian.us 76 : 12273304 : unsigned short infomask = 0;
77 : 12273304 : bool hasnull = false;
9660 vadim4o@yahoo.com 78 : 12273304 : uint16 tupmask = 0;
9715 bruce@momjian.us 79 : 12273304 : int numberOfAttributes = tupleDescriptor->natts;
80 : :
81 : : #ifdef TOAST_INDEX_HACK
187 peter@eisentraut.org 82 :GNC 12273304 : Datum untoasted_values[INDEX_MAX_KEYS] = {0};
83 : 12273304 : bool untoasted_free[INDEX_MAX_KEYS] = {0};
84 : : #endif
85 : :
8860 bruce@momjian.us 86 [ - + ]:CBC 12273304 : if (numberOfAttributes > INDEX_MAX_KEYS)
7573 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
8667 JanWieck@Yahoo.com 93 [ + + ]:CBC 31484531 : for (i = 0; i < numberOfAttributes; i++)
94 : : {
2429 andres@anarazel.de 95 : 19211227 : Form_pg_attribute att = TupleDescAttr(tupleDescriptor, i);
96 : :
6964 tgl@sss.pgh.pa.us 97 : 19211227 : untoasted_values[i] = values[i];
8459 98 : 19211227 : untoasted_free[i] = false;
99 : :
100 : : /* Do nothing if value is NULL or not of varlena type */
6964 101 [ + + + + ]: 19211227 : if (isnull[i] || att->attlen != -1)
8459 102 : 18533031 : 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 : : */
5841 alvherre@alvh.no-ip. 108 [ + + ]: 678196 : if (VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
109 : : {
6218 tgl@sss.pgh.pa.us 110 : 181 : untoasted_values[i] =
1654 rhaas@postgresql.org 111 : 181 : PointerGetDatum(detoast_external_attr((struct varlena *)
2489 tgl@sss.pgh.pa.us 112 : 181 : DatumGetPointer(values[i])));
8459 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 : : */
5841 alvherre@alvh.no-ip. 120 [ + + ]: 678196 : if (!VARATT_IS_EXTENDED(DatumGetPointer(untoasted_values[i])) &&
2489 tgl@sss.pgh.pa.us 121 [ + + ]: 381539 : VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
1502 122 [ + + ]: 63161 : (att->attstorage == TYPSTORAGE_EXTENDED ||
123 [ - + ]: 56703 : att->attstorage == TYPSTORAGE_MAIN))
124 : : {
125 : : Datum cvalue;
126 : :
1053 127 : 6458 : cvalue = toast_compress_datum(untoasted_values[i],
128 : 6458 : att->attcompression);
129 : :
8459 130 [ + + ]: 6458 : if (DatumGetPointer(cvalue) != NULL)
131 : : {
132 : : /* successful compression */
133 [ - + ]: 1365 : if (untoasted_free[i])
6964 tgl@sss.pgh.pa.us 134 :UBC 0 : pfree(DatumGetPointer(untoasted_values[i]));
6964 tgl@sss.pgh.pa.us 135 :CBC 1365 : untoasted_values[i] = cvalue;
8667 JanWieck@Yahoo.com 136 : 1365 : untoasted_free[i] = true;
137 : : }
138 : : }
139 : : }
140 : : #endif
141 : :
8604 tgl@sss.pgh.pa.us 142 [ + + ]: 31430121 : for (i = 0; i < numberOfAttributes; i++)
143 : : {
6964 144 [ + + ]: 19210161 : if (isnull[i])
145 : : {
9716 bruce@momjian.us 146 : 53344 : hasnull = true;
8604 tgl@sss.pgh.pa.us 147 : 53344 : break;
148 : : }
149 : : }
150 : :
9716 bruce@momjian.us 151 [ + + ]: 12273304 : if (hasnull)
152 : 53344 : infomask |= INDEX_NULL_MASK;
153 : :
154 : 12273304 : hoff = IndexInfoFindDataOffset(infomask);
155 : : #ifdef TOAST_INDEX_HACK
6218 tgl@sss.pgh.pa.us 156 : 12273304 : 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 : 12273304 : size = hoff + data_size;
8768 bruce@momjian.us 163 : 12273304 : size = MAXALIGN(size); /* be conservative */
164 : :
647 drowley@postgresql.o 165 : 12273304 : tp = (char *) MemoryContextAllocZero(context, size);
9716 bruce@momjian.us 166 : 12273304 : tuple = (IndexTuple) tp;
167 : :
6964 tgl@sss.pgh.pa.us 168 [ + + ]: 12273304 : 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
8667 JanWieck@Yahoo.com 181 [ + + ]: 31484531 : for (i = 0; i < numberOfAttributes; i++)
182 : : {
183 [ + + ]: 19211227 : if (untoasted_free[i])
6964 tgl@sss.pgh.pa.us 184 : 1546 : 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 : : */
7903 194 [ + + ]: 12273304 : if (tupmask & HEAP_HASVARWIDTH)
9716 bruce@momjian.us 195 : 1554642 : infomask |= INDEX_VAR_MASK;
196 : :
197 : : /* Also assert we got rid of external attributes */
198 : : #ifdef TOAST_INDEX_HACK
3636 tgl@sss.pgh.pa.us 199 [ - + ]: 12273304 : 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 : : */
8604 206 [ - + ]: 12273304 : if ((size & INDEX_SIZE_MASK) != size)
7573 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 : :
9716 bruce@momjian.us 212 :CBC 12273304 : infomask |= size;
213 : :
214 : : /*
215 : : * initialize metadata
216 : : */
217 : 12273304 : tuple->t_info = infomask;
9357 218 : 12273304 : 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
9570 241 : 3044119 : nocache_index_getattr(IndexTuple tup,
242 : : int attnum,
243 : : TupleDesc tupleDesc)
244 : : {
245 : : char *tp; /* ptr to data part of tuple */
6218 tgl@sss.pgh.pa.us 246 : 3044119 : bits8 *bp = NULL; /* ptr to null bitmap in tuple */
247 : 3044119 : 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 : :
6958 260 : 3044119 : data_off = IndexInfoFindDataOffset(tup->t_info);
261 : :
8536 262 : 3044119 : attnum--;
263 : :
5208 rhaas@postgresql.org 264 [ + + ]: 3044119 : 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! */
6958 tgl@sss.pgh.pa.us 273 : 57544 : bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData));
274 : :
275 : : /*
276 : : * Now check to see if any preceding bits are null...
277 : : */
278 : : {
4753 bruce@momjian.us 279 : 57544 : int byte = attnum >> 3;
8536 tgl@sss.pgh.pa.us 280 : 57544 : int finalbit = attnum & 0x07;
281 : :
282 : : /* check for nulls "before" final bit of last byte */
283 [ + + ]: 57544 : if ((~bp[byte]) & ((1 << finalbit) - 1))
284 : 6 : slow = true;
285 : : else
286 : : {
287 : : /* check for nulls in any "earlier" bytes */
288 : : int i;
289 : :
290 [ - + ]: 57538 : for (i = 0; i < byte; i++)
291 : : {
8536 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 : :
9566 bruce@momjian.us 302 :CBC 3044119 : tp = (char *) tup + data_off;
303 : :
9716 304 [ + + ]: 3044119 : if (!slow)
305 : : {
306 : : Form_pg_attribute 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 : : */
2429 andres@anarazel.de 312 : 3044113 : att = TupleDescAttr(tupleDesc, attnum);
313 [ + + ]: 3044113 : if (att->attcacheoff >= 0)
314 : 57458 : 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 : : */
6218 tgl@sss.pgh.pa.us 321 [ + + ]: 2986655 : if (IndexTupleHasVarwidths(tup))
322 : : {
323 : : int j;
324 : :
325 [ + + ]: 3254134 : for (j = 0; j <= attnum; j++)
326 : : {
2429 andres@anarazel.de 327 [ + + ]: 3253114 : if (TupleDescAttr(tupleDesc, j)->attlen <= 0)
328 : : {
8536 tgl@sss.pgh.pa.us 329 : 2951149 : slow = true;
330 : 2951149 : break;
331 : : }
332 : : }
333 : : }
334 : : }
335 : :
9716 bruce@momjian.us 336 [ + + ]: 2986661 : if (!slow)
337 : : {
6218 tgl@sss.pgh.pa.us 338 : 35506 : int natts = tupleDesc->natts;
9544 bruce@momjian.us 339 : 35506 : 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 : : */
2429 andres@anarazel.de 350 : 35506 : TupleDescAttr(tupleDesc, 0)->attcacheoff = 0;
351 : :
352 : : /* we might have set some offsets in the slow path previously */
353 [ + + - + ]: 35506 : while (j < natts && TupleDescAttr(tupleDesc, j)->attcacheoff > 0)
9716 bruce@momjian.us 354 :UBC 0 : j++;
355 : :
2429 andres@anarazel.de 356 :CBC 35506 : off = TupleDescAttr(tupleDesc, j - 1)->attcacheoff +
357 : 35506 : TupleDescAttr(tupleDesc, j - 1)->attlen;
358 : :
6218 tgl@sss.pgh.pa.us 359 [ + + ]: 90033 : for (; j < natts; j++)
360 : : {
2429 andres@anarazel.de 361 : 55526 : Form_pg_attribute att = TupleDescAttr(tupleDesc, j);
362 : :
363 [ + + ]: 55526 : if (att->attlen <= 0)
6218 tgl@sss.pgh.pa.us 364 : 999 : break;
365 : :
2429 andres@anarazel.de 366 [ + + + + : 54527 : off = att_align_nominal(off, att->attalign);
+ + - + ]
367 : :
368 : 54527 : att->attcacheoff = off;
369 : :
370 : 54527 : off += att->attlen;
371 : : }
372 : :
6218 tgl@sss.pgh.pa.us 373 [ - + ]: 35506 : Assert(j > attnum);
374 : :
2429 andres@anarazel.de 375 : 35506 : off = TupleDescAttr(tupleDesc, attnum)->attcacheoff;
376 : : }
377 : : else
378 : : {
9544 bruce@momjian.us 379 : 2951155 : 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 : : */
6218 tgl@sss.pgh.pa.us 392 : 2951155 : off = 0;
5995 bruce@momjian.us 393 : 2951155 : for (i = 0;; i++) /* loop exit is at "break" */
10141 scrappy@hub.org 394 : 3637607 : {
2429 andres@anarazel.de 395 : 6588762 : Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
396 : :
6218 tgl@sss.pgh.pa.us 397 [ + + + + ]: 6588762 : if (IndexTupleHasNulls(tup) && att_isnull(i, bp))
398 : : {
399 : 6 : usecache = false;
5995 bruce@momjian.us 400 : 6 : continue; /* this cannot be the target att */
401 : : }
402 : :
403 : : /* If we know the next offset, we can skip the rest */
2429 andres@anarazel.de 404 [ + + + + ]: 6588756 : if (usecache && att->attcacheoff >= 0)
405 : 3242034 : off = att->attcacheoff;
406 [ + + ]: 3346722 : 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 : : */
6218 tgl@sss.pgh.pa.us 414 [ + + + + ]: 578338 : if (usecache &&
2429 andres@anarazel.de 415 [ + + - + : 4542 : off == att_align_nominal(off, att->attalign))
+ - - - ]
416 : 762 : att->attcacheoff = off;
417 : : else
418 : : {
419 [ + + + - : 573034 : off = att_align_pointer(off, att->attalign, -1,
- - - - -
- ]
420 : : tp + off);
6218 tgl@sss.pgh.pa.us 421 : 573034 : usecache = false;
422 : : }
423 : : }
424 : : else
425 : : {
426 : : /* not varlena, so safe to use att_align_nominal */
2429 andres@anarazel.de 427 [ + + + + : 2772926 : off = att_align_nominal(off, att->attalign);
+ + - + ]
428 : :
9566 bruce@momjian.us 429 [ + + ]: 2772926 : if (usecache)
2429 andres@anarazel.de 430 : 4715 : att->attcacheoff = off;
431 : : }
432 : :
6218 tgl@sss.pgh.pa.us 433 [ + + ]: 6588756 : if (i == attnum)
434 : 2951155 : break;
435 : :
2429 andres@anarazel.de 436 [ + + + + : 3637601 : off = att_addlength_pointer(off, att->attlen, tp + off);
- + - - -
- - - + +
- + ]
437 : :
438 [ + + + + ]: 3637601 : if (usecache && att->attlen <= 0)
8536 tgl@sss.pgh.pa.us 439 : 2941916 : usecache = false;
440 : : }
441 : : }
442 : :
2429 andres@anarazel.de 443 : 2986661 : return fetchatt(TupleDescAttr(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
5370 tgl@sss.pgh.pa.us 456 : 1989421 : 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! */
1869 463 : 1989421 : bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData));
464 : :
465 : 1989421 : tp = (char *) tup + IndexInfoFindDataOffset(tup->t_info);
466 : :
1105 467 : 1989421 : index_deform_tuple_internal(tupleDescriptor, values, isnull,
468 : 1989421 : tp, bp, IndexTupleHasNulls(tup));
469 : 1989421 : }
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 : 2019930 : index_deform_tuple_internal(TupleDesc tupleDescriptor,
480 : : Datum *values, bool *isnull,
481 : : char *tp, bits8 *bp, int hasnulls)
482 : : {
483 : 2019930 : int natts = tupleDescriptor->natts; /* number of atts to extract */
484 : : int attnum;
485 : 2019930 : int off = 0; /* offset in tuple data */
486 : 2019930 : bool slow = false; /* can we use/set attcacheoff? */
487 : :
488 : : /* Assert to protect callers who allocate fixed-size arrays */
489 [ - + ]: 2019930 : Assert(natts <= INDEX_MAX_KEYS);
490 : :
1869 491 [ + + ]: 4665563 : for (attnum = 0; attnum < natts; attnum++)
492 : : {
493 : 2645633 : Form_pg_attribute thisatt = TupleDescAttr(tupleDescriptor, attnum);
494 : :
495 [ + + + + ]: 2645633 : if (hasnulls && att_isnull(attnum, bp))
496 : : {
497 : 2724 : values[attnum] = (Datum) 0;
498 : 2724 : isnull[attnum] = true;
499 : 2724 : slow = true; /* can't use attcacheoff anymore */
500 : 2724 : continue;
501 : : }
502 : :
503 : 2642909 : isnull[attnum] = false;
504 : :
505 [ + + + + ]: 2642909 : if (!slow && thisatt->attcacheoff >= 0)
506 : 2632198 : off = thisatt->attcacheoff;
507 [ + + ]: 10711 : 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 [ + + + - ]: 5684 : if (!slow &&
516 [ + - - - : 5 : off == att_align_nominal(off, thisatt->attalign))
- - - - ]
517 : 5 : thisatt->attcacheoff = off;
518 : : else
519 : : {
520 [ + + + - : 5674 : off = att_align_pointer(off, thisatt->attalign, -1,
- - - - -
- ]
521 : : tp + off);
522 : 5674 : slow = true;
523 : : }
524 : : }
525 : : else
526 : : {
527 : : /* not varlena, so safe to use att_align_nominal */
528 [ + + + + : 5032 : off = att_align_nominal(off, thisatt->attalign);
+ + - + ]
529 : :
530 [ + + ]: 5032 : if (!slow)
531 : 957 : thisatt->attcacheoff = off;
532 : : }
533 : :
534 : 2642909 : values[attnum] = fetchatt(thisatt, tp + off);
535 : :
536 [ + + + + : 2642909 : off = att_addlength_pointer(off, thisatt->attlen, tp + off);
- + - - -
- - - + +
- + ]
537 : :
538 [ + + ]: 2642909 : if (thisatt->attlen <= 0)
539 : 35411 : slow = true; /* can't use attcacheoff anymore */
540 : : }
5370 541 : 2019930 : }
542 : :
543 : : /*
544 : : * Create a palloc'd copy of an index tuple.
545 : : */
546 : : IndexTuple
7721 547 : 2485553 : CopyIndexTuple(IndexTuple source)
548 : : {
549 : : IndexTuple result;
550 : : Size size;
551 : :
9716 bruce@momjian.us 552 : 2485553 : size = IndexTupleSize(source);
7721 tgl@sss.pgh.pa.us 553 : 2485553 : result = (IndexTuple) palloc(size);
554 : 2485553 : memcpy(result, source, size);
555 : 2485553 : 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
2187 teodor@sigaev.ru 576 : 29103 : 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 : :
1852 pg@bowt.ie 584 [ - + ]: 29103 : Assert(leavenatts <= sourceDescriptor->natts);
585 : :
586 : : /* Easy case: no truncation actually required */
587 [ + + ]: 29103 : if (leavenatts == sourceDescriptor->natts)
588 : 17371 : return CopyIndexTuple(source);
589 : :
590 : : /* Create temporary descriptor to scribble on */
2187 teodor@sigaev.ru 591 : 11732 : truncdesc = palloc(TupleDescSize(sourceDescriptor));
592 : 11732 : TupleDescCopy(truncdesc, sourceDescriptor);
593 : 11732 : truncdesc->natts = leavenatts;
594 : :
595 : : /* Deform, form copy of tuple with fewer attributes */
596 : 11732 : index_deform_tuple(source, truncdesc, values, isnull);
597 : 11732 : truncated = index_form_tuple(truncdesc, values, isnull);
598 : 11732 : truncated->t_tid = source->t_tid;
599 [ - + ]: 11732 : Assert(IndexTupleSize(truncated) <= IndexTupleSize(source));
600 : :
601 : : /*
602 : : * Cannot leak memory here, TupleDescCopy() doesn't allocate any inner
603 : : * structure, so, plain pfree() should clean all allocated memory
604 : : */
605 : 11732 : pfree(truncdesc);
606 : :
607 : 11732 : return truncated;
608 : : }
|