LCOV - code coverage report
Current view: top level - src/backend/access/brin - brin_tuple.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 96.7 % 306 296
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 85.2 % 128 109

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

Generated by: LCOV version 2.3.2-1