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-2026, 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
1398 drowley@postgresql.o 44 :CBC 9410281 : index_form_tuple(TupleDesc tupleDescriptor,
45 : : const Datum *values,
46 : : const bool *isnull)
47 : : {
48 : 9410281 : 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 : 17579590 : 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;
10466 bruce@momjian.us 76 : 17579590 : unsigned short infomask = 0;
77 : 17579590 : bool hasnull = false;
10411 vadim4o@yahoo.com 78 : 17579590 : uint16 tupmask = 0;
10466 bruce@momjian.us 79 : 17579590 : int numberOfAttributes = tupleDescriptor->natts;
80 : :
81 : : #ifdef TOAST_INDEX_HACK
938 peter@eisentraut.org 82 : 17579590 : Datum untoasted_values[INDEX_MAX_KEYS] = {0};
83 : 17579590 : bool untoasted_free[INDEX_MAX_KEYS] = {0};
84 : : #endif
85 : :
9611 bruce@momjian.us 86 [ - + ]: 17579590 : if (numberOfAttributes > INDEX_MAX_KEYS)
8324 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
9418 JanWieck@Yahoo.com 93 [ + + ]:CBC 44784591 : for (i = 0; i < numberOfAttributes; i++)
94 : : {
3180 andres@anarazel.de 95 : 27205001 : Form_pg_attribute att = TupleDescAttr(tupleDescriptor, i);
96 : :
7715 tgl@sss.pgh.pa.us 97 : 27205001 : untoasted_values[i] = values[i];
9210 98 : 27205001 : untoasted_free[i] = false;
99 : :
100 : : /* Do nothing if value is NULL or not of varlena type */
7715 101 [ + + + + ]: 27205001 : if (isnull[i] || att->attlen != -1)
9210 102 : 26345856 : 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 : : */
6592 alvherre@alvh.no-ip. 108 [ + + ]: 859145 : if (VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
109 : : {
6969 tgl@sss.pgh.pa.us 110 : 241 : untoasted_values[i] =
83 michael@paquier.xyz 111 :GNC 241 : PointerGetDatum(detoast_external_attr((varlena *)
3240 tgl@sss.pgh.pa.us 112 :ECB (181) : DatumGetPointer(values[i])));
9210 tgl@sss.pgh.pa.us 113 :CBC 241 : 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 : : */
6592 alvherre@alvh.no-ip. 120 [ + + + + ]: 1323897 : if (!VARATT_IS_EXTENDED(DatumGetPointer(untoasted_values[i])) &&
3240 tgl@sss.pgh.pa.us 121 [ + + ]: 464752 : VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
2253 122 [ + + ]: 64801 : (att->attstorage == TYPSTORAGE_EXTENDED ||
123 [ - + ]: 58187 : att->attstorage == TYPSTORAGE_MAIN))
124 : : {
125 : : Datum cvalue;
126 : :
1804 127 : 6614 : cvalue = toast_compress_datum(untoasted_values[i],
128 : 6614 : att->attcompression);
129 : :
9210 130 [ + + ]: 6614 : if (DatumGetPointer(cvalue) != NULL)
131 : : {
132 : : /* successful compression */
133 [ - + ]: 1831 : if (untoasted_free[i])
7715 tgl@sss.pgh.pa.us 134 :UBC 0 : pfree(DatumGetPointer(untoasted_values[i]));
7715 tgl@sss.pgh.pa.us 135 :CBC 1831 : untoasted_values[i] = cvalue;
9418 JanWieck@Yahoo.com 136 : 1831 : untoasted_free[i] = true;
137 : : }
138 : : }
139 : : }
140 : : #endif
141 : :
9355 tgl@sss.pgh.pa.us 142 [ + + ]: 44726424 : for (i = 0; i < numberOfAttributes; i++)
143 : : {
7715 144 [ + + ]: 27203465 : if (isnull[i])
145 : : {
10467 bruce@momjian.us 146 : 56631 : hasnull = true;
9355 tgl@sss.pgh.pa.us 147 : 56631 : break;
148 : : }
149 : : }
150 : :
10467 bruce@momjian.us 151 [ + + ]: 17579590 : if (hasnull)
152 : 56631 : infomask |= INDEX_NULL_MASK;
153 : :
154 : 17579590 : hoff = IndexInfoFindDataOffset(infomask);
155 : : #ifdef TOAST_INDEX_HACK
6969 tgl@sss.pgh.pa.us 156 : 17579590 : 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 : 17579590 : size = hoff + data_size;
9519 bruce@momjian.us 163 : 17579590 : size = MAXALIGN(size); /* be conservative */
164 : :
1398 drowley@postgresql.o 165 : 17579590 : tp = (char *) MemoryContextAllocZero(context, size);
10467 bruce@momjian.us 166 : 17579590 : tuple = (IndexTuple) tp;
167 : :
7715 tgl@sss.pgh.pa.us 168 [ + + ]: 17579590 : heap_fill_tuple(tupleDescriptor,
169 : : #ifdef TOAST_INDEX_HACK
170 : : untoasted_values,
171 : : #else
172 : : values,
173 : : #endif
174 : : isnull,
175 : : tp + hoff,
176 : : data_size,
177 : : &tupmask,
178 : : (hasnull ? (uint8 *) tp + sizeof(IndexTupleData) : NULL));
179 : :
180 : : #ifdef TOAST_INDEX_HACK
9418 JanWieck@Yahoo.com 181 [ + + ]: 44784591 : for (i = 0; i < numberOfAttributes; i++)
182 : : {
183 [ + + ]: 27205001 : if (untoasted_free[i])
7715 tgl@sss.pgh.pa.us 184 : 2072 : 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 : : */
8654 194 [ + + ]: 17579590 : if (tupmask & HEAP_HASVARWIDTH)
10467 bruce@momjian.us 195 : 2137156 : infomask |= INDEX_VAR_MASK;
196 : :
197 : : /* Also assert we got rid of external attributes */
198 : : #ifdef TOAST_INDEX_HACK
4387 tgl@sss.pgh.pa.us 199 [ - + ]: 17579590 : 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 : : */
9355 206 [ - + ]: 17579590 : if ((size & INDEX_SIZE_MASK) != size)
8324 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 : :
10467 bruce@momjian.us 212 :CBC 17579590 : infomask |= size;
213 : :
214 : : /*
215 : : * initialize metadata
216 : : */
217 : 17579590 : tuple->t_info = infomask;
10108 218 : 17579590 : 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 attcacheoff and the value is not null.
226 : : * ----------------
227 : : */
228 : : Datum
10321 229 : 33043439 : nocache_index_getattr(IndexTuple tup,
230 : : int attnum,
231 : : TupleDesc tupleDesc)
232 : : {
233 : : CompactAttribute *cattr;
234 : : char *tp; /* ptr to data part of tuple */
36 nathan@postgresql.or 235 :GNC 33043439 : uint8 *bp = NULL; /* ptr to null bitmap in tuple */
236 : : int data_off; /* tuple data offset */
237 : : int off; /* current offset within data */
238 : : int startAttr;
239 : : int firstNullAttr;
50 drowley@postgresql.o 240 : 33043439 : bool hasnulls = IndexTupleHasNulls(tup);
241 : : int i;
242 : :
243 : : /* Did someone forget to call TupleDescFinalize()? */
244 [ - + ]: 33043439 : Assert(tupleDesc->firstNonCachedOffsetAttr >= 0);
245 : :
9287 tgl@sss.pgh.pa.us 246 :CBC 33043439 : attnum--;
247 : :
50 drowley@postgresql.o 248 :GNC 33043439 : data_off = IndexInfoFindDataOffset(tup->t_info);
50 drowley@postgresql.o 249 :CBC 33043439 : tp = (char *) tup + data_off;
250 : :
251 : : /*
252 : : * To minimize the number of attributes we need to look at, start walking
253 : : * the tuple at the attribute with the highest attcacheoff prior to attnum
254 : : * or the first NULL attribute prior to attnum, whichever comes first.
255 : : */
50 drowley@postgresql.o 256 [ + + ]:GNC 33043439 : if (hasnulls)
257 : : {
36 nathan@postgresql.or 258 : 66689 : bp = (uint8 *) ((char *) tup + sizeof(IndexTupleData));
50 drowley@postgresql.o 259 : 66689 : firstNullAttr = first_null_attr(bp, attnum);
260 : : }
261 : : else
262 : 32976750 : firstNullAttr = attnum;
263 : :
49 264 [ + + + + ]: 33043439 : if (tupleDesc->firstNonCachedOffsetAttr > 0 && firstNullAttr > 0)
265 : : {
266 : : /*
267 : : * Try to start with the highest attribute with an attcacheoff that's
268 : : * prior to the one we're looking for, or with the attribute prior to
269 : : * the first NULL attribute, if there is one.
270 : : */
271 : 6253334 : startAttr = Min(tupleDesc->firstNonCachedOffsetAttr - 1, firstNullAttr - 1);
50 272 : 6253334 : off = TupleDescCompactAttr(tupleDesc, startAttr)->attcacheoff;
273 : : }
274 : : else
275 : : {
276 : : /* Otherwise, start at the beginning... */
277 : 26790105 : startAttr = 0;
278 : 26790105 : off = 0;
279 : : }
280 : :
281 : : /*
282 : : * Calculate 'off' up to the first NULL attr. We use two cheaper loops
283 : : * when the tuple has no variable-width columns. When variable-width
284 : : * columns exists, we use att_addlength_pointer() to move the offset
285 : : * beyond the current attribute.
286 : : */
287 [ + + ]: 33043439 : if (IndexTupleHasVarwidths(tup))
288 : : {
289 : : /* Calculate the offset up until the first NULL */
290 [ + + ]: 43633754 : for (i = startAttr; i < firstNullAttr; i++)
291 : : {
292 : 10637714 : cattr = TupleDescCompactAttr(tupleDesc, i);
293 : :
294 [ + + + + ]: 10637714 : off = att_pointer_alignby(off,
295 : : cattr->attalignby,
296 : : cattr->attlen,
297 : : tp + off);
298 [ + + + + : 10637714 : off = att_addlength_pointer(off, cattr->attlen, tp + off);
- + ]
299 : : }
300 : :
301 : : /* Calculate the offset for any remaining columns. */
302 [ + + ]: 32996048 : for (; i < attnum; i++)
303 : : {
304 [ - + ]: 8 : Assert(hasnulls);
305 : :
306 [ + - ]: 8 : if (att_isnull(i, bp))
307 : 8 : continue;
308 : :
50 drowley@postgresql.o 309 :UNC 0 : cattr = TupleDescCompactAttr(tupleDesc, i);
310 : :
311 [ # # # # ]: 0 : off = att_pointer_alignby(off,
312 : : cattr->attalignby,
313 : : cattr->attlen,
314 : : tp + off);
315 [ # # # # : 0 : off = att_addlength_pointer(off, cattr->attlen, tp + off);
# # ]
316 : : }
317 : : }
318 : : else
319 : : {
320 : : /* Handle tuples with only fixed-width attributes */
321 : :
322 : : /* Calculate the offset up until the first NULL */
50 drowley@postgresql.o 323 [ + + ]:GNC 48659 : for (i = startAttr; i < firstNullAttr; i++)
10892 scrappy@hub.org 324 :ECB (3959255) : {
50 drowley@postgresql.o 325 :GNC 1260 : cattr = TupleDescCompactAttr(tupleDesc, i);
326 : :
327 [ - + ]: 1260 : Assert(cattr->attlen > 0);
328 : 1260 : off = att_nominal_alignby(off, cattr->attalignby);
329 : 1260 : off += cattr->attlen;
330 : : }
331 : :
332 : : /* Calculate the offset for any remaining columns. */
333 [ + + ]: 47927 : for (; i < attnum; i++)
334 : : {
335 [ - + ]: 528 : Assert(hasnulls);
336 : :
337 [ + - ]: 528 : if (att_isnull(i, bp))
338 : 528 : continue;
339 : :
50 drowley@postgresql.o 340 :UNC 0 : cattr = TupleDescCompactAttr(tupleDesc, i);
341 : :
342 [ # # ]: 0 : Assert(cattr->attlen > 0);
343 : 0 : off = att_nominal_alignby(off, cattr->attalignby);
344 : 0 : off += cattr->attlen;
345 : : }
346 : : }
347 : :
50 drowley@postgresql.o 348 :GNC 33043439 : cattr = TupleDescCompactAttr(tupleDesc, attnum);
349 [ + + + + ]: 33043439 : off = att_pointer_alignby(off, cattr->attalignby,
350 : : cattr->attlen, tp + off);
351 : 33043439 : return fetchatt(cattr, tp + off);
352 : : }
353 : :
354 : : /*
355 : : * Convert an index tuple into Datum/isnull arrays.
356 : : *
357 : : * The caller must allocate sufficient storage for the output arrays.
358 : : * (INDEX_MAX_KEYS entries should be enough.)
359 : : *
360 : : * This is nearly the same as heap_deform_tuple(), but for IndexTuples.
361 : : * One difference is that the tuple should never have any missing columns.
362 : : */
363 : : void
6121 tgl@sss.pgh.pa.us 364 :CBC 2871521 : index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor,
365 : : Datum *values, bool *isnull)
366 : : {
367 : : char *tp; /* ptr to tuple data */
368 : : uint8 *bp; /* ptr to null bitmap in tuple */
369 : :
370 : : /* XXX "knows" t_bits are just after fixed tuple header! */
36 nathan@postgresql.or 371 :GNC 2871521 : bp = (uint8 *) ((char *) tup + sizeof(IndexTupleData));
372 : :
2620 tgl@sss.pgh.pa.us 373 :CBC 2871521 : tp = (char *) tup + IndexInfoFindDataOffset(tup->t_info);
374 : :
1856 375 : 2871521 : index_deform_tuple_internal(tupleDescriptor, values, isnull,
376 : 2871521 : tp, bp, IndexTupleHasNulls(tup));
377 : 2871521 : }
378 : :
379 : : /*
380 : : * Convert an index tuple into Datum/isnull arrays,
381 : : * without assuming any specific layout of the index tuple header.
382 : : *
383 : : * Caller must supply pointer to data area, pointer to nulls bitmap
384 : : * (which can be NULL if !hasnulls), and hasnulls flag.
385 : : */
386 : : void
387 : 2912264 : index_deform_tuple_internal(TupleDesc tupleDescriptor,
388 : : Datum *values, bool *isnull,
389 : : char *tp, uint8 *bp, int hasnulls)
390 : : {
391 : : CompactAttribute *cattr;
392 : 2912264 : int natts = tupleDescriptor->natts; /* number of atts to extract */
50 drowley@postgresql.o 393 :GNC 2912264 : int attnum = 0;
394 : 2912264 : uint32 off = 0; /* offset in tuple data */
395 : : int firstNonCacheOffsetAttr;
396 : : int firstNullAttr;
397 : :
398 : : /* Assert to protect callers who allocate fixed-size arrays */
1856 tgl@sss.pgh.pa.us 399 [ - + ]:CBC 2912264 : Assert(natts <= INDEX_MAX_KEYS);
400 : :
401 : : /* Did someone forget to call TupleDescFinalize()? */
50 drowley@postgresql.o 402 [ - + ]:GNC 2912264 : Assert(tupleDescriptor->firstNonCachedOffsetAttr >= 0);
403 : :
404 : 2912264 : firstNonCacheOffsetAttr = Min(tupleDescriptor->firstNonCachedOffsetAttr, natts);
405 : :
406 [ + + ]: 2912264 : if (hasnulls)
407 : : {
408 : 2688 : firstNullAttr = first_null_attr(bp, natts);
409 : 2688 : firstNonCacheOffsetAttr = Min(firstNonCacheOffsetAttr, firstNullAttr);
410 : : }
411 : : else
412 : 2909576 : firstNullAttr = natts;
413 : :
414 [ + + ]: 2912264 : if (firstNonCacheOffsetAttr > 0)
415 : : {
416 : : #ifdef USE_ASSERT_CHECKING
417 : : /* In Assert enabled builds, verify attcacheoff is correct */
418 : 2855649 : off = 0;
419 : : #endif
420 : :
421 : : do
422 : : {
423 : 3923992 : isnull[attnum] = false;
424 : 3923992 : cattr = TupleDescCompactAttr(tupleDescriptor, attnum);
425 : :
426 : : #ifdef USE_ASSERT_CHECKING
427 : 3923992 : off = att_nominal_alignby(off, cattr->attalignby);
428 [ - + ]: 3923992 : Assert(off == cattr->attcacheoff);
429 : 3923992 : off += cattr->attlen;
430 : : #endif
431 : :
432 : 7847984 : values[attnum] = fetch_att_noerr(tp + cattr->attcacheoff, cattr->attbyval,
433 : 3923992 : cattr->attlen);
434 [ + + ]: 3923992 : } while (++attnum < firstNonCacheOffsetAttr);
435 : :
436 : 2855649 : off = cattr->attcacheoff + cattr->attlen;
437 : : }
438 : :
439 [ + + ]: 2988509 : for (; attnum < firstNullAttr; attnum++)
440 : : {
441 : 76245 : isnull[attnum] = false;
442 : 76245 : cattr = TupleDescCompactAttr(tupleDescriptor, attnum);
443 : :
444 : : /* align 'off', fetch the datum, and increment off beyond the datum */
445 : 76245 : values[attnum] = align_fetch_then_add(tp,
446 : : &off,
447 : 76245 : cattr->attbyval,
448 : 76245 : cattr->attlen,
449 : 76245 : cattr->attalignby);
450 : : }
451 : :
452 [ + + ]: 2917429 : for (; attnum < natts; attnum++)
453 : : {
454 [ - + ]: 5165 : Assert(hasnulls);
455 : :
456 [ + + ]: 5165 : if (att_isnull(attnum, bp))
457 : : {
458 : 2704 : values[attnum] = (Datum) 0;
459 : 2704 : isnull[attnum] = true;
460 : 2704 : continue;
461 : : }
462 : :
463 : 2461 : isnull[attnum] = false;
464 : 2461 : cattr = TupleDescCompactAttr(tupleDescriptor, attnum);
465 : :
466 : : /* align 'off', fetch the attr's value, and increment off beyond it */
467 : 2461 : values[attnum] = align_fetch_then_add(tp,
468 : : &off,
469 : 2461 : cattr->attbyval,
470 : 2461 : cattr->attlen,
471 : 2461 : cattr->attalignby);
472 : : }
6121 tgl@sss.pgh.pa.us 473 :CBC 2912264 : }
474 : :
475 : : /*
476 : : * Create a palloc'd copy of an index tuple.
477 : : */
478 : : IndexTuple
8472 479 : 3471187 : CopyIndexTuple(IndexTuple source)
480 : : {
481 : : IndexTuple result;
482 : : Size size;
483 : :
10467 bruce@momjian.us 484 : 3471187 : size = IndexTupleSize(source);
8472 tgl@sss.pgh.pa.us 485 : 3471187 : result = (IndexTuple) palloc(size);
486 : 3471187 : memcpy(result, source, size);
487 : 3471187 : return result;
488 : : }
489 : :
490 : : /*
491 : : * Create a palloc'd copy of an index tuple, leaving only the first
492 : : * leavenatts attributes remaining.
493 : : *
494 : : * Truncation is guaranteed to result in an index tuple that is no
495 : : * larger than the original. It is safe to use the IndexTuple with
496 : : * the original tuple descriptor, but caller must avoid actually
497 : : * accessing truncated attributes from returned tuple! In practice
498 : : * this means that index_getattr() must be called with special care,
499 : : * and that the truncated tuple should only ever be accessed by code
500 : : * under caller's direct control.
501 : : *
502 : : * It's safe to call this function with a buffer lock held, since it
503 : : * never performs external table access. If it ever became possible
504 : : * for index tuples to contain EXTERNAL TOAST values, then this would
505 : : * have to be revisited.
506 : : */
507 : : IndexTuple
2938 teodor@sigaev.ru 508 : 40547 : index_truncate_tuple(TupleDesc sourceDescriptor, IndexTuple source,
509 : : int leavenatts)
510 : : {
511 : : TupleDesc truncdesc;
512 : : Datum values[INDEX_MAX_KEYS];
513 : : bool isnull[INDEX_MAX_KEYS];
514 : : IndexTuple truncated;
515 : :
2603 pg@bowt.ie 516 [ - + ]: 40547 : Assert(leavenatts <= sourceDescriptor->natts);
517 : :
518 : : /* Easy case: no truncation actually required */
519 [ + + ]: 40547 : if (leavenatts == sourceDescriptor->natts)
520 : 24078 : return CopyIndexTuple(source);
521 : :
522 : : /* Create temporary truncated tuple descriptor */
501 drowley@postgresql.o 523 : 16469 : truncdesc = CreateTupleDescTruncatedCopy(sourceDescriptor, leavenatts);
524 : :
525 : : /* Deform, form copy of tuple with fewer attributes */
2938 teodor@sigaev.ru 526 : 16469 : index_deform_tuple(source, truncdesc, values, isnull);
527 : 16469 : truncated = index_form_tuple(truncdesc, values, isnull);
528 : 16469 : truncated->t_tid = source->t_tid;
529 [ - + ]: 16469 : Assert(IndexTupleSize(truncated) <= IndexTupleSize(source));
530 : :
531 : : /*
532 : : * Cannot leak memory here, TupleDescCopy() doesn't allocate any inner
533 : : * structure, so, plain pfree() should clean all allocated memory
534 : : */
535 : 16469 : pfree(truncdesc);
536 : :
537 : 16469 : return truncated;
538 : : }
|