LCOV - code coverage report
Current view: top level - src/backend/access/heap - tuptoaster.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 593 723 82.0 %
Date: 2017-09-29 15:12:54 Functions: 21 22 95.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * tuptoaster.c
       4             :  *    Support routines for external and compressed storage of
       5             :  *    variable size attributes.
       6             :  *
       7             :  * Copyright (c) 2000-2017, PostgreSQL Global Development Group
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/access/heap/tuptoaster.c
      12             :  *
      13             :  *
      14             :  * INTERFACE ROUTINES
      15             :  *      toast_insert_or_update -
      16             :  *          Try to make a given tuple fit into one page by compressing
      17             :  *          or moving off attributes
      18             :  *
      19             :  *      toast_delete -
      20             :  *          Reclaim toast storage when a tuple is deleted
      21             :  *
      22             :  *      heap_tuple_untoast_attr -
      23             :  *          Fetch back a given value from the "secondary" relation
      24             :  *
      25             :  *-------------------------------------------------------------------------
      26             :  */
      27             : 
      28             : #include "postgres.h"
      29             : 
      30             : #include <unistd.h>
      31             : #include <fcntl.h>
      32             : 
      33             : #include "access/genam.h"
      34             : #include "access/heapam.h"
      35             : #include "access/tuptoaster.h"
      36             : #include "access/xact.h"
      37             : #include "catalog/catalog.h"
      38             : #include "common/pg_lzcompress.h"
      39             : #include "miscadmin.h"
      40             : #include "utils/expandeddatum.h"
      41             : #include "utils/fmgroids.h"
      42             : #include "utils/rel.h"
      43             : #include "utils/snapmgr.h"
      44             : #include "utils/typcache.h"
      45             : #include "utils/tqual.h"
      46             : 
      47             : 
      48             : #undef TOAST_DEBUG
      49             : 
      50             : /*
      51             :  *  The information at the start of the compressed toast data.
      52             :  */
      53             : typedef struct toast_compress_header
      54             : {
      55             :     int32       vl_len_;        /* varlena header (do not touch directly!) */
      56             :     int32       rawsize;
      57             : } toast_compress_header;
      58             : 
      59             : /*
      60             :  * Utilities for manipulation of header information for compressed
      61             :  * toast entries.
      62             :  */
      63             : #define TOAST_COMPRESS_HDRSZ        ((int32) sizeof(toast_compress_header))
      64             : #define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->rawsize)
      65             : #define TOAST_COMPRESS_RAWDATA(ptr) \
      66             :     (((char *) (ptr)) + TOAST_COMPRESS_HDRSZ)
      67             : #define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
      68             :     (((toast_compress_header *) (ptr))->rawsize = (len))
      69             : 
      70             : static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
      71             : static Datum toast_save_datum(Relation rel, Datum value,
      72             :                  struct varlena *oldexternal, int options);
      73             : static bool toastrel_valueid_exists(Relation toastrel, Oid valueid);
      74             : static bool toastid_valueid_exists(Oid toastrelid, Oid valueid);
      75             : static struct varlena *toast_fetch_datum(struct varlena *attr);
      76             : static struct varlena *toast_fetch_datum_slice(struct varlena *attr,
      77             :                         int32 sliceoffset, int32 length);
      78             : static struct varlena *toast_decompress_datum(struct varlena *attr);
      79             : static int toast_open_indexes(Relation toastrel,
      80             :                    LOCKMODE lock,
      81             :                    Relation **toastidxs,
      82             :                    int *num_indexes);
      83             : static void toast_close_indexes(Relation *toastidxs, int num_indexes,
      84             :                     LOCKMODE lock);
      85             : static void init_toast_snapshot(Snapshot toast_snapshot);
      86             : 
      87             : 
      88             : /* ----------
      89             :  * heap_tuple_fetch_attr -
      90             :  *
      91             :  *  Public entry point to get back a toasted value from
      92             :  *  external source (possibly still in compressed format).
      93             :  *
      94             :  * This will return a datum that contains all the data internally, ie, not
      95             :  * relying on external storage or memory, but it can still be compressed or
      96             :  * have a short header.  Note some callers assume that if the input is an
      97             :  * EXTERNAL datum, the result will be a pfree'able chunk.
      98             :  * ----------
      99             :  */
     100             : struct varlena *
     101         610 : heap_tuple_fetch_attr(struct varlena *attr)
     102             : {
     103             :     struct varlena *result;
     104             : 
     105         610 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
     106             :     {
     107             :         /*
     108             :          * This is an external stored plain value
     109             :          */
     110         125 :         result = toast_fetch_datum(attr);
     111             :     }
     112         485 :     else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
     113          82 :     {
     114             :         /*
     115             :          * This is an indirect pointer --- dereference it
     116             :          */
     117             :         struct varatt_indirect redirect;
     118             : 
     119          82 :         VARATT_EXTERNAL_GET_POINTER(redirect, attr);
     120          82 :         attr = (struct varlena *) redirect.pointer;
     121             : 
     122             :         /* nested indirect Datums aren't allowed */
     123          82 :         Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
     124             : 
     125             :         /* recurse if value is still external in some other way */
     126          82 :         if (VARATT_IS_EXTERNAL(attr))
     127           0 :             return heap_tuple_fetch_attr(attr);
     128             : 
     129             :         /*
     130             :          * Copy into the caller's memory context, in case caller tries to
     131             :          * pfree the result.
     132             :          */
     133          82 :         result = (struct varlena *) palloc(VARSIZE_ANY(attr));
     134          82 :         memcpy(result, attr, VARSIZE_ANY(attr));
     135             :     }
     136         403 :     else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
     137         403 :     {
     138             :         /*
     139             :          * This is an expanded-object pointer --- get flat format
     140             :          */
     141             :         ExpandedObjectHeader *eoh;
     142             :         Size        resultsize;
     143             : 
     144         403 :         eoh = DatumGetEOHP(PointerGetDatum(attr));
     145         403 :         resultsize = EOH_get_flat_size(eoh);
     146         403 :         result = (struct varlena *) palloc(resultsize);
     147         403 :         EOH_flatten_into(eoh, (void *) result, resultsize);
     148             :     }
     149             :     else
     150             :     {
     151             :         /*
     152             :          * This is a plain value inside of the main tuple - why am I called?
     153             :          */
     154           0 :         result = attr;
     155             :     }
     156             : 
     157         610 :     return result;
     158             : }
     159             : 
     160             : 
     161             : /* ----------
     162             :  * heap_tuple_untoast_attr -
     163             :  *
     164             :  *  Public entry point to get back a toasted value from compression
     165             :  *  or external storage.  The result is always non-extended varlena form.
     166             :  *
     167             :  * Note some callers assume that if the input is an EXTERNAL or COMPRESSED
     168             :  * datum, the result will be a pfree'able chunk.
     169             :  * ----------
     170             :  */
     171             : struct varlena *
     172     1814087 : heap_tuple_untoast_attr(struct varlena *attr)
     173             : {
     174     1814087 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
     175             :     {
     176             :         /*
     177             :          * This is an externally stored datum --- fetch it back from there
     178             :          */
     179         214 :         attr = toast_fetch_datum(attr);
     180             :         /* If it's compressed, decompress it */
     181         428 :         if (VARATT_IS_COMPRESSED(attr))
     182             :         {
     183         214 :             struct varlena *tmp = attr;
     184             : 
     185         214 :             attr = toast_decompress_datum(tmp);
     186         214 :             pfree(tmp);
     187             :         }
     188             :     }
     189     1813873 :     else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
     190          10 :     {
     191             :         /*
     192             :          * This is an indirect pointer --- dereference it
     193             :          */
     194             :         struct varatt_indirect redirect;
     195             : 
     196          10 :         VARATT_EXTERNAL_GET_POINTER(redirect, attr);
     197          10 :         attr = (struct varlena *) redirect.pointer;
     198             : 
     199             :         /* nested indirect Datums aren't allowed */
     200          10 :         Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
     201             : 
     202             :         /* recurse in case value is still extended in some other way */
     203          10 :         attr = heap_tuple_untoast_attr(attr);
     204             : 
     205             :         /* if it isn't, we'd better copy it */
     206          10 :         if (attr == (struct varlena *) redirect.pointer)
     207             :         {
     208             :             struct varlena *result;
     209             : 
     210           0 :             result = (struct varlena *) palloc(VARSIZE_ANY(attr));
     211           0 :             memcpy(result, attr, VARSIZE_ANY(attr));
     212           0 :             attr = result;
     213             :         }
     214             :     }
     215     1813863 :     else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
     216             :     {
     217             :         /*
     218             :          * This is an expanded-object pointer --- get flat format
     219             :          */
     220         403 :         attr = heap_tuple_fetch_attr(attr);
     221             :         /* flatteners are not allowed to produce compressed/short output */
     222         403 :         Assert(!VARATT_IS_EXTENDED(attr));
     223             :     }
     224     1813460 :     else if (VARATT_IS_COMPRESSED(attr))
     225             :     {
     226             :         /*
     227             :          * This is a compressed value inside of the main tuple
     228             :          */
     229        5198 :         attr = toast_decompress_datum(attr);
     230             :     }
     231     1808262 :     else if (VARATT_IS_SHORT(attr))
     232             :     {
     233             :         /*
     234             :          * This is a short-header varlena --- convert to 4-byte header format
     235             :          */
     236     1808262 :         Size        data_size = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT;
     237     1808262 :         Size        new_size = data_size + VARHDRSZ;
     238             :         struct varlena *new_attr;
     239             : 
     240     1808262 :         new_attr = (struct varlena *) palloc(new_size);
     241     1808262 :         SET_VARSIZE(new_attr, new_size);
     242     1808262 :         memcpy(VARDATA(new_attr), VARDATA_SHORT(attr), data_size);
     243     1808262 :         attr = new_attr;
     244             :     }
     245             : 
     246     1814087 :     return attr;
     247             : }
     248             : 
     249             : 
     250             : /* ----------
     251             :  * heap_tuple_untoast_attr_slice -
     252             :  *
     253             :  *      Public entry point to get back part of a toasted value
     254             :  *      from compression or external storage.
     255             :  * ----------
     256             :  */
     257             : struct varlena *
     258          35 : heap_tuple_untoast_attr_slice(struct varlena *attr,
     259             :                               int32 sliceoffset, int32 slicelength)
     260             : {
     261             :     struct varlena *preslice;
     262             :     struct varlena *result;
     263             :     char       *attrdata;
     264             :     int32       attrsize;
     265             : 
     266          35 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
     267           5 :     {
     268             :         struct varatt_external toast_pointer;
     269             : 
     270          17 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     271             : 
     272             :         /* fast path for non-compressed external datums */
     273          17 :         if (!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
     274          12 :             return toast_fetch_datum_slice(attr, sliceoffset, slicelength);
     275             : 
     276             :         /* fetch it back (compressed marker will get set automatically) */
     277           5 :         preslice = toast_fetch_datum(attr);
     278             :     }
     279          18 :     else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
     280             :     {
     281             :         struct varatt_indirect redirect;
     282             : 
     283           0 :         VARATT_EXTERNAL_GET_POINTER(redirect, attr);
     284             : 
     285             :         /* nested indirect Datums aren't allowed */
     286           0 :         Assert(!VARATT_IS_EXTERNAL_INDIRECT(redirect.pointer));
     287             : 
     288           0 :         return heap_tuple_untoast_attr_slice(redirect.pointer,
     289             :                                              sliceoffset, slicelength);
     290             :     }
     291          18 :     else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
     292             :     {
     293             :         /* pass it off to heap_tuple_fetch_attr to flatten */
     294           0 :         preslice = heap_tuple_fetch_attr(attr);
     295             :     }
     296             :     else
     297          18 :         preslice = attr;
     298             : 
     299          23 :     Assert(!VARATT_IS_EXTERNAL(preslice));
     300             : 
     301          23 :     if (VARATT_IS_COMPRESSED(preslice))
     302             :     {
     303          17 :         struct varlena *tmp = preslice;
     304             : 
     305          17 :         preslice = toast_decompress_datum(tmp);
     306             : 
     307          17 :         if (tmp != attr)
     308           5 :             pfree(tmp);
     309             :     }
     310             : 
     311          23 :     if (VARATT_IS_SHORT(preslice))
     312             :     {
     313           0 :         attrdata = VARDATA_SHORT(preslice);
     314           0 :         attrsize = VARSIZE_SHORT(preslice) - VARHDRSZ_SHORT;
     315             :     }
     316             :     else
     317             :     {
     318          23 :         attrdata = VARDATA(preslice);
     319          23 :         attrsize = VARSIZE(preslice) - VARHDRSZ;
     320             :     }
     321             : 
     322             :     /* slicing of datum for compressed cases and plain value */
     323             : 
     324          23 :     if (sliceoffset >= attrsize)
     325             :     {
     326           3 :         sliceoffset = 0;
     327           3 :         slicelength = 0;
     328             :     }
     329             : 
     330          23 :     if (((sliceoffset + slicelength) > attrsize) || slicelength < 0)
     331           8 :         slicelength = attrsize - sliceoffset;
     332             : 
     333          23 :     result = (struct varlena *) palloc(slicelength + VARHDRSZ);
     334          23 :     SET_VARSIZE(result, slicelength + VARHDRSZ);
     335             : 
     336          23 :     memcpy(VARDATA(result), attrdata + sliceoffset, slicelength);
     337             : 
     338          23 :     if (preslice != attr)
     339          17 :         pfree(preslice);
     340             : 
     341          23 :     return result;
     342             : }
     343             : 
     344             : 
     345             : /* ----------
     346             :  * toast_raw_datum_size -
     347             :  *
     348             :  *  Return the raw (detoasted) size of a varlena datum
     349             :  *  (including the VARHDRSZ header)
     350             :  * ----------
     351             :  */
     352             : Size
     353      395786 : toast_raw_datum_size(Datum value)
     354             : {
     355      395786 :     struct varlena *attr = (struct varlena *) DatumGetPointer(value);
     356             :     Size        result;
     357             : 
     358      395786 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
     359         136 :     {
     360             :         /* va_rawsize is the size of the original datum -- including header */
     361             :         struct varatt_external toast_pointer;
     362             : 
     363         136 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     364         136 :         result = toast_pointer.va_rawsize;
     365             :     }
     366      395650 :     else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
     367             :     {
     368             :         struct varatt_indirect toast_pointer;
     369             : 
     370           0 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     371             : 
     372             :         /* nested indirect Datums aren't allowed */
     373           0 :         Assert(!VARATT_IS_EXTERNAL_INDIRECT(toast_pointer.pointer));
     374             : 
     375           0 :         return toast_raw_datum_size(PointerGetDatum(toast_pointer.pointer));
     376             :     }
     377      395650 :     else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
     378             :     {
     379           0 :         result = EOH_get_flat_size(DatumGetEOHP(value));
     380             :     }
     381      395650 :     else if (VARATT_IS_COMPRESSED(attr))
     382             :     {
     383             :         /* here, va_rawsize is just the payload size */
     384         772 :         result = VARRAWSIZE_4B_C(attr) + VARHDRSZ;
     385             :     }
     386      394878 :     else if (VARATT_IS_SHORT(attr))
     387             :     {
     388             :         /*
     389             :          * we have to normalize the header length to VARHDRSZ or else the
     390             :          * callers of this function will be confused.
     391             :          */
     392      266960 :         result = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT + VARHDRSZ;
     393             :     }
     394             :     else
     395             :     {
     396             :         /* plain untoasted datum */
     397      127918 :         result = VARSIZE(attr);
     398             :     }
     399      395786 :     return result;
     400             : }
     401             : 
     402             : /* ----------
     403             :  * toast_datum_size
     404             :  *
     405             :  *  Return the physical storage size (possibly compressed) of a varlena datum
     406             :  * ----------
     407             :  */
     408             : Size
     409          10 : toast_datum_size(Datum value)
     410             : {
     411          10 :     struct varlena *attr = (struct varlena *) DatumGetPointer(value);
     412             :     Size        result;
     413             : 
     414          10 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
     415           0 :     {
     416             :         /*
     417             :          * Attribute is stored externally - return the extsize whether
     418             :          * compressed or not.  We do not count the size of the toast pointer
     419             :          * ... should we?
     420             :          */
     421             :         struct varatt_external toast_pointer;
     422             : 
     423           0 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     424           0 :         result = toast_pointer.va_extsize;
     425             :     }
     426          10 :     else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
     427             :     {
     428             :         struct varatt_indirect toast_pointer;
     429             : 
     430           0 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     431             : 
     432             :         /* nested indirect Datums aren't allowed */
     433           0 :         Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
     434             : 
     435           0 :         return toast_datum_size(PointerGetDatum(toast_pointer.pointer));
     436             :     }
     437          10 :     else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
     438             :     {
     439           0 :         result = EOH_get_flat_size(DatumGetEOHP(value));
     440             :     }
     441          10 :     else if (VARATT_IS_SHORT(attr))
     442             :     {
     443           0 :         result = VARSIZE_SHORT(attr);
     444             :     }
     445             :     else
     446             :     {
     447             :         /*
     448             :          * Attribute is stored inline either compressed or not, just calculate
     449             :          * the size of the datum in either case.
     450             :          */
     451          10 :         result = VARSIZE(attr);
     452             :     }
     453          10 :     return result;
     454             : }
     455             : 
     456             : 
     457             : /* ----------
     458             :  * toast_delete -
     459             :  *
     460             :  *  Cascaded delete toast-entries on DELETE
     461             :  * ----------
     462             :  */
     463             : void
     464           7 : toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
     465             : {
     466             :     TupleDesc   tupleDesc;
     467             :     int         numAttrs;
     468             :     int         i;
     469             :     Datum       toast_values[MaxHeapAttributeNumber];
     470             :     bool        toast_isnull[MaxHeapAttributeNumber];
     471             : 
     472             :     /*
     473             :      * We should only ever be called for tuples of plain relations or
     474             :      * materialized views --- recursing on a toast rel is bad news.
     475             :      */
     476           7 :     Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
     477             :            rel->rd_rel->relkind == RELKIND_MATVIEW);
     478             : 
     479             :     /*
     480             :      * Get the tuple descriptor and break down the tuple into fields.
     481             :      *
     482             :      * NOTE: it's debatable whether to use heap_deform_tuple() here or just
     483             :      * heap_getattr() only the varlena columns.  The latter could win if there
     484             :      * are few varlena columns and many non-varlena ones. However,
     485             :      * heap_deform_tuple costs only O(N) while the heap_getattr way would cost
     486             :      * O(N^2) if there are many varlena columns, so it seems better to err on
     487             :      * the side of linear cost.  (We won't even be here unless there's at
     488             :      * least one varlena column, by the way.)
     489             :      */
     490           7 :     tupleDesc = rel->rd_att;
     491           7 :     numAttrs = tupleDesc->natts;
     492             : 
     493           7 :     Assert(numAttrs <= MaxHeapAttributeNumber);
     494           7 :     heap_deform_tuple(oldtup, tupleDesc, toast_values, toast_isnull);
     495             : 
     496             :     /*
     497             :      * Check for external stored attributes and delete them from the secondary
     498             :      * relation.
     499             :      */
     500          50 :     for (i = 0; i < numAttrs; i++)
     501             :     {
     502          43 :         if (TupleDescAttr(tupleDesc, i)->attlen == -1)
     503             :         {
     504          13 :             Datum       value = toast_values[i];
     505             : 
     506          13 :             if (toast_isnull[i])
     507           0 :                 continue;
     508          13 :             else if (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
     509           7 :                 toast_delete_datum(rel, value, is_speculative);
     510             :         }
     511             :     }
     512           7 : }
     513             : 
     514             : 
     515             : /* ----------
     516             :  * toast_insert_or_update -
     517             :  *
     518             :  *  Delete no-longer-used toast-entries and create new ones to
     519             :  *  make the new tuple fit on INSERT or UPDATE
     520             :  *
     521             :  * Inputs:
     522             :  *  newtup: the candidate new tuple to be inserted
     523             :  *  oldtup: the old row version for UPDATE, or NULL for INSERT
     524             :  *  options: options to be passed to heap_insert() for toast rows
     525             :  * Result:
     526             :  *  either newtup if no toasting is needed, or a palloc'd modified tuple
     527             :  *  that is what should actually get stored
     528             :  *
     529             :  * NOTE: neither newtup nor oldtup will be modified.  This is a change
     530             :  * from the pre-8.1 API of this routine.
     531             :  * ----------
     532             :  */
     533             : HeapTuple
     534        1889 : toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
     535             :                        int options)
     536             : {
     537             :     HeapTuple   result_tuple;
     538             :     TupleDesc   tupleDesc;
     539             :     int         numAttrs;
     540             :     int         i;
     541             : 
     542        1889 :     bool        need_change = false;
     543        1889 :     bool        need_free = false;
     544        1889 :     bool        need_delold = false;
     545        1889 :     bool        has_nulls = false;
     546             : 
     547             :     Size        maxDataLen;
     548             :     Size        hoff;
     549             : 
     550             :     char        toast_action[MaxHeapAttributeNumber];
     551             :     bool        toast_isnull[MaxHeapAttributeNumber];
     552             :     bool        toast_oldisnull[MaxHeapAttributeNumber];
     553             :     Datum       toast_values[MaxHeapAttributeNumber];
     554             :     Datum       toast_oldvalues[MaxHeapAttributeNumber];
     555             :     struct varlena *toast_oldexternal[MaxHeapAttributeNumber];
     556             :     int32       toast_sizes[MaxHeapAttributeNumber];
     557             :     bool        toast_free[MaxHeapAttributeNumber];
     558             :     bool        toast_delold[MaxHeapAttributeNumber];
     559             : 
     560             :     /*
     561             :      * Ignore the INSERT_SPECULATIVE option. Speculative insertions/super
     562             :      * deletions just normally insert/delete the toast values. It seems
     563             :      * easiest to deal with that here, instead on, potentially, multiple
     564             :      * callers.
     565             :      */
     566        1889 :     options &= ~HEAP_INSERT_SPECULATIVE;
     567             : 
     568             :     /*
     569             :      * We should only ever be called for tuples of plain relations or
     570             :      * materialized views --- recursing on a toast rel is bad news.
     571             :      */
     572        1889 :     Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
     573             :            rel->rd_rel->relkind == RELKIND_MATVIEW);
     574             : 
     575             :     /*
     576             :      * Get the tuple descriptor and break down the tuple(s) into fields.
     577             :      */
     578        1889 :     tupleDesc = rel->rd_att;
     579        1889 :     numAttrs = tupleDesc->natts;
     580             : 
     581        1889 :     Assert(numAttrs <= MaxHeapAttributeNumber);
     582        1889 :     heap_deform_tuple(newtup, tupleDesc, toast_values, toast_isnull);
     583        1889 :     if (oldtup != NULL)
     584          69 :         heap_deform_tuple(oldtup, tupleDesc, toast_oldvalues, toast_oldisnull);
     585             : 
     586             :     /* ----------
     587             :      * Then collect information about the values given
     588             :      *
     589             :      * NOTE: toast_action[i] can have these values:
     590             :      *      ' '     default handling
     591             :      *      'p'     already processed --- don't touch it
     592             :      *      'x'     incompressible, but OK to move off
     593             :      *
     594             :      * NOTE: toast_sizes[i] is only made valid for varlena attributes with
     595             :      *      toast_action[i] different from 'p'.
     596             :      * ----------
     597             :      */
     598        1889 :     memset(toast_action, ' ', numAttrs * sizeof(char));
     599        1889 :     memset(toast_oldexternal, 0, numAttrs * sizeof(struct varlena *));
     600        1889 :     memset(toast_free, 0, numAttrs * sizeof(bool));
     601        1889 :     memset(toast_delold, 0, numAttrs * sizeof(bool));
     602             : 
     603       10879 :     for (i = 0; i < numAttrs; i++)
     604             :     {
     605        8990 :         Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
     606             :         struct varlena *old_value;
     607             :         struct varlena *new_value;
     608             : 
     609        8990 :         if (oldtup != NULL)
     610             :         {
     611             :             /*
     612             :              * For UPDATE get the old and new values of this attribute
     613             :              */
     614         670 :             old_value = (struct varlena *) DatumGetPointer(toast_oldvalues[i]);
     615         670 :             new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
     616             : 
     617             :             /*
     618             :              * If the old value is stored on disk, check if it has changed so
     619             :              * we have to delete it later.
     620             :              */
     621         833 :             if (att->attlen == -1 && !toast_oldisnull[i] &&
     622         193 :                 VARATT_IS_EXTERNAL_ONDISK(old_value))
     623             :             {
     624          40 :                 if (toast_isnull[i] || !VARATT_IS_EXTERNAL_ONDISK(new_value) ||
     625          20 :                     memcmp((char *) old_value, (char *) new_value,
     626          20 :                            VARSIZE_EXTERNAL(old_value)) != 0)
     627             :                 {
     628             :                     /*
     629             :                      * The old external stored value isn't needed any more
     630             :                      * after the update
     631             :                      */
     632          20 :                     toast_delold[i] = true;
     633          20 :                     need_delold = true;
     634             :                 }
     635             :                 else
     636             :                 {
     637             :                     /*
     638             :                      * This attribute isn't changed by this update so we reuse
     639             :                      * the original reference to the old value in the new
     640             :                      * tuple.
     641             :                      */
     642          10 :                     toast_action[i] = 'p';
     643          10 :                     continue;
     644             :                 }
     645             :             }
     646             :         }
     647             :         else
     648             :         {
     649             :             /*
     650             :              * For INSERT simply get the new value
     651             :              */
     652        8320 :             new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
     653             :         }
     654             : 
     655             :         /*
     656             :          * Handle NULL attributes
     657             :          */
     658        8980 :         if (toast_isnull[i])
     659             :         {
     660         435 :             toast_action[i] = 'p';
     661         435 :             has_nulls = true;
     662         435 :             continue;
     663             :         }
     664             : 
     665             :         /*
     666             :          * Now look at varlena attributes
     667             :          */
     668        8545 :         if (att->attlen == -1)
     669             :         {
     670             :             /*
     671             :              * If the table's attribute says PLAIN always, force it so.
     672             :              */
     673        2546 :             if (att->attstorage == 'p')
     674           2 :                 toast_action[i] = 'p';
     675             : 
     676             :             /*
     677             :              * We took care of UPDATE above, so any external value we find
     678             :              * still in the tuple must be someone else's that we cannot reuse
     679             :              * (this includes the case of an out-of-line in-memory datum).
     680             :              * Fetch it back (without decompression, unless we are forcing
     681             :              * PLAIN storage).  If necessary, we'll push it out as a new
     682             :              * external value below.
     683             :              */
     684        2546 :             if (VARATT_IS_EXTERNAL(new_value))
     685             :             {
     686          43 :                 toast_oldexternal[i] = new_value;
     687          43 :                 if (att->attstorage == 'p')
     688           0 :                     new_value = heap_tuple_untoast_attr(new_value);
     689             :                 else
     690          43 :                     new_value = heap_tuple_fetch_attr(new_value);
     691          43 :                 toast_values[i] = PointerGetDatum(new_value);
     692          43 :                 toast_free[i] = true;
     693          43 :                 need_change = true;
     694          43 :                 need_free = true;
     695             :             }
     696             : 
     697             :             /*
     698             :              * Remember the size of this attribute
     699             :              */
     700        2546 :             toast_sizes[i] = VARSIZE_ANY(new_value);
     701             :         }
     702             :         else
     703             :         {
     704             :             /*
     705             :              * Not a varlena attribute, plain storage always
     706             :              */
     707        5999 :             toast_action[i] = 'p';
     708             :         }
     709             :     }
     710             : 
     711             :     /* ----------
     712             :      * Compress and/or save external until data fits into target length
     713             :      *
     714             :      *  1: Inline compress attributes with attstorage 'x', and store very
     715             :      *     large attributes with attstorage 'x' or 'e' external immediately
     716             :      *  2: Store attributes with attstorage 'x' or 'e' external
     717             :      *  3: Inline compress attributes with attstorage 'm'
     718             :      *  4: Store attributes with attstorage 'm' external
     719             :      * ----------
     720             :      */
     721             : 
     722             :     /* compute header overhead --- this should match heap_form_tuple() */
     723        1889 :     hoff = SizeofHeapTupleHeader;
     724        1889 :     if (has_nulls)
     725          82 :         hoff += BITMAPLEN(numAttrs);
     726        1889 :     if (newtup->t_data->t_infomask & HEAP_HASOID)
     727         467 :         hoff += sizeof(Oid);
     728        1889 :     hoff = MAXALIGN(hoff);
     729             :     /* now convert to a limit on the tuple data size */
     730        1889 :     maxDataLen = TOAST_TUPLE_TARGET - hoff;
     731             : 
     732             :     /*
     733             :      * Look for attributes with attstorage 'x' to compress.  Also find large
     734             :      * attributes with attstorage 'x' or 'e', and store them external.
     735             :      */
     736        5719 :     while (heap_compute_data_size(tupleDesc,
     737             :                                   toast_values, toast_isnull) > maxDataLen)
     738             :     {
     739        1957 :         int         biggest_attno = -1;
     740        1957 :         int32       biggest_size = MAXALIGN(TOAST_POINTER_SIZE);
     741             :         Datum       old_value;
     742             :         Datum       new_value;
     743             : 
     744             :         /*
     745             :          * Search for the biggest yet unprocessed internal attribute
     746             :          */
     747       12733 :         for (i = 0; i < numAttrs; i++)
     748             :         {
     749       10776 :             Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
     750             : 
     751       10776 :             if (toast_action[i] != ' ')
     752        7958 :                 continue;
     753        2818 :             if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
     754           0 :                 continue;       /* can't happen, toast_action would be 'p' */
     755        2818 :             if (VARATT_IS_COMPRESSED(DatumGetPointer(toast_values[i])))
     756         135 :                 continue;
     757        2683 :             if (att->attstorage != 'x' && att->attstorage != 'e')
     758           0 :                 continue;
     759        2683 :             if (toast_sizes[i] > biggest_size)
     760             :             {
     761        2114 :                 biggest_attno = i;
     762        2114 :                 biggest_size = toast_sizes[i];
     763             :             }
     764             :         }
     765             : 
     766        1957 :         if (biggest_attno < 0)
     767          16 :             break;
     768             : 
     769             :         /*
     770             :          * Attempt to compress it inline, if it has attstorage 'x'
     771             :          */
     772        1941 :         i = biggest_attno;
     773        1941 :         if (TupleDescAttr(tupleDesc, i)->attstorage == 'x')
     774             :         {
     775        1937 :             old_value = toast_values[i];
     776        1937 :             new_value = toast_compress_datum(old_value);
     777             : 
     778        1937 :             if (DatumGetPointer(new_value) != NULL)
     779             :             {
     780             :                 /* successful compression */
     781        1924 :                 if (toast_free[i])
     782          15 :                     pfree(DatumGetPointer(old_value));
     783        1924 :                 toast_values[i] = new_value;
     784        1924 :                 toast_free[i] = true;
     785        1924 :                 toast_sizes[i] = VARSIZE(DatumGetPointer(toast_values[i]));
     786        1924 :                 need_change = true;
     787        1924 :                 need_free = true;
     788             :             }
     789             :             else
     790             :             {
     791             :                 /* incompressible, ignore on subsequent compression passes */
     792          13 :                 toast_action[i] = 'x';
     793             :             }
     794             :         }
     795             :         else
     796             :         {
     797             :             /* has attstorage 'e', ignore on subsequent compression passes */
     798           4 :             toast_action[i] = 'x';
     799             :         }
     800             : 
     801             :         /*
     802             :          * If this value is by itself more than maxDataLen (after compression
     803             :          * if any), push it out to the toast table immediately, if possible.
     804             :          * This avoids uselessly compressing other fields in the common case
     805             :          * where we have one long field and several short ones.
     806             :          *
     807             :          * XXX maybe the threshold should be less than maxDataLen?
     808             :          */
     809        2051 :         if (toast_sizes[i] > maxDataLen &&
     810         110 :             rel->rd_rel->reltoastrelid != InvalidOid)
     811             :         {
     812         110 :             old_value = toast_values[i];
     813         110 :             toast_action[i] = 'p';
     814         110 :             toast_values[i] = toast_save_datum(rel, toast_values[i],
     815             :                                                toast_oldexternal[i], options);
     816         110 :             if (toast_free[i])
     817         106 :                 pfree(DatumGetPointer(old_value));
     818         110 :             toast_free[i] = true;
     819         110 :             need_change = true;
     820         110 :             need_free = true;
     821             :         }
     822             :     }
     823             : 
     824             :     /*
     825             :      * Second we look for attributes of attstorage 'x' or 'e' that are still
     826             :      * inline.  But skip this if there's no toast table to push them to.
     827             :      */
     828        3794 :     while (heap_compute_data_size(tupleDesc,
     829          16 :                                   toast_values, toast_isnull) > maxDataLen &&
     830          16 :            rel->rd_rel->reltoastrelid != InvalidOid)
     831             :     {
     832          16 :         int         biggest_attno = -1;
     833          16 :         int32       biggest_size = MAXALIGN(TOAST_POINTER_SIZE);
     834             :         Datum       old_value;
     835             : 
     836             :         /*------
     837             :          * Search for the biggest yet inlined attribute with
     838             :          * attstorage equals 'x' or 'e'
     839             :          *------
     840             :          */
     841         334 :         for (i = 0; i < numAttrs; i++)
     842             :         {
     843         318 :             Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
     844             : 
     845         318 :             if (toast_action[i] == 'p')
     846         262 :                 continue;
     847          56 :             if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
     848           0 :                 continue;       /* can't happen, toast_action would be 'p' */
     849          56 :             if (att->attstorage != 'x' && att->attstorage != 'e')
     850           0 :                 continue;
     851          56 :             if (toast_sizes[i] > biggest_size)
     852             :             {
     853          39 :                 biggest_attno = i;
     854          39 :                 biggest_size = toast_sizes[i];
     855             :             }
     856             :         }
     857             : 
     858          16 :         if (biggest_attno < 0)
     859           0 :             break;
     860             : 
     861             :         /*
     862             :          * Store this external
     863             :          */
     864          16 :         i = biggest_attno;
     865          16 :         old_value = toast_values[i];
     866          16 :         toast_action[i] = 'p';
     867          16 :         toast_values[i] = toast_save_datum(rel, toast_values[i],
     868             :                                            toast_oldexternal[i], options);
     869          16 :         if (toast_free[i])
     870          16 :             pfree(DatumGetPointer(old_value));
     871          16 :         toast_free[i] = true;
     872             : 
     873          16 :         need_change = true;
     874          16 :         need_free = true;
     875             :     }
     876             : 
     877             :     /*
     878             :      * Round 3 - this time we take attributes with storage 'm' into
     879             :      * compression
     880             :      */
     881        3778 :     while (heap_compute_data_size(tupleDesc,
     882             :                                   toast_values, toast_isnull) > maxDataLen)
     883             :     {
     884           0 :         int         biggest_attno = -1;
     885           0 :         int32       biggest_size = MAXALIGN(TOAST_POINTER_SIZE);
     886             :         Datum       old_value;
     887             :         Datum       new_value;
     888             : 
     889             :         /*
     890             :          * Search for the biggest yet uncompressed internal attribute
     891             :          */
     892           0 :         for (i = 0; i < numAttrs; i++)
     893             :         {
     894           0 :             if (toast_action[i] != ' ')
     895           0 :                 continue;
     896           0 :             if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
     897           0 :                 continue;       /* can't happen, toast_action would be 'p' */
     898           0 :             if (VARATT_IS_COMPRESSED(DatumGetPointer(toast_values[i])))
     899           0 :                 continue;
     900           0 :             if (TupleDescAttr(tupleDesc, i)->attstorage != 'm')
     901           0 :                 continue;
     902           0 :             if (toast_sizes[i] > biggest_size)
     903             :             {
     904           0 :                 biggest_attno = i;
     905           0 :                 biggest_size = toast_sizes[i];
     906             :             }
     907             :         }
     908             : 
     909           0 :         if (biggest_attno < 0)
     910           0 :             break;
     911             : 
     912             :         /*
     913             :          * Attempt to compress it inline
     914             :          */
     915           0 :         i = biggest_attno;
     916           0 :         old_value = toast_values[i];
     917           0 :         new_value = toast_compress_datum(old_value);
     918             : 
     919           0 :         if (DatumGetPointer(new_value) != NULL)
     920             :         {
     921             :             /* successful compression */
     922           0 :             if (toast_free[i])
     923           0 :                 pfree(DatumGetPointer(old_value));
     924           0 :             toast_values[i] = new_value;
     925           0 :             toast_free[i] = true;
     926           0 :             toast_sizes[i] = VARSIZE(DatumGetPointer(toast_values[i]));
     927           0 :             need_change = true;
     928           0 :             need_free = true;
     929             :         }
     930             :         else
     931             :         {
     932             :             /* incompressible, ignore on subsequent compression passes */
     933           0 :             toast_action[i] = 'x';
     934             :         }
     935             :     }
     936             : 
     937             :     /*
     938             :      * Finally we store attributes of type 'm' externally.  At this point we
     939             :      * increase the target tuple size, so that 'm' attributes aren't stored
     940             :      * externally unless really necessary.
     941             :      */
     942        1889 :     maxDataLen = TOAST_TUPLE_TARGET_MAIN - hoff;
     943             : 
     944        3778 :     while (heap_compute_data_size(tupleDesc,
     945           0 :                                   toast_values, toast_isnull) > maxDataLen &&
     946           0 :            rel->rd_rel->reltoastrelid != InvalidOid)
     947             :     {
     948           0 :         int         biggest_attno = -1;
     949           0 :         int32       biggest_size = MAXALIGN(TOAST_POINTER_SIZE);
     950             :         Datum       old_value;
     951             : 
     952             :         /*--------
     953             :          * Search for the biggest yet inlined attribute with
     954             :          * attstorage = 'm'
     955             :          *--------
     956             :          */
     957           0 :         for (i = 0; i < numAttrs; i++)
     958             :         {
     959           0 :             if (toast_action[i] == 'p')
     960           0 :                 continue;
     961           0 :             if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
     962           0 :                 continue;       /* can't happen, toast_action would be 'p' */
     963           0 :             if (TupleDescAttr(tupleDesc, i)->attstorage != 'm')
     964           0 :                 continue;
     965           0 :             if (toast_sizes[i] > biggest_size)
     966             :             {
     967           0 :                 biggest_attno = i;
     968           0 :                 biggest_size = toast_sizes[i];
     969             :             }
     970             :         }
     971             : 
     972           0 :         if (biggest_attno < 0)
     973           0 :             break;
     974             : 
     975             :         /*
     976             :          * Store this external
     977             :          */
     978           0 :         i = biggest_attno;
     979           0 :         old_value = toast_values[i];
     980           0 :         toast_action[i] = 'p';
     981           0 :         toast_values[i] = toast_save_datum(rel, toast_values[i],
     982             :                                            toast_oldexternal[i], options);
     983           0 :         if (toast_free[i])
     984           0 :             pfree(DatumGetPointer(old_value));
     985           0 :         toast_free[i] = true;
     986             : 
     987           0 :         need_change = true;
     988           0 :         need_free = true;
     989             :     }
     990             : 
     991             :     /*
     992             :      * In the case we toasted any values, we need to build a new heap tuple
     993             :      * with the changed values.
     994             :      */
     995        1889 :     if (need_change)
     996             :     {
     997        1883 :         HeapTupleHeader olddata = newtup->t_data;
     998             :         HeapTupleHeader new_data;
     999             :         int32       new_header_len;
    1000             :         int32       new_data_len;
    1001             :         int32       new_tuple_len;
    1002             : 
    1003             :         /*
    1004             :          * Calculate the new size of the tuple.
    1005             :          *
    1006             :          * Note: we used to assume here that the old tuple's t_hoff must equal
    1007             :          * the new_header_len value, but that was incorrect.  The old tuple
    1008             :          * might have a smaller-than-current natts, if there's been an ALTER
    1009             :          * TABLE ADD COLUMN since it was stored; and that would lead to a
    1010             :          * different conclusion about the size of the null bitmap, or even
    1011             :          * whether there needs to be one at all.
    1012             :          */
    1013        1883 :         new_header_len = SizeofHeapTupleHeader;
    1014        1883 :         if (has_nulls)
    1015          78 :             new_header_len += BITMAPLEN(numAttrs);
    1016        1883 :         if (olddata->t_infomask & HEAP_HASOID)
    1017         467 :             new_header_len += sizeof(Oid);
    1018        1883 :         new_header_len = MAXALIGN(new_header_len);
    1019        1883 :         new_data_len = heap_compute_data_size(tupleDesc,
    1020             :                                               toast_values, toast_isnull);
    1021        1883 :         new_tuple_len = new_header_len + new_data_len;
    1022             : 
    1023             :         /*
    1024             :          * Allocate and zero the space needed, and fill HeapTupleData fields.
    1025             :          */
    1026        1883 :         result_tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + new_tuple_len);
    1027        1883 :         result_tuple->t_len = new_tuple_len;
    1028        1883 :         result_tuple->t_self = newtup->t_self;
    1029        1883 :         result_tuple->t_tableOid = newtup->t_tableOid;
    1030        1883 :         new_data = (HeapTupleHeader) ((char *) result_tuple + HEAPTUPLESIZE);
    1031        1883 :         result_tuple->t_data = new_data;
    1032             : 
    1033             :         /*
    1034             :          * Copy the existing tuple header, but adjust natts and t_hoff.
    1035             :          */
    1036        1883 :         memcpy(new_data, olddata, SizeofHeapTupleHeader);
    1037        1883 :         HeapTupleHeaderSetNatts(new_data, numAttrs);
    1038        1883 :         new_data->t_hoff = new_header_len;
    1039        1883 :         if (olddata->t_infomask & HEAP_HASOID)
    1040         467 :             HeapTupleHeaderSetOid(new_data, HeapTupleHeaderGetOid(olddata));
    1041             : 
    1042             :         /* Copy over the data, and fill the null bitmap if needed */
    1043        1883 :         heap_fill_tuple(tupleDesc,
    1044             :                         toast_values,
    1045             :                         toast_isnull,
    1046             :                         (char *) new_data + new_header_len,
    1047             :                         new_data_len,
    1048             :                         &(new_data->t_infomask),
    1049             :                         has_nulls ? new_data->t_bits : NULL);
    1050             :     }
    1051             :     else
    1052           6 :         result_tuple = newtup;
    1053             : 
    1054             :     /*
    1055             :      * Free allocated temp values
    1056             :      */
    1057        1889 :     if (need_free)
    1058       10849 :         for (i = 0; i < numAttrs; i++)
    1059        8966 :             if (toast_free[i])
    1060        1956 :                 pfree(DatumGetPointer(toast_values[i]));
    1061             : 
    1062             :     /*
    1063             :      * Delete external values from the old tuple
    1064             :      */
    1065        1889 :     if (need_delold)
    1066         166 :         for (i = 0; i < numAttrs; i++)
    1067         151 :             if (toast_delold[i])
    1068          20 :                 toast_delete_datum(rel, toast_oldvalues[i], false);
    1069             : 
    1070        1889 :     return result_tuple;
    1071             : }
    1072             : 
    1073             : 
    1074             : /* ----------
    1075             :  * toast_flatten_tuple -
    1076             :  *
    1077             :  *  "Flatten" a tuple to contain no out-of-line toasted fields.
    1078             :  *  (This does not eliminate compressed or short-header datums.)
    1079             :  *
    1080             :  *  Note: we expect the caller already checked HeapTupleHasExternal(tup),
    1081             :  *  so there is no need for a short-circuit path.
    1082             :  * ----------
    1083             :  */
    1084             : HeapTuple
    1085          92 : toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
    1086             : {
    1087             :     HeapTuple   new_tuple;
    1088          92 :     int         numAttrs = tupleDesc->natts;
    1089             :     int         i;
    1090             :     Datum       toast_values[MaxTupleAttributeNumber];
    1091             :     bool        toast_isnull[MaxTupleAttributeNumber];
    1092             :     bool        toast_free[MaxTupleAttributeNumber];
    1093             : 
    1094             :     /*
    1095             :      * Break down the tuple into fields.
    1096             :      */
    1097          92 :     Assert(numAttrs <= MaxTupleAttributeNumber);
    1098          92 :     heap_deform_tuple(tup, tupleDesc, toast_values, toast_isnull);
    1099             : 
    1100          92 :     memset(toast_free, 0, numAttrs * sizeof(bool));
    1101             : 
    1102        2465 :     for (i = 0; i < numAttrs; i++)
    1103             :     {
    1104             :         /*
    1105             :          * Look at non-null varlena attributes
    1106             :          */
    1107        2373 :         if (!toast_isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
    1108             :         {
    1109             :             struct varlena *new_value;
    1110             : 
    1111         385 :             new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
    1112         385 :             if (VARATT_IS_EXTERNAL(new_value))
    1113             :             {
    1114          97 :                 new_value = heap_tuple_fetch_attr(new_value);
    1115          97 :                 toast_values[i] = PointerGetDatum(new_value);
    1116          97 :                 toast_free[i] = true;
    1117             :             }
    1118             :         }
    1119             :     }
    1120             : 
    1121             :     /*
    1122             :      * Form the reconfigured tuple.
    1123             :      */
    1124          92 :     new_tuple = heap_form_tuple(tupleDesc, toast_values, toast_isnull);
    1125             : 
    1126             :     /*
    1127             :      * Be sure to copy the tuple's OID and identity fields.  We also make a
    1128             :      * point of copying visibility info, just in case anybody looks at those
    1129             :      * fields in a syscache entry.
    1130             :      */
    1131          92 :     if (tupleDesc->tdhasoid)
    1132           1 :         HeapTupleSetOid(new_tuple, HeapTupleGetOid(tup));
    1133             : 
    1134          92 :     new_tuple->t_self = tup->t_self;
    1135          92 :     new_tuple->t_tableOid = tup->t_tableOid;
    1136             : 
    1137          92 :     new_tuple->t_data->t_choice = tup->t_data->t_choice;
    1138          92 :     new_tuple->t_data->t_ctid = tup->t_data->t_ctid;
    1139          92 :     new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK;
    1140         184 :     new_tuple->t_data->t_infomask |=
    1141          92 :         tup->t_data->t_infomask & HEAP_XACT_MASK;
    1142          92 :     new_tuple->t_data->t_infomask2 &= ~HEAP2_XACT_MASK;
    1143         184 :     new_tuple->t_data->t_infomask2 |=
    1144          92 :         tup->t_data->t_infomask2 & HEAP2_XACT_MASK;
    1145             : 
    1146             :     /*
    1147             :      * Free allocated temp values
    1148             :      */
    1149        2465 :     for (i = 0; i < numAttrs; i++)
    1150        2373 :         if (toast_free[i])
    1151          97 :             pfree(DatumGetPointer(toast_values[i]));
    1152             : 
    1153          92 :     return new_tuple;
    1154             : }
    1155             : 
    1156             : 
    1157             : /* ----------
    1158             :  * toast_flatten_tuple_to_datum -
    1159             :  *
    1160             :  *  "Flatten" a tuple containing out-of-line toasted fields into a Datum.
    1161             :  *  The result is always palloc'd in the current memory context.
    1162             :  *
    1163             :  *  We have a general rule that Datums of container types (rows, arrays,
    1164             :  *  ranges, etc) must not contain any external TOAST pointers.  Without
    1165             :  *  this rule, we'd have to look inside each Datum when preparing a tuple
    1166             :  *  for storage, which would be expensive and would fail to extend cleanly
    1167             :  *  to new sorts of container types.
    1168             :  *
    1169             :  *  However, we don't want to say that tuples represented as HeapTuples
    1170             :  *  can't contain toasted fields, so instead this routine should be called
    1171             :  *  when such a HeapTuple is being converted into a Datum.
    1172             :  *
    1173             :  *  While we're at it, we decompress any compressed fields too.  This is not
    1174             :  *  necessary for correctness, but reflects an expectation that compression
    1175             :  *  will be more effective if applied to the whole tuple not individual
    1176             :  *  fields.  We are not so concerned about that that we want to deconstruct
    1177             :  *  and reconstruct tuples just to get rid of compressed fields, however.
    1178             :  *  So callers typically won't call this unless they see that the tuple has
    1179             :  *  at least one external field.
    1180             :  *
    1181             :  *  On the other hand, in-line short-header varlena fields are left alone.
    1182             :  *  If we "untoasted" them here, they'd just get changed back to short-header
    1183             :  *  format anyway within heap_fill_tuple.
    1184             :  * ----------
    1185             :  */
    1186             : Datum
    1187          10 : toast_flatten_tuple_to_datum(HeapTupleHeader tup,
    1188             :                              uint32 tup_len,
    1189             :                              TupleDesc tupleDesc)
    1190             : {
    1191             :     HeapTupleHeader new_data;
    1192             :     int32       new_header_len;
    1193             :     int32       new_data_len;
    1194             :     int32       new_tuple_len;
    1195             :     HeapTupleData tmptup;
    1196          10 :     int         numAttrs = tupleDesc->natts;
    1197             :     int         i;
    1198          10 :     bool        has_nulls = false;
    1199             :     Datum       toast_values[MaxTupleAttributeNumber];
    1200             :     bool        toast_isnull[MaxTupleAttributeNumber];
    1201             :     bool        toast_free[MaxTupleAttributeNumber];
    1202             : 
    1203             :     /* Build a temporary HeapTuple control structure */
    1204          10 :     tmptup.t_len = tup_len;
    1205          10 :     ItemPointerSetInvalid(&(tmptup.t_self));
    1206          10 :     tmptup.t_tableOid = InvalidOid;
    1207          10 :     tmptup.t_data = tup;
    1208             : 
    1209             :     /*
    1210             :      * Break down the tuple into fields.
    1211             :      */
    1212          10 :     Assert(numAttrs <= MaxTupleAttributeNumber);
    1213          10 :     heap_deform_tuple(&tmptup, tupleDesc, toast_values, toast_isnull);
    1214             : 
    1215          10 :     memset(toast_free, 0, numAttrs * sizeof(bool));
    1216             : 
    1217          47 :     for (i = 0; i < numAttrs; i++)
    1218             :     {
    1219             :         /*
    1220             :          * Look at non-null varlena attributes
    1221             :          */
    1222          37 :         if (toast_isnull[i])
    1223           5 :             has_nulls = true;
    1224          32 :         else if (TupleDescAttr(tupleDesc, i)->attlen == -1)
    1225             :         {
    1226             :             struct varlena *new_value;
    1227             : 
    1228          24 :             new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
    1229          35 :             if (VARATT_IS_EXTERNAL(new_value) ||
    1230          11 :                 VARATT_IS_COMPRESSED(new_value))
    1231             :             {
    1232          13 :                 new_value = heap_tuple_untoast_attr(new_value);
    1233          13 :                 toast_values[i] = PointerGetDatum(new_value);
    1234          13 :                 toast_free[i] = true;
    1235             :             }
    1236             :         }
    1237             :     }
    1238             : 
    1239             :     /*
    1240             :      * Calculate the new size of the tuple.
    1241             :      *
    1242             :      * This should match the reconstruction code in toast_insert_or_update.
    1243             :      */
    1244          10 :     new_header_len = SizeofHeapTupleHeader;
    1245          10 :     if (has_nulls)
    1246           5 :         new_header_len += BITMAPLEN(numAttrs);
    1247          10 :     if (tup->t_infomask & HEAP_HASOID)
    1248           0 :         new_header_len += sizeof(Oid);
    1249          10 :     new_header_len = MAXALIGN(new_header_len);
    1250          10 :     new_data_len = heap_compute_data_size(tupleDesc,
    1251             :                                           toast_values, toast_isnull);
    1252          10 :     new_tuple_len = new_header_len + new_data_len;
    1253             : 
    1254          10 :     new_data = (HeapTupleHeader) palloc0(new_tuple_len);
    1255             : 
    1256             :     /*
    1257             :      * Copy the existing tuple header, but adjust natts and t_hoff.
    1258             :      */
    1259          10 :     memcpy(new_data, tup, SizeofHeapTupleHeader);
    1260          10 :     HeapTupleHeaderSetNatts(new_data, numAttrs);
    1261          10 :     new_data->t_hoff = new_header_len;
    1262          10 :     if (tup->t_infomask & HEAP_HASOID)
    1263           0 :         HeapTupleHeaderSetOid(new_data, HeapTupleHeaderGetOid(tup));
    1264             : 
    1265             :     /* Set the composite-Datum header fields correctly */
    1266          10 :     HeapTupleHeaderSetDatumLength(new_data, new_tuple_len);
    1267          10 :     HeapTupleHeaderSetTypeId(new_data, tupleDesc->tdtypeid);
    1268          10 :     HeapTupleHeaderSetTypMod(new_data, tupleDesc->tdtypmod);
    1269             : 
    1270             :     /* Copy over the data, and fill the null bitmap if needed */
    1271          10 :     heap_fill_tuple(tupleDesc,
    1272             :                     toast_values,
    1273             :                     toast_isnull,
    1274             :                     (char *) new_data + new_header_len,
    1275             :                     new_data_len,
    1276             :                     &(new_data->t_infomask),
    1277             :                     has_nulls ? new_data->t_bits : NULL);
    1278             : 
    1279             :     /*
    1280             :      * Free allocated temp values
    1281             :      */
    1282          47 :     for (i = 0; i < numAttrs; i++)
    1283          37 :         if (toast_free[i])
    1284          13 :             pfree(DatumGetPointer(toast_values[i]));
    1285             : 
    1286          10 :     return PointerGetDatum(new_data);
    1287             : }
    1288             : 
    1289             : 
    1290             : /* ----------
    1291             :  * toast_build_flattened_tuple -
    1292             :  *
    1293             :  *  Build a tuple containing no out-of-line toasted fields.
    1294             :  *  (This does not eliminate compressed or short-header datums.)
    1295             :  *
    1296             :  *  This is essentially just like heap_form_tuple, except that it will
    1297             :  *  expand any external-data pointers beforehand.
    1298             :  *
    1299             :  *  It's not very clear whether it would be preferable to decompress
    1300             :  *  in-line compressed datums while at it.  For now, we don't.
    1301             :  * ----------
    1302             :  */
    1303             : HeapTuple
    1304        1041 : toast_build_flattened_tuple(TupleDesc tupleDesc,
    1305             :                             Datum *values,
    1306             :                             bool *isnull)
    1307             : {
    1308             :     HeapTuple   new_tuple;
    1309        1041 :     int         numAttrs = tupleDesc->natts;
    1310             :     int         num_to_free;
    1311             :     int         i;
    1312             :     Datum       new_values[MaxTupleAttributeNumber];
    1313             :     Pointer     freeable_values[MaxTupleAttributeNumber];
    1314             : 
    1315             :     /*
    1316             :      * We can pass the caller's isnull array directly to heap_form_tuple, but
    1317             :      * we potentially need to modify the values array.
    1318             :      */
    1319        1041 :     Assert(numAttrs <= MaxTupleAttributeNumber);
    1320        1041 :     memcpy(new_values, values, numAttrs * sizeof(Datum));
    1321             : 
    1322        1041 :     num_to_free = 0;
    1323        3731 :     for (i = 0; i < numAttrs; i++)
    1324             :     {
    1325             :         /*
    1326             :          * Look at non-null varlena attributes
    1327             :          */
    1328        2690 :         if (!isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
    1329             :         {
    1330             :             struct varlena *new_value;
    1331             : 
    1332         908 :             new_value = (struct varlena *) DatumGetPointer(new_values[i]);
    1333         908 :             if (VARATT_IS_EXTERNAL(new_value))
    1334             :             {
    1335          67 :                 new_value = heap_tuple_fetch_attr(new_value);
    1336          67 :                 new_values[i] = PointerGetDatum(new_value);
    1337          67 :                 freeable_values[num_to_free++] = (Pointer) new_value;
    1338             :             }
    1339             :         }
    1340             :     }
    1341             : 
    1342             :     /*
    1343             :      * Form the reconfigured tuple.
    1344             :      */
    1345        1041 :     new_tuple = heap_form_tuple(tupleDesc, new_values, isnull);
    1346             : 
    1347             :     /*
    1348             :      * Free allocated temp values
    1349             :      */
    1350        1108 :     for (i = 0; i < num_to_free; i++)
    1351          67 :         pfree(freeable_values[i]);
    1352             : 
    1353        1041 :     return new_tuple;
    1354             : }
    1355             : 
    1356             : 
    1357             : /* ----------
    1358             :  * toast_compress_datum -
    1359             :  *
    1360             :  *  Create a compressed version of a varlena datum
    1361             :  *
    1362             :  *  If we fail (ie, compressed result is actually bigger than original)
    1363             :  *  then return NULL.  We must not use compressed data if it'd expand
    1364             :  *  the tuple!
    1365             :  *
    1366             :  *  We use VAR{SIZE,DATA}_ANY so we can handle short varlenas here without
    1367             :  *  copying them.  But we can't handle external or compressed datums.
    1368             :  * ----------
    1369             :  */
    1370             : Datum
    1371        2037 : toast_compress_datum(Datum value)
    1372             : {
    1373             :     struct varlena *tmp;
    1374        2037 :     int32       valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
    1375             :     int32       len;
    1376             : 
    1377        2037 :     Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
    1378        2037 :     Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
    1379             : 
    1380             :     /*
    1381             :      * No point in wasting a palloc cycle if value size is out of the allowed
    1382             :      * range for compression
    1383             :      */
    1384        4063 :     if (valsize < PGLZ_strategy_default->min_input_size ||
    1385        2026 :         valsize > PGLZ_strategy_default->max_input_size)
    1386          11 :         return PointerGetDatum(NULL);
    1387             : 
    1388        2026 :     tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
    1389             :                                     TOAST_COMPRESS_HDRSZ);
    1390             : 
    1391             :     /*
    1392             :      * We recheck the actual size even if pglz_compress() reports success,
    1393             :      * because it might be satisfied with having saved as little as one byte
    1394             :      * in the compressed data --- which could turn into a net loss once you
    1395             :      * consider header and alignment padding.  Worst case, the compressed
    1396             :      * format might require three padding bytes (plus header, which is
    1397             :      * included in VARSIZE(tmp)), whereas the uncompressed format would take
    1398             :      * only one header byte and no padding if the value is short enough.  So
    1399             :      * we insist on a savings of more than 2 bytes to ensure we have a gain.
    1400             :      */
    1401        2026 :     len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
    1402             :                         valsize,
    1403             :                         TOAST_COMPRESS_RAWDATA(tmp),
    1404             :                         PGLZ_strategy_default);
    1405        3950 :     if (len >= 0 &&
    1406        1924 :         len + TOAST_COMPRESS_HDRSZ < valsize - 2)
    1407             :     {
    1408        1924 :         TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
    1409        1924 :         SET_VARSIZE_COMPRESSED(tmp, len + TOAST_COMPRESS_HDRSZ);
    1410             :         /* successful compression */
    1411        1924 :         return PointerGetDatum(tmp);
    1412             :     }
    1413             :     else
    1414             :     {
    1415             :         /* incompressible data */
    1416         102 :         pfree(tmp);
    1417         102 :         return PointerGetDatum(NULL);
    1418             :     }
    1419             : }
    1420             : 
    1421             : 
    1422             : /* ----------
    1423             :  * toast_get_valid_index
    1424             :  *
    1425             :  *  Get OID of valid index associated to given toast relation. A toast
    1426             :  *  relation can have only one valid index at the same time.
    1427             :  */
    1428             : Oid
    1429          50 : toast_get_valid_index(Oid toastoid, LOCKMODE lock)
    1430             : {
    1431             :     int         num_indexes;
    1432             :     int         validIndex;
    1433             :     Oid         validIndexOid;
    1434             :     Relation   *toastidxs;
    1435             :     Relation    toastrel;
    1436             : 
    1437             :     /* Open the toast relation */
    1438          50 :     toastrel = heap_open(toastoid, lock);
    1439             : 
    1440             :     /* Look for the valid index of the toast relation */
    1441          50 :     validIndex = toast_open_indexes(toastrel,
    1442             :                                     lock,
    1443             :                                     &toastidxs,
    1444             :                                     &num_indexes);
    1445          50 :     validIndexOid = RelationGetRelid(toastidxs[validIndex]);
    1446             : 
    1447             :     /* Close the toast relation and all its indexes */
    1448          50 :     toast_close_indexes(toastidxs, num_indexes, lock);
    1449          50 :     heap_close(toastrel, lock);
    1450             : 
    1451          50 :     return validIndexOid;
    1452             : }
    1453             : 
    1454             : 
    1455             : /* ----------
    1456             :  * toast_save_datum -
    1457             :  *
    1458             :  *  Save one single datum into the secondary relation and return
    1459             :  *  a Datum reference for it.
    1460             :  *
    1461             :  * rel: the main relation we're working with (not the toast rel!)
    1462             :  * value: datum to be pushed to toast storage
    1463             :  * oldexternal: if not NULL, toast pointer previously representing the datum
    1464             :  * options: options to be passed to heap_insert() for toast rows
    1465             :  * ----------
    1466             :  */
    1467             : static Datum
    1468         126 : toast_save_datum(Relation rel, Datum value,
    1469             :                  struct varlena *oldexternal, int options)
    1470             : {
    1471             :     Relation    toastrel;
    1472             :     Relation   *toastidxs;
    1473             :     HeapTuple   toasttup;
    1474             :     TupleDesc   toasttupDesc;
    1475             :     Datum       t_values[3];
    1476             :     bool        t_isnull[3];
    1477         126 :     CommandId   mycid = GetCurrentCommandId(true);
    1478             :     struct varlena *result;
    1479             :     struct varatt_external toast_pointer;
    1480             :     union
    1481             :     {
    1482             :         struct varlena hdr;
    1483             :         /* this is to make the union big enough for a chunk: */
    1484             :         char        data[TOAST_MAX_CHUNK_SIZE + VARHDRSZ];
    1485             :         /* ensure union is aligned well enough: */
    1486             :         int32       align_it;
    1487             :     }           chunk_data;
    1488             :     int32       chunk_size;
    1489         126 :     int32       chunk_seq = 0;
    1490             :     char       *data_p;
    1491             :     int32       data_todo;
    1492         126 :     Pointer     dval = DatumGetPointer(value);
    1493             :     int         num_indexes;
    1494             :     int         validIndex;
    1495             : 
    1496         126 :     Assert(!VARATT_IS_EXTERNAL(value));
    1497             : 
    1498             :     /*
    1499             :      * Open the toast relation and its indexes.  We can use the index to check
    1500             :      * uniqueness of the OID we assign to the toasted item, even though it has
    1501             :      * additional columns besides OID.
    1502             :      */
    1503         126 :     toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock);
    1504         126 :     toasttupDesc = toastrel->rd_att;
    1505             : 
    1506             :     /* Open all the toast indexes and look for the valid one */
    1507         126 :     validIndex = toast_open_indexes(toastrel,
    1508             :                                     RowExclusiveLock,
    1509             :                                     &toastidxs,
    1510             :                                     &num_indexes);
    1511             : 
    1512             :     /*
    1513             :      * Get the data pointer and length, and compute va_rawsize and va_extsize.
    1514             :      *
    1515             :      * va_rawsize is the size of the equivalent fully uncompressed datum, so
    1516             :      * we have to adjust for short headers.
    1517             :      *
    1518             :      * va_extsize is the actual size of the data payload in the toast records.
    1519             :      */
    1520         126 :     if (VARATT_IS_SHORT(dval))
    1521             :     {
    1522           0 :         data_p = VARDATA_SHORT(dval);
    1523           0 :         data_todo = VARSIZE_SHORT(dval) - VARHDRSZ_SHORT;
    1524           0 :         toast_pointer.va_rawsize = data_todo + VARHDRSZ;    /* as if not short */
    1525           0 :         toast_pointer.va_extsize = data_todo;
    1526             :     }
    1527         126 :     else if (VARATT_IS_COMPRESSED(dval))
    1528             :     {
    1529         122 :         data_p = VARDATA(dval);
    1530         122 :         data_todo = VARSIZE(dval) - VARHDRSZ;
    1531             :         /* rawsize in a compressed datum is just the size of the payload */
    1532         122 :         toast_pointer.va_rawsize = VARRAWSIZE_4B_C(dval) + VARHDRSZ;
    1533         122 :         toast_pointer.va_extsize = data_todo;
    1534             :         /* Assert that the numbers look like it's compressed */
    1535         122 :         Assert(VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
    1536             :     }
    1537             :     else
    1538             :     {
    1539           4 :         data_p = VARDATA(dval);
    1540           4 :         data_todo = VARSIZE(dval) - VARHDRSZ;
    1541           4 :         toast_pointer.va_rawsize = VARSIZE(dval);
    1542           4 :         toast_pointer.va_extsize = data_todo;
    1543             :     }
    1544             : 
    1545             :     /*
    1546             :      * Insert the correct table OID into the result TOAST pointer.
    1547             :      *
    1548             :      * Normally this is the actual OID of the target toast table, but during
    1549             :      * table-rewriting operations such as CLUSTER, we have to insert the OID
    1550             :      * of the table's real permanent toast table instead.  rd_toastoid is set
    1551             :      * if we have to substitute such an OID.
    1552             :      */
    1553         126 :     if (OidIsValid(rel->rd_toastoid))
    1554           1 :         toast_pointer.va_toastrelid = rel->rd_toastoid;
    1555             :     else
    1556         125 :         toast_pointer.va_toastrelid = RelationGetRelid(toastrel);
    1557             : 
    1558             :     /*
    1559             :      * Choose an OID to use as the value ID for this toast value.
    1560             :      *
    1561             :      * Normally we just choose an unused OID within the toast table.  But
    1562             :      * during table-rewriting operations where we are preserving an existing
    1563             :      * toast table OID, we want to preserve toast value OIDs too.  So, if
    1564             :      * rd_toastoid is set and we had a prior external value from that same
    1565             :      * toast table, re-use its value ID.  If we didn't have a prior external
    1566             :      * value (which is a corner case, but possible if the table's attstorage
    1567             :      * options have been changed), we have to pick a value ID that doesn't
    1568             :      * conflict with either new or existing toast value OIDs.
    1569             :      */
    1570         126 :     if (!OidIsValid(rel->rd_toastoid))
    1571             :     {
    1572             :         /* normal case: just choose an unused OID */
    1573         125 :         toast_pointer.va_valueid =
    1574         125 :             GetNewOidWithIndex(toastrel,
    1575         125 :                                RelationGetRelid(toastidxs[validIndex]),
    1576             :                                (AttrNumber) 1);
    1577             :     }
    1578             :     else
    1579             :     {
    1580             :         /* rewrite case: check to see if value was in old toast table */
    1581           1 :         toast_pointer.va_valueid = InvalidOid;
    1582           1 :         if (oldexternal != NULL)
    1583             :         {
    1584             :             struct varatt_external old_toast_pointer;
    1585             : 
    1586           1 :             Assert(VARATT_IS_EXTERNAL_ONDISK(oldexternal));
    1587             :             /* Must copy to access aligned fields */
    1588           1 :             VARATT_EXTERNAL_GET_POINTER(old_toast_pointer, oldexternal);
    1589           1 :             if (old_toast_pointer.va_toastrelid == rel->rd_toastoid)
    1590             :             {
    1591             :                 /* This value came from the old toast table; reuse its OID */
    1592           1 :                 toast_pointer.va_valueid = old_toast_pointer.va_valueid;
    1593             : 
    1594             :                 /*
    1595             :                  * There is a corner case here: the table rewrite might have
    1596             :                  * to copy both live and recently-dead versions of a row, and
    1597             :                  * those versions could easily reference the same toast value.
    1598             :                  * When we copy the second or later version of such a row,
    1599             :                  * reusing the OID will mean we select an OID that's already
    1600             :                  * in the new toast table.  Check for that, and if so, just
    1601             :                  * fall through without writing the data again.
    1602             :                  *
    1603             :                  * While annoying and ugly-looking, this is a good thing
    1604             :                  * because it ensures that we wind up with only one copy of
    1605             :                  * the toast value when there is only one copy in the old
    1606             :                  * toast table.  Before we detected this case, we'd have made
    1607             :                  * multiple copies, wasting space; and what's worse, the
    1608             :                  * copies belonging to already-deleted heap tuples would not
    1609             :                  * be reclaimed by VACUUM.
    1610             :                  */
    1611           1 :                 if (toastrel_valueid_exists(toastrel,
    1612             :                                             toast_pointer.va_valueid))
    1613             :                 {
    1614             :                     /* Match, so short-circuit the data storage loop below */
    1615           0 :                     data_todo = 0;
    1616             :                 }
    1617             :             }
    1618             :         }
    1619           1 :         if (toast_pointer.va_valueid == InvalidOid)
    1620             :         {
    1621             :             /*
    1622             :              * new value; must choose an OID that doesn't conflict in either
    1623             :              * old or new toast table
    1624             :              */
    1625             :             do
    1626             :             {
    1627           0 :                 toast_pointer.va_valueid =
    1628           0 :                     GetNewOidWithIndex(toastrel,
    1629           0 :                                        RelationGetRelid(toastidxs[validIndex]),
    1630             :                                        (AttrNumber) 1);
    1631           0 :             } while (toastid_valueid_exists(rel->rd_toastoid,
    1632           0 :                                             toast_pointer.va_valueid));
    1633             :         }
    1634             :     }
    1635             : 
    1636             :     /*
    1637             :      * Initialize constant parts of the tuple data
    1638             :      */
    1639         126 :     t_values[0] = ObjectIdGetDatum(toast_pointer.va_valueid);
    1640         126 :     t_values[2] = PointerGetDatum(&chunk_data);
    1641         126 :     t_isnull[0] = false;
    1642         126 :     t_isnull[1] = false;
    1643         126 :     t_isnull[2] = false;
    1644             : 
    1645             :     /*
    1646             :      * Split up the item into chunks
    1647             :      */
    1648         972 :     while (data_todo > 0)
    1649             :     {
    1650             :         int         i;
    1651             : 
    1652         720 :         CHECK_FOR_INTERRUPTS();
    1653             : 
    1654             :         /*
    1655             :          * Calculate the size of this chunk
    1656             :          */
    1657         720 :         chunk_size = Min(TOAST_MAX_CHUNK_SIZE, data_todo);
    1658             : 
    1659             :         /*
    1660             :          * Build a tuple and store it
    1661             :          */
    1662         720 :         t_values[1] = Int32GetDatum(chunk_seq++);
    1663         720 :         SET_VARSIZE(&chunk_data, chunk_size + VARHDRSZ);
    1664         720 :         memcpy(VARDATA(&chunk_data), data_p, chunk_size);
    1665         720 :         toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull);
    1666             : 
    1667         720 :         heap_insert(toastrel, toasttup, mycid, options, NULL);
    1668             : 
    1669             :         /*
    1670             :          * Create the index entry.  We cheat a little here by not using
    1671             :          * FormIndexDatum: this relies on the knowledge that the index columns
    1672             :          * are the same as the initial columns of the table for all the
    1673             :          * indexes.  We also cheat by not providing an IndexInfo: this is okay
    1674             :          * for now because btree doesn't need one, but we might have to be
    1675             :          * more honest someday.
    1676             :          *
    1677             :          * Note also that there had better not be any user-created index on
    1678             :          * the TOAST table, since we don't bother to update anything else.
    1679             :          */
    1680        1440 :         for (i = 0; i < num_indexes; i++)
    1681             :         {
    1682             :             /* Only index relations marked as ready can be updated */
    1683         720 :             if (IndexIsReady(toastidxs[i]->rd_index))
    1684         720 :                 index_insert(toastidxs[i], t_values, t_isnull,
    1685             :                              &(toasttup->t_self),
    1686             :                              toastrel,
    1687         720 :                              toastidxs[i]->rd_index->indisunique ?
    1688             :                              UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
    1689             :                              NULL);
    1690             :         }
    1691             : 
    1692             :         /*
    1693             :          * Free memory
    1694             :          */
    1695         720 :         heap_freetuple(toasttup);
    1696             : 
    1697             :         /*
    1698             :          * Move on to next chunk
    1699             :          */
    1700         720 :         data_todo -= chunk_size;
    1701         720 :         data_p += chunk_size;
    1702             :     }
    1703             : 
    1704             :     /*
    1705             :      * Done - close toast relation and its indexes
    1706             :      */
    1707         126 :     toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
    1708         126 :     heap_close(toastrel, RowExclusiveLock);
    1709             : 
    1710             :     /*
    1711             :      * Create the TOAST pointer value that we'll return
    1712             :      */
    1713         126 :     result = (struct varlena *) palloc(TOAST_POINTER_SIZE);
    1714         126 :     SET_VARTAG_EXTERNAL(result, VARTAG_ONDISK);
    1715         126 :     memcpy(VARDATA_EXTERNAL(result), &toast_pointer, sizeof(toast_pointer));
    1716             : 
    1717         126 :     return PointerGetDatum(result);
    1718             : }
    1719             : 
    1720             : 
    1721             : /* ----------
    1722             :  * toast_delete_datum -
    1723             :  *
    1724             :  *  Delete a single external stored value.
    1725             :  * ----------
    1726             :  */
    1727             : static void
    1728          27 : toast_delete_datum(Relation rel, Datum value, bool is_speculative)
    1729             : {
    1730          27 :     struct varlena *attr = (struct varlena *) DatumGetPointer(value);
    1731             :     struct varatt_external toast_pointer;
    1732             :     Relation    toastrel;
    1733             :     Relation   *toastidxs;
    1734             :     ScanKeyData toastkey;
    1735             :     SysScanDesc toastscan;
    1736             :     HeapTuple   toasttup;
    1737             :     int         num_indexes;
    1738             :     int         validIndex;
    1739             :     SnapshotData SnapshotToast;
    1740             : 
    1741          27 :     if (!VARATT_IS_EXTERNAL_ONDISK(attr))
    1742          27 :         return;
    1743             : 
    1744             :     /* Must copy to access aligned fields */
    1745          27 :     VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
    1746             : 
    1747             :     /*
    1748             :      * Open the toast relation and its indexes
    1749             :      */
    1750          27 :     toastrel = heap_open(toast_pointer.va_toastrelid, RowExclusiveLock);
    1751             : 
    1752             :     /* Fetch valid relation used for process */
    1753          27 :     validIndex = toast_open_indexes(toastrel,
    1754             :                                     RowExclusiveLock,
    1755             :                                     &toastidxs,
    1756             :                                     &num_indexes);
    1757             : 
    1758             :     /*
    1759             :      * Setup a scan key to find chunks with matching va_valueid
    1760             :      */
    1761          27 :     ScanKeyInit(&toastkey,
    1762             :                 (AttrNumber) 1,
    1763             :                 BTEqualStrategyNumber, F_OIDEQ,
    1764             :                 ObjectIdGetDatum(toast_pointer.va_valueid));
    1765             : 
    1766             :     /*
    1767             :      * Find all the chunks.  (We don't actually care whether we see them in
    1768             :      * sequence or not, but since we've already locked the index we might as
    1769             :      * well use systable_beginscan_ordered.)
    1770             :      */
    1771          27 :     init_toast_snapshot(&SnapshotToast);
    1772          27 :     toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
    1773             :                                            &SnapshotToast, 1, &toastkey);
    1774         171 :     while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
    1775             :     {
    1776             :         /*
    1777             :          * Have a chunk, delete it
    1778             :          */
    1779         117 :         if (is_speculative)
    1780           0 :             heap_abort_speculative(toastrel, toasttup);
    1781             :         else
    1782         117 :             simple_heap_delete(toastrel, &toasttup->t_self);
    1783             :     }
    1784             : 
    1785             :     /*
    1786             :      * End scan and close relations
    1787             :      */
    1788          27 :     systable_endscan_ordered(toastscan);
    1789          27 :     toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
    1790          27 :     heap_close(toastrel, RowExclusiveLock);
    1791             : }
    1792             : 
    1793             : 
    1794             : /* ----------
    1795             :  * toastrel_valueid_exists -
    1796             :  *
    1797             :  *  Test whether a toast value with the given ID exists in the toast relation
    1798             :  * ----------
    1799             :  */
    1800             : static bool
    1801           1 : toastrel_valueid_exists(Relation toastrel, Oid valueid)
    1802             : {
    1803           1 :     bool        result = false;
    1804             :     ScanKeyData toastkey;
    1805             :     SysScanDesc toastscan;
    1806             :     int         num_indexes;
    1807             :     int         validIndex;
    1808             :     Relation   *toastidxs;
    1809             :     SnapshotData SnapshotToast;
    1810             : 
    1811             :     /* Fetch a valid index relation */
    1812           1 :     validIndex = toast_open_indexes(toastrel,
    1813             :                                     RowExclusiveLock,
    1814             :                                     &toastidxs,
    1815             :                                     &num_indexes);
    1816             : 
    1817             :     /*
    1818             :      * Setup a scan key to find chunks with matching va_valueid
    1819             :      */
    1820           1 :     ScanKeyInit(&toastkey,
    1821             :                 (AttrNumber) 1,
    1822             :                 BTEqualStrategyNumber, F_OIDEQ,
    1823             :                 ObjectIdGetDatum(valueid));
    1824             : 
    1825             :     /*
    1826             :      * Is there any such chunk?
    1827             :      */
    1828           1 :     init_toast_snapshot(&SnapshotToast);
    1829           1 :     toastscan = systable_beginscan(toastrel,
    1830           1 :                                    RelationGetRelid(toastidxs[validIndex]),
    1831             :                                    true, &SnapshotToast, 1, &toastkey);
    1832             : 
    1833           1 :     if (systable_getnext(toastscan) != NULL)
    1834           0 :         result = true;
    1835             : 
    1836           1 :     systable_endscan(toastscan);
    1837             : 
    1838             :     /* Clean up */
    1839           1 :     toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
    1840             : 
    1841           1 :     return result;
    1842             : }
    1843             : 
    1844             : /* ----------
    1845             :  * toastid_valueid_exists -
    1846             :  *
    1847             :  *  As above, but work from toast rel's OID not an open relation
    1848             :  * ----------
    1849             :  */
    1850             : static bool
    1851           0 : toastid_valueid_exists(Oid toastrelid, Oid valueid)
    1852             : {
    1853             :     bool        result;
    1854             :     Relation    toastrel;
    1855             : 
    1856           0 :     toastrel = heap_open(toastrelid, AccessShareLock);
    1857             : 
    1858           0 :     result = toastrel_valueid_exists(toastrel, valueid);
    1859             : 
    1860           0 :     heap_close(toastrel, AccessShareLock);
    1861             : 
    1862           0 :     return result;
    1863             : }
    1864             : 
    1865             : 
    1866             : /* ----------
    1867             :  * toast_fetch_datum -
    1868             :  *
    1869             :  *  Reconstruct an in memory Datum from the chunks saved
    1870             :  *  in the toast relation
    1871             :  * ----------
    1872             :  */
    1873             : static struct varlena *
    1874         344 : toast_fetch_datum(struct varlena *attr)
    1875             : {
    1876             :     Relation    toastrel;
    1877             :     Relation   *toastidxs;
    1878             :     ScanKeyData toastkey;
    1879             :     SysScanDesc toastscan;
    1880             :     HeapTuple   ttup;
    1881             :     TupleDesc   toasttupDesc;
    1882             :     struct varlena *result;
    1883             :     struct varatt_external toast_pointer;
    1884             :     int32       ressize;
    1885             :     int32       residx,
    1886             :                 nextidx;
    1887             :     int32       numchunks;
    1888             :     Pointer     chunk;
    1889             :     bool        isnull;
    1890             :     char       *chunkdata;
    1891             :     int32       chunksize;
    1892             :     int         num_indexes;
    1893             :     int         validIndex;
    1894             :     SnapshotData SnapshotToast;
    1895             : 
    1896         344 :     if (!VARATT_IS_EXTERNAL_ONDISK(attr))
    1897           0 :         elog(ERROR, "toast_fetch_datum shouldn't be called for non-ondisk datums");
    1898             : 
    1899             :     /* Must copy to access aligned fields */
    1900         344 :     VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
    1901             : 
    1902         344 :     ressize = toast_pointer.va_extsize;
    1903         344 :     numchunks = ((ressize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
    1904             : 
    1905         344 :     result = (struct varlena *) palloc(ressize + VARHDRSZ);
    1906             : 
    1907         344 :     if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
    1908         344 :         SET_VARSIZE_COMPRESSED(result, ressize + VARHDRSZ);
    1909             :     else
    1910           0 :         SET_VARSIZE(result, ressize + VARHDRSZ);
    1911             : 
    1912             :     /*
    1913             :      * Open the toast relation and its indexes
    1914             :      */
    1915         344 :     toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
    1916         344 :     toasttupDesc = toastrel->rd_att;
    1917             : 
    1918             :     /* Look for the valid index of the toast relation */
    1919         344 :     validIndex = toast_open_indexes(toastrel,
    1920             :                                     AccessShareLock,
    1921             :                                     &toastidxs,
    1922             :                                     &num_indexes);
    1923             : 
    1924             :     /*
    1925             :      * Setup a scan key to fetch from the index by va_valueid
    1926             :      */
    1927         344 :     ScanKeyInit(&toastkey,
    1928             :                 (AttrNumber) 1,
    1929             :                 BTEqualStrategyNumber, F_OIDEQ,
    1930             :                 ObjectIdGetDatum(toast_pointer.va_valueid));
    1931             : 
    1932             :     /*
    1933             :      * Read the chunks by index
    1934             :      *
    1935             :      * Note that because the index is actually on (valueid, chunkidx) we will
    1936             :      * see the chunks in chunkidx order, even though we didn't explicitly ask
    1937             :      * for it.
    1938             :      */
    1939         344 :     nextidx = 0;
    1940             : 
    1941         344 :     init_toast_snapshot(&SnapshotToast);
    1942         344 :     toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
    1943             :                                            &SnapshotToast, 1, &toastkey);
    1944        1995 :     while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
    1945             :     {
    1946             :         /*
    1947             :          * Have a chunk, extract the sequence number and the data
    1948             :          */
    1949        1307 :         residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
    1950        1307 :         Assert(!isnull);
    1951        1307 :         chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
    1952        1307 :         Assert(!isnull);
    1953        1307 :         if (!VARATT_IS_EXTENDED(chunk))
    1954             :         {
    1955        1307 :             chunksize = VARSIZE(chunk) - VARHDRSZ;
    1956        1307 :             chunkdata = VARDATA(chunk);
    1957             :         }
    1958           0 :         else if (VARATT_IS_SHORT(chunk))
    1959             :         {
    1960             :             /* could happen due to heap_form_tuple doing its thing */
    1961           0 :             chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
    1962           0 :             chunkdata = VARDATA_SHORT(chunk);
    1963             :         }
    1964             :         else
    1965             :         {
    1966             :             /* should never happen */
    1967           0 :             elog(ERROR, "found toasted toast chunk for toast value %u in %s",
    1968             :                  toast_pointer.va_valueid,
    1969             :                  RelationGetRelationName(toastrel));
    1970             :             chunksize = 0;      /* keep compiler quiet */
    1971             :             chunkdata = NULL;
    1972             :         }
    1973             : 
    1974             :         /*
    1975             :          * Some checks on the data we've found
    1976             :          */
    1977        1307 :         if (residx != nextidx)
    1978           0 :             elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u in %s",
    1979             :                  residx, nextidx,
    1980             :                  toast_pointer.va_valueid,
    1981             :                  RelationGetRelationName(toastrel));
    1982        1307 :         if (residx < numchunks - 1)
    1983             :         {
    1984         963 :             if (chunksize != TOAST_MAX_CHUNK_SIZE)
    1985           0 :                 elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s",
    1986             :                      chunksize, (int) TOAST_MAX_CHUNK_SIZE,
    1987             :                      residx, numchunks,
    1988             :                      toast_pointer.va_valueid,
    1989             :                      RelationGetRelationName(toastrel));
    1990             :         }
    1991         344 :         else if (residx == numchunks - 1)
    1992             :         {
    1993         344 :             if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != ressize)
    1994           0 :                 elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s",
    1995             :                      chunksize,
    1996             :                      (int) (ressize - residx * TOAST_MAX_CHUNK_SIZE),
    1997             :                      residx,
    1998             :                      toast_pointer.va_valueid,
    1999             :                      RelationGetRelationName(toastrel));
    2000             :         }
    2001             :         else
    2002           0 :             elog(ERROR, "unexpected chunk number %d (out of range %d..%d) for toast value %u in %s",
    2003             :                  residx,
    2004             :                  0, numchunks - 1,
    2005             :                  toast_pointer.va_valueid,
    2006             :                  RelationGetRelationName(toastrel));
    2007             : 
    2008             :         /*
    2009             :          * Copy the data into proper place in our result
    2010             :          */
    2011        1307 :         memcpy(VARDATA(result) + residx * TOAST_MAX_CHUNK_SIZE,
    2012             :                chunkdata,
    2013             :                chunksize);
    2014             : 
    2015        1307 :         nextidx++;
    2016             :     }
    2017             : 
    2018             :     /*
    2019             :      * Final checks that we successfully fetched the datum
    2020             :      */
    2021         344 :     if (nextidx != numchunks)
    2022           0 :         elog(ERROR, "missing chunk number %d for toast value %u in %s",
    2023             :              nextidx,
    2024             :              toast_pointer.va_valueid,
    2025             :              RelationGetRelationName(toastrel));
    2026             : 
    2027             :     /*
    2028             :      * End scan and close relations
    2029             :      */
    2030         344 :     systable_endscan_ordered(toastscan);
    2031         344 :     toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
    2032         344 :     heap_close(toastrel, AccessShareLock);
    2033             : 
    2034         344 :     return result;
    2035             : }
    2036             : 
    2037             : /* ----------
    2038             :  * toast_fetch_datum_slice -
    2039             :  *
    2040             :  *  Reconstruct a segment of a Datum from the chunks saved
    2041             :  *  in the toast relation
    2042             :  * ----------
    2043             :  */
    2044             : static struct varlena *
    2045          12 : toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, int32 length)
    2046             : {
    2047             :     Relation    toastrel;
    2048             :     Relation   *toastidxs;
    2049             :     ScanKeyData toastkey[3];
    2050             :     int         nscankeys;
    2051             :     SysScanDesc toastscan;
    2052             :     HeapTuple   ttup;
    2053             :     TupleDesc   toasttupDesc;
    2054             :     struct varlena *result;
    2055             :     struct varatt_external toast_pointer;
    2056             :     int32       attrsize;
    2057             :     int32       residx;
    2058             :     int32       nextidx;
    2059             :     int         numchunks;
    2060             :     int         startchunk;
    2061             :     int         endchunk;
    2062             :     int32       startoffset;
    2063             :     int32       endoffset;
    2064             :     int         totalchunks;
    2065             :     Pointer     chunk;
    2066             :     bool        isnull;
    2067             :     char       *chunkdata;
    2068             :     int32       chunksize;
    2069             :     int32       chcpystrt;
    2070             :     int32       chcpyend;
    2071             :     int         num_indexes;
    2072             :     int         validIndex;
    2073             :     SnapshotData SnapshotToast;
    2074             : 
    2075          12 :     if (!VARATT_IS_EXTERNAL_ONDISK(attr))
    2076           0 :         elog(ERROR, "toast_fetch_datum_slice shouldn't be called for non-ondisk datums");
    2077             : 
    2078             :     /* Must copy to access aligned fields */
    2079          12 :     VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
    2080             : 
    2081             :     /*
    2082             :      * It's nonsense to fetch slices of a compressed datum -- this isn't lo_*
    2083             :      * we can't return a compressed datum which is meaningful to toast later
    2084             :      */
    2085          12 :     Assert(!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
    2086             : 
    2087          12 :     attrsize = toast_pointer.va_extsize;
    2088          12 :     totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
    2089             : 
    2090          12 :     if (sliceoffset >= attrsize)
    2091             :     {
    2092           0 :         sliceoffset = 0;
    2093           0 :         length = 0;
    2094             :     }
    2095             : 
    2096          12 :     if (((sliceoffset + length) > attrsize) || length < 0)
    2097           8 :         length = attrsize - sliceoffset;
    2098             : 
    2099          12 :     result = (struct varlena *) palloc(length + VARHDRSZ);
    2100             : 
    2101          12 :     if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
    2102           0 :         SET_VARSIZE_COMPRESSED(result, length + VARHDRSZ);
    2103             :     else
    2104          12 :         SET_VARSIZE(result, length + VARHDRSZ);
    2105             : 
    2106          12 :     if (length == 0)
    2107           0 :         return result;          /* Can save a lot of work at this point! */
    2108             : 
    2109          12 :     startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE;
    2110          12 :     endchunk = (sliceoffset + length - 1) / TOAST_MAX_CHUNK_SIZE;
    2111          12 :     numchunks = (endchunk - startchunk) + 1;
    2112             : 
    2113          12 :     startoffset = sliceoffset % TOAST_MAX_CHUNK_SIZE;
    2114          12 :     endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE;
    2115             : 
    2116             :     /*
    2117             :      * Open the toast relation and its indexes
    2118             :      */
    2119          12 :     toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
    2120          12 :     toasttupDesc = toastrel->rd_att;
    2121             : 
    2122             :     /* Look for the valid index of toast relation */
    2123          12 :     validIndex = toast_open_indexes(toastrel,
    2124             :                                     AccessShareLock,
    2125             :                                     &toastidxs,
    2126             :                                     &num_indexes);
    2127             : 
    2128             :     /*
    2129             :      * Setup a scan key to fetch from the index. This is either two keys or
    2130             :      * three depending on the number of chunks.
    2131             :      */
    2132          12 :     ScanKeyInit(&toastkey[0],
    2133             :                 (AttrNumber) 1,
    2134             :                 BTEqualStrategyNumber, F_OIDEQ,
    2135             :                 ObjectIdGetDatum(toast_pointer.va_valueid));
    2136             : 
    2137             :     /*
    2138             :      * Use equality condition for one chunk, a range condition otherwise:
    2139             :      */
    2140          12 :     if (numchunks == 1)
    2141             :     {
    2142           8 :         ScanKeyInit(&toastkey[1],
    2143             :                     (AttrNumber) 2,
    2144             :                     BTEqualStrategyNumber, F_INT4EQ,
    2145             :                     Int32GetDatum(startchunk));
    2146           8 :         nscankeys = 2;
    2147             :     }
    2148             :     else
    2149             :     {
    2150           4 :         ScanKeyInit(&toastkey[1],
    2151             :                     (AttrNumber) 2,
    2152             :                     BTGreaterEqualStrategyNumber, F_INT4GE,
    2153             :                     Int32GetDatum(startchunk));
    2154           4 :         ScanKeyInit(&toastkey[2],
    2155             :                     (AttrNumber) 2,
    2156             :                     BTLessEqualStrategyNumber, F_INT4LE,
    2157             :                     Int32GetDatum(endchunk));
    2158           4 :         nscankeys = 3;
    2159             :     }
    2160             : 
    2161             :     /*
    2162             :      * Read the chunks by index
    2163             :      *
    2164             :      * The index is on (valueid, chunkidx) so they will come in order
    2165             :      */
    2166          12 :     init_toast_snapshot(&SnapshotToast);
    2167          12 :     nextidx = startchunk;
    2168          12 :     toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
    2169             :                                            &SnapshotToast, nscankeys, toastkey);
    2170         232 :     while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
    2171             :     {
    2172             :         /*
    2173             :          * Have a chunk, extract the sequence number and the data
    2174             :          */
    2175         208 :         residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
    2176         208 :         Assert(!isnull);
    2177         208 :         chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
    2178         208 :         Assert(!isnull);
    2179         208 :         if (!VARATT_IS_EXTENDED(chunk))
    2180             :         {
    2181         208 :             chunksize = VARSIZE(chunk) - VARHDRSZ;
    2182         208 :             chunkdata = VARDATA(chunk);
    2183             :         }
    2184           0 :         else if (VARATT_IS_SHORT(chunk))
    2185             :         {
    2186             :             /* could happen due to heap_form_tuple doing its thing */
    2187           0 :             chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
    2188           0 :             chunkdata = VARDATA_SHORT(chunk);
    2189             :         }
    2190             :         else
    2191             :         {
    2192             :             /* should never happen */
    2193           0 :             elog(ERROR, "found toasted toast chunk for toast value %u in %s",
    2194             :                  toast_pointer.va_valueid,
    2195             :                  RelationGetRelationName(toastrel));
    2196             :             chunksize = 0;      /* keep compiler quiet */
    2197             :             chunkdata = NULL;
    2198             :         }
    2199             : 
    2200             :         /*
    2201             :          * Some checks on the data we've found
    2202             :          */
    2203         208 :         if ((residx != nextidx) || (residx > endchunk) || (residx < startchunk))
    2204           0 :             elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u in %s",
    2205             :                  residx, nextidx,
    2206             :                  toast_pointer.va_valueid,
    2207             :                  RelationGetRelationName(toastrel));
    2208         208 :         if (residx < totalchunks - 1)
    2209             :         {
    2210         200 :             if (chunksize != TOAST_MAX_CHUNK_SIZE)
    2211           0 :                 elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s when fetching slice",
    2212             :                      chunksize, (int) TOAST_MAX_CHUNK_SIZE,
    2213             :                      residx, totalchunks,
    2214             :                      toast_pointer.va_valueid,
    2215             :                      RelationGetRelationName(toastrel));
    2216             :         }
    2217           8 :         else if (residx == totalchunks - 1)
    2218             :         {
    2219           8 :             if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize)
    2220           0 :                 elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s when fetching slice",
    2221             :                      chunksize,
    2222             :                      (int) (attrsize - residx * TOAST_MAX_CHUNK_SIZE),
    2223             :                      residx,
    2224             :                      toast_pointer.va_valueid,
    2225             :                      RelationGetRelationName(toastrel));
    2226             :         }
    2227             :         else
    2228           0 :             elog(ERROR, "unexpected chunk number %d (out of range %d..%d) for toast value %u in %s",
    2229             :                  residx,
    2230             :                  0, totalchunks - 1,
    2231             :                  toast_pointer.va_valueid,
    2232             :                  RelationGetRelationName(toastrel));
    2233             : 
    2234             :         /*
    2235             :          * Copy the data into proper place in our result
    2236             :          */
    2237         208 :         chcpystrt = 0;
    2238         208 :         chcpyend = chunksize - 1;
    2239         208 :         if (residx == startchunk)
    2240          12 :             chcpystrt = startoffset;
    2241         208 :         if (residx == endchunk)
    2242          12 :             chcpyend = endoffset;
    2243             : 
    2244         624 :         memcpy(VARDATA(result) +
    2245         416 :                (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
    2246         208 :                chunkdata + chcpystrt,
    2247         208 :                (chcpyend - chcpystrt) + 1);
    2248             : 
    2249         208 :         nextidx++;
    2250             :     }
    2251             : 
    2252             :     /*
    2253             :      * Final checks that we successfully fetched the datum
    2254             :      */
    2255          12 :     if (nextidx != (endchunk + 1))
    2256           0 :         elog(ERROR, "missing chunk number %d for toast value %u in %s",
    2257             :              nextidx,
    2258             :              toast_pointer.va_valueid,
    2259             :              RelationGetRelationName(toastrel));
    2260             : 
    2261             :     /*
    2262             :      * End scan and close relations
    2263             :      */
    2264          12 :     systable_endscan_ordered(toastscan);
    2265          12 :     toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
    2266          12 :     heap_close(toastrel, AccessShareLock);
    2267             : 
    2268          12 :     return result;
    2269             : }
    2270             : 
    2271             : /* ----------
    2272             :  * toast_decompress_datum -
    2273             :  *
    2274             :  * Decompress a compressed version of a varlena datum
    2275             :  */
    2276             : static struct varlena *
    2277        5429 : toast_decompress_datum(struct varlena *attr)
    2278             : {
    2279             :     struct varlena *result;
    2280             : 
    2281        5429 :     Assert(VARATT_IS_COMPRESSED(attr));
    2282             : 
    2283        5429 :     result = (struct varlena *)
    2284        5429 :         palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
    2285        5429 :     SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
    2286             : 
    2287       10858 :     if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
    2288        5429 :                         VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
    2289        5429 :                         VARDATA(result),
    2290             :                         TOAST_COMPRESS_RAWSIZE(attr)) < 0)
    2291           0 :         elog(ERROR, "compressed data is corrupted");
    2292             : 
    2293        5429 :     return result;
    2294             : }
    2295             : 
    2296             : 
    2297             : /* ----------
    2298             :  * toast_open_indexes
    2299             :  *
    2300             :  *  Get an array of the indexes associated to the given toast relation
    2301             :  *  and return as well the position of the valid index used by the toast
    2302             :  *  relation in this array. It is the responsibility of the caller of this
    2303             :  *  function to close the indexes as well as free them.
    2304             :  */
    2305             : static int
    2306         560 : toast_open_indexes(Relation toastrel,
    2307             :                    LOCKMODE lock,
    2308             :                    Relation **toastidxs,
    2309             :                    int *num_indexes)
    2310             : {
    2311         560 :     int         i = 0;
    2312         560 :     int         res = 0;
    2313         560 :     bool        found = false;
    2314             :     List       *indexlist;
    2315             :     ListCell   *lc;
    2316             : 
    2317             :     /* Get index list of the toast relation */
    2318         560 :     indexlist = RelationGetIndexList(toastrel);
    2319         560 :     Assert(indexlist != NIL);
    2320             : 
    2321         560 :     *num_indexes = list_length(indexlist);
    2322             : 
    2323             :     /* Open all the index relations */
    2324         560 :     *toastidxs = (Relation *) palloc(*num_indexes * sizeof(Relation));
    2325        1120 :     foreach(lc, indexlist)
    2326         560 :         (*toastidxs)[i++] = index_open(lfirst_oid(lc), lock);
    2327             : 
    2328             :     /* Fetch the first valid index in list */
    2329         560 :     for (i = 0; i < *num_indexes; i++)
    2330             :     {
    2331         560 :         Relation    toastidx = (*toastidxs)[i];
    2332             : 
    2333         560 :         if (toastidx->rd_index->indisvalid)
    2334             :         {
    2335         560 :             res = i;
    2336         560 :             found = true;
    2337         560 :             break;
    2338             :         }
    2339             :     }
    2340             : 
    2341             :     /*
    2342             :      * Free index list, not necessary anymore as relations are opened and a
    2343             :      * valid index has been found.
    2344             :      */
    2345         560 :     list_free(indexlist);
    2346             : 
    2347             :     /*
    2348             :      * The toast relation should have one valid index, so something is going
    2349             :      * wrong if there is nothing.
    2350             :      */
    2351         560 :     if (!found)
    2352           0 :         elog(ERROR, "no valid index found for toast relation with Oid %u",
    2353             :              RelationGetRelid(toastrel));
    2354             : 
    2355         560 :     return res;
    2356             : }
    2357             : 
    2358             : /* ----------
    2359             :  * toast_close_indexes
    2360             :  *
    2361             :  *  Close an array of indexes for a toast relation and free it. This should
    2362             :  *  be called for a set of indexes opened previously with toast_open_indexes.
    2363             :  */
    2364             : static void
    2365         560 : toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock)
    2366             : {
    2367             :     int         i;
    2368             : 
    2369             :     /* Close relations and clean up things */
    2370        1120 :     for (i = 0; i < num_indexes; i++)
    2371         560 :         index_close(toastidxs[i], lock);
    2372         560 :     pfree(toastidxs);
    2373         560 : }
    2374             : 
    2375             : /* ----------
    2376             :  * init_toast_snapshot
    2377             :  *
    2378             :  *  Initialize an appropriate TOAST snapshot.  We must use an MVCC snapshot
    2379             :  *  to initialize the TOAST snapshot; since we don't know which one to use,
    2380             :  *  just use the oldest one.  This is safe: at worst, we will get a "snapshot
    2381             :  *  too old" error that might have been avoided otherwise.
    2382             :  */
    2383             : static void
    2384         384 : init_toast_snapshot(Snapshot toast_snapshot)
    2385             : {
    2386         384 :     Snapshot    snapshot = GetOldestSnapshot();
    2387             : 
    2388         384 :     if (snapshot == NULL)
    2389           0 :         elog(ERROR, "no known snapshots");
    2390             : 
    2391         384 :     InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
    2392         384 : }

Generated by: LCOV version 1.11