LCOV - code coverage report
Current view: top level - src/backend/access/brin - brin_tuple.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 96.0 % 224 215
Test Date: 2026-09-26 12:15:43 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 89.5 % 114 102

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

Generated by: LCOV version 2.0-1