LCOV - differential code coverage report
Current view: top level - src/backend/access/brin - brin_tuple.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 95.9 % 221 212 9 212
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 10 10 10
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*
       2                 :  * brin_tuple.c
       3                 :  *      Method implementations for tuples in BRIN indexes.
       4                 :  *
       5                 :  * Intended usage is that code outside this file only deals with
       6                 :  * BrinMemTuples, and convert to and from the on-disk representation through
       7                 :  * functions in this file.
       8                 :  *
       9                 :  * NOTES
      10                 :  *
      11                 :  * A BRIN tuple is similar to a heap tuple, with a few key differences.  The
      12                 :  * first interesting difference is that the tuple header is much simpler, only
      13                 :  * containing its total length and a small area for flags.  Also, the stored
      14                 :  * data does not match the relation tuple descriptor exactly: for each
      15                 :  * attribute in the descriptor, the index tuple carries an arbitrary number
      16                 :  * of values, depending on the opclass.
      17                 :  *
      18                 :  * Also, for each column of the index relation there are two null bits: one
      19                 :  * (hasnulls) stores whether any tuple within the page range has that column
      20                 :  * set to null; the other one (allnulls) stores whether the column values are
      21                 :  * all null.  If allnulls is true, then the tuple data area does not contain
      22                 :  * values for that column at all; whereas it does if the hasnulls is set.
      23                 :  * Note the size of the null bitmask may not be the same as that of the
      24                 :  * datum array.
      25                 :  *
      26                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
      27                 :  * Portions Copyright (c) 1994, Regents of the University of California
      28                 :  *
      29                 :  * IDENTIFICATION
      30                 :  *    src/backend/access/brin/brin_tuple.c
      31                 :  */
      32                 : #include "postgres.h"
      33                 : 
      34                 : #include "access/brin_tuple.h"
      35                 : #include "access/detoast.h"
      36                 : #include "access/heaptoast.h"
      37                 : #include "access/htup_details.h"
      38                 : #include "access/toast_internals.h"
      39                 : #include "access/tupdesc.h"
      40                 : #include "access/tupmacs.h"
      41                 : #include "utils/datum.h"
      42                 : #include "utils/memutils.h"
      43                 : 
      44                 : 
      45                 : /*
      46                 :  * This enables de-toasting of index entries.  Needed until VACUUM is
      47                 :  * smart enough to rebuild indexes from scratch.
      48                 :  */
      49                 : #define TOAST_INDEX_HACK
      50                 : 
      51                 : 
      52                 : static inline void brin_deconstruct_tuple(BrinDesc *brdesc,
      53                 :                                           char *tp, bits8 *nullbits, bool nulls,
      54                 :                                           Datum *values, bool *allnulls, bool *hasnulls);
      55                 : 
      56                 : 
      57                 : /*
      58                 :  * Return a tuple descriptor used for on-disk storage of BRIN tuples.
      59                 :  */
      60                 : static TupleDesc
      61 CBC      110572 : brtuple_disk_tupdesc(BrinDesc *brdesc)
      62                 : {
      63                 :     /* We cache these in the BrinDesc */
      64          110572 :     if (brdesc->bd_disktdesc == NULL)
      65                 :     {
      66                 :         int         i;
      67                 :         int         j;
      68            1966 :         AttrNumber  attno = 1;
      69                 :         TupleDesc   tupdesc;
      70                 :         MemoryContext oldcxt;
      71                 : 
      72                 :         /* make sure it's in the bdesc's context */
      73            1966 :         oldcxt = MemoryContextSwitchTo(brdesc->bd_context);
      74                 : 
      75            1966 :         tupdesc = CreateTemplateTupleDesc(brdesc->bd_totalstored);
      76                 : 
      77           37092 :         for (i = 0; i < brdesc->bd_tupdesc->natts; i++)
      78                 :         {
      79           96744 :             for (j = 0; j < brdesc->bd_info[i]->oi_nstored; j++)
      80           61618 :                 TupleDescInitEntry(tupdesc, attno++, NULL,
      81           61618 :                                    brdesc->bd_info[i]->oi_typcache[j]->type_id,
      82                 :                                    -1, 0);
      83                 :         }
      84                 : 
      85            1966 :         MemoryContextSwitchTo(oldcxt);
      86                 : 
      87            1966 :         brdesc->bd_disktdesc = tupdesc;
      88                 :     }
      89                 : 
      90          110572 :     return brdesc->bd_disktdesc;
      91                 : }
      92                 : 
      93                 : /*
      94                 :  * Generate a new on-disk tuple to be inserted in a BRIN index.
      95                 :  *
      96                 :  * See brin_form_placeholder_tuple if you touch this.
      97                 :  */
      98                 : BrinTuple *
      99            4269 : brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
     100                 :                 Size *size)
     101                 : {
     102                 :     Datum      *values;
     103                 :     bool       *nulls;
     104            4269 :     bool        anynulls = false;
     105                 :     BrinTuple  *rettuple;
     106                 :     int         keyno;
     107                 :     int         idxattno;
     108            4269 :     uint16      phony_infomask = 0;
     109                 :     bits8      *phony_nullbitmap;
     110                 :     Size        len,
     111                 :                 hoff,
     112                 :                 data_len;
     113                 :     int         i;
     114                 : 
     115                 : #ifdef TOAST_INDEX_HACK
     116                 :     Datum      *untoasted_values;
     117            4269 :     int         nuntoasted = 0;
     118                 : #endif
     119                 : 
     120            4269 :     Assert(brdesc->bd_totalstored > 0);
     121                 : 
     122            4269 :     values = (Datum *) palloc(sizeof(Datum) * brdesc->bd_totalstored);
     123            4269 :     nulls = (bool *) palloc0(sizeof(bool) * brdesc->bd_totalstored);
     124                 :     phony_nullbitmap = (bits8 *)
     125            4269 :         palloc(sizeof(bits8) * BITMAPLEN(brdesc->bd_totalstored));
     126                 : 
     127                 : #ifdef TOAST_INDEX_HACK
     128            4269 :     untoasted_values = (Datum *) palloc(sizeof(Datum) * brdesc->bd_totalstored);
     129                 : #endif
     130                 : 
     131                 :     /*
     132                 :      * Set up the values/nulls arrays for heap_fill_tuple
     133                 :      */
     134            4269 :     idxattno = 0;
     135           58855 :     for (keyno = 0; keyno < brdesc->bd_tupdesc->natts; keyno++)
     136                 :     {
     137                 :         int         datumno;
     138                 : 
     139                 :         /*
     140                 :          * "allnulls" is set when there's no nonnull value in any row in the
     141                 :          * column; when this happens, there is no data to store.  Thus set the
     142                 :          * nullable bits for all data elements of this column and we're done.
     143                 :          */
     144           54586 :         if (tuple->bt_columns[keyno].bv_allnulls)
     145                 :         {
     146             297 :             for (datumno = 0;
     147             666 :                  datumno < brdesc->bd_info[keyno]->oi_nstored;
     148             369 :                  datumno++)
     149             369 :                 nulls[idxattno++] = true;
     150             297 :             anynulls = true;
     151             297 :             continue;
     152                 :         }
     153                 : 
     154                 :         /*
     155                 :          * The "hasnulls" bit is set when there are some null values in the
     156                 :          * data.  We still need to store a real value, but the presence of
     157                 :          * this means we need a null bitmap.
     158                 :          */
     159           54289 :         if (tuple->bt_columns[keyno].bv_hasnulls)
     160            4779 :             anynulls = true;
     161                 : 
     162                 :         /* If needed, serialize the values before forming the on-disk tuple. */
     163           54289 :         if (tuple->bt_columns[keyno].bv_serialize)
     164                 :         {
     165            8535 :             tuple->bt_columns[keyno].bv_serialize(brdesc,
     166                 :                                                   tuple->bt_columns[keyno].bv_mem_value,
     167                 :                                                   tuple->bt_columns[keyno].bv_values);
     168                 :         }
     169                 : 
     170                 :         /*
     171                 :          * Now obtain the values of each stored datum.  Note that some values
     172                 :          * might be toasted, and we cannot rely on the original heap values
     173                 :          * sticking around forever, so we must detoast them.  Also try to
     174                 :          * compress them.
     175                 :          */
     176           54289 :         for (datumno = 0;
     177          143112 :              datumno < brdesc->bd_info[keyno]->oi_nstored;
     178           88823 :              datumno++)
     179                 :         {
     180           88823 :             Datum       value = tuple->bt_columns[keyno].bv_values[datumno];
     181                 : 
     182                 : #ifdef TOAST_INDEX_HACK
     183                 : 
     184                 :             /* We must look at the stored type, not at the index descriptor. */
     185           88823 :             TypeCacheEntry *atttype = brdesc->bd_info[keyno]->oi_typcache[datumno];
     186                 : 
     187                 :             /* Do we need to free the value at the end? */
     188           88823 :             bool        free_value = false;
     189                 : 
     190                 :             /* For non-varlena types we don't need to do anything special */
     191           88823 :             if (atttype->typlen != -1)
     192                 :             {
     193           47147 :                 values[idxattno++] = value;
     194           47147 :                 continue;
     195                 :             }
     196                 : 
     197                 :             /*
     198                 :              * Do nothing if value is not of varlena type. We don't need to
     199                 :              * care about NULL values here, thanks to bv_allnulls above.
     200                 :              *
     201                 :              * If value is stored EXTERNAL, must fetch it so we are not
     202                 :              * depending on outside storage.
     203                 :              *
     204                 :              * XXX Is this actually true? Could it be that the summary is NULL
     205                 :              * even for range with non-NULL data? E.g. degenerate bloom filter
     206                 :              * may be thrown away, etc.
     207                 :              */
     208           41676 :             if (VARATT_IS_EXTERNAL(DatumGetPointer(value)))
     209                 :             {
     210              12 :                 value = PointerGetDatum(detoast_external_attr((struct varlena *)
     211              12 :                                                               DatumGetPointer(value)));
     212              12 :                 free_value = true;
     213                 :             }
     214                 : 
     215                 :             /*
     216                 :              * If value is above size target, and is of a compressible
     217                 :              * datatype, try to compress it in-line.
     218                 :              */
     219           41676 :             if (!VARATT_IS_EXTENDED(DatumGetPointer(value)) &&
     220           23661 :                 VARSIZE(DatumGetPointer(value)) > TOAST_INDEX_TARGET &&
     221              30 :                 (atttype->typstorage == TYPSTORAGE_EXTENDED ||
     222 UBC           0 :                  atttype->typstorage == TYPSTORAGE_MAIN))
     223                 :             {
     224                 :                 Datum       cvalue;
     225                 :                 char        compression;
     226 CBC          30 :                 Form_pg_attribute att = TupleDescAttr(brdesc->bd_tupdesc,
     227                 :                                                       keyno);
     228                 : 
     229                 :                 /*
     230                 :                  * If the BRIN summary and indexed attribute use the same data
     231                 :                  * type and it has a valid compression method, we can use the
     232                 :                  * same compression method. Otherwise we have to use the
     233                 :                  * default method.
     234                 :                  */
     235              30 :                 if (att->atttypid == atttype->type_id)
     236              24 :                     compression = att->attcompression;
     237                 :                 else
     238               6 :                     compression = InvalidCompressionMethod;
     239                 : 
     240              30 :                 cvalue = toast_compress_datum(value, compression);
     241                 : 
     242              30 :                 if (DatumGetPointer(cvalue) != NULL)
     243                 :                 {
     244                 :                     /* successful compression */
     245               6 :                     if (free_value)
     246 UBC           0 :                         pfree(DatumGetPointer(value));
     247                 : 
     248 CBC           6 :                     value = cvalue;
     249               6 :                     free_value = true;
     250                 :                 }
     251                 :             }
     252                 : 
     253                 :             /*
     254                 :              * If we untoasted / compressed the value, we need to free it
     255                 :              * after forming the index tuple.
     256                 :              */
     257           41676 :             if (free_value)
     258              18 :                 untoasted_values[nuntoasted++] = value;
     259                 : 
     260                 : #endif
     261                 : 
     262           41676 :             values[idxattno++] = value;
     263                 :         }
     264                 :     }
     265                 : 
     266                 :     /* Assert we did not overrun temp arrays */
     267            4269 :     Assert(idxattno <= brdesc->bd_totalstored);
     268                 : 
     269                 :     /* compute total space needed */
     270            4269 :     len = SizeOfBrinTuple;
     271            4269 :     if (anynulls)
     272                 :     {
     273                 :         /*
     274                 :          * We need a double-length bitmap on an on-disk BRIN index tuple; the
     275                 :          * first half stores the "allnulls" bits, the second stores
     276                 :          * "hasnulls".
     277                 :          */
     278             284 :         len += BITMAPLEN(brdesc->bd_tupdesc->natts * 2);
     279                 :     }
     280                 : 
     281            4269 :     len = hoff = MAXALIGN(len);
     282                 : 
     283            4269 :     data_len = heap_compute_data_size(brtuple_disk_tupdesc(brdesc),
     284                 :                                       values, nulls);
     285            4269 :     len += data_len;
     286                 : 
     287            4269 :     len = MAXALIGN(len);
     288                 : 
     289            4269 :     rettuple = palloc0(len);
     290            4269 :     rettuple->bt_blkno = blkno;
     291            4269 :     rettuple->bt_info = hoff;
     292                 : 
     293                 :     /* Assert that hoff fits in the space available */
     294            4269 :     Assert((rettuple->bt_info & BRIN_OFFSET_MASK) == hoff);
     295                 : 
     296                 :     /*
     297                 :      * The infomask and null bitmap as computed by heap_fill_tuple are useless
     298                 :      * to us.  However, that function will not accept a null infomask; and we
     299                 :      * need to pass a valid null bitmap so that it will correctly skip
     300                 :      * outputting null attributes in the data area.
     301                 :      */
     302            4269 :     heap_fill_tuple(brtuple_disk_tupdesc(brdesc),
     303                 :                     values,
     304                 :                     nulls,
     305                 :                     (char *) rettuple + hoff,
     306                 :                     data_len,
     307                 :                     &phony_infomask,
     308                 :                     phony_nullbitmap);
     309                 : 
     310                 :     /* done with these */
     311            4269 :     pfree(values);
     312            4269 :     pfree(nulls);
     313            4269 :     pfree(phony_nullbitmap);
     314                 : 
     315                 : #ifdef TOAST_INDEX_HACK
     316            4287 :     for (i = 0; i < nuntoasted; i++)
     317              18 :         pfree(DatumGetPointer(untoasted_values[i]));
     318                 : #endif
     319                 : 
     320                 :     /*
     321                 :      * Now fill in the real null bitmasks.  allnulls first.
     322                 :      */
     323            4269 :     if (anynulls)
     324                 :     {
     325                 :         bits8      *bitP;
     326                 :         int         bitmask;
     327                 : 
     328             284 :         rettuple->bt_info |= BRIN_NULLS_MASK;
     329                 : 
     330                 :         /*
     331                 :          * Note that we reverse the sense of null bits in this module: we
     332                 :          * store a 1 for a null attribute rather than a 0.  So we must reverse
     333                 :          * the sense of the att_isnull test in brin_deconstruct_tuple as well.
     334                 :          */
     335             284 :         bitP = ((bits8 *) ((char *) rettuple + SizeOfBrinTuple)) - 1;
     336             284 :         bitmask = HIGHBIT;
     337            6029 :         for (keyno = 0; keyno < brdesc->bd_tupdesc->natts; keyno++)
     338                 :         {
     339            5745 :             if (bitmask != HIGHBIT)
     340            4893 :                 bitmask <<= 1;
     341                 :             else
     342                 :             {
     343             852 :                 bitP += 1;
     344             852 :                 *bitP = 0x0;
     345             852 :                 bitmask = 1;
     346                 :             }
     347                 : 
     348            5745 :             if (!tuple->bt_columns[keyno].bv_allnulls)
     349            5448 :                 continue;
     350                 : 
     351             297 :             *bitP |= bitmask;
     352                 :         }
     353                 :         /* hasnulls bits follow */
     354            6029 :         for (keyno = 0; keyno < brdesc->bd_tupdesc->natts; keyno++)
     355                 :         {
     356            5745 :             if (bitmask != HIGHBIT)
     357            5097 :                 bitmask <<= 1;
     358                 :             else
     359                 :             {
     360             648 :                 bitP += 1;
     361             648 :                 *bitP = 0x0;
     362             648 :                 bitmask = 1;
     363                 :             }
     364                 : 
     365            5745 :             if (!tuple->bt_columns[keyno].bv_hasnulls)
     366             777 :                 continue;
     367                 : 
     368            4968 :             *bitP |= bitmask;
     369                 :         }
     370                 :     }
     371                 : 
     372            4269 :     if (tuple->bt_placeholder)
     373 UBC           0 :         rettuple->bt_info |= BRIN_PLACEHOLDER_MASK;
     374                 : 
     375 CBC        4269 :     *size = len;
     376            4269 :     return rettuple;
     377                 : }
     378                 : 
     379                 : /*
     380                 :  * Generate a new on-disk tuple with no data values, marked as placeholder.
     381                 :  *
     382                 :  * This is a cut-down version of brin_form_tuple.
     383                 :  */
     384                 : BrinTuple *
     385            1467 : brin_form_placeholder_tuple(BrinDesc *brdesc, BlockNumber blkno, Size *size)
     386                 : {
     387                 :     Size        len;
     388                 :     Size        hoff;
     389                 :     BrinTuple  *rettuple;
     390                 :     int         keyno;
     391                 :     bits8      *bitP;
     392                 :     int         bitmask;
     393                 : 
     394                 :     /* compute total space needed: always add nulls */
     395            1467 :     len = SizeOfBrinTuple;
     396            1467 :     len += BITMAPLEN(brdesc->bd_tupdesc->natts * 2);
     397            1467 :     len = hoff = MAXALIGN(len);
     398                 : 
     399            1467 :     rettuple = palloc0(len);
     400            1467 :     rettuple->bt_blkno = blkno;
     401            1467 :     rettuple->bt_info = hoff;
     402            1467 :     rettuple->bt_info |= BRIN_NULLS_MASK | BRIN_PLACEHOLDER_MASK;
     403                 : 
     404            1467 :     bitP = ((bits8 *) ((char *) rettuple + SizeOfBrinTuple)) - 1;
     405            1467 :     bitmask = HIGHBIT;
     406                 :     /* set allnulls true for all attributes */
     407            4110 :     for (keyno = 0; keyno < brdesc->bd_tupdesc->natts; keyno++)
     408                 :     {
     409            2643 :         if (bitmask != HIGHBIT)
     410            1059 :             bitmask <<= 1;
     411                 :         else
     412                 :         {
     413            1584 :             bitP += 1;
     414            1584 :             *bitP = 0x0;
     415            1584 :             bitmask = 1;
     416                 :         }
     417                 : 
     418            2643 :         *bitP |= bitmask;
     419                 :     }
     420                 :     /* no need to set hasnulls */
     421                 : 
     422            1467 :     *size = len;
     423            1467 :     return rettuple;
     424                 : }
     425                 : 
     426                 : /*
     427                 :  * Free a tuple created by brin_form_tuple
     428                 :  */
     429                 : void
     430            2934 : brin_free_tuple(BrinTuple *tuple)
     431                 : {
     432            2934 :     pfree(tuple);
     433            2934 : }
     434                 : 
     435                 : /*
     436                 :  * Given a brin tuple of size len, create a copy of it.  If 'dest' is not
     437                 :  * NULL, its size is destsz, and can be used as output buffer; if the tuple
     438                 :  * to be copied does not fit, it is enlarged by repalloc, and the size is
     439                 :  * updated to match.  This avoids palloc/free cycles when many brin tuples
     440                 :  * are being processed in loops.
     441                 :  */
     442                 : BrinTuple *
     443           95896 : brin_copy_tuple(BrinTuple *tuple, Size len, BrinTuple *dest, Size *destsz)
     444                 : {
     445           95896 :     if (!destsz || *destsz == 0)
     446           95896 :         dest = palloc(len);
     447 UBC           0 :     else if (len > *destsz)
     448                 :     {
     449               0 :         dest = repalloc(dest, len);
     450               0 :         *destsz = len;
     451                 :     }
     452                 : 
     453 CBC       95896 :     memcpy(dest, tuple, len);
     454                 : 
     455           95896 :     return dest;
     456                 : }
     457                 : 
     458                 : /*
     459                 :  * Return whether two BrinTuples are bitwise identical.
     460                 :  */
     461                 : bool
     462            3436 : brin_tuples_equal(const BrinTuple *a, Size alen, const BrinTuple *b, Size blen)
     463                 : {
     464            3436 :     if (alen != blen)
     465 UBC           0 :         return false;
     466 CBC        3436 :     if (memcmp(a, b, alen) != 0)
     467 UBC           0 :         return false;
     468 CBC        3436 :     return true;
     469                 : }
     470                 : 
     471                 : /*
     472                 :  * Create a new BrinMemTuple from scratch, and initialize it to an empty
     473                 :  * state.
     474                 :  *
     475                 :  * Note: we don't provide any means to free a deformed tuple, so make sure to
     476                 :  * use a temporary memory context.
     477                 :  */
     478                 : BrinMemTuple *
     479            9555 : brin_new_memtuple(BrinDesc *brdesc)
     480                 : {
     481                 :     BrinMemTuple *dtup;
     482                 :     long        basesize;
     483                 : 
     484            9555 :     basesize = MAXALIGN(sizeof(BrinMemTuple) +
     485                 :                         sizeof(BrinValues) * brdesc->bd_tupdesc->natts);
     486            9555 :     dtup = palloc0(basesize + sizeof(Datum) * brdesc->bd_totalstored);
     487                 : 
     488            9555 :     dtup->bt_values = palloc(sizeof(Datum) * brdesc->bd_totalstored);
     489            9555 :     dtup->bt_allnulls = palloc(sizeof(bool) * brdesc->bd_tupdesc->natts);
     490            9555 :     dtup->bt_hasnulls = palloc(sizeof(bool) * brdesc->bd_tupdesc->natts);
     491                 : 
     492            9555 :     dtup->bt_context = AllocSetContextCreate(CurrentMemoryContext,
     493                 :                                              "brin dtuple",
     494                 :                                              ALLOCSET_DEFAULT_SIZES);
     495                 : 
     496            9555 :     brin_memtuple_initialize(dtup, brdesc);
     497                 : 
     498            9555 :     return dtup;
     499                 : }
     500                 : 
     501                 : /*
     502                 :  * Reset a BrinMemTuple to initial state.  We return the same tuple, for
     503                 :  * notational convenience.
     504                 :  */
     505                 : BrinMemTuple *
     506          105954 : brin_memtuple_initialize(BrinMemTuple *dtuple, BrinDesc *brdesc)
     507                 : {
     508                 :     int         i;
     509                 :     char       *currdatum;
     510                 : 
     511          105954 :     MemoryContextReset(dtuple->bt_context);
     512                 : 
     513          105954 :     currdatum = (char *) dtuple +
     514          105954 :         MAXALIGN(sizeof(BrinMemTuple) +
     515                 :                  sizeof(BrinValues) * brdesc->bd_tupdesc->natts);
     516         2839060 :     for (i = 0; i < brdesc->bd_tupdesc->natts; i++)
     517                 :     {
     518         2733106 :         dtuple->bt_columns[i].bv_attno = i + 1;
     519         2733106 :         dtuple->bt_columns[i].bv_allnulls = true;
     520         2733106 :         dtuple->bt_columns[i].bv_hasnulls = false;
     521         2733106 :         dtuple->bt_columns[i].bv_values = (Datum *) currdatum;
     522                 : 
     523         2733106 :         dtuple->bt_columns[i].bv_mem_value = PointerGetDatum(NULL);
     524         2733106 :         dtuple->bt_columns[i].bv_serialize = NULL;
     525         2733106 :         dtuple->bt_columns[i].bv_context = dtuple->bt_context;
     526                 : 
     527         2733106 :         currdatum += sizeof(Datum) * brdesc->bd_info[i]->oi_nstored;
     528                 :     }
     529                 : 
     530          105954 :     return dtuple;
     531                 : }
     532                 : 
     533                 : /*
     534                 :  * Convert a BrinTuple back to a BrinMemTuple.  This is the reverse of
     535                 :  * brin_form_tuple.
     536                 :  *
     537                 :  * As an optimization, the caller can pass a previously allocated 'dMemtuple'.
     538                 :  * This avoids having to allocate it here, which can be useful when this
     539                 :  * function is called many times in a loop.  It is caller's responsibility
     540                 :  * that the given BrinMemTuple matches what we need here.
     541                 :  *
     542                 :  * Note we don't need the "on disk tupdesc" here; we rely on our own routine to
     543                 :  * deconstruct the tuple from the on-disk format.
     544                 :  */
     545                 : BrinMemTuple *
     546          102034 : brin_deform_tuple(BrinDesc *brdesc, BrinTuple *tuple, BrinMemTuple *dMemtuple)
     547                 : {
     548                 :     BrinMemTuple *dtup;
     549                 :     Datum      *values;
     550                 :     bool       *allnulls;
     551                 :     bool       *hasnulls;
     552                 :     char       *tp;
     553                 :     bits8      *nullbits;
     554                 :     int         keyno;
     555                 :     int         valueno;
     556                 :     MemoryContext oldcxt;
     557                 : 
     558          102034 :     dtup = dMemtuple ? brin_memtuple_initialize(dMemtuple, brdesc) :
     559            8107 :         brin_new_memtuple(brdesc);
     560                 : 
     561          102034 :     if (BrinTupleIsPlaceholder(tuple))
     562 UBC           0 :         dtup->bt_placeholder = true;
     563 CBC      102034 :     dtup->bt_blkno = tuple->bt_blkno;
     564                 : 
     565          102034 :     values = dtup->bt_values;
     566          102034 :     allnulls = dtup->bt_allnulls;
     567          102034 :     hasnulls = dtup->bt_hasnulls;
     568                 : 
     569          102034 :     tp = (char *) tuple + BrinTupleDataOffset(tuple);
     570                 : 
     571          102034 :     if (BrinTupleHasNulls(tuple))
     572           11511 :         nullbits = (bits8 *) ((char *) tuple + SizeOfBrinTuple);
     573                 :     else
     574           90523 :         nullbits = NULL;
     575          102034 :     brin_deconstruct_tuple(brdesc,
     576          102034 :                            tp, nullbits, BrinTupleHasNulls(tuple),
     577                 :                            values, allnulls, hasnulls);
     578                 : 
     579                 :     /*
     580                 :      * Iterate to assign each of the values to the corresponding item in the
     581                 :      * values array of each column.  The copies occur in the tuple's context.
     582                 :      */
     583          102034 :     oldcxt = MemoryContextSwitchTo(dtup->bt_context);
     584         2783495 :     for (valueno = 0, keyno = 0; keyno < brdesc->bd_tupdesc->natts; keyno++)
     585                 :     {
     586                 :         int         i;
     587                 : 
     588         2681461 :         if (allnulls[keyno])
     589                 :         {
     590            5508 :             valueno += brdesc->bd_info[keyno]->oi_nstored;
     591            5508 :             continue;
     592                 :         }
     593                 : 
     594                 :         /*
     595                 :          * We would like to skip datumCopy'ing the values datum in some cases,
     596                 :          * caller permitting ...
     597                 :          */
     598         7909500 :         for (i = 0; i < brdesc->bd_info[keyno]->oi_nstored; i++)
     599         5233547 :             dtup->bt_columns[keyno].bv_values[i] =
     600         5233547 :                 datumCopy(values[valueno++],
     601         5233547 :                           brdesc->bd_info[keyno]->oi_typcache[i]->typbyval,
     602         5233547 :                           brdesc->bd_info[keyno]->oi_typcache[i]->typlen);
     603                 : 
     604         2675953 :         dtup->bt_columns[keyno].bv_hasnulls = hasnulls[keyno];
     605         2675953 :         dtup->bt_columns[keyno].bv_allnulls = false;
     606                 : 
     607         2675953 :         dtup->bt_columns[keyno].bv_mem_value = PointerGetDatum(NULL);
     608         2675953 :         dtup->bt_columns[keyno].bv_serialize = NULL;
     609         2675953 :         dtup->bt_columns[keyno].bv_context = dtup->bt_context;
     610                 :     }
     611                 : 
     612          102034 :     MemoryContextSwitchTo(oldcxt);
     613                 : 
     614          102034 :     return dtup;
     615                 : }
     616                 : 
     617                 : /*
     618                 :  * brin_deconstruct_tuple
     619                 :  *      Guts of attribute extraction from an on-disk BRIN tuple.
     620                 :  *
     621                 :  * Its arguments are:
     622                 :  *  brdesc      BRIN descriptor for the stored tuple
     623                 :  *  tp          pointer to the tuple data area
     624                 :  *  nullbits    pointer to the tuple nulls bitmask
     625                 :  *  nulls       "has nulls" bit in tuple infomask
     626                 :  *  values      output values, array of size brdesc->bd_totalstored
     627                 :  *  allnulls    output "allnulls", size brdesc->bd_tupdesc->natts
     628                 :  *  hasnulls    output "hasnulls", size brdesc->bd_tupdesc->natts
     629                 :  *
     630                 :  * Output arrays must have been allocated by caller.
     631                 :  */
     632                 : static inline void
     633          102034 : brin_deconstruct_tuple(BrinDesc *brdesc,
     634                 :                        char *tp, bits8 *nullbits, bool nulls,
     635                 :                        Datum *values, bool *allnulls, bool *hasnulls)
     636                 : {
     637                 :     int         attnum;
     638                 :     int         stored;
     639                 :     TupleDesc   diskdsc;
     640                 :     long        off;
     641                 : 
     642                 :     /*
     643                 :      * First iterate to natts to obtain both null flags for each attribute.
     644                 :      * Note that we reverse the sense of the att_isnull test, because we store
     645                 :      * 1 for a null value (rather than a 1 for a not null value as is the
     646                 :      * att_isnull convention used elsewhere.)  See brin_form_tuple.
     647                 :      */
     648         2783495 :     for (attnum = 0; attnum < brdesc->bd_tupdesc->natts; attnum++)
     649                 :     {
     650                 :         /*
     651                 :          * the "all nulls" bit means that all values in the page range for
     652                 :          * this column are nulls.  Therefore there are no values in the tuple
     653                 :          * data area.
     654                 :          */
     655         2681461 :         allnulls[attnum] = nulls && !att_isnull(attnum, nullbits);
     656                 : 
     657                 :         /*
     658                 :          * the "has nulls" bit means that some tuples have nulls, but others
     659                 :          * have not-null values.  Therefore we know the tuple contains data
     660                 :          * for this column.
     661                 :          *
     662                 :          * The hasnulls bits follow the allnulls bits in the same bitmask.
     663                 :          */
     664         2681461 :         hasnulls[attnum] =
     665         2681461 :             nulls && !att_isnull(brdesc->bd_tupdesc->natts + attnum, nullbits);
     666                 :     }
     667                 : 
     668                 :     /*
     669                 :      * Iterate to obtain each attribute's stored values.  Note that since we
     670                 :      * may reuse attribute entries for more than one column, we cannot cache
     671                 :      * offsets here.
     672                 :      */
     673          102034 :     diskdsc = brtuple_disk_tupdesc(brdesc);
     674          102034 :     stored = 0;
     675          102034 :     off = 0;
     676         2783495 :     for (attnum = 0; attnum < brdesc->bd_tupdesc->natts; attnum++)
     677                 :     {
     678                 :         int         datumno;
     679                 : 
     680         2681461 :         if (allnulls[attnum])
     681                 :         {
     682            5508 :             stored += brdesc->bd_info[attnum]->oi_nstored;
     683            5508 :             continue;
     684                 :         }
     685                 : 
     686         2675953 :         for (datumno = 0;
     687         7909500 :              datumno < brdesc->bd_info[attnum]->oi_nstored;
     688         5233547 :              datumno++)
     689                 :         {
     690         5233547 :             Form_pg_attribute thisatt = TupleDescAttr(diskdsc, stored);
     691                 : 
     692         5233547 :             if (thisatt->attlen == -1)
     693                 :             {
     694         1844085 :                 off = att_align_pointer(off, thisatt->attalign, -1,
     695                 :                                         tp + off);
     696                 :             }
     697                 :             else
     698                 :             {
     699                 :                 /* not varlena, so safe to use att_align_nominal */
     700         3389462 :                 off = att_align_nominal(off, thisatt->attalign);
     701                 :             }
     702                 : 
     703         5233547 :             values[stored++] = fetchatt(thisatt, tp + off);
     704                 : 
     705         5233547 :             off = att_addlength_pointer(off, thisatt->attlen, tp + off);
     706                 :         }
     707                 :     }
     708          102034 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a