LCOV - code coverage report
Current view: top level - src/backend/access/common - indextuple.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 94.0 % 149 140
Test Date: 2026-07-12 08:15:39 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 81.2 % 96 78

             Branch data     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
      44                 :     8665476 : index_form_tuple(TupleDesc tupleDescriptor,
      45                 :             :                  const Datum *values,
      46                 :             :                  const bool *isnull)
      47                 :             : {
      48                 :     8665476 :     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                 :    16686002 : 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;
      76                 :    16686002 :     unsigned short infomask = 0;
      77                 :    16686002 :     bool        hasnull = false;
      78                 :    16686002 :     uint16      tupmask = 0;
      79                 :    16686002 :     int         numberOfAttributes = tupleDescriptor->natts;
      80                 :             : 
      81                 :             : #ifdef TOAST_INDEX_HACK
      82                 :    16686002 :     Datum       untoasted_values[INDEX_MAX_KEYS] = {0};
      83                 :    16686002 :     bool        untoasted_free[INDEX_MAX_KEYS] = {0};
      84                 :             : #endif
      85                 :             : 
      86         [ -  + ]:    16686002 :     if (numberOfAttributes > INDEX_MAX_KEYS)
      87         [ #  # ]:           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
      93         [ +  + ]:    42963215 :     for (i = 0; i < numberOfAttributes; i++)
      94                 :             :     {
      95                 :    26277213 :         Form_pg_attribute att = TupleDescAttr(tupleDescriptor, i);
      96                 :             : 
      97                 :    26277213 :         untoasted_values[i] = values[i];
      98                 :    26277213 :         untoasted_free[i] = false;
      99                 :             : 
     100                 :             :         /* Do nothing if value is NULL or not of varlena type */
     101   [ +  +  +  + ]:    26277213 :         if (isnull[i] || att->attlen != -1)
     102                 :    25415311 :             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                 :             :          */
     108         [ +  + ]:      861902 :         if (VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
     109                 :             :         {
     110                 :         241 :             untoasted_values[i] =
     111                 :         241 :                 PointerGetDatum(detoast_external_attr((varlena *)
     112                 :             :                                                       DatumGetPointer(values[i])));
     113                 :         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                 :             :          */
     120   [ +  +  +  + ]:     1327879 :         if (!VARATT_IS_EXTENDED(DatumGetPointer(untoasted_values[i])) &&
     121                 :      465977 :             VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
     122         [ +  + ]:       64878 :             (att->attstorage == TYPSTORAGE_EXTENDED ||
     123         [ -  + ]:       58231 :              att->attstorage == TYPSTORAGE_MAIN))
     124                 :             :         {
     125                 :             :             Datum       cvalue;
     126                 :             : 
     127                 :        6647 :             cvalue = toast_compress_datum(untoasted_values[i],
     128                 :        6647 :                                           att->attcompression);
     129                 :             : 
     130         [ +  + ]:        6647 :             if (DatumGetPointer(cvalue) != NULL)
     131                 :             :             {
     132                 :             :                 /* successful compression */
     133         [ -  + ]:        1932 :                 if (untoasted_free[i])
     134                 :           0 :                     pfree(DatumGetPointer(untoasted_values[i]));
     135                 :        1932 :                 untoasted_values[i] = cvalue;
     136                 :        1932 :                 untoasted_free[i] = true;
     137                 :             :             }
     138                 :             :         }
     139                 :             :     }
     140                 :             : #endif
     141                 :             : 
     142         [ +  + ]:    42904822 :     for (i = 0; i < numberOfAttributes; i++)
     143                 :             :     {
     144         [ +  + ]:    26275566 :         if (isnull[i])
     145                 :             :         {
     146                 :       56746 :             hasnull = true;
     147                 :       56746 :             break;
     148                 :             :         }
     149                 :             :     }
     150                 :             : 
     151         [ +  + ]:    16686002 :     if (hasnull)
     152                 :       56746 :         infomask |= INDEX_NULL_MASK;
     153                 :             : 
     154                 :    16686002 :     hoff = IndexInfoFindDataOffset(infomask);
     155                 :             : #ifdef TOAST_INDEX_HACK
     156                 :    16686002 :     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                 :    16686002 :     size = hoff + data_size;
     163                 :    16686002 :     size = MAXALIGN(size);      /* be conservative */
     164                 :             : 
     165                 :    16686002 :     tp = (char *) MemoryContextAllocZero(context, size);
     166                 :    16686002 :     tuple = (IndexTuple) tp;
     167                 :             : 
     168         [ +  + ]:    16686002 :     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
     181         [ +  + ]:    42963215 :     for (i = 0; i < numberOfAttributes; i++)
     182                 :             :     {
     183         [ +  + ]:    26277213 :         if (untoasted_free[i])
     184                 :        2173 :             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                 :             :      */
     194         [ +  + ]:    16686002 :     if (tupmask & HEAP_HASVARWIDTH)
     195                 :     2110494 :         infomask |= INDEX_VAR_MASK;
     196                 :             : 
     197                 :             :     /* Also assert we got rid of external attributes */
     198                 :             : #ifdef TOAST_INDEX_HACK
     199                 :             :     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                 :             :      */
     206         [ -  + ]:    16686002 :     if ((size & INDEX_SIZE_MASK) != size)
     207         [ #  # ]:           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                 :             : 
     212                 :    16686002 :     infomask |= size;
     213                 :             : 
     214                 :             :     /*
     215                 :             :      * initialize metadata
     216                 :             :      */
     217                 :    16686002 :     tuple->t_info = infomask;
     218                 :    16686002 :     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
     229                 :    30531406 : nocache_index_getattr(IndexTuple tup,
     230                 :             :                       int attnum,
     231                 :             :                       TupleDesc tupleDesc)
     232                 :             : {
     233                 :             :     CompactAttribute *cattr;
     234                 :             :     char       *tp;             /* ptr to data part of tuple */
     235                 :    30531406 :     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;
     240                 :    30531406 :     bool        hasnulls = IndexTupleHasNulls(tup);
     241                 :             :     int         i;
     242                 :             : 
     243                 :             :     /* Did someone forget to call TupleDescFinalize()? */
     244                 :             :     Assert(tupleDesc->firstNonCachedOffsetAttr >= 0);
     245                 :             : 
     246                 :    30531406 :     attnum--;
     247                 :             : 
     248                 :    30531406 :     data_off = IndexInfoFindDataOffset(tup->t_info);
     249                 :    30531406 :     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                 :             :      */
     256         [ +  + ]:    30531406 :     if (hasnulls)
     257                 :             :     {
     258                 :       64240 :         bp = (uint8 *) ((char *) tup + sizeof(IndexTupleData));
     259                 :       64240 :         firstNullAttr = first_null_attr(bp, attnum);
     260                 :             :     }
     261                 :             :     else
     262                 :    30467166 :         firstNullAttr = attnum;
     263                 :             : 
     264   [ +  +  +  + ]:    30531406 :     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                 :     5697697 :         startAttr = Min(tupleDesc->firstNonCachedOffsetAttr - 1, firstNullAttr - 1);
     272                 :     5697697 :         off = TupleDescCompactAttr(tupleDesc, startAttr)->attcacheoff;
     273                 :             :     }
     274                 :             :     else
     275                 :             :     {
     276                 :             :         /* Otherwise, start at the beginning... */
     277                 :    24833709 :         startAttr = 0;
     278                 :    24833709 :         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         [ +  + ]:    30531406 :     if (IndexTupleHasVarwidths(tup))
     288                 :             :     {
     289                 :             :         /* Calculate the offset up until the first NULL */
     290         [ +  + ]:    40343836 :         for (i = startAttr; i < firstNullAttr; i++)
     291                 :             :         {
     292                 :     9859379 :             cattr = TupleDescCompactAttr(tupleDesc, i);
     293                 :             : 
     294   [ +  +  +  + ]:     9859379 :             off = att_pointer_alignby(off,
     295                 :             :                                       cattr->attalignby,
     296                 :             :                                       cattr->attlen,
     297                 :             :                                       tp + off);
     298   [ +  +  +  + ]:     9859379 :             off = att_addlength_pointer(off, cattr->attlen, tp + off);
     299                 :             :         }
     300                 :             : 
     301                 :             :         /* Calculate the offset for any remaining columns. */
     302         [ +  + ]:    30484465 :         for (; i < attnum; i++)
     303                 :             :         {
     304                 :             :             Assert(hasnulls);
     305                 :             : 
     306         [ +  - ]:           8 :             if (att_isnull(i, bp))
     307                 :           8 :                 continue;
     308                 :             : 
     309                 :           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 */
     323         [ +  + ]:       48209 :         for (i = startAttr; i < firstNullAttr; i++)
     324                 :             :         {
     325                 :        1260 :             cattr = TupleDescCompactAttr(tupleDesc, i);
     326                 :             : 
     327                 :             :             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         [ +  + ]:       47401 :         for (; i < attnum; i++)
     334                 :             :         {
     335                 :             :             Assert(hasnulls);
     336                 :             : 
     337         [ +  - ]:         452 :             if (att_isnull(i, bp))
     338                 :         452 :                 continue;
     339                 :             : 
     340                 :           0 :             cattr = TupleDescCompactAttr(tupleDesc, i);
     341                 :             : 
     342                 :             :             Assert(cattr->attlen > 0);
     343                 :           0 :             off = att_nominal_alignby(off, cattr->attalignby);
     344                 :           0 :             off += cattr->attlen;
     345                 :             :         }
     346                 :             :     }
     347                 :             : 
     348                 :    30531406 :     cattr = TupleDescCompactAttr(tupleDesc, attnum);
     349   [ +  +  +  + ]:    30531406 :     off = att_pointer_alignby(off, cattr->attalignby,
     350                 :             :                               cattr->attlen, tp + off);
     351                 :    30531406 :     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
     364                 :     2887393 : 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! */
     371                 :     2887393 :     bp = (uint8 *) ((char *) tup + sizeof(IndexTupleData));
     372                 :             : 
     373                 :     2887393 :     tp = (char *) tup + IndexInfoFindDataOffset(tup->t_info);
     374                 :             : 
     375                 :     2887393 :     index_deform_tuple_internal(tupleDescriptor, values, isnull,
     376                 :     2887393 :                                 tp, bp, IndexTupleHasNulls(tup));
     377                 :     2887393 : }
     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                 :     2927918 : index_deform_tuple_internal(TupleDesc tupleDescriptor,
     388                 :             :                             Datum *values, bool *isnull,
     389                 :             :                             char *tp, uint8 *bp, int hasnulls)
     390                 :             : {
     391                 :             :     CompactAttribute *cattr;
     392                 :     2927918 :     int         natts = tupleDescriptor->natts; /* number of atts to extract */
     393                 :     2927918 :     int         attnum = 0;
     394                 :     2927918 :     uint32      off = 0;        /* offset in tuple data */
     395                 :             :     int         firstNonCacheOffsetAttr;
     396                 :             :     int         firstNullAttr;
     397                 :             : 
     398                 :             :     /* Assert to protect callers who allocate fixed-size arrays */
     399                 :             :     Assert(natts <= INDEX_MAX_KEYS);
     400                 :             : 
     401                 :             :     /* Did someone forget to call TupleDescFinalize()? */
     402                 :             :     Assert(tupleDescriptor->firstNonCachedOffsetAttr >= 0);
     403                 :             : 
     404                 :     2927918 :     firstNonCacheOffsetAttr = Min(tupleDescriptor->firstNonCachedOffsetAttr, natts);
     405                 :             : 
     406         [ +  + ]:     2927918 :     if (hasnulls)
     407                 :             :     {
     408                 :        2711 :         firstNullAttr = first_null_attr(bp, natts);
     409                 :        2711 :         firstNonCacheOffsetAttr = Min(firstNonCacheOffsetAttr, firstNullAttr);
     410                 :             :     }
     411                 :             :     else
     412                 :     2925207 :         firstNullAttr = natts;
     413                 :             : 
     414         [ +  + ]:     2927918 :     if (firstNonCacheOffsetAttr > 0)
     415                 :             :     {
     416                 :             : #ifdef USE_ASSERT_CHECKING
     417                 :             :         /* In Assert enabled builds, verify attcacheoff is correct */
     418                 :             :         off = 0;
     419                 :             : #endif
     420                 :             : 
     421                 :             :         do
     422                 :             :         {
     423                 :     3976455 :             isnull[attnum] = false;
     424                 :     3976455 :             cattr = TupleDescCompactAttr(tupleDescriptor, attnum);
     425                 :             : 
     426                 :             : #ifdef USE_ASSERT_CHECKING
     427                 :             :             off = att_nominal_alignby(off, cattr->attalignby);
     428                 :             :             Assert(off == cattr->attcacheoff);
     429                 :             :             off += cattr->attlen;
     430                 :             : #endif
     431                 :             : 
     432                 :     7952910 :             values[attnum] = fetch_att_noerr(tp + cattr->attcacheoff, cattr->attbyval,
     433                 :     3976455 :                                              cattr->attlen);
     434         [ +  + ]:     3976455 :         } while (++attnum < firstNonCacheOffsetAttr);
     435                 :             : 
     436                 :     2880175 :         off = cattr->attcacheoff + cattr->attlen;
     437                 :             :     }
     438                 :             : 
     439         [ +  + ]:     2985357 :     for (; attnum < firstNullAttr; attnum++)
     440                 :             :     {
     441                 :       57439 :         isnull[attnum] = false;
     442                 :       57439 :         cattr = TupleDescCompactAttr(tupleDescriptor, attnum);
     443                 :             : 
     444                 :             :         /* align 'off', fetch the datum, and increment off beyond the datum */
     445                 :       57439 :         values[attnum] = align_fetch_then_add(tp,
     446                 :             :                                               &off,
     447                 :       57439 :                                               cattr->attbyval,
     448                 :       57439 :                                               cattr->attlen,
     449                 :       57439 :                                               cattr->attalignby);
     450                 :             :     }
     451                 :             : 
     452         [ +  + ]:     2933117 :     for (; attnum < natts; attnum++)
     453                 :             :     {
     454                 :             :         Assert(hasnulls);
     455                 :             : 
     456         [ +  + ]:        5199 :         if (att_isnull(attnum, bp))
     457                 :             :         {
     458                 :        2730 :             values[attnum] = (Datum) 0;
     459                 :        2730 :             isnull[attnum] = true;
     460                 :        2730 :             continue;
     461                 :             :         }
     462                 :             : 
     463                 :        2469 :         isnull[attnum] = false;
     464                 :        2469 :         cattr = TupleDescCompactAttr(tupleDescriptor, attnum);
     465                 :             : 
     466                 :             :         /* align 'off', fetch the attr's value, and increment off beyond it */
     467                 :        2469 :         values[attnum] = align_fetch_then_add(tp,
     468                 :             :                                               &off,
     469                 :        2469 :                                               cattr->attbyval,
     470                 :        2469 :                                               cattr->attlen,
     471                 :        2469 :                                               cattr->attalignby);
     472                 :             :     }
     473                 :     2927918 : }
     474                 :             : 
     475                 :             : /*
     476                 :             :  * Create a palloc'd copy of an index tuple.
     477                 :             :  */
     478                 :             : IndexTuple
     479                 :     3306371 : CopyIndexTuple(IndexTuple source)
     480                 :             : {
     481                 :             :     IndexTuple  result;
     482                 :             :     Size        size;
     483                 :             : 
     484                 :     3306371 :     size = IndexTupleSize(source);
     485                 :     3306371 :     result = (IndexTuple) palloc(size);
     486                 :     3306371 :     memcpy(result, source, size);
     487                 :     3306371 :     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
     508                 :       39462 : 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                 :             : 
     516                 :             :     Assert(leavenatts <= sourceDescriptor->natts);
     517                 :             : 
     518                 :             :     /* Easy case: no truncation actually required */
     519         [ +  + ]:       39462 :     if (leavenatts == sourceDescriptor->natts)
     520                 :       23112 :         return CopyIndexTuple(source);
     521                 :             : 
     522                 :             :     /* Create temporary truncated tuple descriptor */
     523                 :       16350 :     truncdesc = CreateTupleDescTruncatedCopy(sourceDescriptor, leavenatts);
     524                 :             : 
     525                 :             :     /* Deform, form copy of tuple with fewer attributes */
     526                 :       16350 :     index_deform_tuple(source, truncdesc, values, isnull);
     527                 :       16350 :     truncated = index_form_tuple(truncdesc, values, isnull);
     528                 :       16350 :     truncated->t_tid = source->t_tid;
     529                 :             :     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                 :       16350 :     pfree(truncdesc);
     536                 :             : 
     537                 :       16350 :     return truncated;
     538                 :             : }
        

Generated by: LCOV version 2.0-1