LCOV - differential code coverage report
Current view: top level - src/backend/access/heap - heaptoast.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 92.8 % 221 205 16 205
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 6 6 6
Baseline: 16@8cea358b128 Branches: 72.9 % 140 102 38 102
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed [..60] days: 100.0 % 1 1 1
(240..) days: 92.7 % 220 204 16 204
Function coverage date bins:
(240..) days: 100.0 % 6 6 6
Branch coverage date bins:
[..60] days: 100.0 % 2 2 2
(240..) days: 72.5 % 138 100 38 100

 Age         Owner                    Branch data    TLA  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-2024, 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/genam.h"
                                 29                 :                : #include "access/heapam.h"
                                 30                 :                : #include "access/heaptoast.h"
                                 31                 :                : #include "access/toast_helper.h"
                                 32                 :                : #include "access/toast_internals.h"
                                 33                 :                : #include "utils/fmgroids.h"
                                 34                 :                : 
                                 35                 :                : 
                                 36                 :                : /* ----------
                                 37                 :                :  * heap_toast_delete -
                                 38                 :                :  *
                                 39                 :                :  *  Cascaded delete toast-entries on DELETE
                                 40                 :                :  * ----------
                                 41                 :                :  */
                                 42                 :                : void
 1654 rhaas@postgresql.org       43                 :CBC         257 : heap_toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
                                 44                 :                : {
                                 45                 :                :     TupleDesc   tupleDesc;
                                 46                 :                :     Datum       toast_values[MaxHeapAttributeNumber];
                                 47                 :                :     bool        toast_isnull[MaxHeapAttributeNumber];
                                 48                 :                : 
                                 49                 :                :     /*
                                 50                 :                :      * We should only ever be called for tuples of plain relations or
                                 51                 :                :      * materialized views --- recursing on a toast rel is bad news.
                                 52                 :                :      */
 1742                            53   [ -  +  -  - ]:            257 :     Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
                                 54                 :                :            rel->rd_rel->relkind == RELKIND_MATVIEW);
                                 55                 :                : 
                                 56                 :                :     /*
                                 57                 :                :      * Get the tuple descriptor and break down the tuple into fields.
                                 58                 :                :      *
                                 59                 :                :      * NOTE: it's debatable whether to use heap_deform_tuple() here or just
                                 60                 :                :      * heap_getattr() only the varlena columns.  The latter could win if there
                                 61                 :                :      * are few varlena columns and many non-varlena ones. However,
                                 62                 :                :      * heap_deform_tuple costs only O(N) while the heap_getattr way would cost
                                 63                 :                :      * O(N^2) if there are many varlena columns, so it seems better to err on
                                 64                 :                :      * the side of linear cost.  (We won't even be here unless there's at
                                 65                 :                :      * least one varlena column, by the way.)
                                 66                 :                :      */
                                 67                 :            257 :     tupleDesc = rel->rd_att;
                                 68                 :                : 
 1682                            69         [ -  + ]:            257 :     Assert(tupleDesc->natts <= MaxHeapAttributeNumber);
 1742                            70                 :            257 :     heap_deform_tuple(oldtup, tupleDesc, toast_values, toast_isnull);
                                 71                 :                : 
                                 72                 :                :     /* Do the real work. */
 1682                            73                 :            257 :     toast_delete_external(rel, toast_values, toast_isnull, is_speculative);
 1742                            74                 :            257 : }
                                 75                 :                : 
                                 76                 :                : 
                                 77                 :                : /* ----------
                                 78                 :                :  * heap_toast_insert_or_update -
                                 79                 :                :  *
                                 80                 :                :  *  Delete no-longer-used toast-entries and create new ones to
                                 81                 :                :  *  make the new tuple fit on INSERT or UPDATE
                                 82                 :                :  *
                                 83                 :                :  * Inputs:
                                 84                 :                :  *  newtup: the candidate new tuple to be inserted
                                 85                 :                :  *  oldtup: the old row version for UPDATE, or NULL for INSERT
                                 86                 :                :  *  options: options to be passed to heap_insert() for toast rows
                                 87                 :                :  * Result:
                                 88                 :                :  *  either newtup if no toasting is needed, or a palloc'd modified tuple
                                 89                 :                :  *  that is what should actually get stored
                                 90                 :                :  *
                                 91                 :                :  * NOTE: neither newtup nor oldtup will be modified.  This is a change
                                 92                 :                :  * from the pre-8.1 API of this routine.
                                 93                 :                :  * ----------
                                 94                 :                :  */
                                 95                 :                : HeapTuple
 1654                            96                 :          17200 : heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
                                 97                 :                :                             int options)
                                 98                 :                : {
                                 99                 :                :     HeapTuple   result_tuple;
                                100                 :                :     TupleDesc   tupleDesc;
                                101                 :                :     int         numAttrs;
                                102                 :                : 
                                103                 :                :     Size        maxDataLen;
                                104                 :                :     Size        hoff;
                                105                 :                : 
                                106                 :                :     bool        toast_isnull[MaxHeapAttributeNumber];
                                107                 :                :     bool        toast_oldisnull[MaxHeapAttributeNumber];
                                108                 :                :     Datum       toast_values[MaxHeapAttributeNumber];
                                109                 :                :     Datum       toast_oldvalues[MaxHeapAttributeNumber];
                                110                 :                :     ToastAttrInfo toast_attr[MaxHeapAttributeNumber];
                                111                 :                :     ToastTupleContext ttc;
                                112                 :                : 
                                113                 :                :     /*
                                114                 :                :      * Ignore the INSERT_SPECULATIVE option. Speculative insertions/super
                                115                 :                :      * deletions just normally insert/delete the toast values. It seems
                                116                 :                :      * easiest to deal with that here, instead on, potentially, multiple
                                117                 :                :      * callers.
                                118                 :                :      */
 1742                           119                 :          17200 :     options &= ~HEAP_INSERT_SPECULATIVE;
                                120                 :                : 
                                121                 :                :     /*
                                122                 :                :      * We should only ever be called for tuples of plain relations or
                                123                 :                :      * materialized views --- recursing on a toast rel is bad news.
                                124                 :                :      */
                                125   [ +  +  -  + ]:          17200 :     Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
                                126                 :                :            rel->rd_rel->relkind == RELKIND_MATVIEW);
                                127                 :                : 
                                128                 :                :     /*
                                129                 :                :      * Get the tuple descriptor and break down the tuple(s) into fields.
                                130                 :                :      */
                                131                 :          17200 :     tupleDesc = rel->rd_att;
                                132                 :          17200 :     numAttrs = tupleDesc->natts;
                                133                 :                : 
                                134         [ -  + ]:          17200 :     Assert(numAttrs <= MaxHeapAttributeNumber);
                                135                 :          17200 :     heap_deform_tuple(newtup, tupleDesc, toast_values, toast_isnull);
                                136         [ +  + ]:          17200 :     if (oldtup != NULL)
                                137                 :           1096 :         heap_deform_tuple(oldtup, tupleDesc, toast_oldvalues, toast_oldisnull);
                                138                 :                : 
                                139                 :                :     /* ----------
                                140                 :                :      * Prepare for toasting
                                141                 :                :      * ----------
                                142                 :                :      */
 1682                           143                 :          17200 :     ttc.ttc_rel = rel;
                                144                 :          17200 :     ttc.ttc_values = toast_values;
                                145                 :          17200 :     ttc.ttc_isnull = toast_isnull;
                                146         [ +  + ]:          17200 :     if (oldtup == NULL)
                                147                 :                :     {
                                148                 :          16104 :         ttc.ttc_oldvalues = NULL;
                                149                 :          16104 :         ttc.ttc_oldisnull = NULL;
                                150                 :                :     }
                                151                 :                :     else
                                152                 :                :     {
                                153                 :           1096 :         ttc.ttc_oldvalues = toast_oldvalues;
                                154                 :           1096 :         ttc.ttc_oldisnull = toast_oldisnull;
                                155                 :                :     }
                                156                 :          17200 :     ttc.ttc_attr = toast_attr;
                                157                 :          17200 :     toast_tuple_init(&ttc);
                                158                 :                : 
                                159                 :                :     /* ----------
                                160                 :                :      * Compress and/or save external until data fits into target length
                                161                 :                :      *
                                162                 :                :      *  1: Inline compress attributes with attstorage EXTENDED, and store very
                                163                 :                :      *     large attributes with attstorage EXTENDED or EXTERNAL external
                                164                 :                :      *     immediately
                                165                 :                :      *  2: Store attributes with attstorage EXTENDED or EXTERNAL external
                                166                 :                :      *  3: Inline compress attributes with attstorage MAIN
                                167                 :                :      *  4: Store attributes with attstorage MAIN external
                                168                 :                :      * ----------
                                169                 :                :      */
                                170                 :                : 
                                171                 :                :     /* compute header overhead --- this should match heap_form_tuple() */
 1742                           172                 :          17200 :     hoff = SizeofHeapTupleHeader;
 1682                           173         [ +  + ]:          17200 :     if ((ttc.ttc_flags & TOAST_HAS_NULLS) != 0)
 1742                           174                 :           2292 :         hoff += BITMAPLEN(numAttrs);
                                175                 :          17200 :     hoff = MAXALIGN(hoff);
                                176                 :                :     /* now convert to a limit on the tuple data size */
    3 akorotkov@postgresql      177         [ +  + ]:          17200 :     maxDataLen = RelationGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
                                178                 :                : 
                                179                 :                :     /*
                                180                 :                :      * Look for attributes with attstorage EXTENDED to compress.  Also find
                                181                 :                :      * large attributes with attstorage EXTENDED or EXTERNAL, and store them
                                182                 :                :      * external.
                                183                 :                :      */
 1742 rhaas@postgresql.org      184                 :          35987 :     while (heap_compute_data_size(tupleDesc,
                                185         [ +  + ]:          35987 :                                   toast_values, toast_isnull) > maxDataLen)
                                186                 :                :     {
                                187                 :                :         int         biggest_attno;
                                188                 :                : 
 1682                           189                 :          19449 :         biggest_attno = toast_tuple_find_biggest_attribute(&ttc, true, false);
 1742                           190         [ +  + ]:          19449 :         if (biggest_attno < 0)
                                191                 :            662 :             break;
                                192                 :                : 
                                193                 :                :         /*
                                194                 :                :          * Attempt to compress it inline, if it has attstorage EXTENDED
                                195                 :                :          */
 1502 tgl@sss.pgh.pa.us         196         [ +  + ]:          18787 :         if (TupleDescAttr(tupleDesc, biggest_attno)->attstorage == TYPSTORAGE_EXTENDED)
 1682 rhaas@postgresql.org      197                 :          15890 :             toast_tuple_try_compression(&ttc, biggest_attno);
                                198                 :                :         else
                                199                 :                :         {
                                200                 :                :             /*
                                201                 :                :              * has attstorage EXTERNAL, ignore on subsequent compression
                                202                 :                :              * passes
                                203                 :                :              */
                                204                 :           2897 :             toast_attr[biggest_attno].tai_colflags |= TOASTCOL_INCOMPRESSIBLE;
                                205                 :                :         }
                                206                 :                : 
                                207                 :                :         /*
                                208                 :                :          * If this value is by itself more than maxDataLen (after compression
                                209                 :                :          * if any), push it out to the toast table immediately, if possible.
                                210                 :                :          * This avoids uselessly compressing other fields in the common case
                                211                 :                :          * where we have one long field and several short ones.
                                212                 :                :          *
                                213                 :                :          * XXX maybe the threshold should be less than maxDataLen?
                                214                 :                :          */
                                215         [ +  + ]:          18787 :         if (toast_attr[biggest_attno].tai_size > maxDataLen &&
 1742                           216         [ +  - ]:           6397 :             rel->rd_rel->reltoastrelid != InvalidOid)
 1682                           217                 :           6397 :             toast_tuple_externalize(&ttc, biggest_attno, options);
                                218                 :                :     }
                                219                 :                : 
                                220                 :                :     /*
                                221                 :                :      * Second we look for attributes of attstorage EXTENDED or EXTERNAL that
                                222                 :                :      * are still inline, and make them external.  But skip this if there's no
                                223                 :                :      * toast table to push them to.
                                224                 :                :      */
 1742                           225                 :          17850 :     while (heap_compute_data_size(tupleDesc,
                                226         [ +  + ]:          17850 :                                   toast_values, toast_isnull) > maxDataLen &&
                                227         [ +  + ]:            683 :            rel->rd_rel->reltoastrelid != InvalidOid)
                                228                 :                :     {
                                229                 :                :         int         biggest_attno;
                                230                 :                : 
 1682                           231                 :            677 :         biggest_attno = toast_tuple_find_biggest_attribute(&ttc, false, false);
 1742                           232         [ +  + ]:            677 :         if (biggest_attno < 0)
                                233                 :             27 :             break;
 1682                           234                 :            650 :         toast_tuple_externalize(&ttc, biggest_attno, options);
                                235                 :                :     }
                                236                 :                : 
                                237                 :                :     /*
                                238                 :                :      * Round 3 - this time we take attributes with storage MAIN into
                                239                 :                :      * compression
                                240                 :                :      */
 1742                           241                 :          17221 :     while (heap_compute_data_size(tupleDesc,
                                242         [ +  + ]:          17221 :                                   toast_values, toast_isnull) > maxDataLen)
                                243                 :                :     {
                                244                 :                :         int         biggest_attno;
                                245                 :                : 
 1682                           246                 :             33 :         biggest_attno = toast_tuple_find_biggest_attribute(&ttc, true, true);
 1742                           247         [ +  + ]:             33 :         if (biggest_attno < 0)
                                248                 :             12 :             break;
                                249                 :                : 
 1682                           250                 :             21 :         toast_tuple_try_compression(&ttc, biggest_attno);
                                251                 :                :     }
                                252                 :                : 
                                253                 :                :     /*
                                254                 :                :      * Finally we store attributes of type MAIN externally.  At this point we
                                255                 :                :      * increase the target tuple size, so that MAIN attributes aren't stored
                                256                 :                :      * externally unless really necessary.
                                257                 :                :      */
 1742                           258                 :          17200 :     maxDataLen = TOAST_TUPLE_TARGET_MAIN - hoff;
                                259                 :                : 
                                260                 :          17200 :     while (heap_compute_data_size(tupleDesc,
                                261         [ -  + ]:          17200 :                                   toast_values, toast_isnull) > maxDataLen &&
 1742 rhaas@postgresql.org      262         [ #  # ]:UBC           0 :            rel->rd_rel->reltoastrelid != InvalidOid)
                                263                 :                :     {
                                264                 :                :         int         biggest_attno;
                                265                 :                : 
 1682                           266                 :              0 :         biggest_attno = toast_tuple_find_biggest_attribute(&ttc, false, true);
 1742                           267         [ #  # ]:              0 :         if (biggest_attno < 0)
                                268                 :              0 :             break;
                                269                 :                : 
 1682                           270                 :              0 :         toast_tuple_externalize(&ttc, biggest_attno, options);
                                271                 :                :     }
                                272                 :                : 
                                273                 :                :     /*
                                274                 :                :      * In the case we toasted any values, we need to build a new heap tuple
                                275                 :                :      * with the changed values.
                                276                 :                :      */
 1682 rhaas@postgresql.org      277         [ +  + ]:CBC       17200 :     if ((ttc.ttc_flags & TOAST_NEEDS_CHANGE) != 0)
                                278                 :                :     {
 1742                           279                 :          17129 :         HeapTupleHeader olddata = newtup->t_data;
                                280                 :                :         HeapTupleHeader new_data;
                                281                 :                :         int32       new_header_len;
                                282                 :                :         int32       new_data_len;
                                283                 :                :         int32       new_tuple_len;
                                284                 :                : 
                                285                 :                :         /*
                                286                 :                :          * Calculate the new size of the tuple.
                                287                 :                :          *
                                288                 :                :          * Note: we used to assume here that the old tuple's t_hoff must equal
                                289                 :                :          * the new_header_len value, but that was incorrect.  The old tuple
                                290                 :                :          * might have a smaller-than-current natts, if there's been an ALTER
                                291                 :                :          * TABLE ADD COLUMN since it was stored; and that would lead to a
                                292                 :                :          * different conclusion about the size of the null bitmap, or even
                                293                 :                :          * whether there needs to be one at all.
                                294                 :                :          */
                                295                 :          17129 :         new_header_len = SizeofHeapTupleHeader;
 1682                           296         [ +  + ]:          17129 :         if ((ttc.ttc_flags & TOAST_HAS_NULLS) != 0)
 1742                           297                 :           2259 :             new_header_len += BITMAPLEN(numAttrs);
                                298                 :          17129 :         new_header_len = MAXALIGN(new_header_len);
                                299                 :          17129 :         new_data_len = heap_compute_data_size(tupleDesc,
                                300                 :                :                                               toast_values, toast_isnull);
                                301                 :          17129 :         new_tuple_len = new_header_len + new_data_len;
                                302                 :                : 
                                303                 :                :         /*
                                304                 :                :          * Allocate and zero the space needed, and fill HeapTupleData fields.
                                305                 :                :          */
                                306                 :          17129 :         result_tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + new_tuple_len);
                                307                 :          17129 :         result_tuple->t_len = new_tuple_len;
                                308                 :          17129 :         result_tuple->t_self = newtup->t_self;
                                309                 :          17129 :         result_tuple->t_tableOid = newtup->t_tableOid;
                                310                 :          17129 :         new_data = (HeapTupleHeader) ((char *) result_tuple + HEAPTUPLESIZE);
                                311                 :          17129 :         result_tuple->t_data = new_data;
                                312                 :                : 
                                313                 :                :         /*
                                314                 :                :          * Copy the existing tuple header, but adjust natts and t_hoff.
                                315                 :                :          */
                                316                 :          17129 :         memcpy(new_data, olddata, SizeofHeapTupleHeader);
                                317                 :          17129 :         HeapTupleHeaderSetNatts(new_data, numAttrs);
                                318                 :          17129 :         new_data->t_hoff = new_header_len;
                                319                 :                : 
                                320                 :                :         /* Copy over the data, and fill the null bitmap if needed */
                                321                 :          17129 :         heap_fill_tuple(tupleDesc,
                                322                 :                :                         toast_values,
                                323                 :                :                         toast_isnull,
                                324                 :                :                         (char *) new_data + new_header_len,
                                325                 :                :                         new_data_len,
                                326                 :                :                         &(new_data->t_infomask),
 1682                           327         [ +  + ]:          17129 :                         ((ttc.ttc_flags & TOAST_HAS_NULLS) != 0) ?
                                328                 :                :                         new_data->t_bits : NULL);
                                329                 :                :     }
                                330                 :                :     else
 1742                           331                 :             71 :         result_tuple = newtup;
                                332                 :                : 
 1682                           333                 :          17200 :     toast_tuple_cleanup(&ttc);
                                334                 :                : 
 1742                           335                 :          17200 :     return result_tuple;
                                336                 :                : }
                                337                 :                : 
                                338                 :                : 
                                339                 :                : /* ----------
                                340                 :                :  * toast_flatten_tuple -
                                341                 :                :  *
                                342                 :                :  *  "Flatten" a tuple to contain no out-of-line toasted fields.
                                343                 :                :  *  (This does not eliminate compressed or short-header datums.)
                                344                 :                :  *
                                345                 :                :  *  Note: we expect the caller already checked HeapTupleHasExternal(tup),
                                346                 :                :  *  so there is no need for a short-circuit path.
                                347                 :                :  * ----------
                                348                 :                :  */
                                349                 :                : HeapTuple
                                350                 :           1657 : toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
                                351                 :                : {
                                352                 :                :     HeapTuple   new_tuple;
                                353                 :           1657 :     int         numAttrs = tupleDesc->natts;
                                354                 :                :     int         i;
                                355                 :                :     Datum       toast_values[MaxTupleAttributeNumber];
                                356                 :                :     bool        toast_isnull[MaxTupleAttributeNumber];
                                357                 :                :     bool        toast_free[MaxTupleAttributeNumber];
                                358                 :                : 
                                359                 :                :     /*
                                360                 :                :      * Break down the tuple into fields.
                                361                 :                :      */
                                362         [ -  + ]:           1657 :     Assert(numAttrs <= MaxTupleAttributeNumber);
                                363                 :           1657 :     heap_deform_tuple(tup, tupleDesc, toast_values, toast_isnull);
                                364                 :                : 
                                365                 :           1657 :     memset(toast_free, 0, numAttrs * sizeof(bool));
                                366                 :                : 
                                367         [ +  + ]:          52153 :     for (i = 0; i < numAttrs; i++)
                                368                 :                :     {
                                369                 :                :         /*
                                370                 :                :          * Look at non-null varlena attributes
                                371                 :                :          */
                                372   [ +  +  +  + ]:          50496 :         if (!toast_isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
                                373                 :                :         {
                                374                 :                :             struct varlena *new_value;
                                375                 :                : 
                                376                 :           6945 :             new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
                                377         [ +  + ]:           6945 :             if (VARATT_IS_EXTERNAL(new_value))
                                378                 :                :             {
 1654                           379                 :           1699 :                 new_value = detoast_external_attr(new_value);
 1742                           380                 :           1699 :                 toast_values[i] = PointerGetDatum(new_value);
                                381                 :           1699 :                 toast_free[i] = true;
                                382                 :                :             }
                                383                 :                :         }
                                384                 :                :     }
                                385                 :                : 
                                386                 :                :     /*
                                387                 :                :      * Form the reconfigured tuple.
                                388                 :                :      */
                                389                 :           1657 :     new_tuple = heap_form_tuple(tupleDesc, toast_values, toast_isnull);
                                390                 :                : 
                                391                 :                :     /*
                                392                 :                :      * Be sure to copy the tuple's identity fields.  We also make a point of
                                393                 :                :      * copying visibility info, just in case anybody looks at those fields in
                                394                 :                :      * a syscache entry.
                                395                 :                :      */
                                396                 :           1657 :     new_tuple->t_self = tup->t_self;
                                397                 :           1657 :     new_tuple->t_tableOid = tup->t_tableOid;
                                398                 :                : 
                                399                 :           1657 :     new_tuple->t_data->t_choice = tup->t_data->t_choice;
                                400                 :           1657 :     new_tuple->t_data->t_ctid = tup->t_data->t_ctid;
                                401                 :           1657 :     new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK;
                                402                 :           1657 :     new_tuple->t_data->t_infomask |=
                                403                 :           1657 :         tup->t_data->t_infomask & HEAP_XACT_MASK;
                                404                 :           1657 :     new_tuple->t_data->t_infomask2 &= ~HEAP2_XACT_MASK;
                                405                 :           1657 :     new_tuple->t_data->t_infomask2 |=
                                406                 :           1657 :         tup->t_data->t_infomask2 & HEAP2_XACT_MASK;
                                407                 :                : 
                                408                 :                :     /*
                                409                 :                :      * Free allocated temp values
                                410                 :                :      */
                                411         [ +  + ]:          52153 :     for (i = 0; i < numAttrs; i++)
                                412         [ +  + ]:          50496 :         if (toast_free[i])
                                413                 :           1699 :             pfree(DatumGetPointer(toast_values[i]));
                                414                 :                : 
                                415                 :           1657 :     return new_tuple;
                                416                 :                : }
                                417                 :                : 
                                418                 :                : 
                                419                 :                : /* ----------
                                420                 :                :  * toast_flatten_tuple_to_datum -
                                421                 :                :  *
                                422                 :                :  *  "Flatten" a tuple containing out-of-line toasted fields into a Datum.
                                423                 :                :  *  The result is always palloc'd in the current memory context.
                                424                 :                :  *
                                425                 :                :  *  We have a general rule that Datums of container types (rows, arrays,
                                426                 :                :  *  ranges, etc) must not contain any external TOAST pointers.  Without
                                427                 :                :  *  this rule, we'd have to look inside each Datum when preparing a tuple
                                428                 :                :  *  for storage, which would be expensive and would fail to extend cleanly
                                429                 :                :  *  to new sorts of container types.
                                430                 :                :  *
                                431                 :                :  *  However, we don't want to say that tuples represented as HeapTuples
                                432                 :                :  *  can't contain toasted fields, so instead this routine should be called
                                433                 :                :  *  when such a HeapTuple is being converted into a Datum.
                                434                 :                :  *
                                435                 :                :  *  While we're at it, we decompress any compressed fields too.  This is not
                                436                 :                :  *  necessary for correctness, but reflects an expectation that compression
                                437                 :                :  *  will be more effective if applied to the whole tuple not individual
                                438                 :                :  *  fields.  We are not so concerned about that that we want to deconstruct
                                439                 :                :  *  and reconstruct tuples just to get rid of compressed fields, however.
                                440                 :                :  *  So callers typically won't call this unless they see that the tuple has
                                441                 :                :  *  at least one external field.
                                442                 :                :  *
                                443                 :                :  *  On the other hand, in-line short-header varlena fields are left alone.
                                444                 :                :  *  If we "untoasted" them here, they'd just get changed back to short-header
                                445                 :                :  *  format anyway within heap_fill_tuple.
                                446                 :                :  * ----------
                                447                 :                :  */
                                448                 :                : Datum
                                449                 :              6 : toast_flatten_tuple_to_datum(HeapTupleHeader tup,
                                450                 :                :                              uint32 tup_len,
                                451                 :                :                              TupleDesc tupleDesc)
                                452                 :                : {
                                453                 :                :     HeapTupleHeader new_data;
                                454                 :                :     int32       new_header_len;
                                455                 :                :     int32       new_data_len;
                                456                 :                :     int32       new_tuple_len;
                                457                 :                :     HeapTupleData tmptup;
                                458                 :              6 :     int         numAttrs = tupleDesc->natts;
                                459                 :                :     int         i;
                                460                 :              6 :     bool        has_nulls = false;
                                461                 :                :     Datum       toast_values[MaxTupleAttributeNumber];
                                462                 :                :     bool        toast_isnull[MaxTupleAttributeNumber];
                                463                 :                :     bool        toast_free[MaxTupleAttributeNumber];
                                464                 :                : 
                                465                 :                :     /* Build a temporary HeapTuple control structure */
                                466                 :              6 :     tmptup.t_len = tup_len;
                                467                 :              6 :     ItemPointerSetInvalid(&(tmptup.t_self));
                                468                 :              6 :     tmptup.t_tableOid = InvalidOid;
                                469                 :              6 :     tmptup.t_data = tup;
                                470                 :                : 
                                471                 :                :     /*
                                472                 :                :      * Break down the tuple into fields.
                                473                 :                :      */
                                474         [ -  + ]:              6 :     Assert(numAttrs <= MaxTupleAttributeNumber);
                                475                 :              6 :     heap_deform_tuple(&tmptup, tupleDesc, toast_values, toast_isnull);
                                476                 :                : 
                                477                 :              6 :     memset(toast_free, 0, numAttrs * sizeof(bool));
                                478                 :                : 
                                479         [ +  + ]:             21 :     for (i = 0; i < numAttrs; i++)
                                480                 :                :     {
                                481                 :                :         /*
                                482                 :                :          * Look at non-null varlena attributes
                                483                 :                :          */
                                484         [ +  + ]:             15 :         if (toast_isnull[i])
                                485                 :              3 :             has_nulls = true;
                                486         [ +  - ]:             12 :         else if (TupleDescAttr(tupleDesc, i)->attlen == -1)
                                487                 :                :         {
                                488                 :                :             struct varlena *new_value;
                                489                 :                : 
                                490                 :             12 :             new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
                                491         [ +  + ]:             12 :             if (VARATT_IS_EXTERNAL(new_value) ||
                                492         [ -  + ]:              3 :                 VARATT_IS_COMPRESSED(new_value))
                                493                 :                :             {
 1654                           494                 :              9 :                 new_value = detoast_attr(new_value);
 1742                           495                 :              9 :                 toast_values[i] = PointerGetDatum(new_value);
                                496                 :              9 :                 toast_free[i] = true;
                                497                 :                :             }
                                498                 :                :         }
                                499                 :                :     }
                                500                 :                : 
                                501                 :                :     /*
                                502                 :                :      * Calculate the new size of the tuple.
                                503                 :                :      *
                                504                 :                :      * This should match the reconstruction code in
                                505                 :                :      * heap_toast_insert_or_update.
                                506                 :                :      */
                                507                 :              6 :     new_header_len = SizeofHeapTupleHeader;
                                508         [ +  + ]:              6 :     if (has_nulls)
                                509                 :              3 :         new_header_len += BITMAPLEN(numAttrs);
                                510                 :              6 :     new_header_len = MAXALIGN(new_header_len);
                                511                 :              6 :     new_data_len = heap_compute_data_size(tupleDesc,
                                512                 :                :                                           toast_values, toast_isnull);
                                513                 :              6 :     new_tuple_len = new_header_len + new_data_len;
                                514                 :                : 
                                515                 :              6 :     new_data = (HeapTupleHeader) palloc0(new_tuple_len);
                                516                 :                : 
                                517                 :                :     /*
                                518                 :                :      * Copy the existing tuple header, but adjust natts and t_hoff.
                                519                 :                :      */
                                520                 :              6 :     memcpy(new_data, tup, SizeofHeapTupleHeader);
                                521                 :              6 :     HeapTupleHeaderSetNatts(new_data, numAttrs);
                                522                 :              6 :     new_data->t_hoff = new_header_len;
                                523                 :                : 
                                524                 :                :     /* Set the composite-Datum header fields correctly */
                                525                 :              6 :     HeapTupleHeaderSetDatumLength(new_data, new_tuple_len);
                                526                 :              6 :     HeapTupleHeaderSetTypeId(new_data, tupleDesc->tdtypeid);
                                527                 :              6 :     HeapTupleHeaderSetTypMod(new_data, tupleDesc->tdtypmod);
                                528                 :                : 
                                529                 :                :     /* Copy over the data, and fill the null bitmap if needed */
                                530         [ +  + ]:              6 :     heap_fill_tuple(tupleDesc,
                                531                 :                :                     toast_values,
                                532                 :                :                     toast_isnull,
                                533                 :                :                     (char *) new_data + new_header_len,
                                534                 :                :                     new_data_len,
                                535                 :                :                     &(new_data->t_infomask),
                                536                 :                :                     has_nulls ? new_data->t_bits : NULL);
                                537                 :                : 
                                538                 :                :     /*
                                539                 :                :      * Free allocated temp values
                                540                 :                :      */
                                541         [ +  + ]:             21 :     for (i = 0; i < numAttrs; i++)
                                542         [ +  + ]:             15 :         if (toast_free[i])
                                543                 :              9 :             pfree(DatumGetPointer(toast_values[i]));
                                544                 :                : 
                                545                 :              6 :     return PointerGetDatum(new_data);
                                546                 :                : }
                                547                 :                : 
                                548                 :                : 
                                549                 :                : /* ----------
                                550                 :                :  * toast_build_flattened_tuple -
                                551                 :                :  *
                                552                 :                :  *  Build a tuple containing no out-of-line toasted fields.
                                553                 :                :  *  (This does not eliminate compressed or short-header datums.)
                                554                 :                :  *
                                555                 :                :  *  This is essentially just like heap_form_tuple, except that it will
                                556                 :                :  *  expand any external-data pointers beforehand.
                                557                 :                :  *
                                558                 :                :  *  It's not very clear whether it would be preferable to decompress
                                559                 :                :  *  in-line compressed datums while at it.  For now, we don't.
                                560                 :                :  * ----------
                                561                 :                :  */
                                562                 :                : HeapTuple
                                563                 :          19541 : toast_build_flattened_tuple(TupleDesc tupleDesc,
                                564                 :                :                             Datum *values,
                                565                 :                :                             bool *isnull)
                                566                 :                : {
                                567                 :                :     HeapTuple   new_tuple;
                                568                 :          19541 :     int         numAttrs = tupleDesc->natts;
                                569                 :                :     int         num_to_free;
                                570                 :                :     int         i;
                                571                 :                :     Datum       new_values[MaxTupleAttributeNumber];
                                572                 :                :     Pointer     freeable_values[MaxTupleAttributeNumber];
                                573                 :                : 
                                574                 :                :     /*
                                575                 :                :      * We can pass the caller's isnull array directly to heap_form_tuple, but
                                576                 :                :      * we potentially need to modify the values array.
                                577                 :                :      */
                                578         [ -  + ]:          19541 :     Assert(numAttrs <= MaxTupleAttributeNumber);
                                579                 :          19541 :     memcpy(new_values, values, numAttrs * sizeof(Datum));
                                580                 :                : 
                                581                 :          19541 :     num_to_free = 0;
                                582         [ +  + ]:         117844 :     for (i = 0; i < numAttrs; i++)
                                583                 :                :     {
                                584                 :                :         /*
                                585                 :                :          * Look at non-null varlena attributes
                                586                 :                :          */
                                587   [ +  +  +  + ]:          98303 :         if (!isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
                                588                 :                :         {
                                589                 :                :             struct varlena *new_value;
                                590                 :                : 
                                591                 :          35208 :             new_value = (struct varlena *) DatumGetPointer(new_values[i]);
                                592         [ +  + ]:          35208 :             if (VARATT_IS_EXTERNAL(new_value))
                                593                 :                :             {
 1654                           594                 :            201 :                 new_value = detoast_external_attr(new_value);
 1742                           595                 :            201 :                 new_values[i] = PointerGetDatum(new_value);
                                596                 :            201 :                 freeable_values[num_to_free++] = (Pointer) new_value;
                                597                 :                :             }
                                598                 :                :         }
                                599                 :                :     }
                                600                 :                : 
                                601                 :                :     /*
                                602                 :                :      * Form the reconfigured tuple.
                                603                 :                :      */
                                604                 :          19541 :     new_tuple = heap_form_tuple(tupleDesc, new_values, isnull);
                                605                 :                : 
                                606                 :                :     /*
                                607                 :                :      * Free allocated temp values
                                608                 :                :      */
                                609         [ +  + ]:          19742 :     for (i = 0; i < num_to_free; i++)
                                610                 :            201 :         pfree(freeable_values[i]);
                                611                 :                : 
                                612                 :          19541 :     return new_tuple;
                                613                 :                : }
                                614                 :                : 
                                615                 :                : /*
                                616                 :                :  * Fetch a TOAST slice from a heap table.
                                617                 :                :  *
                                618                 :                :  * toastrel is the relation from which chunks are to be fetched.
                                619                 :                :  * valueid identifies the TOAST value from which chunks are being fetched.
                                620                 :                :  * attrsize is the total size of the TOAST value.
                                621                 :                :  * sliceoffset is the byte offset within the TOAST value from which to fetch.
                                622                 :                :  * slicelength is the number of bytes to be fetched from the TOAST value.
                                623                 :                :  * result is the varlena into which the results should be written.
                                624                 :                :  */
                                625                 :                : void
 1559                           626                 :           8958 : heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
                                627                 :                :                        int32 sliceoffset, int32 slicelength,
                                628                 :                :                        struct varlena *result)
                                629                 :                : {
                                630                 :                :     Relation   *toastidxs;
                                631                 :                :     ScanKeyData toastkey[3];
                                632                 :           8958 :     TupleDesc   toasttupDesc = toastrel->rd_att;
                                633                 :                :     int         nscankeys;
                                634                 :                :     SysScanDesc toastscan;
                                635                 :                :     HeapTuple   ttup;
                                636                 :                :     int32       expectedchunk;
                                637                 :           8958 :     int32       totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
                                638                 :                :     int         startchunk;
                                639                 :                :     int         endchunk;
                                640                 :                :     int         num_indexes;
                                641                 :                :     int         validIndex;
                                642                 :                :     SnapshotData SnapshotToast;
                                643                 :                : 
                                644                 :                :     /* Look for the valid index of toast relation */
                                645                 :           8958 :     validIndex = toast_open_indexes(toastrel,
                                646                 :                :                                     AccessShareLock,
                                647                 :                :                                     &toastidxs,
                                648                 :                :                                     &num_indexes);
                                649                 :                : 
                                650                 :           8958 :     startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE;
                                651                 :           8958 :     endchunk = (sliceoffset + slicelength - 1) / TOAST_MAX_CHUNK_SIZE;
                                652         [ -  + ]:           8958 :     Assert(endchunk <= totalchunks);
                                653                 :                : 
                                654                 :                :     /* Set up a scan key to fetch from the index. */
                                655                 :           8958 :     ScanKeyInit(&toastkey[0],
                                656                 :                :                 (AttrNumber) 1,
                                657                 :                :                 BTEqualStrategyNumber, F_OIDEQ,
                                658                 :                :                 ObjectIdGetDatum(valueid));
                                659                 :                : 
                                660                 :                :     /*
                                661                 :                :      * No additional condition if fetching all chunks. Otherwise, use an
                                662                 :                :      * equality condition for one chunk, and a range condition otherwise.
                                663                 :                :      */
                                664   [ +  +  +  + ]:           8958 :     if (startchunk == 0 && endchunk == totalchunks - 1)
                                665                 :           8820 :         nscankeys = 1;
                                666         [ +  - ]:            138 :     else if (startchunk == endchunk)
                                667                 :                :     {
                                668                 :            138 :         ScanKeyInit(&toastkey[1],
                                669                 :                :                     (AttrNumber) 2,
                                670                 :                :                     BTEqualStrategyNumber, F_INT4EQ,
                                671                 :                :                     Int32GetDatum(startchunk));
                                672                 :            138 :         nscankeys = 2;
                                673                 :                :     }
                                674                 :                :     else
                                675                 :                :     {
 1559 rhaas@postgresql.org      676                 :UBC           0 :         ScanKeyInit(&toastkey[1],
                                677                 :                :                     (AttrNumber) 2,
                                678                 :                :                     BTGreaterEqualStrategyNumber, F_INT4GE,
                                679                 :                :                     Int32GetDatum(startchunk));
                                680                 :              0 :         ScanKeyInit(&toastkey[2],
                                681                 :                :                     (AttrNumber) 2,
                                682                 :                :                     BTLessEqualStrategyNumber, F_INT4LE,
                                683                 :                :                     Int32GetDatum(endchunk));
                                684                 :              0 :         nscankeys = 3;
                                685                 :                :     }
                                686                 :                : 
                                687                 :                :     /* Prepare for scan */
 1559 rhaas@postgresql.org      688                 :CBC        8958 :     init_toast_snapshot(&SnapshotToast);
                                689                 :           8958 :     toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
                                690                 :                :                                            &SnapshotToast, nscankeys, toastkey);
                                691                 :                : 
                                692                 :                :     /*
                                693                 :                :      * Read the chunks by index
                                694                 :                :      *
                                695                 :                :      * The index is on (valueid, chunkidx) so they will come in order
                                696                 :                :      */
                                697                 :           8958 :     expectedchunk = startchunk;
                                698         [ +  + ]:          36930 :     while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
                                699                 :                :     {
                                700                 :                :         int32       curchunk;
                                701                 :                :         Pointer     chunk;
                                702                 :                :         bool        isnull;
                                703                 :                :         char       *chunkdata;
                                704                 :                :         int32       chunksize;
                                705                 :                :         int32       expected_size;
                                706                 :                :         int32       chcpystrt;
                                707                 :                :         int32       chcpyend;
                                708                 :                : 
                                709                 :                :         /*
                                710                 :                :          * Have a chunk, extract the sequence number and the data
                                711                 :                :          */
                                712                 :          27972 :         curchunk = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
                                713         [ -  + ]:          27972 :         Assert(!isnull);
                                714                 :          27972 :         chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
                                715         [ -  + ]:          27972 :         Assert(!isnull);
                                716         [ +  - ]:          27972 :         if (!VARATT_IS_EXTENDED(chunk))
                                717                 :                :         {
                                718                 :          27972 :             chunksize = VARSIZE(chunk) - VARHDRSZ;
                                719                 :          27972 :             chunkdata = VARDATA(chunk);
                                720                 :                :         }
 1559 rhaas@postgresql.org      721         [ #  # ]:UBC           0 :         else if (VARATT_IS_SHORT(chunk))
                                722                 :                :         {
                                723                 :                :             /* could happen due to heap_form_tuple doing its thing */
                                724                 :              0 :             chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
                                725                 :              0 :             chunkdata = VARDATA_SHORT(chunk);
                                726                 :                :         }
                                727                 :                :         else
                                728                 :                :         {
                                729                 :                :             /* should never happen */
                                730         [ #  # ]:              0 :             elog(ERROR, "found toasted toast chunk for toast value %u in %s",
                                731                 :                :                  valueid, RelationGetRelationName(toastrel));
                                732                 :                :             chunksize = 0;      /* keep compiler quiet */
                                733                 :                :             chunkdata = NULL;
                                734                 :                :         }
                                735                 :                : 
                                736                 :                :         /*
                                737                 :                :          * Some checks on the data we've found
                                738                 :                :          */
 1559 rhaas@postgresql.org      739         [ -  + ]:CBC       27972 :         if (curchunk != expectedchunk)
 1559 rhaas@postgresql.org      740         [ #  # ]:UBC           0 :             ereport(ERROR,
                                741                 :                :                     (errcode(ERRCODE_DATA_CORRUPTED),
                                742                 :                :                      errmsg_internal("unexpected chunk number %d (expected %d) for toast value %u in %s",
                                743                 :                :                                      curchunk, expectedchunk, valueid,
                                744                 :                :                                      RelationGetRelationName(toastrel))));
 1559 rhaas@postgresql.org      745         [ -  + ]:CBC       27972 :         if (curchunk > endchunk)
 1559 rhaas@postgresql.org      746         [ #  # ]:UBC           0 :             ereport(ERROR,
                                747                 :                :                     (errcode(ERRCODE_DATA_CORRUPTED),
                                748                 :                :                      errmsg_internal("unexpected chunk number %d (out of range %d..%d) for toast value %u in %s",
                                749                 :                :                                      curchunk,
                                750                 :                :                                      startchunk, endchunk, valueid,
                                751                 :                :                                      RelationGetRelationName(toastrel))));
 1559 rhaas@postgresql.org      752         [ +  + ]:CBC       27972 :         expected_size = curchunk < totalchunks - 1 ? TOAST_MAX_CHUNK_SIZE
                                753                 :           8832 :             : attrsize - ((totalchunks - 1) * TOAST_MAX_CHUNK_SIZE);
                                754         [ -  + ]:          27972 :         if (chunksize != expected_size)
 1559 rhaas@postgresql.org      755         [ #  # ]:UBC           0 :             ereport(ERROR,
                                756                 :                :                     (errcode(ERRCODE_DATA_CORRUPTED),
                                757                 :                :                      errmsg_internal("unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s",
                                758                 :                :                                      chunksize, expected_size,
                                759                 :                :                                      curchunk, totalchunks, valueid,
                                760                 :                :                                      RelationGetRelationName(toastrel))));
                                761                 :                : 
                                762                 :                :         /*
                                763                 :                :          * Copy the data into proper place in our result
                                764                 :                :          */
 1559 rhaas@postgresql.org      765                 :CBC       27972 :         chcpystrt = 0;
                                766                 :          27972 :         chcpyend = chunksize - 1;
                                767         [ +  + ]:          27972 :         if (curchunk == startchunk)
                                768                 :           8958 :             chcpystrt = sliceoffset % TOAST_MAX_CHUNK_SIZE;
                                769         [ +  + ]:          27972 :         if (curchunk == endchunk)
                                770                 :           8958 :             chcpyend = (sliceoffset + slicelength - 1) % TOAST_MAX_CHUNK_SIZE;
                                771                 :                : 
                                772                 :          27972 :         memcpy(VARDATA(result) +
                                773                 :          27972 :                (curchunk * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
                                774                 :          27972 :                chunkdata + chcpystrt,
                                775                 :          27972 :                (chcpyend - chcpystrt) + 1);
                                776                 :                : 
                                777                 :          27972 :         expectedchunk++;
                                778                 :                :     }
                                779                 :                : 
                                780                 :                :     /*
                                781                 :                :      * Final checks that we successfully fetched the datum
                                782                 :                :      */
                                783         [ -  + ]:           8958 :     if (expectedchunk != (endchunk + 1))
 1559 rhaas@postgresql.org      784         [ #  # ]:UBC           0 :         ereport(ERROR,
                                785                 :                :                 (errcode(ERRCODE_DATA_CORRUPTED),
                                786                 :                :                  errmsg_internal("missing chunk number %d for toast value %u in %s",
                                787                 :                :                                  expectedchunk, valueid,
                                788                 :                :                                  RelationGetRelationName(toastrel))));
                                789                 :                : 
                                790                 :                :     /* End scan and close indexes. */
 1559 rhaas@postgresql.org      791                 :CBC        8958 :     systable_endscan_ordered(toastscan);
                                792                 :           8958 :     toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
                                793                 :           8958 : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622