LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistutil.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 88.7 % 461 409
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 29 29
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 64.5 % 228 147

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * gistutil.c
       4                 :             :  *        utilities routines for the postgres GiST index access method.
       5                 :             :  *
       6                 :             :  *
       7                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *                      src/backend/access/gist/gistutil.c
      12                 :             :  *-------------------------------------------------------------------------
      13                 :             :  */
      14                 :             : #include "postgres.h"
      15                 :             : 
      16                 :             : #include <math.h>
      17                 :             : 
      18                 :             : #include "access/gist_private.h"
      19                 :             : #include "access/htup_details.h"
      20                 :             : #include "access/reloptions.h"
      21                 :             : #include "common/pg_prng.h"
      22                 :             : #include "storage/indexfsm.h"
      23                 :             : #include "utils/float.h"
      24                 :             : #include "utils/fmgrprotos.h"
      25                 :             : #include "utils/lsyscache.h"
      26                 :             : #include "utils/rel.h"
      27                 :             : #include "utils/snapmgr.h"
      28                 :             : #include "utils/syscache.h"
      29                 :             : 
      30                 :             : /*
      31                 :             :  * Write itup vector to page, has no control of free space.
      32                 :             :  */
      33                 :             : void
      34                 :      148581 : gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
      35                 :             : {
      36                 :      148581 :         int                     i;
      37                 :             : 
      38         [ -  + ]:      148581 :         if (off == InvalidOffsetNumber)
      39         [ +  + ]:      148581 :                 off = (PageIsEmpty(page)) ? FirstOffsetNumber :
      40                 :      148267 :                         OffsetNumberNext(PageGetMaxOffsetNumber(page));
      41                 :             : 
      42         [ +  + ]:      298798 :         for (i = 0; i < len; i++)
      43                 :             :         {
      44                 :      150217 :                 Size            sz = IndexTupleSize(itup[i]);
      45                 :      150217 :                 OffsetNumber l;
      46                 :             : 
      47                 :      150217 :                 l = PageAddItem(page, itup[i], sz, off, false, false);
      48         [ +  - ]:      150217 :                 if (l == InvalidOffsetNumber)
      49   [ #  #  #  # ]:           0 :                         elog(ERROR, "failed to add item to GiST index page, item %d out of %d, size %zu bytes",
      50                 :             :                                  i, len, sz);
      51                 :      150217 :                 off++;
      52                 :      150217 :         }
      53                 :      148581 : }
      54                 :             : 
      55                 :             : /*
      56                 :             :  * Check space for itup vector on page
      57                 :             :  */
      58                 :             : bool
      59                 :      202698 : gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
      60                 :             : {
      61                 :      202698 :         unsigned int size = freespace,
      62                 :      202698 :                                 deleted = 0;
      63                 :      202698 :         int                     i;
      64                 :             : 
      65         [ +  + ]:      407045 :         for (i = 0; i < len; i++)
      66                 :      204347 :                 size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
      67                 :             : 
      68         [ +  + ]:      202698 :         if (todelete != InvalidOffsetNumber)
      69                 :             :         {
      70                 :       92619 :                 IndexTuple      itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, todelete));
      71                 :             : 
      72                 :       92619 :                 deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
      73                 :       92619 :         }
      74                 :             : 
      75                 :      405396 :         return (PageGetFreeSpace(page) + deleted < size);
      76                 :      202698 : }
      77                 :             : 
      78                 :             : bool
      79                 :        3762 : gistfitpage(IndexTuple *itvec, int len)
      80                 :             : {
      81                 :        3762 :         int                     i;
      82                 :        3762 :         Size            size = 0;
      83                 :             : 
      84         [ +  + ]:      316157 :         for (i = 0; i < len; i++)
      85                 :      312395 :                 size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
      86                 :             : 
      87                 :             :         /* TODO: Consider fillfactor */
      88                 :        7524 :         return (size <= GiSTPageSize);
      89                 :        3762 : }
      90                 :             : 
      91                 :             : /*
      92                 :             :  * Read buffer into itup vector
      93                 :             :  */
      94                 :             : IndexTuple *
      95                 :        1871 : gistextractpage(Page page, int *len /* out */ )
      96                 :             : {
      97                 :        1871 :         OffsetNumber i,
      98                 :             :                                 maxoff;
      99                 :        1871 :         IndexTuple *itvec;
     100                 :             : 
     101                 :        1871 :         maxoff = PageGetMaxOffsetNumber(page);
     102                 :        1871 :         *len = maxoff;
     103                 :        1871 :         itvec = palloc_array(IndexTuple, maxoff);
     104         [ +  + ]:      259491 :         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     105                 :      257620 :                 itvec[i - FirstOffsetNumber] = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
     106                 :             : 
     107                 :        3742 :         return itvec;
     108                 :        1871 : }
     109                 :             : 
     110                 :             : /*
     111                 :             :  * join two vectors into one
     112                 :             :  */
     113                 :             : IndexTuple *
     114                 :        1819 : gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
     115                 :             : {
     116                 :        1819 :         itvec = repalloc_array(itvec, IndexTuple, (*len) + addlen);
     117                 :        1819 :         memmove(&itvec[*len], additvec, sizeof(IndexTuple) * addlen);
     118                 :        1819 :         *len += addlen;
     119                 :        1819 :         return itvec;
     120                 :             : }
     121                 :             : 
     122                 :             : /*
     123                 :             :  * make plain IndexTuple vector
     124                 :             :  */
     125                 :             : 
     126                 :             : IndexTupleData *
     127                 :        3632 : gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
     128                 :             : {
     129                 :        3632 :         char       *ptr,
     130                 :             :                            *ret;
     131                 :        3632 :         int                     i;
     132                 :             : 
     133                 :        3632 :         *memlen = 0;
     134                 :             : 
     135         [ +  + ]:      262976 :         for (i = 0; i < veclen; i++)
     136                 :      259344 :                 *memlen += IndexTupleSize(vec[i]);
     137                 :             : 
     138                 :        3632 :         ptr = ret = palloc(*memlen);
     139                 :             : 
     140         [ +  + ]:      262976 :         for (i = 0; i < veclen; i++)
     141                 :             :         {
     142                 :      259344 :                 memcpy(ptr, vec[i], IndexTupleSize(vec[i]));
     143                 :      259344 :                 ptr += IndexTupleSize(vec[i]);
     144                 :      259344 :         }
     145                 :             : 
     146                 :        7264 :         return (IndexTupleData *) ret;
     147                 :        3632 : }
     148                 :             : 
     149                 :             : /*
     150                 :             :  * Make unions of keys in IndexTuple vector (one union datum per index column).
     151                 :             :  * Union Datums are returned into the attr/isnull arrays.
     152                 :             :  * Resulting Datums aren't compressed.
     153                 :             :  */
     154                 :             : void
     155                 :         854 : gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
     156                 :             :                                    Datum *attr, bool *isnull)
     157                 :             : {
     158                 :         854 :         int                     i;
     159                 :         854 :         GistEntryVector *evec;
     160                 :         854 :         int                     attrsize = 0;   /* silence compiler warning */
     161                 :             : 
     162                 :         854 :         evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
     163                 :             : 
     164         [ +  + ]:        2548 :         for (i = 0; i < giststate->nonLeafTupdesc->natts; i++)
     165                 :             :         {
     166                 :        1694 :                 int                     j;
     167                 :             : 
     168                 :             :                 /* Collect non-null datums for this column */
     169                 :        1694 :                 evec->n = 0;
     170         [ +  + ]:       84333 :                 for (j = 0; j < len; j++)
     171                 :             :                 {
     172                 :       82639 :                         Datum           datum;
     173                 :       82639 :                         bool            IsNull;
     174                 :             : 
     175                 :       82639 :                         datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc,
     176                 :             :                                                                   &IsNull);
     177         [ +  + ]:       82639 :                         if (IsNull)
     178                 :          72 :                                 continue;
     179                 :             : 
     180                 :      165134 :                         gistdentryinit(giststate, i,
     181                 :       82567 :                                                    evec->vector + evec->n,
     182                 :       82567 :                                                    datum,
     183                 :             :                                                    NULL, NULL, (OffsetNumber) 0,
     184                 :       82567 :                                                    false, IsNull);
     185                 :       82567 :                         evec->n++;
     186      [ -  +  + ]:       82639 :                 }
     187                 :             : 
     188                 :             :                 /* If this column was all NULLs, the union is NULL */
     189         [ +  + ]:        1694 :                 if (evec->n == 0)
     190                 :             :                 {
     191                 :           6 :                         attr[i] = (Datum) 0;
     192                 :           6 :                         isnull[i] = true;
     193                 :           6 :                 }
     194                 :             :                 else
     195                 :             :                 {
     196         [ +  - ]:        1688 :                         if (evec->n == 1)
     197                 :             :                         {
     198                 :             :                                 /* unionFn may expect at least two inputs */
     199                 :           0 :                                 evec->n = 2;
     200                 :           0 :                                 evec->vector[1] = evec->vector[0];
     201                 :           0 :                         }
     202                 :             : 
     203                 :             :                         /* Make union and store in attr array */
     204                 :        3376 :                         attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
     205                 :        1688 :                                                                                 giststate->supportCollation[i],
     206                 :        1688 :                                                                                 PointerGetDatum(evec),
     207                 :        1688 :                                                                                 PointerGetDatum(&attrsize));
     208                 :             : 
     209                 :        1688 :                         isnull[i] = false;
     210                 :             :                 }
     211                 :        1694 :         }
     212                 :         854 : }
     213                 :             : 
     214                 :             : /*
     215                 :             :  * Return an IndexTuple containing the result of applying the "union"
     216                 :             :  * method to the specified IndexTuple vector.
     217                 :             :  */
     218                 :             : IndexTuple
     219                 :           2 : gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
     220                 :             : {
     221                 :           2 :         Datum           attr[INDEX_MAX_KEYS];
     222                 :           2 :         bool            isnull[INDEX_MAX_KEYS];
     223                 :             : 
     224                 :           2 :         gistMakeUnionItVec(giststate, itvec, len, attr, isnull);
     225                 :             : 
     226                 :           4 :         return gistFormTuple(giststate, r, attr, isnull, false);
     227                 :           2 : }
     228                 :             : 
     229                 :             : /*
     230                 :             :  * makes union of two key
     231                 :             :  */
     232                 :             : void
     233                 :      163738 : gistMakeUnionKey(GISTSTATE *giststate, int attno,
     234                 :             :                                  GISTENTRY *entry1, bool isnull1,
     235                 :             :                                  GISTENTRY *entry2, bool isnull2,
     236                 :             :                                  Datum *dst, bool *dstisnull)
     237                 :             : {
     238                 :             :         /* we need a GistEntryVector with room for exactly 2 elements */
     239                 :      163738 :         union
     240                 :             :         {
     241                 :             :                 GistEntryVector gev;
     242                 :             :                 char            padding[2 * sizeof(GISTENTRY) + GEVHDRSZ];
     243                 :             :         }                       storage;
     244                 :      163738 :         GistEntryVector *evec = &storage.gev;
     245                 :      163738 :         int                     dstsize = 0;    /* silence compiler warning */
     246                 :             : 
     247                 :      163738 :         evec->n = 2;
     248                 :             : 
     249   [ +  +  -  + ]:      163738 :         if (isnull1 && isnull2)
     250                 :             :         {
     251                 :        1596 :                 *dstisnull = true;
     252                 :        1596 :                 *dst = (Datum) 0;
     253                 :        1596 :         }
     254                 :             :         else
     255                 :             :         {
     256   [ +  -  -  + ]:      162142 :                 if (isnull1 == false && isnull2 == false)
     257                 :             :                 {
     258                 :      162142 :                         evec->vector[0] = *entry1;
     259                 :      162142 :                         evec->vector[1] = *entry2;
     260                 :      162142 :                 }
     261         [ #  # ]:           0 :                 else if (isnull1 == false)
     262                 :             :                 {
     263                 :           0 :                         evec->vector[0] = *entry1;
     264                 :           0 :                         evec->vector[1] = *entry1;
     265                 :           0 :                 }
     266                 :             :                 else
     267                 :             :                 {
     268                 :           0 :                         evec->vector[0] = *entry2;
     269                 :           0 :                         evec->vector[1] = *entry2;
     270                 :             :                 }
     271                 :             : 
     272                 :      162142 :                 *dstisnull = false;
     273                 :      324284 :                 *dst = FunctionCall2Coll(&giststate->unionFn[attno],
     274                 :      162142 :                                                                  giststate->supportCollation[attno],
     275                 :      162142 :                                                                  PointerGetDatum(evec),
     276                 :      162142 :                                                                  PointerGetDatum(&dstsize));
     277                 :             :         }
     278                 :      163738 : }
     279                 :             : 
     280                 :             : bool
     281                 :      132066 : gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
     282                 :             : {
     283                 :      132066 :         bool            result = false; /* silence compiler warning */
     284                 :             : 
     285                 :      264132 :         FunctionCall3Coll(&giststate->equalFn[attno],
     286                 :      132066 :                                           giststate->supportCollation[attno],
     287                 :      132066 :                                           a, b,
     288                 :      132066 :                                           PointerGetDatum(&result));
     289                 :      264132 :         return result;
     290                 :      132066 : }
     291                 :             : 
     292                 :             : /*
     293                 :             :  * Decompress all keys in tuple
     294                 :             :  */
     295                 :             : void
     296                 :      404686 : gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
     297                 :             :                                   OffsetNumber o, GISTENTRY *attdata, bool *isnull)
     298                 :             : {
     299                 :      404686 :         int                     i;
     300                 :             : 
     301         [ +  + ]:      900860 :         for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
     302                 :             :         {
     303                 :      496174 :                 Datum           datum;
     304                 :             : 
     305                 :      496174 :                 datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
     306                 :      992348 :                 gistdentryinit(giststate, i, &attdata[i],
     307                 :      496174 :                                            datum, r, p, o,
     308                 :      496174 :                                            false, isnull[i]);
     309                 :      496174 :         }
     310                 :      404686 : }
     311                 :             : 
     312                 :             : /*
     313                 :             :  * Forms union of oldtup and addtup, if union == oldtup then return NULL
     314                 :             :  */
     315                 :             : IndexTuple
     316                 :      133242 : gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
     317                 :             : {
     318                 :      133242 :         bool            neednew = false;
     319                 :      133242 :         GISTENTRY       oldentries[INDEX_MAX_KEYS],
     320                 :             :                                 addentries[INDEX_MAX_KEYS];
     321                 :      133242 :         bool            oldisnull[INDEX_MAX_KEYS],
     322                 :             :                                 addisnull[INDEX_MAX_KEYS];
     323                 :      133242 :         Datum           attr[INDEX_MAX_KEYS];
     324                 :      133242 :         bool            isnull[INDEX_MAX_KEYS];
     325                 :      133242 :         IndexTuple      newtup = NULL;
     326                 :      133242 :         int                     i;
     327                 :             : 
     328                 :      266484 :         gistDeCompressAtt(giststate, r, oldtup, NULL,
     329                 :      133242 :                                           (OffsetNumber) 0, oldentries, oldisnull);
     330                 :             : 
     331                 :      266484 :         gistDeCompressAtt(giststate, r, addtup, NULL,
     332                 :      133242 :                                           (OffsetNumber) 0, addentries, addisnull);
     333                 :             : 
     334         [ +  + ]:      296980 :         for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
     335                 :             :         {
     336                 :      327476 :                 gistMakeUnionKey(giststate, i,
     337                 :      163738 :                                                  oldentries + i, oldisnull[i],
     338                 :      163738 :                                                  addentries + i, addisnull[i],
     339                 :      163738 :                                                  attr + i, isnull + i);
     340                 :             : 
     341         [ +  + ]:      163738 :                 if (neednew)
     342                 :             :                         /* we already need new key, so we can skip check */
     343                 :       30496 :                         continue;
     344                 :             : 
     345         [ +  + ]:      133242 :                 if (isnull[i])
     346                 :             :                         /* union of key may be NULL if and only if both keys are NULL */
     347                 :        1596 :                         continue;
     348                 :             : 
     349         [ -  + ]:      131646 :                 if (!addisnull[i])
     350                 :             :                 {
     351   [ +  -  +  + ]:      131646 :                         if (oldisnull[i] ||
     352                 :      131646 :                                 !gistKeyIsEQ(giststate, i, oldentries[i].key, attr[i]))
     353                 :       95926 :                                 neednew = true;
     354                 :      131646 :                 }
     355                 :      131646 :         }
     356                 :             : 
     357         [ +  + ]:      133242 :         if (neednew)
     358                 :             :         {
     359                 :             :                 /* need to update key */
     360                 :       95926 :                 newtup = gistFormTuple(giststate, r, attr, isnull, false);
     361                 :       95926 :                 newtup->t_tid = oldtup->t_tid;
     362                 :       95926 :         }
     363                 :             : 
     364                 :      266484 :         return newtup;
     365                 :      133242 : }
     366                 :             : 
     367                 :             : /*
     368                 :             :  * Search an upper index page for the entry with lowest penalty for insertion
     369                 :             :  * of the new index key contained in "it".
     370                 :             :  *
     371                 :             :  * Returns the index of the page entry to insert into.
     372                 :             :  */
     373                 :             : OffsetNumber
     374                 :      128285 : gistchoose(Relation r, Page p, IndexTuple it,   /* it has compressed entry */
     375                 :             :                    GISTSTATE *giststate)
     376                 :             : {
     377                 :      128285 :         OffsetNumber result;
     378                 :      128285 :         OffsetNumber maxoff;
     379                 :      128285 :         OffsetNumber i;
     380                 :      128285 :         float           best_penalty[INDEX_MAX_KEYS];
     381                 :      128285 :         GISTENTRY       entry,
     382                 :             :                                 identry[INDEX_MAX_KEYS];
     383                 :      128285 :         bool            isnull[INDEX_MAX_KEYS];
     384                 :      128285 :         int                     keep_current_best;
     385                 :             : 
     386         [ +  - ]:      128285 :         Assert(!GistPageIsLeaf(p));
     387                 :             : 
     388                 :      256570 :         gistDeCompressAtt(giststate, r,
     389                 :      128285 :                                           it, NULL, (OffsetNumber) 0,
     390                 :      128285 :                                           identry, isnull);
     391                 :             : 
     392                 :             :         /* we'll return FirstOffsetNumber if page is empty (shouldn't happen) */
     393                 :      128285 :         result = FirstOffsetNumber;
     394                 :             : 
     395                 :             :         /*
     396                 :             :          * The index may have multiple columns, and there's a penalty value for
     397                 :             :          * each column.  The penalty associated with a column that appears earlier
     398                 :             :          * in the index definition is strictly more important than the penalty of
     399                 :             :          * a column that appears later in the index definition.
     400                 :             :          *
     401                 :             :          * best_penalty[j] is the best penalty we have seen so far for column j,
     402                 :             :          * or -1 when we haven't yet examined column j.  Array entries to the
     403                 :             :          * right of the first -1 are undefined.
     404                 :             :          */
     405                 :      128285 :         best_penalty[0] = -1;
     406                 :             : 
     407                 :             :         /*
     408                 :             :          * If we find a tuple that's exactly as good as the currently best one, we
     409                 :             :          * could use either one.  When inserting a lot of tuples with the same or
     410                 :             :          * similar keys, it's preferable to descend down the same path when
     411                 :             :          * possible, as that's more cache-friendly.  On the other hand, if all
     412                 :             :          * inserts land on the same leaf page after a split, we're never going to
     413                 :             :          * insert anything to the other half of the split, and will end up using
     414                 :             :          * only 50% of the available space.  Distributing the inserts evenly would
     415                 :             :          * lead to better space usage, but that hurts cache-locality during
     416                 :             :          * insertion.  To get the best of both worlds, when we find a tuple that's
     417                 :             :          * exactly as good as the previous best, choose randomly whether to stick
     418                 :             :          * to the old best, or use the new one.  Once we decide to stick to the
     419                 :             :          * old best, we keep sticking to it for any subsequent equally good tuples
     420                 :             :          * we might find.  This favors tuples with low offsets, but still allows
     421                 :             :          * some inserts to go to other equally-good subtrees.
     422                 :             :          *
     423                 :             :          * keep_current_best is -1 if we haven't yet had to make a random choice
     424                 :             :          * whether to keep the current best tuple.  If we have done so, and
     425                 :             :          * decided to keep it, keep_current_best is 1; if we've decided to
     426                 :             :          * replace, keep_current_best is 0.  (This state will be reset to -1 as
     427                 :             :          * soon as we've made the replacement, but sometimes we make the choice in
     428                 :             :          * advance of actually finding a replacement best tuple.)
     429                 :             :          */
     430                 :      128285 :         keep_current_best = -1;
     431                 :             : 
     432                 :             :         /*
     433                 :             :          * Loop over tuples on page.
     434                 :             :          */
     435                 :      128285 :         maxoff = PageGetMaxOffsetNumber(p);
     436         [ +  - ]:      128285 :         Assert(maxoff >= FirstOffsetNumber);
     437                 :             : 
     438         [ +  + ]:     5655783 :         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     439                 :             :         {
     440                 :     5547219 :                 IndexTuple      itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
     441                 :     5547219 :                 bool            zero_penalty;
     442                 :     5547219 :                 int                     j;
     443                 :             : 
     444                 :     5547219 :                 zero_penalty = true;
     445                 :             : 
     446                 :             :                 /* Loop over index attributes. */
     447         [ +  + ]:    11434924 :                 for (j = 0; j < IndexRelationGetNumberOfKeyAttributes(r); j++)
     448                 :             :                 {
     449                 :     6773875 :                         Datum           datum;
     450                 :     6773875 :                         float           usize;
     451                 :     6773875 :                         bool            IsNull;
     452                 :             : 
     453                 :             :                         /* Compute penalty for this column. */
     454                 :     6773875 :                         datum = index_getattr(itup, j + 1, giststate->leafTupdesc,
     455                 :             :                                                                   &IsNull);
     456                 :    13547750 :                         gistdentryinit(giststate, j, &entry, datum, r, p, i,
     457                 :     6773875 :                                                    false, IsNull);
     458                 :    13547750 :                         usize = gistpenalty(giststate, j, &entry, IsNull,
     459                 :     6773875 :                                                                 &identry[j], isnull[j]);
     460         [ +  + ]:     6773875 :                         if (usize > 0)
     461                 :     6734318 :                                 zero_penalty = false;
     462                 :             : 
     463   [ +  +  +  + ]:     6773875 :                         if (best_penalty[j] < 0 || usize < best_penalty[j])
     464                 :             :                         {
     465                 :             :                                 /*
     466                 :             :                                  * New best penalty for column.  Tentatively select this tuple
     467                 :             :                                  * as the target, and record the best penalty.  Then reset the
     468                 :             :                                  * next column's penalty to "unknown" (and indirectly, the
     469                 :             :                                  * same for all the ones to its right).  This will force us to
     470                 :             :                                  * adopt this tuple's penalty values as the best for all the
     471                 :             :                                  * remaining columns during subsequent loop iterations.
     472                 :             :                                  */
     473                 :     5849687 :                                 result = i;
     474                 :     5849687 :                                 best_penalty[j] = usize;
     475                 :             : 
     476         [ +  + ]:     5849687 :                                 if (j < IndexRelationGetNumberOfKeyAttributes(r) - 1)
     477                 :     1226656 :                                         best_penalty[j + 1] = -1;
     478                 :             : 
     479                 :             :                                 /* we have new best, so reset keep-it decision */
     480                 :     5849687 :                                 keep_current_best = -1;
     481                 :     5849687 :                         }
     482         [ +  + ]:      924188 :                         else if (best_penalty[j] == usize)
     483                 :             :                         {
     484                 :             :                                 /*
     485                 :             :                                  * The current tuple is exactly as good for this column as the
     486                 :             :                                  * best tuple seen so far.  The next iteration of this loop
     487                 :             :                                  * will compare the next column.
     488                 :             :                                  */
     489                 :       38018 :                         }
     490                 :             :                         else
     491                 :             :                         {
     492                 :             :                                 /*
     493                 :             :                                  * The current tuple is worse for this column than the best
     494                 :             :                                  * tuple seen so far.  Skip the remaining columns and move on
     495                 :             :                                  * to the next tuple, if any.
     496                 :             :                                  */
     497                 :      886170 :                                 zero_penalty = false;   /* so outer loop won't exit */
     498                 :      886170 :                                 break;
     499                 :             :                         }
     500         [ +  + ]:     6773875 :                 }
     501                 :             : 
     502                 :             :                 /*
     503                 :             :                  * If we looped past the last column, and did not update "result",
     504                 :             :                  * then this tuple is exactly as good as the prior best tuple.
     505                 :             :                  */
     506   [ +  +  +  + ]:     5547219 :                 if (j == IndexRelationGetNumberOfKeyAttributes(r) && result != i)
     507                 :             :                 {
     508         [ +  + ]:       38018 :                         if (keep_current_best == -1)
     509                 :             :                         {
     510                 :             :                                 /* we didn't make the random choice yet for this old best */
     511                 :        7236 :                                 keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
     512                 :        7236 :                         }
     513         [ +  + ]:       38018 :                         if (keep_current_best == 0)
     514                 :             :                         {
     515                 :             :                                 /* we choose to use the new tuple */
     516                 :        6362 :                                 result = i;
     517                 :             :                                 /* choose again if there are even more exactly-as-good ones */
     518                 :        6362 :                                 keep_current_best = -1;
     519                 :        6362 :                         }
     520                 :       38018 :                 }
     521                 :             : 
     522                 :             :                 /*
     523                 :             :                  * If we find a tuple with zero penalty for all columns, and we've
     524                 :             :                  * decided we don't want to search for another tuple with equal
     525                 :             :                  * penalty, there's no need to examine remaining tuples; just break
     526                 :             :                  * out of the loop and return it.
     527                 :             :                  */
     528         [ +  + ]:     5547219 :                 if (zero_penalty)
     529                 :             :                 {
     530         [ -  + ]:       39557 :                         if (keep_current_best == -1)
     531                 :             :                         {
     532                 :             :                                 /* we didn't make the random choice yet for this old best */
     533                 :       39557 :                                 keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
     534                 :       39557 :                         }
     535         [ +  + ]:       39557 :                         if (keep_current_best == 1)
     536                 :       19721 :                                 break;
     537                 :       19836 :                 }
     538         [ +  + ]:     5547219 :         }
     539                 :             : 
     540                 :      256570 :         return result;
     541                 :      128285 : }
     542                 :             : 
     543                 :             : /*
     544                 :             :  * initialize a GiST entry with a decompressed version of key
     545                 :             :  */
     546                 :             : void
     547                 :     7873588 : gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
     548                 :             :                            Datum k, Relation r, Page pg, OffsetNumber o,
     549                 :             :                            bool l, bool isNull)
     550                 :             : {
     551         [ +  + ]:     7873588 :         if (!isNull)
     552                 :             :         {
     553                 :     7849516 :                 GISTENTRY  *dep;
     554                 :             : 
     555                 :     7849516 :                 gistentryinit(*e, k, r, pg, o, l);
     556                 :             : 
     557                 :             :                 /* there may not be a decompress function in opclass */
     558         [ +  + ]:     7849516 :                 if (!OidIsValid(giststate->decompressFn[nkey].fn_oid))
     559                 :     7781742 :                         return;
     560                 :             : 
     561                 :       67774 :                 dep = (GISTENTRY *)
     562                 :      135548 :                         DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
     563                 :       67774 :                                                                                           giststate->supportCollation[nkey],
     564                 :       67774 :                                                                                           PointerGetDatum(e)));
     565                 :             :                 /* decompressFn may just return the given pointer */
     566         [ +  - ]:       67774 :                 if (dep != e)
     567                 :           0 :                         gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
     568                 :             :                                                   dep->leafkey);
     569      [ -  +  + ]:     7849516 :         }
     570                 :             :         else
     571                 :       24072 :                 gistentryinit(*e, (Datum) 0, r, pg, o, l);
     572                 :     7873588 : }
     573                 :             : 
     574                 :             : IndexTuple
     575                 :      209612 : gistFormTuple(GISTSTATE *giststate, Relation r,
     576                 :             :                           const Datum *attdata, const bool *isnull, bool isleaf)
     577                 :             : {
     578                 :      209612 :         Datum           compatt[INDEX_MAX_KEYS];
     579                 :      209612 :         IndexTuple      res;
     580                 :             : 
     581                 :      209612 :         gistCompressValues(giststate, r, attdata, isnull, isleaf, compatt);
     582                 :             : 
     583         [ +  + ]:      209612 :         res = index_form_tuple(isleaf ? giststate->leafTupdesc :
     584                 :       99533 :                                                    giststate->nonLeafTupdesc,
     585                 :      209612 :                                                    compatt, isnull);
     586                 :             : 
     587                 :             :         /*
     588                 :             :          * The offset number on tuples on internal pages is unused. For historical
     589                 :             :          * reasons, it is set to 0xffff.
     590                 :             :          */
     591                 :      209612 :         ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff);
     592                 :      419224 :         return res;
     593                 :      209612 : }
     594                 :             : 
     595                 :             : void
     596                 :      247880 : gistCompressValues(GISTSTATE *giststate, Relation r,
     597                 :             :                                    const Datum *attdata, const bool *isnull, bool isleaf, Datum *compatt)
     598                 :             : {
     599                 :      247880 :         int                     i;
     600                 :             : 
     601                 :             :         /*
     602                 :             :          * Call the compress method on each attribute.
     603                 :             :          */
     604         [ +  + ]:      547451 :         for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
     605                 :             :         {
     606         [ +  + ]:      299571 :                 if (isnull[i])
     607                 :        1702 :                         compatt[i] = (Datum) 0;
     608                 :             :                 else
     609                 :             :                 {
     610                 :      297869 :                         GISTENTRY       centry;
     611                 :      297869 :                         GISTENTRY  *cep;
     612                 :             : 
     613                 :      297869 :                         gistentryinit(centry, attdata[i], r, NULL, (OffsetNumber) 0,
     614                 :             :                                                   isleaf);
     615                 :             :                         /* there may not be a compress function in opclass */
     616         [ +  + ]:      297869 :                         if (OidIsValid(giststate->compressFn[i].fn_oid))
     617                 :      220682 :                                 cep = (GISTENTRY *)
     618                 :      441364 :                                         DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
     619                 :      220682 :                                                                                                           giststate->supportCollation[i],
     620                 :      220682 :                                                                                                           PointerGetDatum(&centry)));
     621                 :             :                         else
     622                 :       77187 :                                 cep = &centry;
     623                 :      297869 :                         compatt[i] = cep->key;
     624                 :      297869 :                 }
     625                 :      299571 :         }
     626                 :             : 
     627         [ +  + ]:      247880 :         if (isleaf)
     628                 :             :         {
     629                 :             :                 /*
     630                 :             :                  * Emplace each included attribute if any.
     631                 :             :                  */
     632         [ +  + ]:      196537 :                 for (; i < r->rd_att->natts; i++)
     633                 :             :                 {
     634         [ -  + ]:       48190 :                         if (isnull[i])
     635                 :           0 :                                 compatt[i] = (Datum) 0;
     636                 :             :                         else
     637                 :       48190 :                                 compatt[i] = attdata[i];
     638                 :       48190 :                 }
     639                 :      148347 :         }
     640                 :      247880 : }
     641                 :             : 
     642                 :             : /*
     643                 :             :  * initialize a GiST entry with fetched value in key field
     644                 :             :  */
     645                 :             : static Datum
     646                 :       10167 : gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
     647                 :             : {
     648                 :       10167 :         GISTENTRY       fentry;
     649                 :       10167 :         GISTENTRY  *fep;
     650                 :             : 
     651                 :       10167 :         gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
     652                 :             : 
     653                 :       10167 :         fep = (GISTENTRY *)
     654                 :       20334 :                 DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
     655                 :       10167 :                                                                                   giststate->supportCollation[nkey],
     656                 :       10167 :                                                                                   PointerGetDatum(&fentry)));
     657                 :             : 
     658                 :             :         /* fetchFn set 'key', return it to the caller */
     659                 :       20334 :         return fep->key;
     660                 :       10167 : }
     661                 :             : 
     662                 :             : /*
     663                 :             :  * Fetch all keys in tuple.
     664                 :             :  * Returns a new HeapTuple containing the originally-indexed data.
     665                 :             :  */
     666                 :             : HeapTuple
     667                 :       79682 : gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple)
     668                 :             : {
     669                 :       79682 :         MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
     670                 :       79682 :         Datum           fetchatt[INDEX_MAX_KEYS];
     671                 :       79682 :         bool            isnull[INDEX_MAX_KEYS];
     672                 :       79682 :         int                     i;
     673                 :             : 
     674         [ +  + ]:      169422 :         for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
     675                 :             :         {
     676                 :       89740 :                 Datum           datum;
     677                 :             : 
     678                 :       89740 :                 datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
     679                 :             : 
     680         [ +  + ]:       89740 :                 if (giststate->fetchFn[i].fn_oid != InvalidOid)
     681                 :             :                 {
     682         [ +  + ]:       10169 :                         if (!isnull[i])
     683                 :       10167 :                                 fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
     684                 :             :                         else
     685                 :           2 :                                 fetchatt[i] = (Datum) 0;
     686                 :       10169 :                 }
     687         [ +  + ]:       79571 :                 else if (giststate->compressFn[i].fn_oid == InvalidOid)
     688                 :             :                 {
     689                 :             :                         /*
     690                 :             :                          * If opclass does not provide compress method that could change
     691                 :             :                          * original value, att is necessarily stored in original form.
     692                 :             :                          */
     693         [ +  + ]:       69513 :                         if (!isnull[i])
     694                 :       68957 :                                 fetchatt[i] = datum;
     695                 :             :                         else
     696                 :         556 :                                 fetchatt[i] = (Datum) 0;
     697                 :       69513 :                 }
     698                 :             :                 else
     699                 :             :                 {
     700                 :             :                         /*
     701                 :             :                          * Index-only scans not supported for this column. Since the
     702                 :             :                          * planner chose an index-only scan anyway, it is not interested
     703                 :             :                          * in this column, and we can replace it with a NULL.
     704                 :             :                          */
     705                 :       10058 :                         isnull[i] = true;
     706                 :       10058 :                         fetchatt[i] = (Datum) 0;
     707                 :             :                 }
     708                 :       89740 :         }
     709                 :             : 
     710                 :             :         /*
     711                 :             :          * Get each included attribute.
     712                 :             :          */
     713         [ -  + ]:       79682 :         for (; i < r->rd_att->natts; i++)
     714                 :             :         {
     715                 :           0 :                 fetchatt[i] = index_getattr(tuple, i + 1, giststate->leafTupdesc,
     716                 :           0 :                                                                         &isnull[i]);
     717                 :           0 :         }
     718                 :       79682 :         MemoryContextSwitchTo(oldcxt);
     719                 :             : 
     720                 :      159364 :         return heap_form_tuple(giststate->fetchTupdesc, fetchatt, isnull);
     721                 :       79682 : }
     722                 :             : 
     723                 :             : float
     724                 :     6824528 : gistpenalty(GISTSTATE *giststate, int attno,
     725                 :             :                         GISTENTRY *orig, bool isNullOrig,
     726                 :             :                         GISTENTRY *add, bool isNullAdd)
     727                 :             : {
     728                 :     6824528 :         float           penalty = 0.0;
     729                 :             : 
     730   [ +  -  +  + ]:    13629844 :         if (giststate->penaltyFn[attno].fn_strict == false ||
     731         [ +  + ]:     6824528 :                 (isNullOrig == false && isNullAdd == false))
     732                 :             :         {
     733                 :    13586988 :                 FunctionCall3Coll(&giststate->penaltyFn[attno],
     734                 :     6793494 :                                                   giststate->supportCollation[attno],
     735                 :     6793494 :                                                   PointerGetDatum(orig),
     736                 :     6793494 :                                                   PointerGetDatum(add),
     737                 :     6793494 :                                                   PointerGetDatum(&penalty));
     738                 :             :                 /* disallow negative or NaN penalty */
     739   [ +  -  -  +  :     6793494 :                 if (isnan(penalty) || penalty < 0.0)
                   #  # ]
     740                 :           0 :                         penalty = 0.0;
     741                 :     6793494 :         }
     742   [ +  +  +  + ]:       31034 :         else if (isNullOrig && isNullAdd)
     743                 :        1596 :                 penalty = 0.0;
     744                 :             :         else
     745                 :             :         {
     746                 :             :                 /* try to prevent mixing null and non-null values */
     747                 :       29438 :                 penalty = get_float4_infinity();
     748                 :             :         }
     749                 :             : 
     750                 :    13649056 :         return penalty;
     751                 :     6824528 : }
     752                 :             : 
     753                 :             : /*
     754                 :             :  * Initialize a new index page
     755                 :             :  */
     756                 :             : void
     757                 :        2348 : gistinitpage(Page page, uint32 f)
     758                 :             : {
     759                 :        2348 :         GISTPageOpaque opaque;
     760                 :             : 
     761                 :        2348 :         PageInit(page, BLCKSZ, sizeof(GISTPageOpaqueData));
     762                 :             : 
     763                 :        2348 :         opaque = GistPageGetOpaque(page);
     764                 :        2348 :         opaque->rightlink = InvalidBlockNumber;
     765                 :        2348 :         opaque->flags = f;
     766                 :        2348 :         opaque->gist_page_id = GIST_PAGE_ID;
     767                 :        2348 : }
     768                 :             : 
     769                 :             : /*
     770                 :             :  * Initialize a new index buffer
     771                 :             :  */
     772                 :             : void
     773                 :        1796 : GISTInitBuffer(Buffer b, uint32 f)
     774                 :             : {
     775                 :        1796 :         Page            page;
     776                 :             : 
     777                 :        1796 :         page = BufferGetPage(b);
     778                 :        1796 :         gistinitpage(page, f);
     779                 :        1796 : }
     780                 :             : 
     781                 :             : /*
     782                 :             :  * Verify that a freshly-read page looks sane.
     783                 :             :  */
     784                 :             : void
     785                 :      226533 : gistcheckpage(Relation rel, Buffer buf)
     786                 :             : {
     787                 :      226533 :         Page            page = BufferGetPage(buf);
     788                 :             : 
     789                 :             :         /*
     790                 :             :          * ReadBuffer verifies that every newly-read page passes
     791                 :             :          * PageHeaderIsValid, which means it either contains a reasonably sane
     792                 :             :          * page header or is all-zero.  We have to defend against the all-zero
     793                 :             :          * case, however.
     794                 :             :          */
     795         [ +  - ]:      226533 :         if (PageIsNew(page))
     796   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     797                 :             :                                 (errcode(ERRCODE_INDEX_CORRUPTED),
     798                 :             :                                  errmsg("index \"%s\" contains unexpected zero page at block %u",
     799                 :             :                                                 RelationGetRelationName(rel),
     800                 :             :                                                 BufferGetBlockNumber(buf)),
     801                 :             :                                  errhint("Please REINDEX it.")));
     802                 :             : 
     803                 :             :         /*
     804                 :             :          * Additionally check that the special area looks sane.
     805                 :             :          */
     806         [ +  - ]:      226533 :         if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
     807   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     808                 :             :                                 (errcode(ERRCODE_INDEX_CORRUPTED),
     809                 :             :                                  errmsg("index \"%s\" contains corrupted page at block %u",
     810                 :             :                                                 RelationGetRelationName(rel),
     811                 :             :                                                 BufferGetBlockNumber(buf)),
     812                 :             :                                  errhint("Please REINDEX it.")));
     813                 :      226533 : }
     814                 :             : 
     815                 :             : 
     816                 :             : /*
     817                 :             :  * Allocate a new page (either by recycling, or by extending the index file)
     818                 :             :  *
     819                 :             :  * The returned buffer is already pinned and exclusive-locked
     820                 :             :  *
     821                 :             :  * Caller is responsible for initializing the page by calling GISTInitBuffer
     822                 :             :  */
     823                 :             : Buffer
     824                 :        1795 : gistNewBuffer(Relation r, Relation heaprel)
     825                 :             : {
     826                 :        1795 :         Buffer          buffer;
     827                 :             : 
     828                 :             :         /* First, try to get a page from FSM */
     829                 :        1795 :         for (;;)
     830                 :             :         {
     831                 :        1795 :                 BlockNumber blkno = GetFreeIndexPage(r);
     832                 :             : 
     833         [ -  + ]:        1795 :                 if (blkno == InvalidBlockNumber)
     834                 :        1795 :                         break;                          /* nothing left in FSM */
     835                 :             : 
     836                 :           0 :                 buffer = ReadBuffer(r, blkno);
     837                 :             : 
     838                 :             :                 /*
     839                 :             :                  * We have to guard against the possibility that someone else already
     840                 :             :                  * recycled this page; the buffer may be locked if so.
     841                 :             :                  */
     842         [ #  # ]:           0 :                 if (ConditionalLockBuffer(buffer))
     843                 :             :                 {
     844                 :           0 :                         Page            page = BufferGetPage(buffer);
     845                 :             : 
     846                 :             :                         /*
     847                 :             :                          * If the page was never initialized, it's OK to use.
     848                 :             :                          */
     849         [ #  # ]:           0 :                         if (PageIsNew(page))
     850                 :           0 :                                 return buffer;
     851                 :             : 
     852                 :           0 :                         gistcheckpage(r, buffer);
     853                 :             : 
     854                 :             :                         /*
     855                 :             :                          * Otherwise, recycle it if deleted, and too old to have any
     856                 :             :                          * processes interested in it.
     857                 :             :                          */
     858         [ #  # ]:           0 :                         if (gistPageRecyclable(page))
     859                 :             :                         {
     860                 :             :                                 /*
     861                 :             :                                  * If we are generating WAL for Hot Standby then create a WAL
     862                 :             :                                  * record that will allow us to conflict with queries running
     863                 :             :                                  * on standby, in case they have snapshots older than the
     864                 :             :                                  * page's deleteXid.
     865                 :             :                                  */
     866   [ #  #  #  #  :           0 :                                 if (XLogStandbyInfoActive() && RelationNeedsWAL(r))
          #  #  #  #  #  
                      # ]
     867                 :           0 :                                         gistXLogPageReuse(r, heaprel, blkno, GistPageGetDeleteXid(page));
     868                 :             : 
     869                 :           0 :                                 return buffer;
     870                 :             :                         }
     871                 :             : 
     872                 :           0 :                         LockBuffer(buffer, GIST_UNLOCK);
     873         [ #  # ]:           0 :                 }
     874                 :             : 
     875                 :             :                 /* Can't use it, so release buffer and try again */
     876                 :           0 :                 ReleaseBuffer(buffer);
     877      [ -  -  + ]:        1795 :         }
     878                 :             : 
     879                 :             :         /* Must extend the file */
     880                 :        1795 :         buffer = ExtendBufferedRel(BMR_REL(r), MAIN_FORKNUM, NULL,
     881                 :             :                                                            EB_LOCK_FIRST);
     882                 :             : 
     883                 :        1795 :         return buffer;
     884                 :        1795 : }
     885                 :             : 
     886                 :             : /* Can this page be recycled yet? */
     887                 :             : bool
     888                 :         324 : gistPageRecyclable(Page page)
     889                 :             : {
     890         [ -  + ]:         324 :         if (PageIsNew(page))
     891                 :           0 :                 return true;
     892         [ -  + ]:         324 :         if (GistPageIsDeleted(page))
     893                 :             :         {
     894                 :             :                 /*
     895                 :             :                  * The page was deleted, but when? If it was just deleted, a scan
     896                 :             :                  * might have seen the downlink to it, and will read the page later.
     897                 :             :                  * As long as that can happen, we must keep the deleted page around as
     898                 :             :                  * a tombstone.
     899                 :             :                  *
     900                 :             :                  * For that check if the deletion XID could still be visible to
     901                 :             :                  * anyone. If not, then no scan that's still in progress could have
     902                 :             :                  * seen its downlink, and we can recycle it.
     903                 :             :                  */
     904                 :           0 :                 FullTransactionId deletexid_full = GistPageGetDeleteXid(page);
     905                 :             : 
     906                 :           0 :                 return GlobalVisCheckRemovableFullXid(NULL, deletexid_full);
     907                 :           0 :         }
     908                 :         324 :         return false;
     909                 :         324 : }
     910                 :             : 
     911                 :             : bytea *
     912                 :          29 : gistoptions(Datum reloptions, bool validate)
     913                 :             : {
     914                 :             :         static const relopt_parse_elt tab[] = {
     915                 :             :                 {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
     916                 :             :                 {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
     917                 :             :         };
     918                 :             : 
     919                 :          29 :         return (bytea *) build_reloptions(reloptions, validate,
     920                 :             :                                                                           RELOPT_KIND_GIST,
     921                 :             :                                                                           sizeof(GiSTOptions),
     922                 :             :                                                                           tab, lengthof(tab));
     923                 :             : }
     924                 :             : 
     925                 :             : /*
     926                 :             :  *      gistproperty() -- Check boolean properties of indexes.
     927                 :             :  *
     928                 :             :  * This is optional for most AMs, but is required for GiST because the core
     929                 :             :  * property code doesn't support AMPROP_DISTANCE_ORDERABLE.  We also handle
     930                 :             :  * AMPROP_RETURNABLE here to save opening the rel to call gistcanreturn.
     931                 :             :  */
     932                 :             : bool
     933                 :          78 : gistproperty(Oid index_oid, int attno,
     934                 :             :                          IndexAMProperty prop, const char *propname,
     935                 :             :                          bool *res, bool *isnull)
     936                 :             : {
     937                 :          78 :         Oid                     opclass,
     938                 :             :                                 opfamily,
     939                 :             :                                 opcintype;
     940                 :          78 :         int16           procno;
     941                 :             : 
     942                 :             :         /* Only answer column-level inquiries */
     943         [ +  + ]:          78 :         if (attno == 0)
     944                 :          49 :                 return false;
     945                 :             : 
     946                 :             :         /*
     947                 :             :          * Currently, GiST distance-ordered scans require that there be a distance
     948                 :             :          * function in the opclass with the default types (i.e. the one loaded
     949                 :             :          * into the relcache entry, see initGISTstate).  So we assume that if such
     950                 :             :          * a function exists, then there's a reason for it (rather than grubbing
     951                 :             :          * through all the opfamily's operators to find an ordered one).
     952                 :             :          *
     953                 :             :          * Essentially the same code can test whether we support returning the
     954                 :             :          * column data, since that's true if the opclass provides a fetch proc.
     955                 :             :          */
     956                 :             : 
     957      [ +  +  + ]:          29 :         switch (prop)
     958                 :             :         {
     959                 :             :                 case AMPROP_DISTANCE_ORDERABLE:
     960                 :           2 :                         procno = GIST_DISTANCE_PROC;
     961                 :           2 :                         break;
     962                 :             :                 case AMPROP_RETURNABLE:
     963                 :           2 :                         procno = GIST_FETCH_PROC;
     964                 :           2 :                         break;
     965                 :             :                 default:
     966                 :          25 :                         return false;
     967                 :             :         }
     968                 :             : 
     969                 :             :         /* First we need to know the column's opclass. */
     970                 :           4 :         opclass = get_index_column_opclass(index_oid, attno);
     971         [ +  - ]:           4 :         if (!OidIsValid(opclass))
     972                 :             :         {
     973                 :           0 :                 *isnull = true;
     974                 :           0 :                 return true;
     975                 :             :         }
     976                 :             : 
     977                 :             :         /* Now look up the opclass family and input datatype. */
     978         [ +  - ]:           4 :         if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
     979                 :             :         {
     980                 :           0 :                 *isnull = true;
     981                 :           0 :                 return true;
     982                 :             :         }
     983                 :             : 
     984                 :             :         /* And now we can check whether the function is provided. */
     985                 :             : 
     986                 :           4 :         *res = SearchSysCacheExists4(AMPROCNUM,
     987                 :             :                                                                  ObjectIdGetDatum(opfamily),
     988                 :             :                                                                  ObjectIdGetDatum(opcintype),
     989                 :             :                                                                  ObjectIdGetDatum(opcintype),
     990                 :             :                                                                  Int16GetDatum(procno));
     991                 :             : 
     992                 :             :         /*
     993                 :             :          * Special case: even without a fetch function, AMPROP_RETURNABLE is true
     994                 :             :          * if the opclass has no compress function.
     995                 :             :          */
     996   [ +  +  -  + ]:           4 :         if (prop == AMPROP_RETURNABLE && !*res)
     997                 :             :         {
     998                 :           2 :                 *res = !SearchSysCacheExists4(AMPROCNUM,
     999                 :             :                                                                           ObjectIdGetDatum(opfamily),
    1000                 :             :                                                                           ObjectIdGetDatum(opcintype),
    1001                 :             :                                                                           ObjectIdGetDatum(opcintype),
    1002                 :             :                                                                           Int16GetDatum(GIST_COMPRESS_PROC));
    1003                 :           2 :         }
    1004                 :             : 
    1005                 :           4 :         *isnull = false;
    1006                 :             : 
    1007                 :           4 :         return true;
    1008                 :          78 : }
    1009                 :             : 
    1010                 :             : /*
    1011                 :             :  * Some indexes are not WAL-logged, but we need LSNs to detect concurrent page
    1012                 :             :  * splits anyway. This function provides a fake sequence of LSNs for that
    1013                 :             :  * purpose.
    1014                 :             :  */
    1015                 :             : XLogRecPtr
    1016                 :          14 : gistGetFakeLSN(Relation rel)
    1017                 :             : {
    1018         [ +  + ]:          14 :         if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
    1019                 :             :         {
    1020                 :             :                 /*
    1021                 :             :                  * Temporary relations are only accessible in our session, so a simple
    1022                 :             :                  * backend-local counter will do.
    1023                 :             :                  */
    1024                 :             :                 static XLogRecPtr counter = FirstNormalUnloggedLSN;
    1025                 :             : 
    1026                 :           3 :                 return counter++;
    1027                 :             :         }
    1028         [ -  + ]:          11 :         else if (RelationIsPermanent(rel))
    1029                 :             :         {
    1030                 :             :                 /*
    1031                 :             :                  * WAL-logging on this relation will start after commit, so its LSNs
    1032                 :             :                  * must be distinct numbers smaller than the LSN at the next commit.
    1033                 :             :                  * Emit a dummy WAL record if insert-LSN hasn't advanced after the
    1034                 :             :                  * last call.
    1035                 :             :                  */
    1036                 :             :                 static XLogRecPtr lastlsn = InvalidXLogRecPtr;
    1037                 :           0 :                 XLogRecPtr      currlsn = GetXLogInsertRecPtr();
    1038                 :             : 
    1039                 :             :                 /* Shouldn't be called for WAL-logging relations */
    1040   [ #  #  #  #  :           0 :                 Assert(!RelationNeedsWAL(rel));
                   #  # ]
    1041                 :             : 
    1042                 :             :                 /* No need for an actual record if we already have a distinct LSN */
    1043   [ #  #  #  # ]:           0 :                 if (XLogRecPtrIsValid(lastlsn) && lastlsn == currlsn)
    1044                 :           0 :                         currlsn = gistXLogAssignLSN();
    1045                 :             : 
    1046                 :           0 :                 lastlsn = currlsn;
    1047                 :           0 :                 return currlsn;
    1048                 :           0 :         }
    1049                 :             :         else
    1050                 :             :         {
    1051                 :             :                 /*
    1052                 :             :                  * Unlogged relations are accessible from other backends, and survive
    1053                 :             :                  * (clean) restarts. GetFakeLSNForUnloggedRel() handles that for us.
    1054                 :             :                  */
    1055         [ +  - ]:          11 :                 Assert(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED);
    1056                 :          11 :                 return GetFakeLSNForUnloggedRel();
    1057                 :             :         }
    1058                 :          14 : }
    1059                 :             : 
    1060                 :             : /*
    1061                 :             :  * This is a stratnum translation support function for GiST opclasses that use
    1062                 :             :  * the RT*StrategyNumber constants.
    1063                 :             :  */
    1064                 :             : Datum
    1065                 :         343 : gist_translate_cmptype_common(PG_FUNCTION_ARGS)
    1066                 :             : {
    1067                 :         343 :         CompareType cmptype = PG_GETARG_INT32(0);
    1068                 :             : 
    1069   [ -  -  -  -  :         343 :         switch (cmptype)
             +  -  +  + ]
    1070                 :             :         {
    1071                 :             :                 case COMPARE_EQ:
    1072                 :         127 :                         PG_RETURN_UINT16(RTEqualStrategyNumber);
    1073                 :             :                 case COMPARE_LT:
    1074                 :           0 :                         PG_RETURN_UINT16(RTLessStrategyNumber);
    1075                 :             :                 case COMPARE_LE:
    1076                 :           0 :                         PG_RETURN_UINT16(RTLessEqualStrategyNumber);
    1077                 :             :                 case COMPARE_GT:
    1078                 :           0 :                         PG_RETURN_UINT16(RTGreaterStrategyNumber);
    1079                 :             :                 case COMPARE_GE:
    1080                 :           0 :                         PG_RETURN_UINT16(RTGreaterEqualStrategyNumber);
    1081                 :             :                 case COMPARE_OVERLAP:
    1082                 :         118 :                         PG_RETURN_UINT16(RTOverlapStrategyNumber);
    1083                 :             :                 case COMPARE_CONTAINED_BY:
    1084                 :          98 :                         PG_RETURN_UINT16(RTContainedByStrategyNumber);
    1085                 :             :                 default:
    1086                 :           0 :                         PG_RETURN_UINT16(InvalidStrategy);
    1087                 :             :         }
    1088                 :         343 : }
    1089                 :             : 
    1090                 :             : /*
    1091                 :             :  * Returns the opclass's private stratnum used for the given compare type.
    1092                 :             :  *
    1093                 :             :  * Calls the opclass's GIST_TRANSLATE_CMPTYPE_PROC support function, if any,
    1094                 :             :  * and returns the result.  Returns InvalidStrategy if the function is not
    1095                 :             :  * defined.
    1096                 :             :  */
    1097                 :             : StrategyNumber
    1098                 :         341 : gisttranslatecmptype(CompareType cmptype, Oid opfamily)
    1099                 :             : {
    1100                 :         341 :         Oid                     funcid;
    1101                 :         341 :         Datum           result;
    1102                 :             : 
    1103                 :             :         /* Check whether the function is provided. */
    1104                 :         341 :         funcid = get_opfamily_proc(opfamily, ANYOID, ANYOID, GIST_TRANSLATE_CMPTYPE_PROC);
    1105         [ +  - ]:         341 :         if (!OidIsValid(funcid))
    1106                 :           0 :                 return InvalidStrategy;
    1107                 :             : 
    1108                 :             :         /* Ask the translation function */
    1109                 :         341 :         result = OidFunctionCall1Coll(funcid, InvalidOid, Int32GetDatum(cmptype));
    1110                 :         341 :         return DatumGetUInt16(result);
    1111                 :         341 : }
        

Generated by: LCOV version 2.3.2-1