LCOV - code coverage report
Current view: top level - src/backend/access/heap - heaptoast.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 146 157 93.0 %
Date: 2019-11-13 22:07:24 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * heaptoast.c
       4             :  *    Heap-specific definitions for external and compressed storage
       5             :  *    of variable size attributes.
       6             :  *
       7             :  * Copyright (c) 2000-2019, PostgreSQL Global Development Group
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/access/heap/heaptoast.c
      12             :  *
      13             :  *
      14             :  * INTERFACE ROUTINES
      15             :  *      heap_toast_insert_or_update -
      16             :  *          Try to make a given tuple fit into one page by compressing
      17             :  *          or moving off attributes
      18             :  *
      19             :  *      heap_toast_delete -
      20             :  *          Reclaim toast storage when a tuple is deleted
      21             :  *
      22             :  *-------------------------------------------------------------------------
      23             :  */
      24             : 
      25             : #include "postgres.h"
      26             : 
      27             : #include "access/detoast.h"
      28             : #include "access/heapam.h"
      29             : #include "access/heaptoast.h"
      30             : #include "access/toast_helper.h"
      31             : #include "access/toast_internals.h"
      32             : 
      33             : 
      34             : /* ----------
      35             :  * heap_toast_delete -
      36             :  *
      37             :  *  Cascaded delete toast-entries on DELETE
      38             :  * ----------
      39             :  */
      40             : void
      41         228 : heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
      42             : {
      43             :     TupleDesc   tupleDesc;
      44             :     Datum       toast_values[MaxHeapAttributeNumber];
      45             :     bool        toast_isnull[MaxHeapAttributeNumber];
      46             : 
      47             :     /*
      48             :      * We should only ever be called for tuples of plain relations or
      49             :      * materialized views --- recursing on a toast rel is bad news.
      50             :      */
      51             :     Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
      52             :            rel->rd_rel->relkind == RELKIND_MATVIEW);
      53             : 
      54             :     /*
      55             :      * Get the tuple descriptor and break down the tuple into fields.
      56             :      *
      57             :      * NOTE: it's debatable whether to use heap_deform_tuple() here or just
      58             :      * heap_getattr() only the varlena columns.  The latter could win if there
      59             :      * are few varlena columns and many non-varlena ones. However,
      60             :      * heap_deform_tuple costs only O(N) while the heap_getattr way would cost
      61             :      * O(N^2) if there are many varlena columns, so it seems better to err on
      62             :      * the side of linear cost.  (We won't even be here unless there's at
      63             :      * least one varlena column, by the way.)
      64             :      */
      65         228 :     tupleDesc = rel->rd_att;
      66             : 
      67             :     Assert(tupleDesc->natts <= MaxHeapAttributeNumber);
      68         228 :     heap_deform_tuple(oldtup, tupleDesc, toast_values, toast_isnull);
      69             : 
      70             :     /* Do the real work. */
      71         228 :     toast_delete_external(rel, toast_values, toast_isnull, is_speculative);
      72         228 : }
      73             : 
      74             : 
      75             : /* ----------
      76             :  * heap_toast_insert_or_update -
      77             :  *
      78             :  *  Delete no-longer-used toast-entries and create new ones to
      79             :  *  make the new tuple fit on INSERT or UPDATE
      80             :  *
      81             :  * Inputs:
      82             :  *  newtup: the candidate new tuple to be inserted
      83             :  *  oldtup: the old row version for UPDATE, or NULL for INSERT
      84             :  *  options: options to be passed to heap_insert() for toast rows
      85             :  * Result:
      86             :  *  either newtup if no toasting is needed, or a palloc'd modified tuple
      87             :  *  that is what should actually get stored
      88             :  *
      89             :  * NOTE: neither newtup nor oldtup will be modified.  This is a change
      90             :  * from the pre-8.1 API of this routine.
      91             :  * ----------
      92             :  */
      93             : HeapTuple
      94       54488 : heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
      95             :                             int options)
      96             : {
      97             :     HeapTuple   result_tuple;
      98             :     TupleDesc   tupleDesc;
      99             :     int         numAttrs;
     100             : 
     101             :     Size        maxDataLen;
     102             :     Size        hoff;
     103             : 
     104             :     bool        toast_isnull[MaxHeapAttributeNumber];
     105             :     bool        toast_oldisnull[MaxHeapAttributeNumber];
     106             :     Datum       toast_values[MaxHeapAttributeNumber];
     107             :     Datum       toast_oldvalues[MaxHeapAttributeNumber];
     108             :     ToastAttrInfo toast_attr[MaxHeapAttributeNumber];
     109             :     ToastTupleContext ttc;
     110             : 
     111             :     /*
     112             :      * Ignore the INSERT_SPECULATIVE option. Speculative insertions/super
     113             :      * deletions just normally insert/delete the toast values. It seems
     114             :      * easiest to deal with that here, instead on, potentially, multiple
     115             :      * callers.
     116             :      */
     117       54488 :     options &= ~HEAP_INSERT_SPECULATIVE;
     118             : 
     119             :     /*
     120             :      * We should only ever be called for tuples of plain relations or
     121             :      * materialized views --- recursing on a toast rel is bad news.
     122             :      */
     123             :     Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
     124             :            rel->rd_rel->relkind == RELKIND_MATVIEW);
     125             : 
     126             :     /*
     127             :      * Get the tuple descriptor and break down the tuple(s) into fields.
     128             :      */
     129       54488 :     tupleDesc = rel->rd_att;
     130       54488 :     numAttrs = tupleDesc->natts;
     131             : 
     132             :     Assert(numAttrs <= MaxHeapAttributeNumber);
     133       54488 :     heap_deform_tuple(newtup, tupleDesc, toast_values, toast_isnull);
     134       54488 :     if (oldtup != NULL)
     135        1442 :         heap_deform_tuple(oldtup, tupleDesc, toast_oldvalues, toast_oldisnull);
     136             : 
     137             :     /* ----------
     138             :      * Prepare for toasting
     139             :      * ----------
     140             :      */
     141       54488 :     ttc.ttc_rel = rel;
     142       54488 :     ttc.ttc_values = toast_values;
     143       54488 :     ttc.ttc_isnull = toast_isnull;
     144       54488 :     if (oldtup == NULL)
     145             :     {
     146       53046 :         ttc.ttc_oldvalues = NULL;
     147       53046 :         ttc.ttc_oldisnull = NULL;
     148             :     }
     149             :     else
     150             :     {
     151        1442 :         ttc.ttc_oldvalues = toast_oldvalues;
     152        1442 :         ttc.ttc_oldisnull = toast_oldisnull;
     153             :     }
     154       54488 :     ttc.ttc_attr = toast_attr;
     155       54488 :     toast_tuple_init(&ttc);
     156             : 
     157             :     /* ----------
     158             :      * Compress and/or save external until data fits into target length
     159             :      *
     160             :      *  1: Inline compress attributes with attstorage 'x', and store very
     161             :      *     large attributes with attstorage 'x' or 'e' external immediately
     162             :      *  2: Store attributes with attstorage 'x' or 'e' external
     163             :      *  3: Inline compress attributes with attstorage 'm'
     164             :      *  4: Store attributes with attstorage 'm' external
     165             :      * ----------
     166             :      */
     167             : 
     168             :     /* compute header overhead --- this should match heap_form_tuple() */
     169       54488 :     hoff = SizeofHeapTupleHeader;
     170       54488 :     if ((ttc.ttc_flags & TOAST_HAS_NULLS) != 0)
     171        6604 :         hoff += BITMAPLEN(numAttrs);
     172       54488 :     hoff = MAXALIGN(hoff);
     173             :     /* now convert to a limit on the tuple data size */
     174       54488 :     maxDataLen = RelationGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
     175             : 
     176             :     /*
     177             :      * Look for attributes with attstorage 'x' to compress.  Also find large
     178             :      * attributes with attstorage 'x' or 'e', and store them external.
     179             :      */
     180      170972 :     while (heap_compute_data_size(tupleDesc,
     181             :                                   toast_values, toast_isnull) > maxDataLen)
     182             :     {
     183             :         int         biggest_attno;
     184             : 
     185       65406 :         biggest_attno = toast_tuple_find_biggest_attribute(&ttc, true, false);
     186       65406 :         if (biggest_attno < 0)
     187        3410 :             break;
     188             : 
     189             :         /*
     190             :          * Attempt to compress it inline, if it has attstorage 'x'
     191             :          */
     192       61996 :         if (TupleDescAttr(tupleDesc, biggest_attno)->attstorage == 'x')
     193       61454 :             toast_tuple_try_compression(&ttc, biggest_attno);
     194             :         else
     195             :         {
     196             :             /* has attstorage 'e', ignore on subsequent compression passes */
     197         542 :             toast_attr[biggest_attno].tai_colflags |= TOASTCOL_INCOMPRESSIBLE;
     198             :         }
     199             : 
     200             :         /*
     201             :          * If this value is by itself more than maxDataLen (after compression
     202             :          * if any), push it out to the toast table immediately, if possible.
     203             :          * This avoids uselessly compressing other fields in the common case
     204             :          * where we have one long field and several short ones.
     205             :          *
     206             :          * XXX maybe the threshold should be less than maxDataLen?
     207             :          */
     208       86730 :         if (toast_attr[biggest_attno].tai_size > maxDataLen &&
     209       24734 :             rel->rd_rel->reltoastrelid != InvalidOid)
     210       24734 :             toast_tuple_externalize(&ttc, biggest_attno, options);
     211             :     }
     212             : 
     213             :     /*
     214             :      * Second we look for attributes of attstorage 'x' or 'e' that are still
     215             :      * inline, and make them external.  But skip this if there's no toast
     216             :      * table to push them to.
     217             :      */
     218      112400 :     while (heap_compute_data_size(tupleDesc,
     219        3424 :                                   toast_values, toast_isnull) > maxDataLen &&
     220        3424 :            rel->rd_rel->reltoastrelid != InvalidOid)
     221             :     {
     222             :         int         biggest_attno;
     223             : 
     224        3424 :         biggest_attno = toast_tuple_find_biggest_attribute(&ttc, false, false);
     225        3424 :         if (biggest_attno < 0)
     226           0 :             break;
     227        3424 :         toast_tuple_externalize(&ttc, biggest_attno, options);
     228             :     }
     229             : 
     230             :     /*
     231             :      * Round 3 - this time we take attributes with storage 'm' into
     232             :      * compression
     233             :      */
     234      108976 :     while (heap_compute_data_size(tupleDesc,
     235             :                                   toast_values, toast_isnull) > maxDataLen)
     236             :     {
     237             :         int         biggest_attno;
     238             : 
     239           0 :         biggest_attno = toast_tuple_find_biggest_attribute(&ttc, true, true);
     240           0 :         if (biggest_attno < 0)
     241           0 :             break;
     242             : 
     243           0 :         toast_tuple_try_compression(&ttc, biggest_attno);
     244             :     }
     245             : 
     246             :     /*
     247             :      * Finally we store attributes of type 'm' externally.  At this point we
     248             :      * increase the target tuple size, so that 'm' attributes aren't stored
     249             :      * externally unless really necessary.
     250             :      */
     251       54488 :     maxDataLen = TOAST_TUPLE_TARGET_MAIN - hoff;
     252             : 
     253      108976 :     while (heap_compute_data_size(tupleDesc,
     254           0 :                                   toast_values, toast_isnull) > maxDataLen &&
     255           0 :            rel->rd_rel->reltoastrelid != InvalidOid)
     256             :     {
     257             :         int         biggest_attno;
     258             : 
     259           0 :         biggest_attno = toast_tuple_find_biggest_attribute(&ttc, false, true);
     260           0 :         if (biggest_attno < 0)
     261           0 :             break;
     262             : 
     263           0 :         toast_tuple_externalize(&ttc, biggest_attno, options);
     264             :     }
     265             : 
     266             :     /*
     267             :      * In the case we toasted any values, we need to build a new heap tuple
     268             :      * with the changed values.
     269             :      */
     270       54488 :     if ((ttc.ttc_flags & TOAST_NEEDS_CHANGE) != 0)
     271             :     {
     272       54320 :         HeapTupleHeader olddata = newtup->t_data;
     273             :         HeapTupleHeader new_data;
     274             :         int32       new_header_len;
     275             :         int32       new_data_len;
     276             :         int32       new_tuple_len;
     277             : 
     278             :         /*
     279             :          * Calculate the new size of the tuple.
     280             :          *
     281             :          * Note: we used to assume here that the old tuple's t_hoff must equal
     282             :          * the new_header_len value, but that was incorrect.  The old tuple
     283             :          * might have a smaller-than-current natts, if there's been an ALTER
     284             :          * TABLE ADD COLUMN since it was stored; and that would lead to a
     285             :          * different conclusion about the size of the null bitmap, or even
     286             :          * whether there needs to be one at all.
     287             :          */
     288       54320 :         new_header_len = SizeofHeapTupleHeader;
     289       54320 :         if ((ttc.ttc_flags & TOAST_HAS_NULLS) != 0)
     290        6462 :             new_header_len += BITMAPLEN(numAttrs);
     291       54320 :         new_header_len = MAXALIGN(new_header_len);
     292       54320 :         new_data_len = heap_compute_data_size(tupleDesc,
     293             :                                               toast_values, toast_isnull);
     294       54320 :         new_tuple_len = new_header_len + new_data_len;
     295             : 
     296             :         /*
     297             :          * Allocate and zero the space needed, and fill HeapTupleData fields.
     298             :          */
     299       54320 :         result_tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + new_tuple_len);
     300       54320 :         result_tuple->t_len = new_tuple_len;
     301       54320 :         result_tuple->t_self = newtup->t_self;
     302       54320 :         result_tuple->t_tableOid = newtup->t_tableOid;
     303       54320 :         new_data = (HeapTupleHeader) ((char *) result_tuple + HEAPTUPLESIZE);
     304       54320 :         result_tuple->t_data = new_data;
     305             : 
     306             :         /*
     307             :          * Copy the existing tuple header, but adjust natts and t_hoff.
     308             :          */
     309       54320 :         memcpy(new_data, olddata, SizeofHeapTupleHeader);
     310       54320 :         HeapTupleHeaderSetNatts(new_data, numAttrs);
     311       54320 :         new_data->t_hoff = new_header_len;
     312             : 
     313             :         /* Copy over the data, and fill the null bitmap if needed */
     314       54320 :         heap_fill_tuple(tupleDesc,
     315             :                         toast_values,
     316             :                         toast_isnull,
     317             :                         (char *) new_data + new_header_len,
     318             :                         new_data_len,
     319             :                         &(new_data->t_infomask),
     320       54320 :                         ((ttc.ttc_flags & TOAST_HAS_NULLS) != 0) ?
     321             :                         new_data->t_bits : NULL);
     322             :     }
     323             :     else
     324         168 :         result_tuple = newtup;
     325             : 
     326       54488 :     toast_tuple_cleanup(&ttc);
     327             : 
     328       54488 :     return result_tuple;
     329             : }
     330             : 
     331             : 
     332             : /* ----------
     333             :  * toast_flatten_tuple -
     334             :  *
     335             :  *  "Flatten" a tuple to contain no out-of-line toasted fields.
     336             :  *  (This does not eliminate compressed or short-header datums.)
     337             :  *
     338             :  *  Note: we expect the caller already checked HeapTupleHasExternal(tup),
     339             :  *  so there is no need for a short-circuit path.
     340             :  * ----------
     341             :  */
     342             : HeapTuple
     343        1406 : toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
     344             : {
     345             :     HeapTuple   new_tuple;
     346        1406 :     int         numAttrs = tupleDesc->natts;
     347             :     int         i;
     348             :     Datum       toast_values[MaxTupleAttributeNumber];
     349             :     bool        toast_isnull[MaxTupleAttributeNumber];
     350             :     bool        toast_free[MaxTupleAttributeNumber];
     351             : 
     352             :     /*
     353             :      * Break down the tuple into fields.
     354             :      */
     355             :     Assert(numAttrs <= MaxTupleAttributeNumber);
     356        1406 :     heap_deform_tuple(tup, tupleDesc, toast_values, toast_isnull);
     357             : 
     358        1406 :     memset(toast_free, 0, numAttrs * sizeof(bool));
     359             : 
     360       44048 :     for (i = 0; i < numAttrs; i++)
     361             :     {
     362             :         /*
     363             :          * Look at non-null varlena attributes
     364             :          */
     365       42642 :         if (!toast_isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
     366             :         {
     367             :             struct varlena *new_value;
     368             : 
     369        5812 :             new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
     370        5812 :             if (VARATT_IS_EXTERNAL(new_value))
     371             :             {
     372        1488 :                 new_value = detoast_external_attr(new_value);
     373        1488 :                 toast_values[i] = PointerGetDatum(new_value);
     374        1488 :                 toast_free[i] = true;
     375             :             }
     376             :         }
     377             :     }
     378             : 
     379             :     /*
     380             :      * Form the reconfigured tuple.
     381             :      */
     382        1406 :     new_tuple = heap_form_tuple(tupleDesc, toast_values, toast_isnull);
     383             : 
     384             :     /*
     385             :      * Be sure to copy the tuple's identity fields.  We also make a point of
     386             :      * copying visibility info, just in case anybody looks at those fields in
     387             :      * a syscache entry.
     388             :      */
     389        1406 :     new_tuple->t_self = tup->t_self;
     390        1406 :     new_tuple->t_tableOid = tup->t_tableOid;
     391             : 
     392        1406 :     new_tuple->t_data->t_choice = tup->t_data->t_choice;
     393        1406 :     new_tuple->t_data->t_ctid = tup->t_data->t_ctid;
     394        1406 :     new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK;
     395        2812 :     new_tuple->t_data->t_infomask |=
     396        1406 :         tup->t_data->t_infomask & HEAP_XACT_MASK;
     397        1406 :     new_tuple->t_data->t_infomask2 &= ~HEAP2_XACT_MASK;
     398        2812 :     new_tuple->t_data->t_infomask2 |=
     399        1406 :         tup->t_data->t_infomask2 & HEAP2_XACT_MASK;
     400             : 
     401             :     /*
     402             :      * Free allocated temp values
     403             :      */
     404       44048 :     for (i = 0; i < numAttrs; i++)
     405       42642 :         if (toast_free[i])
     406        1488 :             pfree(DatumGetPointer(toast_values[i]));
     407             : 
     408        1406 :     return new_tuple;
     409             : }
     410             : 
     411             : 
     412             : /* ----------
     413             :  * toast_flatten_tuple_to_datum -
     414             :  *
     415             :  *  "Flatten" a tuple containing out-of-line toasted fields into a Datum.
     416             :  *  The result is always palloc'd in the current memory context.
     417             :  *
     418             :  *  We have a general rule that Datums of container types (rows, arrays,
     419             :  *  ranges, etc) must not contain any external TOAST pointers.  Without
     420             :  *  this rule, we'd have to look inside each Datum when preparing a tuple
     421             :  *  for storage, which would be expensive and would fail to extend cleanly
     422             :  *  to new sorts of container types.
     423             :  *
     424             :  *  However, we don't want to say that tuples represented as HeapTuples
     425             :  *  can't contain toasted fields, so instead this routine should be called
     426             :  *  when such a HeapTuple is being converted into a Datum.
     427             :  *
     428             :  *  While we're at it, we decompress any compressed fields too.  This is not
     429             :  *  necessary for correctness, but reflects an expectation that compression
     430             :  *  will be more effective if applied to the whole tuple not individual
     431             :  *  fields.  We are not so concerned about that that we want to deconstruct
     432             :  *  and reconstruct tuples just to get rid of compressed fields, however.
     433             :  *  So callers typically won't call this unless they see that the tuple has
     434             :  *  at least one external field.
     435             :  *
     436             :  *  On the other hand, in-line short-header varlena fields are left alone.
     437             :  *  If we "untoasted" them here, they'd just get changed back to short-header
     438             :  *  format anyway within heap_fill_tuple.
     439             :  * ----------
     440             :  */
     441             : Datum
     442           8 : toast_flatten_tuple_to_datum(HeapTupleHeader tup,
     443             :                              uint32 tup_len,
     444             :                              TupleDesc tupleDesc)
     445             : {
     446             :     HeapTupleHeader new_data;
     447             :     int32       new_header_len;
     448             :     int32       new_data_len;
     449             :     int32       new_tuple_len;
     450             :     HeapTupleData tmptup;
     451           8 :     int         numAttrs = tupleDesc->natts;
     452             :     int         i;
     453           8 :     bool        has_nulls = false;
     454             :     Datum       toast_values[MaxTupleAttributeNumber];
     455             :     bool        toast_isnull[MaxTupleAttributeNumber];
     456             :     bool        toast_free[MaxTupleAttributeNumber];
     457             : 
     458             :     /* Build a temporary HeapTuple control structure */
     459           8 :     tmptup.t_len = tup_len;
     460           8 :     ItemPointerSetInvalid(&(tmptup.t_self));
     461           8 :     tmptup.t_tableOid = InvalidOid;
     462           8 :     tmptup.t_data = tup;
     463             : 
     464             :     /*
     465             :      * Break down the tuple into fields.
     466             :      */
     467             :     Assert(numAttrs <= MaxTupleAttributeNumber);
     468           8 :     heap_deform_tuple(&tmptup, tupleDesc, toast_values, toast_isnull);
     469             : 
     470           8 :     memset(toast_free, 0, numAttrs * sizeof(bool));
     471             : 
     472          28 :     for (i = 0; i < numAttrs; i++)
     473             :     {
     474             :         /*
     475             :          * Look at non-null varlena attributes
     476             :          */
     477          20 :         if (toast_isnull[i])
     478           4 :             has_nulls = true;
     479          16 :         else if (TupleDescAttr(tupleDesc, i)->attlen == -1)
     480             :         {
     481             :             struct varlena *new_value;
     482             : 
     483          16 :             new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
     484          20 :             if (VARATT_IS_EXTERNAL(new_value) ||
     485           4 :                 VARATT_IS_COMPRESSED(new_value))
     486             :             {
     487          12 :                 new_value = detoast_attr(new_value);
     488          12 :                 toast_values[i] = PointerGetDatum(new_value);
     489          12 :                 toast_free[i] = true;
     490             :             }
     491             :         }
     492             :     }
     493             : 
     494             :     /*
     495             :      * Calculate the new size of the tuple.
     496             :      *
     497             :      * This should match the reconstruction code in
     498             :      * heap_toast_insert_or_update.
     499             :      */
     500           8 :     new_header_len = SizeofHeapTupleHeader;
     501           8 :     if (has_nulls)
     502           4 :         new_header_len += BITMAPLEN(numAttrs);
     503           8 :     new_header_len = MAXALIGN(new_header_len);
     504           8 :     new_data_len = heap_compute_data_size(tupleDesc,
     505             :                                           toast_values, toast_isnull);
     506           8 :     new_tuple_len = new_header_len + new_data_len;
     507             : 
     508           8 :     new_data = (HeapTupleHeader) palloc0(new_tuple_len);
     509             : 
     510             :     /*
     511             :      * Copy the existing tuple header, but adjust natts and t_hoff.
     512             :      */
     513           8 :     memcpy(new_data, tup, SizeofHeapTupleHeader);
     514           8 :     HeapTupleHeaderSetNatts(new_data, numAttrs);
     515           8 :     new_data->t_hoff = new_header_len;
     516             : 
     517             :     /* Set the composite-Datum header fields correctly */
     518           8 :     HeapTupleHeaderSetDatumLength(new_data, new_tuple_len);
     519           8 :     HeapTupleHeaderSetTypeId(new_data, tupleDesc->tdtypeid);
     520           8 :     HeapTupleHeaderSetTypMod(new_data, tupleDesc->tdtypmod);
     521             : 
     522             :     /* Copy over the data, and fill the null bitmap if needed */
     523           8 :     heap_fill_tuple(tupleDesc,
     524             :                     toast_values,
     525             :                     toast_isnull,
     526             :                     (char *) new_data + new_header_len,
     527             :                     new_data_len,
     528             :                     &(new_data->t_infomask),
     529             :                     has_nulls ? new_data->t_bits : NULL);
     530             : 
     531             :     /*
     532             :      * Free allocated temp values
     533             :      */
     534          28 :     for (i = 0; i < numAttrs; i++)
     535          20 :         if (toast_free[i])
     536          12 :             pfree(DatumGetPointer(toast_values[i]));
     537             : 
     538           8 :     return PointerGetDatum(new_data);
     539             : }
     540             : 
     541             : 
     542             : /* ----------
     543             :  * toast_build_flattened_tuple -
     544             :  *
     545             :  *  Build a tuple containing no out-of-line toasted fields.
     546             :  *  (This does not eliminate compressed or short-header datums.)
     547             :  *
     548             :  *  This is essentially just like heap_form_tuple, except that it will
     549             :  *  expand any external-data pointers beforehand.
     550             :  *
     551             :  *  It's not very clear whether it would be preferable to decompress
     552             :  *  in-line compressed datums while at it.  For now, we don't.
     553             :  * ----------
     554             :  */
     555             : HeapTuple
     556       32490 : toast_build_flattened_tuple(TupleDesc tupleDesc,
     557             :                             Datum *values,
     558             :                             bool *isnull)
     559             : {
     560             :     HeapTuple   new_tuple;
     561       32490 :     int         numAttrs = tupleDesc->natts;
     562             :     int         num_to_free;
     563             :     int         i;
     564             :     Datum       new_values[MaxTupleAttributeNumber];
     565             :     Pointer     freeable_values[MaxTupleAttributeNumber];
     566             : 
     567             :     /*
     568             :      * We can pass the caller's isnull array directly to heap_form_tuple, but
     569             :      * we potentially need to modify the values array.
     570             :      */
     571             :     Assert(numAttrs <= MaxTupleAttributeNumber);
     572       32490 :     memcpy(new_values, values, numAttrs * sizeof(Datum));
     573             : 
     574       32490 :     num_to_free = 0;
     575      210214 :     for (i = 0; i < numAttrs; i++)
     576             :     {
     577             :         /*
     578             :          * Look at non-null varlena attributes
     579             :          */
     580      177724 :         if (!isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
     581             :         {
     582             :             struct varlena *new_value;
     583             : 
     584       64194 :             new_value = (struct varlena *) DatumGetPointer(new_values[i]);
     585       64194 :             if (VARATT_IS_EXTERNAL(new_value))
     586             :             {
     587         268 :                 new_value = detoast_external_attr(new_value);
     588         268 :                 new_values[i] = PointerGetDatum(new_value);
     589         268 :                 freeable_values[num_to_free++] = (Pointer) new_value;
     590             :             }
     591             :         }
     592             :     }
     593             : 
     594             :     /*
     595             :      * Form the reconfigured tuple.
     596             :      */
     597       32490 :     new_tuple = heap_form_tuple(tupleDesc, new_values, isnull);
     598             : 
     599             :     /*
     600             :      * Free allocated temp values
     601             :      */
     602       32758 :     for (i = 0; i < num_to_free; i++)
     603         268 :         pfree(freeable_values[i]);
     604             : 
     605       32490 :     return new_tuple;
     606             : }

Generated by: LCOV version 1.13