LCOV - code coverage report
Current view: top level - src/backend/access/common - toast_internals.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 133 152 87.5 %
Date: 2019-11-13 22:07:24 Functions: 8 9 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * toast_internals.c
       4             :  *    Functions for internal use by the TOAST system.
       5             :  *
       6             :  * Copyright (c) 2000-2019, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *    src/backend/access/common/toast_internals.c
      10             :  *
      11             :  *-------------------------------------------------------------------------
      12             :  */
      13             : 
      14             : #include "postgres.h"
      15             : 
      16             : #include "access/detoast.h"
      17             : #include "access/genam.h"
      18             : #include "access/heapam.h"
      19             : #include "access/heaptoast.h"
      20             : #include "access/table.h"
      21             : #include "access/toast_internals.h"
      22             : #include "access/xact.h"
      23             : #include "catalog/catalog.h"
      24             : #include "common/pg_lzcompress.h"
      25             : #include "miscadmin.h"
      26             : #include "utils/fmgroids.h"
      27             : #include "utils/rel.h"
      28             : #include "utils/snapmgr.h"
      29             : 
      30             : static bool toastrel_valueid_exists(Relation toastrel, Oid valueid);
      31             : static bool toastid_valueid_exists(Oid toastrelid, Oid valueid);
      32             : 
      33             : /* ----------
      34             :  * toast_compress_datum -
      35             :  *
      36             :  *  Create a compressed version of a varlena datum
      37             :  *
      38             :  *  If we fail (ie, compressed result is actually bigger than original)
      39             :  *  then return NULL.  We must not use compressed data if it'd expand
      40             :  *  the tuple!
      41             :  *
      42             :  *  We use VAR{SIZE,DATA}_ANY so we can handle short varlenas here without
      43             :  *  copying them.  But we can't handle external or compressed datums.
      44             :  * ----------
      45             :  */
      46             : Datum
      47       63304 : toast_compress_datum(Datum value)
      48             : {
      49             :     struct varlena *tmp;
      50       63304 :     int32       valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
      51             :     int32       len;
      52             : 
      53             :     Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
      54             :     Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
      55             : 
      56             :     /*
      57             :      * No point in wasting a palloc cycle if value size is out of the allowed
      58             :      * range for compression
      59             :      */
      60      124672 :     if (valsize < PGLZ_strategy_default->min_input_size ||
      61       61368 :         valsize > PGLZ_strategy_default->max_input_size)
      62        1936 :         return PointerGetDatum(NULL);
      63             : 
      64       61368 :     tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
      65             :                                     TOAST_COMPRESS_HDRSZ);
      66             : 
      67             :     /*
      68             :      * We recheck the actual size even if pglz_compress() reports success,
      69             :      * because it might be satisfied with having saved as little as one byte
      70             :      * in the compressed data --- which could turn into a net loss once you
      71             :      * consider header and alignment padding.  Worst case, the compressed
      72             :      * format might require three padding bytes (plus header, which is
      73             :      * included in VARSIZE(tmp)), whereas the uncompressed format would take
      74             :      * only one header byte and no padding if the value is short enough.  So
      75             :      * we insist on a savings of more than 2 bytes to ensure we have a gain.
      76             :      */
      77       61368 :     len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
      78             :                         valsize,
      79             :                         TOAST_COMPRESS_RAWDATA(tmp),
      80             :                         PGLZ_strategy_default);
      81      121160 :     if (len >= 0 &&
      82       59792 :         len + TOAST_COMPRESS_HDRSZ < valsize - 2)
      83             :     {
      84       59792 :         TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
      85       59792 :         SET_VARSIZE_COMPRESSED(tmp, len + TOAST_COMPRESS_HDRSZ);
      86             :         /* successful compression */
      87       59792 :         return PointerGetDatum(tmp);
      88             :     }
      89             :     else
      90             :     {
      91             :         /* incompressible data */
      92        1576 :         pfree(tmp);
      93        1576 :         return PointerGetDatum(NULL);
      94             :     }
      95             : }
      96             : 
      97             : /* ----------
      98             :  * toast_save_datum -
      99             :  *
     100             :  *  Save one single datum into the secondary relation and return
     101             :  *  a Datum reference for it.
     102             :  *
     103             :  * rel: the main relation we're working with (not the toast rel!)
     104             :  * value: datum to be pushed to toast storage
     105             :  * oldexternal: if not NULL, toast pointer previously representing the datum
     106             :  * options: options to be passed to heap_insert() for toast rows
     107             :  * ----------
     108             :  */
     109             : Datum
     110       28158 : toast_save_datum(Relation rel, Datum value,
     111             :                  struct varlena *oldexternal, int options)
     112             : {
     113             :     Relation    toastrel;
     114             :     Relation   *toastidxs;
     115             :     HeapTuple   toasttup;
     116             :     TupleDesc   toasttupDesc;
     117             :     Datum       t_values[3];
     118             :     bool        t_isnull[3];
     119       28158 :     CommandId   mycid = GetCurrentCommandId(true);
     120             :     struct varlena *result;
     121             :     struct varatt_external toast_pointer;
     122             :     union
     123             :     {
     124             :         struct varlena hdr;
     125             :         /* this is to make the union big enough for a chunk: */
     126             :         char        data[TOAST_MAX_CHUNK_SIZE + VARHDRSZ];
     127             :         /* ensure union is aligned well enough: */
     128             :         int32       align_it;
     129             :     }           chunk_data;
     130             :     int32       chunk_size;
     131       28158 :     int32       chunk_seq = 0;
     132             :     char       *data_p;
     133             :     int32       data_todo;
     134       28158 :     Pointer     dval = DatumGetPointer(value);
     135             :     int         num_indexes;
     136             :     int         validIndex;
     137             : 
     138             :     Assert(!VARATT_IS_EXTERNAL(value));
     139             : 
     140             :     /*
     141             :      * Open the toast relation and its indexes.  We can use the index to check
     142             :      * uniqueness of the OID we assign to the toasted item, even though it has
     143             :      * additional columns besides OID.
     144             :      */
     145       28158 :     toastrel = table_open(rel->rd_rel->reltoastrelid, RowExclusiveLock);
     146       28158 :     toasttupDesc = toastrel->rd_att;
     147             : 
     148             :     /* Open all the toast indexes and look for the valid one */
     149       28158 :     validIndex = toast_open_indexes(toastrel,
     150             :                                     RowExclusiveLock,
     151             :                                     &toastidxs,
     152             :                                     &num_indexes);
     153             : 
     154             :     /*
     155             :      * Get the data pointer and length, and compute va_rawsize and va_extsize.
     156             :      *
     157             :      * va_rawsize is the size of the equivalent fully uncompressed datum, so
     158             :      * we have to adjust for short headers.
     159             :      *
     160             :      * va_extsize is the actual size of the data payload in the toast records.
     161             :      */
     162       28158 :     if (VARATT_IS_SHORT(dval))
     163             :     {
     164           0 :         data_p = VARDATA_SHORT(dval);
     165           0 :         data_todo = VARSIZE_SHORT(dval) - VARHDRSZ_SHORT;
     166           0 :         toast_pointer.va_rawsize = data_todo + VARHDRSZ;    /* as if not short */
     167           0 :         toast_pointer.va_extsize = data_todo;
     168             :     }
     169       28158 :     else if (VARATT_IS_COMPRESSED(dval))
     170             :     {
     171       27566 :         data_p = VARDATA(dval);
     172       27566 :         data_todo = VARSIZE(dval) - VARHDRSZ;
     173             :         /* rawsize in a compressed datum is just the size of the payload */
     174       27566 :         toast_pointer.va_rawsize = VARRAWSIZE_4B_C(dval) + VARHDRSZ;
     175       27566 :         toast_pointer.va_extsize = data_todo;
     176             :         /* Assert that the numbers look like it's compressed */
     177             :         Assert(VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
     178             :     }
     179             :     else
     180             :     {
     181         592 :         data_p = VARDATA(dval);
     182         592 :         data_todo = VARSIZE(dval) - VARHDRSZ;
     183         592 :         toast_pointer.va_rawsize = VARSIZE(dval);
     184         592 :         toast_pointer.va_extsize = data_todo;
     185             :     }
     186             : 
     187             :     /*
     188             :      * Insert the correct table OID into the result TOAST pointer.
     189             :      *
     190             :      * Normally this is the actual OID of the target toast table, but during
     191             :      * table-rewriting operations such as CLUSTER, we have to insert the OID
     192             :      * of the table's real permanent toast table instead.  rd_toastoid is set
     193             :      * if we have to substitute such an OID.
     194             :      */
     195       28158 :     if (OidIsValid(rel->rd_toastoid))
     196         428 :         toast_pointer.va_toastrelid = rel->rd_toastoid;
     197             :     else
     198       27730 :         toast_pointer.va_toastrelid = RelationGetRelid(toastrel);
     199             : 
     200             :     /*
     201             :      * Choose an OID to use as the value ID for this toast value.
     202             :      *
     203             :      * Normally we just choose an unused OID within the toast table.  But
     204             :      * during table-rewriting operations where we are preserving an existing
     205             :      * toast table OID, we want to preserve toast value OIDs too.  So, if
     206             :      * rd_toastoid is set and we had a prior external value from that same
     207             :      * toast table, re-use its value ID.  If we didn't have a prior external
     208             :      * value (which is a corner case, but possible if the table's attstorage
     209             :      * options have been changed), we have to pick a value ID that doesn't
     210             :      * conflict with either new or existing toast value OIDs.
     211             :      */
     212       28158 :     if (!OidIsValid(rel->rd_toastoid))
     213             :     {
     214             :         /* normal case: just choose an unused OID */
     215       27730 :         toast_pointer.va_valueid =
     216       27730 :             GetNewOidWithIndex(toastrel,
     217       27730 :                                RelationGetRelid(toastidxs[validIndex]),
     218             :                                (AttrNumber) 1);
     219             :     }
     220             :     else
     221             :     {
     222             :         /* rewrite case: check to see if value was in old toast table */
     223         428 :         toast_pointer.va_valueid = InvalidOid;
     224         428 :         if (oldexternal != NULL)
     225             :         {
     226             :             struct varatt_external old_toast_pointer;
     227             : 
     228             :             Assert(VARATT_IS_EXTERNAL_ONDISK(oldexternal));
     229             :             /* Must copy to access aligned fields */
     230         428 :             VARATT_EXTERNAL_GET_POINTER(old_toast_pointer, oldexternal);
     231         428 :             if (old_toast_pointer.va_toastrelid == rel->rd_toastoid)
     232             :             {
     233             :                 /* This value came from the old toast table; reuse its OID */
     234         428 :                 toast_pointer.va_valueid = old_toast_pointer.va_valueid;
     235             : 
     236             :                 /*
     237             :                  * There is a corner case here: the table rewrite might have
     238             :                  * to copy both live and recently-dead versions of a row, and
     239             :                  * those versions could easily reference the same toast value.
     240             :                  * When we copy the second or later version of such a row,
     241             :                  * reusing the OID will mean we select an OID that's already
     242             :                  * in the new toast table.  Check for that, and if so, just
     243             :                  * fall through without writing the data again.
     244             :                  *
     245             :                  * While annoying and ugly-looking, this is a good thing
     246             :                  * because it ensures that we wind up with only one copy of
     247             :                  * the toast value when there is only one copy in the old
     248             :                  * toast table.  Before we detected this case, we'd have made
     249             :                  * multiple copies, wasting space; and what's worse, the
     250             :                  * copies belonging to already-deleted heap tuples would not
     251             :                  * be reclaimed by VACUUM.
     252             :                  */
     253         428 :                 if (toastrel_valueid_exists(toastrel,
     254             :                                             toast_pointer.va_valueid))
     255             :                 {
     256             :                     /* Match, so short-circuit the data storage loop below */
     257           0 :                     data_todo = 0;
     258             :                 }
     259             :             }
     260             :         }
     261         428 :         if (toast_pointer.va_valueid == InvalidOid)
     262             :         {
     263             :             /*
     264             :              * new value; must choose an OID that doesn't conflict in either
     265             :              * old or new toast table
     266             :              */
     267             :             do
     268             :             {
     269           0 :                 toast_pointer.va_valueid =
     270           0 :                     GetNewOidWithIndex(toastrel,
     271           0 :                                        RelationGetRelid(toastidxs[validIndex]),
     272             :                                        (AttrNumber) 1);
     273           0 :             } while (toastid_valueid_exists(rel->rd_toastoid,
     274           0 :                                             toast_pointer.va_valueid));
     275             :         }
     276             :     }
     277             : 
     278             :     /*
     279             :      * Initialize constant parts of the tuple data
     280             :      */
     281       28158 :     t_values[0] = ObjectIdGetDatum(toast_pointer.va_valueid);
     282       28158 :     t_values[2] = PointerGetDatum(&chunk_data);
     283       28158 :     t_isnull[0] = false;
     284       28158 :     t_isnull[1] = false;
     285       28158 :     t_isnull[2] = false;
     286             : 
     287             :     /*
     288             :      * Split up the item into chunks
     289             :      */
     290      145222 :     while (data_todo > 0)
     291             :     {
     292             :         int         i;
     293             : 
     294       88906 :         CHECK_FOR_INTERRUPTS();
     295             : 
     296             :         /*
     297             :          * Calculate the size of this chunk
     298             :          */
     299       88906 :         chunk_size = Min(TOAST_MAX_CHUNK_SIZE, data_todo);
     300             : 
     301             :         /*
     302             :          * Build a tuple and store it
     303             :          */
     304       88906 :         t_values[1] = Int32GetDatum(chunk_seq++);
     305       88906 :         SET_VARSIZE(&chunk_data, chunk_size + VARHDRSZ);
     306       88906 :         memcpy(VARDATA(&chunk_data), data_p, chunk_size);
     307       88906 :         toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull);
     308             : 
     309       88906 :         heap_insert(toastrel, toasttup, mycid, options, NULL);
     310             : 
     311             :         /*
     312             :          * Create the index entry.  We cheat a little here by not using
     313             :          * FormIndexDatum: this relies on the knowledge that the index columns
     314             :          * are the same as the initial columns of the table for all the
     315             :          * indexes.  We also cheat by not providing an IndexInfo: this is okay
     316             :          * for now because btree doesn't need one, but we might have to be
     317             :          * more honest someday.
     318             :          *
     319             :          * Note also that there had better not be any user-created index on
     320             :          * the TOAST table, since we don't bother to update anything else.
     321             :          */
     322      177812 :         for (i = 0; i < num_indexes; i++)
     323             :         {
     324             :             /* Only index relations marked as ready can be updated */
     325       88906 :             if (toastidxs[i]->rd_index->indisready)
     326       88906 :                 index_insert(toastidxs[i], t_values, t_isnull,
     327             :                              &(toasttup->t_self),
     328             :                              toastrel,
     329       88906 :                              toastidxs[i]->rd_index->indisunique ?
     330             :                              UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
     331             :                              NULL);
     332             :         }
     333             : 
     334             :         /*
     335             :          * Free memory
     336             :          */
     337       88906 :         heap_freetuple(toasttup);
     338             : 
     339             :         /*
     340             :          * Move on to next chunk
     341             :          */
     342       88906 :         data_todo -= chunk_size;
     343       88906 :         data_p += chunk_size;
     344             :     }
     345             : 
     346             :     /*
     347             :      * Done - close toast relation and its indexes
     348             :      */
     349       28158 :     toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
     350       28158 :     table_close(toastrel, RowExclusiveLock);
     351             : 
     352             :     /*
     353             :      * Create the TOAST pointer value that we'll return
     354             :      */
     355       28158 :     result = (struct varlena *) palloc(TOAST_POINTER_SIZE);
     356       28158 :     SET_VARTAG_EXTERNAL(result, VARTAG_ONDISK);
     357       28158 :     memcpy(VARDATA_EXTERNAL(result), &toast_pointer, sizeof(toast_pointer));
     358             : 
     359       28158 :     return PointerGetDatum(result);
     360             : }
     361             : 
     362             : /* ----------
     363             :  * toast_delete_datum -
     364             :  *
     365             :  *  Delete a single external stored value.
     366             :  * ----------
     367             :  */
     368             : void
     369         716 : toast_delete_datum(Relation rel, Datum value, bool is_speculative)
     370             : {
     371         716 :     struct varlena *attr = (struct varlena *) DatumGetPointer(value);
     372             :     struct varatt_external toast_pointer;
     373             :     Relation    toastrel;
     374             :     Relation   *toastidxs;
     375             :     ScanKeyData toastkey;
     376             :     SysScanDesc toastscan;
     377             :     HeapTuple   toasttup;
     378             :     int         num_indexes;
     379             :     int         validIndex;
     380             :     SnapshotData SnapshotToast;
     381             : 
     382         716 :     if (!VARATT_IS_EXTERNAL_ONDISK(attr))
     383           0 :         return;
     384             : 
     385             :     /* Must copy to access aligned fields */
     386         716 :     VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     387             : 
     388             :     /*
     389             :      * Open the toast relation and its indexes
     390             :      */
     391         716 :     toastrel = table_open(toast_pointer.va_toastrelid, RowExclusiveLock);
     392             : 
     393             :     /* Fetch valid relation used for process */
     394         716 :     validIndex = toast_open_indexes(toastrel,
     395             :                                     RowExclusiveLock,
     396             :                                     &toastidxs,
     397             :                                     &num_indexes);
     398             : 
     399             :     /*
     400             :      * Setup a scan key to find chunks with matching va_valueid
     401             :      */
     402         716 :     ScanKeyInit(&toastkey,
     403             :                 (AttrNumber) 1,
     404             :                 BTEqualStrategyNumber, F_OIDEQ,
     405         716 :                 ObjectIdGetDatum(toast_pointer.va_valueid));
     406             : 
     407             :     /*
     408             :      * Find all the chunks.  (We don't actually care whether we see them in
     409             :      * sequence or not, but since we've already locked the index we might as
     410             :      * well use systable_beginscan_ordered.)
     411             :      */
     412         716 :     init_toast_snapshot(&SnapshotToast);
     413         716 :     toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
     414             :                                            &SnapshotToast, 1, &toastkey);
     415        3362 :     while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
     416             :     {
     417             :         /*
     418             :          * Have a chunk, delete it
     419             :          */
     420        1930 :         if (is_speculative)
     421          10 :             heap_abort_speculative(toastrel, &toasttup->t_self);
     422             :         else
     423        1920 :             simple_heap_delete(toastrel, &toasttup->t_self);
     424             :     }
     425             : 
     426             :     /*
     427             :      * End scan and close relations
     428             :      */
     429         716 :     systable_endscan_ordered(toastscan);
     430         716 :     toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
     431         716 :     table_close(toastrel, RowExclusiveLock);
     432             : }
     433             : 
     434             : /* ----------
     435             :  * toastrel_valueid_exists -
     436             :  *
     437             :  *  Test whether a toast value with the given ID exists in the toast relation.
     438             :  *  For safety, we consider a value to exist if there are either live or dead
     439             :  *  toast rows with that ID; see notes for GetNewOidWithIndex().
     440             :  * ----------
     441             :  */
     442             : static bool
     443         428 : toastrel_valueid_exists(Relation toastrel, Oid valueid)
     444             : {
     445         428 :     bool        result = false;
     446             :     ScanKeyData toastkey;
     447             :     SysScanDesc toastscan;
     448             :     int         num_indexes;
     449             :     int         validIndex;
     450             :     Relation   *toastidxs;
     451             : 
     452             :     /* Fetch a valid index relation */
     453         428 :     validIndex = toast_open_indexes(toastrel,
     454             :                                     RowExclusiveLock,
     455             :                                     &toastidxs,
     456             :                                     &num_indexes);
     457             : 
     458             :     /*
     459             :      * Setup a scan key to find chunks with matching va_valueid
     460             :      */
     461         428 :     ScanKeyInit(&toastkey,
     462             :                 (AttrNumber) 1,
     463             :                 BTEqualStrategyNumber, F_OIDEQ,
     464             :                 ObjectIdGetDatum(valueid));
     465             : 
     466             :     /*
     467             :      * Is there any such chunk?
     468             :      */
     469         428 :     toastscan = systable_beginscan(toastrel,
     470         428 :                                    RelationGetRelid(toastidxs[validIndex]),
     471             :                                    true, SnapshotAny, 1, &toastkey);
     472             : 
     473         428 :     if (systable_getnext(toastscan) != NULL)
     474           0 :         result = true;
     475             : 
     476         428 :     systable_endscan(toastscan);
     477             : 
     478             :     /* Clean up */
     479         428 :     toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
     480             : 
     481         428 :     return result;
     482             : }
     483             : 
     484             : /* ----------
     485             :  * toastid_valueid_exists -
     486             :  *
     487             :  *  As above, but work from toast rel's OID not an open relation
     488             :  * ----------
     489             :  */
     490             : static bool
     491           0 : toastid_valueid_exists(Oid toastrelid, Oid valueid)
     492             : {
     493             :     bool        result;
     494             :     Relation    toastrel;
     495             : 
     496           0 :     toastrel = table_open(toastrelid, AccessShareLock);
     497             : 
     498           0 :     result = toastrel_valueid_exists(toastrel, valueid);
     499             : 
     500           0 :     table_close(toastrel, AccessShareLock);
     501             : 
     502           0 :     return result;
     503             : }
     504             : 
     505             : /* ----------
     506             :  * toast_get_valid_index
     507             :  *
     508             :  *  Get OID of valid index associated to given toast relation. A toast
     509             :  *  relation can have only one valid index at the same time.
     510             :  */
     511             : Oid
     512         492 : toast_get_valid_index(Oid toastoid, LOCKMODE lock)
     513             : {
     514             :     int         num_indexes;
     515             :     int         validIndex;
     516             :     Oid         validIndexOid;
     517             :     Relation   *toastidxs;
     518             :     Relation    toastrel;
     519             : 
     520             :     /* Open the toast relation */
     521         492 :     toastrel = table_open(toastoid, lock);
     522             : 
     523             :     /* Look for the valid index of the toast relation */
     524         492 :     validIndex = toast_open_indexes(toastrel,
     525             :                                     lock,
     526             :                                     &toastidxs,
     527             :                                     &num_indexes);
     528         492 :     validIndexOid = RelationGetRelid(toastidxs[validIndex]);
     529             : 
     530             :     /* Close the toast relation and all its indexes */
     531         492 :     toast_close_indexes(toastidxs, num_indexes, lock);
     532         492 :     table_close(toastrel, lock);
     533             : 
     534         492 :     return validIndexOid;
     535             : }
     536             : 
     537             : /* ----------
     538             :  * toast_open_indexes
     539             :  *
     540             :  *  Get an array of the indexes associated to the given toast relation
     541             :  *  and return as well the position of the valid index used by the toast
     542             :  *  relation in this array. It is the responsibility of the caller of this
     543             :  *  function to close the indexes as well as free them.
     544             :  */
     545             : int
     546       65684 : toast_open_indexes(Relation toastrel,
     547             :                    LOCKMODE lock,
     548             :                    Relation **toastidxs,
     549             :                    int *num_indexes)
     550             : {
     551       65684 :     int         i = 0;
     552       65684 :     int         res = 0;
     553       65684 :     bool        found = false;
     554             :     List       *indexlist;
     555             :     ListCell   *lc;
     556             : 
     557             :     /* Get index list of the toast relation */
     558       65684 :     indexlist = RelationGetIndexList(toastrel);
     559             :     Assert(indexlist != NIL);
     560             : 
     561       65684 :     *num_indexes = list_length(indexlist);
     562             : 
     563             :     /* Open all the index relations */
     564       65684 :     *toastidxs = (Relation *) palloc(*num_indexes * sizeof(Relation));
     565      131368 :     foreach(lc, indexlist)
     566       65684 :         (*toastidxs)[i++] = index_open(lfirst_oid(lc), lock);
     567             : 
     568             :     /* Fetch the first valid index in list */
     569       65684 :     for (i = 0; i < *num_indexes; i++)
     570             :     {
     571       65684 :         Relation    toastidx = (*toastidxs)[i];
     572             : 
     573       65684 :         if (toastidx->rd_index->indisvalid)
     574             :         {
     575       65684 :             res = i;
     576       65684 :             found = true;
     577       65684 :             break;
     578             :         }
     579             :     }
     580             : 
     581             :     /*
     582             :      * Free index list, not necessary anymore as relations are opened and a
     583             :      * valid index has been found.
     584             :      */
     585       65684 :     list_free(indexlist);
     586             : 
     587             :     /*
     588             :      * The toast relation should have one valid index, so something is going
     589             :      * wrong if there is nothing.
     590             :      */
     591       65684 :     if (!found)
     592           0 :         elog(ERROR, "no valid index found for toast relation with Oid %u",
     593             :              RelationGetRelid(toastrel));
     594             : 
     595       65684 :     return res;
     596             : }
     597             : 
     598             : /* ----------
     599             :  * toast_close_indexes
     600             :  *
     601             :  *  Close an array of indexes for a toast relation and free it. This should
     602             :  *  be called for a set of indexes opened previously with toast_open_indexes.
     603             :  */
     604             : void
     605       65684 : toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock)
     606             : {
     607             :     int         i;
     608             : 
     609             :     /* Close relations and clean up things */
     610      131368 :     for (i = 0; i < num_indexes; i++)
     611       65684 :         index_close(toastidxs[i], lock);
     612       65684 :     pfree(toastidxs);
     613       65684 : }
     614             : 
     615             : /* ----------
     616             :  * init_toast_snapshot
     617             :  *
     618             :  *  Initialize an appropriate TOAST snapshot.  We must use an MVCC snapshot
     619             :  *  to initialize the TOAST snapshot; since we don't know which one to use,
     620             :  *  just use the oldest one.  This is safe: at worst, we will get a "snapshot
     621             :  *  too old" error that might have been avoided otherwise.
     622             :  */
     623             : void
     624       36606 : init_toast_snapshot(Snapshot toast_snapshot)
     625             : {
     626       36606 :     Snapshot    snapshot = GetOldestSnapshot();
     627             : 
     628       36606 :     if (snapshot == NULL)
     629           0 :         elog(ERROR, "no known snapshots");
     630             : 
     631       36606 :     InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
     632       36606 : }

Generated by: LCOV version 1.13