LCOV - code coverage report
Current view: top level - src/backend/access/common - detoast.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 217 257 84.4 %
Date: 2019-11-21 12:06:29 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * detoast.c
       4             :  *    Retrieve compressed or external variable size attributes.
       5             :  *
       6             :  * Copyright (c) 2000-2019, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *    src/backend/access/common/detoast.c
      10             :  *
      11             :  *-------------------------------------------------------------------------
      12             :  */
      13             : 
      14             : #include "postgres.h"
      15             : 
      16             : #include "access/detoast.h"
      17             : #include "access/genam.h"
      18             : #include "access/heaptoast.h"
      19             : #include "access/table.h"
      20             : #include "access/toast_internals.h"
      21             : #include "common/pg_lzcompress.h"
      22             : #include "utils/expandeddatum.h"
      23             : #include "utils/fmgroids.h"
      24             : #include "utils/rel.h"
      25             : 
      26             : static struct varlena *toast_fetch_datum(struct varlena *attr);
      27             : static struct varlena *toast_fetch_datum_slice(struct varlena *attr,
      28             :                                                int32 sliceoffset, int32 length);
      29             : static struct varlena *toast_decompress_datum(struct varlena *attr);
      30             : static struct varlena *toast_decompress_datum_slice(struct varlena *attr, int32 slicelength);
      31             : 
      32             : /* ----------
      33             :  * detoast_external_attr -
      34             :  *
      35             :  *  Public entry point to get back a toasted value from
      36             :  *  external source (possibly still in compressed format).
      37             :  *
      38             :  * This will return a datum that contains all the data internally, ie, not
      39             :  * relying on external storage or memory, but it can still be compressed or
      40             :  * have a short header.  Note some callers assume that if the input is an
      41             :  * EXTERNAL datum, the result will be a pfree'able chunk.
      42             :  * ----------
      43             :  */
      44             : struct varlena *
      45        8840 : detoast_external_attr(struct varlena *attr)
      46             : {
      47             :     struct varlena *result;
      48             : 
      49        8840 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
      50             :     {
      51             :         /*
      52             :          * This is an external stored plain value
      53             :          */
      54        2344 :         result = toast_fetch_datum(attr);
      55             :     }
      56        6496 :     else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
      57         328 :     {
      58             :         /*
      59             :          * This is an indirect pointer --- dereference it
      60             :          */
      61             :         struct varatt_indirect redirect;
      62             : 
      63         328 :         VARATT_EXTERNAL_GET_POINTER(redirect, attr);
      64         328 :         attr = (struct varlena *) redirect.pointer;
      65             : 
      66             :         /* nested indirect Datums aren't allowed */
      67             :         Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
      68             : 
      69             :         /* recurse if value is still external in some other way */
      70         328 :         if (VARATT_IS_EXTERNAL(attr))
      71           0 :             return detoast_external_attr(attr);
      72             : 
      73             :         /*
      74             :          * Copy into the caller's memory context, in case caller tries to
      75             :          * pfree the result.
      76             :          */
      77         328 :         result = (struct varlena *) palloc(VARSIZE_ANY(attr));
      78         328 :         memcpy(result, attr, VARSIZE_ANY(attr));
      79             :     }
      80        6168 :     else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
      81        6168 :     {
      82             :         /*
      83             :          * This is an expanded-object pointer --- get flat format
      84             :          */
      85             :         ExpandedObjectHeader *eoh;
      86             :         Size        resultsize;
      87             : 
      88        6168 :         eoh = DatumGetEOHP(PointerGetDatum(attr));
      89        6168 :         resultsize = EOH_get_flat_size(eoh);
      90        6168 :         result = (struct varlena *) palloc(resultsize);
      91        6168 :         EOH_flatten_into(eoh, (void *) result, resultsize);
      92             :     }
      93             :     else
      94             :     {
      95             :         /*
      96             :          * This is a plain value inside of the main tuple - why am I called?
      97             :          */
      98           0 :         result = attr;
      99             :     }
     100             : 
     101        8840 :     return result;
     102             : }
     103             : 
     104             : 
     105             : /* ----------
     106             :  * detoast_attr -
     107             :  *
     108             :  *  Public entry point to get back a toasted value from compression
     109             :  *  or external storage.  The result is always non-extended varlena form.
     110             :  *
     111             :  * Note some callers assume that if the input is an EXTERNAL or COMPRESSED
     112             :  * datum, the result will be a pfree'able chunk.
     113             :  * ----------
     114             :  */
     115             : struct varlena *
     116    18353892 : detoast_attr(struct varlena *attr)
     117             : {
     118    18353892 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
     119             :     {
     120             :         /*
     121             :          * This is an externally stored datum --- fetch it back from there
     122             :          */
     123       33484 :         attr = toast_fetch_datum(attr);
     124             :         /* If it's compressed, decompress it */
     125       66968 :         if (VARATT_IS_COMPRESSED(attr))
     126             :         {
     127       33428 :             struct varlena *tmp = attr;
     128             : 
     129       33428 :             attr = toast_decompress_datum(tmp);
     130       33428 :             pfree(tmp);
     131             :         }
     132             :     }
     133    18320408 :     else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
     134          82 :     {
     135             :         /*
     136             :          * This is an indirect pointer --- dereference it
     137             :          */
     138             :         struct varatt_indirect redirect;
     139             : 
     140          82 :         VARATT_EXTERNAL_GET_POINTER(redirect, attr);
     141          82 :         attr = (struct varlena *) redirect.pointer;
     142             : 
     143             :         /* nested indirect Datums aren't allowed */
     144             :         Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
     145             : 
     146             :         /* recurse in case value is still extended in some other way */
     147          82 :         attr = detoast_attr(attr);
     148             : 
     149             :         /* if it isn't, we'd better copy it */
     150          82 :         if (attr == (struct varlena *) redirect.pointer)
     151             :         {
     152             :             struct varlena *result;
     153             : 
     154          36 :             result = (struct varlena *) palloc(VARSIZE_ANY(attr));
     155          36 :             memcpy(result, attr, VARSIZE_ANY(attr));
     156          36 :             attr = result;
     157             :         }
     158             :     }
     159    18320326 :     else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
     160             :     {
     161             :         /*
     162             :          * This is an expanded-object pointer --- get flat format
     163             :          */
     164        6168 :         attr = detoast_external_attr(attr);
     165             :         /* flatteners are not allowed to produce compressed/short output */
     166        6168 :         Assert(!VARATT_IS_EXTENDED(attr));
     167             :     }
     168    18314158 :     else if (VARATT_IS_COMPRESSED(attr))
     169             :     {
     170             :         /*
     171             :          * This is a compressed value inside of the main tuple
     172             :          */
     173       54902 :         attr = toast_decompress_datum(attr);
     174             :     }
     175    18259256 :     else if (VARATT_IS_SHORT(attr))
     176             :     {
     177             :         /*
     178             :          * This is a short-header varlena --- convert to 4-byte header format
     179             :          */
     180    18259220 :         Size        data_size = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT;
     181    18259220 :         Size        new_size = data_size + VARHDRSZ;
     182             :         struct varlena *new_attr;
     183             : 
     184    18259220 :         new_attr = (struct varlena *) palloc(new_size);
     185    18259220 :         SET_VARSIZE(new_attr, new_size);
     186    18259220 :         memcpy(VARDATA(new_attr), VARDATA_SHORT(attr), data_size);
     187    18259220 :         attr = new_attr;
     188             :     }
     189             : 
     190    18353892 :     return attr;
     191             : }
     192             : 
     193             : 
     194             : /* ----------
     195             :  * detoast_attr_slice -
     196             :  *
     197             :  *      Public entry point to get back part of a toasted value
     198             :  *      from compression or external storage.
     199             :  *
     200             :  * Note: When slicelength is negative, return suffix of the value.
     201             :  * ----------
     202             :  */
     203             : struct varlena *
     204         152 : detoast_attr_slice(struct varlena *attr,
     205             :                               int32 sliceoffset, int32 slicelength)
     206             : {
     207             :     struct varlena *preslice;
     208             :     struct varlena *result;
     209             :     char       *attrdata;
     210             :     int32       attrsize;
     211             : 
     212         152 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
     213          20 :     {
     214             :         struct varatt_external toast_pointer;
     215             : 
     216          68 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     217             : 
     218             :         /* fast path for non-compressed external datums */
     219          68 :         if (!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
     220          48 :             return toast_fetch_datum_slice(attr, sliceoffset, slicelength);
     221             : 
     222             :         /*
     223             :          * For compressed values, we need to fetch enough slices to decompress
     224             :          * at least the requested part (when a prefix is requested). Otherwise,
     225             :          * just fetch all slices.
     226             :          */
     227          20 :         if (slicelength > 0 && sliceoffset >= 0)
     228          20 :         {
     229             :             int32 max_size;
     230             : 
     231             :             /*
     232             :              * Determine maximum amount of compressed data needed for a prefix
     233             :              * of a given length (after decompression).
     234             :              */
     235          20 :             max_size = pglz_maximum_compressed_size(sliceoffset + slicelength,
     236             :                                                     toast_pointer.va_extsize);
     237             : 
     238             :             /*
     239             :              * Fetch enough compressed slices (compressed marker will get set
     240             :              * automatically).
     241             :              */
     242          20 :             preslice = toast_fetch_datum_slice(attr, 0, max_size);
     243             :         }
     244             :         else
     245           0 :             preslice = toast_fetch_datum(attr);
     246             :     }
     247          84 :     else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
     248             :     {
     249             :         struct varatt_indirect redirect;
     250             : 
     251           0 :         VARATT_EXTERNAL_GET_POINTER(redirect, attr);
     252             : 
     253             :         /* nested indirect Datums aren't allowed */
     254             :         Assert(!VARATT_IS_EXTERNAL_INDIRECT(redirect.pointer));
     255             : 
     256           0 :         return detoast_attr_slice(redirect.pointer,
     257             :                                              sliceoffset, slicelength);
     258             :     }
     259          84 :     else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
     260             :     {
     261             :         /* pass it off to detoast_external_attr to flatten */
     262           0 :         preslice = detoast_external_attr(attr);
     263             :     }
     264             :     else
     265          84 :         preslice = attr;
     266             : 
     267             :     Assert(!VARATT_IS_EXTERNAL(preslice));
     268             : 
     269         104 :     if (VARATT_IS_COMPRESSED(preslice))
     270             :     {
     271          68 :         struct varlena *tmp = preslice;
     272             : 
     273             :         /* Decompress enough to encompass the slice and the offset */
     274          68 :         if (slicelength > 0 && sliceoffset >= 0)
     275          52 :             preslice = toast_decompress_datum_slice(tmp, slicelength + sliceoffset);
     276             :         else
     277          16 :             preslice = toast_decompress_datum(tmp);
     278             : 
     279          68 :         if (tmp != attr)
     280          20 :             pfree(tmp);
     281             :     }
     282             : 
     283         104 :     if (VARATT_IS_SHORT(preslice))
     284             :     {
     285           0 :         attrdata = VARDATA_SHORT(preslice);
     286           0 :         attrsize = VARSIZE_SHORT(preslice) - VARHDRSZ_SHORT;
     287             :     }
     288             :     else
     289             :     {
     290         104 :         attrdata = VARDATA(preslice);
     291         104 :         attrsize = VARSIZE(preslice) - VARHDRSZ;
     292             :     }
     293             : 
     294             :     /* slicing of datum for compressed cases and plain value */
     295             : 
     296         104 :     if (sliceoffset >= attrsize)
     297             :     {
     298          12 :         sliceoffset = 0;
     299          12 :         slicelength = 0;
     300             :     }
     301             : 
     302         104 :     if (((sliceoffset + slicelength) > attrsize) || slicelength < 0)
     303          32 :         slicelength = attrsize - sliceoffset;
     304             : 
     305         104 :     result = (struct varlena *) palloc(slicelength + VARHDRSZ);
     306         104 :     SET_VARSIZE(result, slicelength + VARHDRSZ);
     307             : 
     308         104 :     memcpy(VARDATA(result), attrdata + sliceoffset, slicelength);
     309             : 
     310         104 :     if (preslice != attr)
     311          68 :         pfree(preslice);
     312             : 
     313         104 :     return result;
     314             : }
     315             : 
     316             : /* ----------
     317             :  * toast_fetch_datum -
     318             :  *
     319             :  *  Reconstruct an in memory Datum from the chunks saved
     320             :  *  in the toast relation
     321             :  * ----------
     322             :  */
     323             : static struct varlena *
     324       35828 : toast_fetch_datum(struct varlena *attr)
     325             : {
     326             :     Relation    toastrel;
     327             :     Relation   *toastidxs;
     328             :     ScanKeyData toastkey;
     329             :     SysScanDesc toastscan;
     330             :     HeapTuple   ttup;
     331             :     TupleDesc   toasttupDesc;
     332             :     struct varlena *result;
     333             :     struct varatt_external toast_pointer;
     334             :     int32       ressize;
     335             :     int32       residx,
     336             :                 nextidx;
     337             :     int32       numchunks;
     338             :     Pointer     chunk;
     339             :     bool        isnull;
     340             :     char       *chunkdata;
     341             :     int32       chunksize;
     342             :     int         num_indexes;
     343             :     int         validIndex;
     344             :     SnapshotData SnapshotToast;
     345             : 
     346       35828 :     if (!VARATT_IS_EXTERNAL_ONDISK(attr))
     347           0 :         elog(ERROR, "toast_fetch_datum shouldn't be called for non-ondisk datums");
     348             : 
     349             :     /* Must copy to access aligned fields */
     350       35828 :     VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     351             : 
     352       35828 :     ressize = toast_pointer.va_extsize;
     353       35828 :     numchunks = ((ressize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
     354             : 
     355       35828 :     result = (struct varlena *) palloc(ressize + VARHDRSZ);
     356             : 
     357       35828 :     if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
     358       35244 :         SET_VARSIZE_COMPRESSED(result, ressize + VARHDRSZ);
     359             :     else
     360         584 :         SET_VARSIZE(result, ressize + VARHDRSZ);
     361             : 
     362             :     /*
     363             :      * Open the toast relation and its indexes
     364             :      */
     365       35828 :     toastrel = table_open(toast_pointer.va_toastrelid, AccessShareLock);
     366       35828 :     toasttupDesc = toastrel->rd_att;
     367             : 
     368             :     /* Look for the valid index of the toast relation */
     369       35828 :     validIndex = toast_open_indexes(toastrel,
     370             :                                     AccessShareLock,
     371             :                                     &toastidxs,
     372             :                                     &num_indexes);
     373             : 
     374             :     /*
     375             :      * Setup a scan key to fetch from the index by va_valueid
     376             :      */
     377       35828 :     ScanKeyInit(&toastkey,
     378             :                 (AttrNumber) 1,
     379             :                 BTEqualStrategyNumber, F_OIDEQ,
     380       35828 :                 ObjectIdGetDatum(toast_pointer.va_valueid));
     381             : 
     382             :     /*
     383             :      * Read the chunks by index
     384             :      *
     385             :      * Note that because the index is actually on (valueid, chunkidx) we will
     386             :      * see the chunks in chunkidx order, even though we didn't explicitly ask
     387             :      * for it.
     388             :      */
     389       35828 :     nextidx = 0;
     390             : 
     391       35828 :     init_toast_snapshot(&SnapshotToast);
     392       35828 :     toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
     393             :                                            &SnapshotToast, 1, &toastkey);
     394      192818 :     while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
     395             :     {
     396             :         /*
     397             :          * Have a chunk, extract the sequence number and the data
     398             :          */
     399      121162 :         residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
     400             :         Assert(!isnull);
     401      121162 :         chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
     402             :         Assert(!isnull);
     403      121162 :         if (!VARATT_IS_EXTENDED(chunk))
     404             :         {
     405      121162 :             chunksize = VARSIZE(chunk) - VARHDRSZ;
     406      121162 :             chunkdata = VARDATA(chunk);
     407             :         }
     408           0 :         else if (VARATT_IS_SHORT(chunk))
     409             :         {
     410             :             /* could happen due to heap_form_tuple doing its thing */
     411           0 :             chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
     412           0 :             chunkdata = VARDATA_SHORT(chunk);
     413             :         }
     414             :         else
     415             :         {
     416             :             /* should never happen */
     417           0 :             elog(ERROR, "found toasted toast chunk for toast value %u in %s",
     418             :                  toast_pointer.va_valueid,
     419             :                  RelationGetRelationName(toastrel));
     420             :             chunksize = 0;      /* keep compiler quiet */
     421             :             chunkdata = NULL;
     422             :         }
     423             : 
     424             :         /*
     425             :          * Some checks on the data we've found
     426             :          */
     427      121162 :         if (residx != nextidx)
     428           0 :             ereport(ERROR,
     429             :                     (errcode(ERRCODE_DATA_CORRUPTED),
     430             :                      errmsg_internal("unexpected chunk number %d (expected %d) for toast value %u in %s",
     431             :                                      residx, nextidx,
     432             :                                      toast_pointer.va_valueid,
     433             :                                      RelationGetRelationName(toastrel))));
     434      121162 :         if (residx < numchunks - 1)
     435             :         {
     436       85334 :             if (chunksize != TOAST_MAX_CHUNK_SIZE)
     437           0 :                 ereport(ERROR,
     438             :                         (errcode(ERRCODE_DATA_CORRUPTED),
     439             :                          errmsg_internal("unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s",
     440             :                                          chunksize, (int) TOAST_MAX_CHUNK_SIZE,
     441             :                                          residx, numchunks,
     442             :                                          toast_pointer.va_valueid,
     443             :                                          RelationGetRelationName(toastrel))));
     444             :         }
     445       35828 :         else if (residx == numchunks - 1)
     446             :         {
     447       35828 :             if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != ressize)
     448           0 :                 ereport(ERROR,
     449             :                         (errcode(ERRCODE_DATA_CORRUPTED),
     450             :                          errmsg_internal("unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s",
     451             :                                          chunksize,
     452             :                                          (int) (ressize - residx * TOAST_MAX_CHUNK_SIZE),
     453             :                                          residx,
     454             :                                          toast_pointer.va_valueid,
     455             :                                          RelationGetRelationName(toastrel))));
     456             :         }
     457             :         else
     458           0 :             ereport(ERROR,
     459             :                     (errcode(ERRCODE_DATA_CORRUPTED),
     460             :                      errmsg_internal("unexpected chunk number %d (out of range %d..%d) for toast value %u in %s",
     461             :                                      residx,
     462             :                                      0, numchunks - 1,
     463             :                                      toast_pointer.va_valueid,
     464             :                                      RelationGetRelationName(toastrel))));
     465             : 
     466             :         /*
     467             :          * Copy the data into proper place in our result
     468             :          */
     469      121162 :         memcpy(VARDATA(result) + residx * TOAST_MAX_CHUNK_SIZE,
     470             :                chunkdata,
     471             :                chunksize);
     472             : 
     473      121162 :         nextidx++;
     474             :     }
     475             : 
     476             :     /*
     477             :      * Final checks that we successfully fetched the datum
     478             :      */
     479       35828 :     if (nextidx != numchunks)
     480           0 :         ereport(ERROR,
     481             :                 (errcode(ERRCODE_DATA_CORRUPTED),
     482             :                  errmsg_internal("missing chunk number %d for toast value %u in %s",
     483             :                                  nextidx,
     484             :                                  toast_pointer.va_valueid,
     485             :                                  RelationGetRelationName(toastrel))));
     486             : 
     487             :     /*
     488             :      * End scan and close relations
     489             :      */
     490       35828 :     systable_endscan_ordered(toastscan);
     491       35828 :     toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
     492       35828 :     table_close(toastrel, AccessShareLock);
     493             : 
     494       35828 :     return result;
     495             : }
     496             : 
     497             : /* ----------
     498             :  * toast_fetch_datum_slice -
     499             :  *
     500             :  *  Reconstruct a segment of a Datum from the chunks saved
     501             :  *  in the toast relation
     502             :  *
     503             :  *  Note that this function supports non-compressed external datums
     504             :  *  and compressed external datums (in which case the requested slice
     505             :  *  has to be a prefix, i.e. sliceoffset has to be 0).
     506             :  * ----------
     507             :  */
     508             : static struct varlena *
     509          68 : toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, int32 length)
     510             : {
     511             :     Relation    toastrel;
     512             :     Relation   *toastidxs;
     513             :     ScanKeyData toastkey[3];
     514             :     int         nscankeys;
     515             :     SysScanDesc toastscan;
     516             :     HeapTuple   ttup;
     517             :     TupleDesc   toasttupDesc;
     518             :     struct varlena *result;
     519             :     struct varatt_external toast_pointer;
     520             :     int32       attrsize;
     521             :     int32       residx;
     522             :     int32       nextidx;
     523             :     int         numchunks;
     524             :     int         startchunk;
     525             :     int         endchunk;
     526             :     int32       startoffset;
     527             :     int32       endoffset;
     528             :     int         totalchunks;
     529             :     Pointer     chunk;
     530             :     bool        isnull;
     531             :     char       *chunkdata;
     532             :     int32       chunksize;
     533             :     int32       chcpystrt;
     534             :     int32       chcpyend;
     535             :     int         num_indexes;
     536             :     int         validIndex;
     537             :     SnapshotData SnapshotToast;
     538             : 
     539          68 :     if (!VARATT_IS_EXTERNAL_ONDISK(attr))
     540           0 :         elog(ERROR, "toast_fetch_datum_slice shouldn't be called for non-ondisk datums");
     541             : 
     542             :     /* Must copy to access aligned fields */
     543          68 :     VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     544             : 
     545             :     /*
     546             :      * It's nonsense to fetch slices of a compressed datum unless when it's
     547             :      * a prefix -- this isn't lo_* we can't return a compressed datum which
     548             :      * is meaningful to toast later.
     549             :      */
     550             :     Assert(!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) || 0 == sliceoffset);
     551             : 
     552          68 :     attrsize = toast_pointer.va_extsize;
     553          68 :     totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
     554             : 
     555          68 :     if (sliceoffset >= attrsize)
     556             :     {
     557           0 :         sliceoffset = 0;
     558           0 :         length = 0;
     559             :     }
     560             : 
     561             :     /*
     562             :      * When fetching a prefix of a compressed external datum, account for the
     563             :      * rawsize tracking amount of raw data, which is stored at the beginning
     564             :      * as an int32 value).
     565             :      */
     566          68 :     if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) && length > 0)
     567          20 :         length = length + sizeof(int32);
     568             : 
     569          68 :     if (((sliceoffset + length) > attrsize) || length < 0)
     570          32 :         length = attrsize - sliceoffset;
     571             : 
     572          68 :     result = (struct varlena *) palloc(length + VARHDRSZ);
     573             : 
     574          68 :     if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
     575          20 :         SET_VARSIZE_COMPRESSED(result, length + VARHDRSZ);
     576             :     else
     577          48 :         SET_VARSIZE(result, length + VARHDRSZ);
     578             : 
     579          68 :     if (length == 0)
     580           0 :         return result;          /* Can save a lot of work at this point! */
     581             : 
     582          68 :     startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE;
     583          68 :     endchunk = (sliceoffset + length - 1) / TOAST_MAX_CHUNK_SIZE;
     584          68 :     numchunks = (endchunk - startchunk) + 1;
     585             : 
     586          68 :     startoffset = sliceoffset % TOAST_MAX_CHUNK_SIZE;
     587          68 :     endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE;
     588             : 
     589             :     /*
     590             :      * Open the toast relation and its indexes
     591             :      */
     592          68 :     toastrel = table_open(toast_pointer.va_toastrelid, AccessShareLock);
     593          68 :     toasttupDesc = toastrel->rd_att;
     594             : 
     595             :     /* Look for the valid index of toast relation */
     596          68 :     validIndex = toast_open_indexes(toastrel,
     597             :                                     AccessShareLock,
     598             :                                     &toastidxs,
     599             :                                     &num_indexes);
     600             : 
     601             :     /*
     602             :      * Setup a scan key to fetch from the index. This is either two keys or
     603             :      * three depending on the number of chunks.
     604             :      */
     605          68 :     ScanKeyInit(&toastkey[0],
     606             :                 (AttrNumber) 1,
     607             :                 BTEqualStrategyNumber, F_OIDEQ,
     608          68 :                 ObjectIdGetDatum(toast_pointer.va_valueid));
     609             : 
     610             :     /*
     611             :      * Use equality condition for one chunk, a range condition otherwise:
     612             :      */
     613          68 :     if (numchunks == 1)
     614             :     {
     615          52 :         ScanKeyInit(&toastkey[1],
     616             :                     (AttrNumber) 2,
     617             :                     BTEqualStrategyNumber, F_INT4EQ,
     618             :                     Int32GetDatum(startchunk));
     619          52 :         nscankeys = 2;
     620             :     }
     621             :     else
     622             :     {
     623          16 :         ScanKeyInit(&toastkey[1],
     624             :                     (AttrNumber) 2,
     625             :                     BTGreaterEqualStrategyNumber, F_INT4GE,
     626             :                     Int32GetDatum(startchunk));
     627          16 :         ScanKeyInit(&toastkey[2],
     628             :                     (AttrNumber) 2,
     629             :                     BTLessEqualStrategyNumber, F_INT4LE,
     630             :                     Int32GetDatum(endchunk));
     631          16 :         nscankeys = 3;
     632             :     }
     633             : 
     634             :     /*
     635             :      * Read the chunks by index
     636             :      *
     637             :      * The index is on (valueid, chunkidx) so they will come in order
     638             :      */
     639          68 :     init_toast_snapshot(&SnapshotToast);
     640          68 :     nextidx = startchunk;
     641          68 :     toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
     642             :                                            &SnapshotToast, nscankeys, toastkey);
     643        1004 :     while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
     644             :     {
     645             :         /*
     646             :          * Have a chunk, extract the sequence number and the data
     647             :          */
     648         868 :         residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
     649             :         Assert(!isnull);
     650         868 :         chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
     651             :         Assert(!isnull);
     652         868 :         if (!VARATT_IS_EXTENDED(chunk))
     653             :         {
     654         868 :             chunksize = VARSIZE(chunk) - VARHDRSZ;
     655         868 :             chunkdata = VARDATA(chunk);
     656             :         }
     657           0 :         else if (VARATT_IS_SHORT(chunk))
     658             :         {
     659             :             /* could happen due to heap_form_tuple doing its thing */
     660           0 :             chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
     661           0 :             chunkdata = VARDATA_SHORT(chunk);
     662             :         }
     663             :         else
     664             :         {
     665             :             /* should never happen */
     666           0 :             elog(ERROR, "found toasted toast chunk for toast value %u in %s",
     667             :                  toast_pointer.va_valueid,
     668             :                  RelationGetRelationName(toastrel));
     669             :             chunksize = 0;      /* keep compiler quiet */
     670             :             chunkdata = NULL;
     671             :         }
     672             : 
     673             :         /*
     674             :          * Some checks on the data we've found
     675             :          */
     676         868 :         if ((residx != nextidx) || (residx > endchunk) || (residx < startchunk))
     677           0 :             elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u in %s",
     678             :                  residx, nextidx,
     679             :                  toast_pointer.va_valueid,
     680             :                  RelationGetRelationName(toastrel));
     681         868 :         if (residx < totalchunks - 1)
     682             :         {
     683         836 :             if (chunksize != TOAST_MAX_CHUNK_SIZE)
     684           0 :                 elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s when fetching slice",
     685             :                      chunksize, (int) TOAST_MAX_CHUNK_SIZE,
     686             :                      residx, totalchunks,
     687             :                      toast_pointer.va_valueid,
     688             :                      RelationGetRelationName(toastrel));
     689             :         }
     690          32 :         else if (residx == totalchunks - 1)
     691             :         {
     692          32 :             if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize)
     693           0 :                 elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s when fetching slice",
     694             :                      chunksize,
     695             :                      (int) (attrsize - residx * TOAST_MAX_CHUNK_SIZE),
     696             :                      residx,
     697             :                      toast_pointer.va_valueid,
     698             :                      RelationGetRelationName(toastrel));
     699             :         }
     700             :         else
     701           0 :             elog(ERROR, "unexpected chunk number %d (out of range %d..%d) for toast value %u in %s",
     702             :                  residx,
     703             :                  0, totalchunks - 1,
     704             :                  toast_pointer.va_valueid,
     705             :                  RelationGetRelationName(toastrel));
     706             : 
     707             :         /*
     708             :          * Copy the data into proper place in our result
     709             :          */
     710         868 :         chcpystrt = 0;
     711         868 :         chcpyend = chunksize - 1;
     712         868 :         if (residx == startchunk)
     713          68 :             chcpystrt = startoffset;
     714         868 :         if (residx == endchunk)
     715          68 :             chcpyend = endoffset;
     716             : 
     717        2604 :         memcpy(VARDATA(result) +
     718         868 :                (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
     719         868 :                chunkdata + chcpystrt,
     720         868 :                (chcpyend - chcpystrt) + 1);
     721             : 
     722         868 :         nextidx++;
     723             :     }
     724             : 
     725             :     /*
     726             :      * Final checks that we successfully fetched the datum
     727             :      */
     728          68 :     if (nextidx != (endchunk + 1))
     729           0 :         elog(ERROR, "missing chunk number %d for toast value %u in %s",
     730             :              nextidx,
     731             :              toast_pointer.va_valueid,
     732             :              RelationGetRelationName(toastrel));
     733             : 
     734             :     /*
     735             :      * End scan and close relations
     736             :      */
     737          68 :     systable_endscan_ordered(toastscan);
     738          68 :     toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
     739          68 :     table_close(toastrel, AccessShareLock);
     740             : 
     741          68 :     return result;
     742             : }
     743             : 
     744             : /* ----------
     745             :  * toast_decompress_datum -
     746             :  *
     747             :  * Decompress a compressed version of a varlena datum
     748             :  */
     749             : static struct varlena *
     750       88346 : toast_decompress_datum(struct varlena *attr)
     751             : {
     752             :     struct varlena *result;
     753             : 
     754             :     Assert(VARATT_IS_COMPRESSED(attr));
     755             : 
     756       88346 :     result = (struct varlena *)
     757       88346 :         palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
     758       88346 :     SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
     759             : 
     760      265038 :     if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
     761      176692 :                         TOAST_COMPRESS_SIZE(attr),
     762       88346 :                         VARDATA(result),
     763             :                         TOAST_COMPRESS_RAWSIZE(attr), true) < 0)
     764           0 :         elog(ERROR, "compressed data is corrupted");
     765             : 
     766       88346 :     return result;
     767             : }
     768             : 
     769             : 
     770             : /* ----------
     771             :  * toast_decompress_datum_slice -
     772             :  *
     773             :  * Decompress the front of a compressed version of a varlena datum.
     774             :  * offset handling happens in detoast_attr_slice.
     775             :  * Here we just decompress a slice from the front.
     776             :  */
     777             : static struct varlena *
     778          52 : toast_decompress_datum_slice(struct varlena *attr, int32 slicelength)
     779             : {
     780             :     struct varlena *result;
     781             :     int32       rawsize;
     782             : 
     783             :     Assert(VARATT_IS_COMPRESSED(attr));
     784             : 
     785          52 :     result = (struct varlena *) palloc(slicelength + VARHDRSZ);
     786             : 
     787          52 :     rawsize = pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
     788          52 :                               VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
     789          52 :                               VARDATA(result),
     790             :                               slicelength, false);
     791          52 :     if (rawsize < 0)
     792           0 :         elog(ERROR, "compressed data is corrupted");
     793             : 
     794          52 :     SET_VARSIZE(result, rawsize + VARHDRSZ);
     795          52 :     return result;
     796             : }
     797             : 
     798             : /* ----------
     799             :  * toast_raw_datum_size -
     800             :  *
     801             :  *  Return the raw (detoasted) size of a varlena datum
     802             :  *  (including the VARHDRSZ header)
     803             :  * ----------
     804             :  */
     805             : Size
     806    13470838 : toast_raw_datum_size(Datum value)
     807             : {
     808    13470838 :     struct varlena *attr = (struct varlena *) DatumGetPointer(value);
     809             :     Size        result;
     810             : 
     811    13470838 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
     812       30486 :     {
     813             :         /* va_rawsize is the size of the original datum -- including header */
     814             :         struct varatt_external toast_pointer;
     815             : 
     816       30486 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     817       30486 :         result = toast_pointer.va_rawsize;
     818             :     }
     819    13440352 :     else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
     820             :     {
     821             :         struct varatt_indirect toast_pointer;
     822             : 
     823           0 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     824             : 
     825             :         /* nested indirect Datums aren't allowed */
     826             :         Assert(!VARATT_IS_EXTERNAL_INDIRECT(toast_pointer.pointer));
     827             : 
     828           0 :         return toast_raw_datum_size(PointerGetDatum(toast_pointer.pointer));
     829             :     }
     830    13440352 :     else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
     831             :     {
     832           0 :         result = EOH_get_flat_size(DatumGetEOHP(value));
     833             :     }
     834    13440352 :     else if (VARATT_IS_COMPRESSED(attr))
     835             :     {
     836             :         /* here, va_rawsize is just the payload size */
     837       23182 :         result = VARRAWSIZE_4B_C(attr) + VARHDRSZ;
     838             :     }
     839    13417170 :     else if (VARATT_IS_SHORT(attr))
     840             :     {
     841             :         /*
     842             :          * we have to normalize the header length to VARHDRSZ or else the
     843             :          * callers of this function will be confused.
     844             :          */
     845    10477872 :         result = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT + VARHDRSZ;
     846             :     }
     847             :     else
     848             :     {
     849             :         /* plain untoasted datum */
     850     2939298 :         result = VARSIZE(attr);
     851             :     }
     852    13470838 :     return result;
     853             : }
     854             : 
     855             : /* ----------
     856             :  * toast_datum_size
     857             :  *
     858             :  *  Return the physical storage size (possibly compressed) of a varlena datum
     859             :  * ----------
     860             :  */
     861             : Size
     862         102 : toast_datum_size(Datum value)
     863             : {
     864         102 :     struct varlena *attr = (struct varlena *) DatumGetPointer(value);
     865             :     Size        result;
     866             : 
     867         102 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
     868           2 :     {
     869             :         /*
     870             :          * Attribute is stored externally - return the extsize whether
     871             :          * compressed or not.  We do not count the size of the toast pointer
     872             :          * ... should we?
     873             :          */
     874             :         struct varatt_external toast_pointer;
     875             : 
     876           2 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     877           2 :         result = toast_pointer.va_extsize;
     878             :     }
     879         100 :     else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
     880             :     {
     881             :         struct varatt_indirect toast_pointer;
     882             : 
     883           0 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     884             : 
     885             :         /* nested indirect Datums aren't allowed */
     886             :         Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
     887             : 
     888           0 :         return toast_datum_size(PointerGetDatum(toast_pointer.pointer));
     889             :     }
     890         100 :     else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
     891             :     {
     892           0 :         result = EOH_get_flat_size(DatumGetEOHP(value));
     893             :     }
     894         100 :     else if (VARATT_IS_SHORT(attr))
     895             :     {
     896           0 :         result = VARSIZE_SHORT(attr);
     897             :     }
     898             :     else
     899             :     {
     900             :         /*
     901             :          * Attribute is stored inline either compressed or not, just calculate
     902             :          * the size of the datum in either case.
     903             :          */
     904         100 :         result = VARSIZE(attr);
     905             :     }
     906         102 :     return result;
     907             : }

Generated by: LCOV version 1.13