LCOV - differential code coverage report
Current view: top level - src/backend/access/gist - gistutil.c (source / functions) Coverage Total Hit UBC CBC
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 86.8 % 326 283 43 283
Current Date: 2025-09-06 07:49:51 +0900 Functions: 100.0 % 29 29 29
Baseline: lcov-20250906-005545-baseline Branches: 70.5 % 207 146 61 146
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 56.0 % 25 14 11 14
(360..) days: 89.4 % 301 269 32 269
Function coverage date bins:
(30,360] days: 100.0 % 2 2 2
(360..) days: 100.0 % 27 27 27
Branch coverage date bins:
(30,360] days: 40.0 % 10 4 6 4
(360..) days: 72.1 % 197 142 55 142

 Age         Owner                    Branch data    TLA  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-2025, 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
 6295 heikki.linnakangas@i       34                 :CBC      582760 : gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
                                 35                 :                : {
                                 36                 :                :     int         i;
                                 37                 :                : 
 7289 bruce@momjian.us           38         [ +  + ]:         582760 :     if (off == InvalidOffsetNumber)
                                 39         [ +  + ]:        1161873 :         off = (PageIsEmpty(page)) ? FirstOffsetNumber :
 7375 teodor@sigaev.ru           40                 :         580026 :             OffsetNumberNext(PageGetMaxOffsetNumber(page));
                                 41                 :                : 
 7389                            42         [ +  + ]:        1252792 :     for (i = 0; i < len; i++)
                                 43                 :                :     {
 5931 bruce@momjian.us           44                 :         670032 :         Size        sz = IndexTupleSize(itup[i]);
                                 45                 :                :         OffsetNumber l;
                                 46                 :                : 
 6295 heikki.linnakangas@i       47                 :         670032 :         l = PageAddItem(page, (Item) itup[i], sz, off, false, false);
 7389 teodor@sigaev.ru           48         [ -  + ]:         670032 :         if (l == InvalidOffsetNumber)
 6295 heikki.linnakangas@i       49         [ #  # ]:UBC           0 :             elog(ERROR, "failed to add item to GiST index page, item %d out of %d, size %d bytes",
                                 50                 :                :                  i, len, (int) sz);
 7389 teodor@sigaev.ru           51                 :CBC      670032 :         off++;
                                 52                 :                :     }
                                 53                 :         582760 : }
                                 54                 :                : 
                                 55                 :                : /*
                                 56                 :                :  * Check space for itup vector on page
                                 57                 :                :  */
                                 58                 :                : bool
 7006 bruce@momjian.us           59                 :         816253 : gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
                                 60                 :                : {
 6912                            61                 :         816253 :     unsigned int size = freespace,
                                 62                 :         816253 :                 deleted = 0;
                                 63                 :                :     int         i;
                                 64                 :                : 
 7389 teodor@sigaev.ru           65         [ +  + ]:        1644826 :     for (i = 0; i < len; i++)
                                 66                 :         828573 :         size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
                                 67                 :                : 
 6912 bruce@momjian.us           68         [ +  + ]:         816253 :     if (todelete != InvalidOffsetNumber)
                                 69                 :                :     {
                                 70                 :         371167 :         IndexTuple  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, todelete));
                                 71                 :                : 
 7059 teodor@sigaev.ru           72                 :         371167 :         deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
                                 73                 :                :     }
                                 74                 :                : 
                                 75                 :         816253 :     return (PageGetFreeSpace(page) + deleted < size);
                                 76                 :                : }
                                 77                 :                : 
                                 78                 :                : bool
 6912 bruce@momjian.us           79                 :          26466 : gistfitpage(IndexTuple *itvec, int len)
                                 80                 :                : {
                                 81                 :                :     int         i;
                                 82                 :          26466 :     Size        size = 0;
                                 83                 :                : 
                                 84         [ +  + ]:        1212140 :     for (i = 0; i < len; i++)
 7059 teodor@sigaev.ru           85                 :        1185674 :         size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
                                 86                 :                : 
                                 87                 :                :     /* TODO: Consider fillfactor */
                                 88                 :          26466 :     return (size <= GiSTPageSize);
                                 89                 :                : }
                                 90                 :                : 
                                 91                 :                : /*
                                 92                 :                :  * Read buffer into itup vector
                                 93                 :                :  */
                                 94                 :                : IndexTuple *
 7052                            95                 :          13185 : gistextractpage(Page page, int *len /* out */ )
                                 96                 :                : {
                                 97                 :                :     OffsetNumber i,
                                 98                 :                :                 maxoff;
                                 99                 :                :     IndexTuple *itvec;
                                100                 :                : 
                                101                 :          13185 :     maxoff = PageGetMaxOffsetNumber(page);
 7389                           102                 :          13185 :     *len = maxoff;
                                103                 :          13185 :     itvec = palloc(sizeof(IndexTuple) * maxoff);
                                104         [ +  + ]:         997916 :     for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
 7052                           105                 :         984731 :         itvec[i - FirstOffsetNumber] = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
                                106                 :                : 
 7389                           107                 :          13185 :     return itvec;
                                108                 :                : }
                                109                 :                : 
                                110                 :                : /*
                                111                 :                :  * join two vectors into one
                                112                 :                :  */
                                113                 :                : IndexTuple *
                                114                 :          12996 : gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
                                115                 :                : {
  942 peter@eisentraut.org      116                 :          12996 :     itvec = (IndexTuple *) repalloc(itvec, sizeof(IndexTuple) * ((*len) + addlen));
 7389 teodor@sigaev.ru          117                 :          12996 :     memmove(&itvec[*len], additvec, sizeof(IndexTuple) * addlen);
                                118                 :          12996 :     *len += addlen;
                                119                 :          12996 :     return itvec;
                                120                 :                : }
                                121                 :                : 
                                122                 :                : /*
                                123                 :                :  * make plain IndexTuple vector
                                124                 :                :  */
                                125                 :                : 
                                126                 :                : IndexTupleData *
 6912 bruce@momjian.us          127                 :          26092 : gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
                                128                 :                : {
                                129                 :                :     char       *ptr,
                                130                 :                :                *ret;
                                131                 :                :     int         i;
                                132                 :                : 
                                133                 :          26092 :     *memlen = 0;
                                134                 :                : 
 7050 teodor@sigaev.ru          135         [ +  + ]:        1023596 :     for (i = 0; i < veclen; i++)
                                136                 :         997504 :         *memlen += IndexTupleSize(vec[i]);
                                137                 :                : 
                                138                 :          26092 :     ptr = ret = palloc(*memlen);
                                139                 :                : 
 6912 bruce@momjian.us          140         [ +  + ]:        1023596 :     for (i = 0; i < veclen; i++)
                                141                 :                :     {
 7050 teodor@sigaev.ru          142                 :         997504 :         memcpy(ptr, vec[i], IndexTupleSize(vec[i]));
                                143                 :         997504 :         ptr += IndexTupleSize(vec[i]);
                                144                 :                :     }
                                145                 :                : 
 6912 bruce@momjian.us          146                 :          26092 :     return (IndexTupleData *) ret;
                                147                 :                : }
                                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
 4594 tgl@sss.pgh.pa.us         155                 :           2749 : gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
                                156                 :                :                    Datum *attr, bool *isnull)
                                157                 :                : {
                                158                 :                :     int         i;
                                159                 :                :     GistEntryVector *evec;
                                160                 :                :     int         attrsize;
                                161                 :                : 
 6912 bruce@momjian.us          162                 :           2749 :     evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
                                163                 :                : 
 2372 akorotkov@postgresql      164         [ +  + ]:           8066 :     for (i = 0; i < giststate->nonLeafTupdesc->natts; i++)
                                165                 :                :     {
                                166                 :                :         int         j;
                                167                 :                : 
                                168                 :                :         /* Collect non-null datums for this column */
 7045 teodor@sigaev.ru          169                 :           5317 :         evec->n = 0;
 6912 bruce@momjian.us          170         [ +  + ]:         279196 :         for (j = 0; j < len; j++)
                                171                 :                :         {
                                172                 :                :             Datum       datum;
                                173                 :                :             bool        IsNull;
                                174                 :                : 
 2372 akorotkov@postgresql      175                 :         273879 :             datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc,
                                176                 :                :                                   &IsNull);
 7389 teodor@sigaev.ru          177         [ +  + ]:         273879 :             if (IsNull)
                                178                 :           1560 :                 continue;
                                179                 :                : 
                                180                 :         272319 :             gistdentryinit(giststate, i,
 7045                           181                 :         272319 :                            evec->vector + evec->n,
                                182                 :                :                            datum,
                                183                 :                :                            NULL, NULL, (OffsetNumber) 0,
                                184                 :                :                            false, IsNull);
                                185                 :         272319 :             evec->n++;
                                186                 :                :         }
                                187                 :                : 
                                188                 :                :         /* If this column was all NULLs, the union is NULL */
 6912 bruce@momjian.us          189         [ +  + ]:           5317 :         if (evec->n == 0)
                                190                 :                :         {
 7389 teodor@sigaev.ru          191                 :             89 :             attr[i] = (Datum) 0;
 2943 peter_e@gmx.net           192                 :             89 :             isnull[i] = true;
                                193                 :                :         }
                                194                 :                :         else
                                195                 :                :         {
 6912 bruce@momjian.us          196         [ +  + ]:           5228 :             if (evec->n == 1)
                                197                 :                :             {
                                198                 :                :                 /* unionFn may expect at least two inputs */
 7389 teodor@sigaev.ru          199                 :              4 :                 evec->n = 2;
 7045                           200                 :              4 :                 evec->vector[1] = evec->vector[0];
                                201                 :                :             }
                                202                 :                : 
                                203                 :                :             /* Make union and store in attr array */
 5251 tgl@sss.pgh.pa.us         204                 :           5228 :             attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
                                205                 :                :                                         giststate->supportCollation[i],
                                206                 :                :                                         PointerGetDatum(evec),
                                207                 :                :                                         PointerGetDatum(&attrsize));
                                208                 :                : 
 2943 peter_e@gmx.net           209                 :           5228 :             isnull[i] = false;
                                210                 :                :         }
                                211                 :                :     }
 7045 teodor@sigaev.ru          212                 :           2749 : }
                                213                 :                : 
                                214                 :                : /*
                                215                 :                :  * Return an IndexTuple containing the result of applying the "union"
                                216                 :                :  * method to the specified IndexTuple vector.
                                217                 :                :  */
                                218                 :                : IndexTuple
                                219                 :              7 : gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
                                220                 :                : {
                                221                 :                :     Datum       attr[INDEX_MAX_KEYS];
                                222                 :                :     bool        isnull[INDEX_MAX_KEYS];
                                223                 :                : 
 4594 tgl@sss.pgh.pa.us         224                 :              7 :     gistMakeUnionItVec(giststate, itvec, len, attr, isnull);
                                225                 :                : 
                                226                 :              7 :     return gistFormTuple(giststate, r, attr, isnull, false);
                                227                 :                : }
                                228                 :                : 
                                229                 :                : /*
                                230                 :                :  * makes union of two key
                                231                 :                :  */
                                232                 :                : void
 6912 bruce@momjian.us          233                 :         702944 : 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                 :                :     union
                                240                 :                :     {
                                241                 :                :         GistEntryVector gev;
                                242                 :                :         char        padding[2 * sizeof(GISTENTRY) + GEVHDRSZ];
                                243                 :                :     }           storage;
 4594 tgl@sss.pgh.pa.us         244                 :         702944 :     GistEntryVector *evec = &storage.gev;
                                245                 :                :     int         dstsize;
                                246                 :                : 
 7045 teodor@sigaev.ru          247                 :         702944 :     evec->n = 2;
                                248                 :                : 
 6912 bruce@momjian.us          249   [ +  +  +  - ]:         702944 :     if (isnull1 && isnull2)
                                250                 :                :     {
 2943 peter_e@gmx.net           251                 :           8619 :         *dstisnull = true;
 6912 bruce@momjian.us          252                 :           8619 :         *dst = (Datum) 0;
                                253                 :                :     }
                                254                 :                :     else
                                255                 :                :     {
 2943 peter_e@gmx.net           256   [ +  -  +  + ]:         694325 :         if (isnull1 == false && isnull2 == false)
                                257                 :                :         {
 7045 teodor@sigaev.ru          258                 :         694119 :             evec->vector[0] = *entry1;
                                259                 :         694119 :             evec->vector[1] = *entry2;
                                260                 :                :         }
 2943 peter_e@gmx.net           261         [ +  - ]:            206 :         else if (isnull1 == false)
                                262                 :                :         {
 7045 teodor@sigaev.ru          263                 :            206 :             evec->vector[0] = *entry1;
                                264                 :            206 :             evec->vector[1] = *entry1;
                                265                 :                :         }
                                266                 :                :         else
                                267                 :                :         {
 7045 teodor@sigaev.ru          268                 :UBC           0 :             evec->vector[0] = *entry2;
                                269                 :              0 :             evec->vector[1] = *entry2;
                                270                 :                :         }
                                271                 :                : 
 2943 peter_e@gmx.net           272                 :CBC      694325 :         *dstisnull = false;
 5251 tgl@sss.pgh.pa.us         273                 :         694325 :         *dst = FunctionCall2Coll(&giststate->unionFn[attno],
                                274                 :                :                                  giststate->supportCollation[attno],
                                275                 :                :                                  PointerGetDatum(evec),
                                276                 :                :                                  PointerGetDatum(&dstsize));
                                277                 :                :     }
 7045 teodor@sigaev.ru          278                 :         702944 : }
                                279                 :                : 
                                280                 :                : bool
 6912 bruce@momjian.us          281                 :         603905 : gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
                                282                 :                : {
 1075 peter@eisentraut.org      283                 :         603905 :     bool        result = false; /* silence compiler warning */
                                284                 :                : 
 5251 tgl@sss.pgh.pa.us         285                 :         603905 :     FunctionCall3Coll(&giststate->equalFn[attno],
                                286                 :                :                       giststate->supportCollation[attno],
                                287                 :                :                       a, b,
                                288                 :                :                       PointerGetDatum(&result));
 7040 teodor@sigaev.ru          289                 :         603905 :     return result;
                                290                 :                : }
                                291                 :                : 
                                292                 :                : /*
                                293                 :                :  * Decompress all keys in tuple
                                294                 :                :  */
                                295                 :                : void
 7010                           296                 :        1842411 : gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
                                297                 :                :                   OffsetNumber o, GISTENTRY *attdata, bool *isnull)
                                298                 :                : {
                                299                 :                :     int         i;
                                300                 :                : 
 2372 akorotkov@postgresql      301         [ +  + ]:        3966117 :     for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
                                302                 :                :     {
                                303                 :                :         Datum       datum;
                                304                 :                : 
                                305                 :        2123706 :         datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
 7010 teodor@sigaev.ru          306                 :        2123706 :         gistdentryinit(giststate, i, &attdata[i],
                                307                 :                :                        datum, r, p, o,
 2943 peter_e@gmx.net           308                 :        2123706 :                        false, isnull[i]);
                                309                 :                :     }
 7010 teodor@sigaev.ru          310                 :        1842411 : }
                                311                 :                : 
                                312                 :                : /*
                                313                 :                :  * Forms union of oldtup and addtup, if union == oldtup then return NULL
                                314                 :                :  */
                                315                 :                : IndexTuple
 7389                           316                 :         609177 : gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
                                317                 :                : {
 2943 peter_e@gmx.net           318                 :         609177 :     bool        neednew = false;
                                319                 :                :     GISTENTRY   oldentries[INDEX_MAX_KEYS],
                                320                 :                :                 addentries[INDEX_MAX_KEYS];
                                321                 :                :     bool        oldisnull[INDEX_MAX_KEYS],
                                322                 :                :                 addisnull[INDEX_MAX_KEYS];
                                323                 :                :     Datum       attr[INDEX_MAX_KEYS];
                                324                 :                :     bool        isnull[INDEX_MAX_KEYS];
 7389 teodor@sigaev.ru          325                 :         609177 :     IndexTuple  newtup = NULL;
                                326                 :                :     int         i;
                                327                 :                : 
                                328                 :         609177 :     gistDeCompressAtt(giststate, r, oldtup, NULL,
                                329                 :                :                       (OffsetNumber) 0, oldentries, oldisnull);
                                330                 :                : 
                                331                 :         609177 :     gistDeCompressAtt(giststate, r, addtup, NULL,
                                332                 :                :                       (OffsetNumber) 0, addentries, addisnull);
                                333                 :                : 
 2372 akorotkov@postgresql      334         [ +  + ]:        1312119 :     for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
                                335                 :                :     {
 6912 bruce@momjian.us          336                 :         702942 :         gistMakeUnionKey(giststate, i,
                                337                 :         702942 :                          oldentries + i, oldisnull[i],
                                338                 :         702942 :                          addentries + i, addisnull[i],
 4594 tgl@sss.pgh.pa.us         339                 :         702942 :                          attr + i, isnull + i);
                                340                 :                : 
 6912 bruce@momjian.us          341         [ +  + ]:         702942 :         if (neednew)
                                342                 :                :             /* we already need new key, so we can skip check */
 7045 teodor@sigaev.ru          343                 :          91494 :             continue;
                                344                 :                : 
 4594 tgl@sss.pgh.pa.us         345         [ +  + ]:         611448 :         if (isnull[i])
                                346                 :                :             /* union of key may be NULL if and only if both keys are NULL */
 7045 teodor@sigaev.ru          347                 :           8619 :             continue;
                                348                 :                : 
 6912 bruce@momjian.us          349         [ +  + ]:         602829 :         if (!addisnull[i])
                                350                 :                :         {
 4594 tgl@sss.pgh.pa.us         351         [ +  - ]:         602623 :             if (oldisnull[i] ||
                                352         [ +  + ]:         602623 :                 !gistKeyIsEQ(giststate, i, oldentries[i].key, attr[i]))
 7045 teodor@sigaev.ru          353                 :         373715 :                 neednew = true;
                                354                 :                :         }
                                355                 :                :     }
                                356                 :                : 
 7389                           357         [ +  + ]:         609177 :     if (neednew)
                                358                 :                :     {
                                359                 :                :         /* need to update key */
 4594 tgl@sss.pgh.pa.us         360                 :         373715 :         newtup = gistFormTuple(giststate, r, attr, isnull, false);
 7389 teodor@sigaev.ru          361                 :         373715 :         newtup->t_tid = oldtup->t_tid;
                                362                 :                :     }
                                363                 :                : 
                                364                 :         609177 :     return newtup;
                                365                 :                : }
                                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                 :         594306 : gistchoose(Relation r, Page p, IndexTuple it,   /* it has compressed entry */
                                375                 :                :            GISTSTATE *giststate)
                                376                 :                : {
                                377                 :                :     OffsetNumber result;
                                378                 :                :     OffsetNumber maxoff;
                                379                 :                :     OffsetNumber i;
                                380                 :                :     float       best_penalty[INDEX_MAX_KEYS];
                                381                 :                :     GISTENTRY   entry,
                                382                 :                :                 identry[INDEX_MAX_KEYS];
                                383                 :                :     bool        isnull[INDEX_MAX_KEYS];
                                384                 :                :     int         keep_current_best;
                                385                 :                : 
 4755 tgl@sss.pgh.pa.us         386         [ -  + ]:         594306 :     Assert(!GistPageIsLeaf(p));
                                387                 :                : 
 7389 teodor@sigaev.ru          388                 :         594306 :     gistDeCompressAtt(giststate, r,
                                389                 :                :                       it, NULL, (OffsetNumber) 0,
                                390                 :                :                       identry, isnull);
                                391                 :                : 
                                392                 :                :     /* we'll return FirstOffsetNumber if page is empty (shouldn't happen) */
 4755 tgl@sss.pgh.pa.us         393                 :         594306 :     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                 :         594306 :     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                 :                :      */
 4607 heikki.linnakangas@i      430                 :         594306 :     keep_current_best = -1;
                                431                 :                : 
                                432                 :                :     /*
                                433                 :                :      * Loop over tuples on page.
                                434                 :                :      */
 4755 tgl@sss.pgh.pa.us         435                 :         594306 :     maxoff = PageGetMaxOffsetNumber(p);
                                436         [ -  + ]:         594306 :     Assert(maxoff >= FirstOffsetNumber);
                                437                 :                : 
                                438         [ +  + ]:       19221160 :     for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
                                439                 :                :     {
 7389 teodor@sigaev.ru          440                 :       18745382 :         IndexTuple  itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
                                441                 :                :         bool        zero_penalty;
                                442                 :                :         int         j;
                                443                 :                : 
 4755 tgl@sss.pgh.pa.us         444                 :       18745382 :         zero_penalty = true;
                                445                 :                : 
                                446                 :                :         /* Loop over index attributes. */
 2372 akorotkov@postgresql      447         [ +  + ]:       38031633 :         for (j = 0; j < IndexRelationGetNumberOfKeyAttributes(r); j++)
                                448                 :                :         {
                                449                 :                :             Datum       datum;
                                450                 :                :             float       usize;
                                451                 :                :             bool        IsNull;
                                452                 :                : 
                                453                 :                :             /* Compute penalty for this column. */
                                454                 :       22430033 :             datum = index_getattr(itup, j + 1, giststate->leafTupdesc,
                                455                 :                :                                   &IsNull);
 7389 teodor@sigaev.ru          456                 :       22430033 :             gistdentryinit(giststate, j, &entry, datum, r, p, i,
                                457                 :                :                            false, IsNull);
 7045                           458                 :       22430033 :             usize = gistpenalty(giststate, j, &entry, IsNull,
                                459                 :       22430033 :                                 &identry[j], isnull[j]);
 4755 tgl@sss.pgh.pa.us         460         [ +  + ]:       22430033 :             if (usize > 0)
                                461                 :       22188876 :                 zero_penalty = false;
                                462                 :                : 
                                463   [ +  +  +  + ]:       22430033 :             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                 :       18098474 :                 result = i;
                                474                 :       18098474 :                 best_penalty[j] = usize;
                                475                 :                : 
 2372 akorotkov@postgresql      476         [ +  + ]:       18098474 :                 if (j < IndexRelationGetNumberOfKeyAttributes(r) - 1)
 4755 tgl@sss.pgh.pa.us         477                 :        3682956 :                     best_penalty[j + 1] = -1;
                                478                 :                : 
                                479                 :                :                 /* we have new best, so reset keep-it decision */
 4607 heikki.linnakangas@i      480                 :       18098474 :                 keep_current_best = -1;
                                481                 :                :             }
 4755 tgl@sss.pgh.pa.us         482         [ +  + ]:        4331559 :             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                 :                :             }
                                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                 :        3143782 :                 zero_penalty = false;   /* so outer loop won't exit */
 7389 teodor@sigaev.ru          498                 :        3143782 :                 break;
                                499                 :                :             }
                                500                 :                :         }
                                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                 :                :          */
 2372 akorotkov@postgresql      506   [ +  +  +  + ]:       18745382 :         if (j == IndexRelationGetNumberOfKeyAttributes(r) && result != i)
                                507                 :                :         {
 4607 heikki.linnakangas@i      508         [ +  + ]:        1186082 :             if (keep_current_best == -1)
                                509                 :                :             {
                                510                 :                :                 /* we didn't make the random choice yet for this old best */
 1378 tgl@sss.pgh.pa.us         511                 :         134774 :                 keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
                                512                 :                :             }
 4607 heikki.linnakangas@i      513         [ +  + ]:        1186082 :             if (keep_current_best == 0)
                                514                 :                :             {
                                515                 :                :                 /* we choose to use the new tuple */
                                516                 :         113069 :                 result = i;
                                517                 :                :                 /* choose again if there are even more exactly-as-good ones */
                                518                 :         113069 :                 keep_current_best = -1;
                                519                 :                :             }
                                520                 :                :         }
                                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                 :                :          */
 4755 tgl@sss.pgh.pa.us         528         [ +  + ]:       18745382 :         if (zero_penalty)
                                529                 :                :         {
 4607 heikki.linnakangas@i      530         [ +  - ]:         237199 :             if (keep_current_best == -1)
                                531                 :                :             {
                                532                 :                :                 /* we didn't make the random choice yet for this old best */
 1378 tgl@sss.pgh.pa.us         533                 :         237199 :                 keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
                                534                 :                :             }
 4607 heikki.linnakangas@i      535         [ +  + ]:         237199 :             if (keep_current_best == 1)
                                536                 :         118528 :                 break;
                                537                 :                :         }
                                538                 :                :     }
                                539                 :                : 
 4755 tgl@sss.pgh.pa.us         540                 :         594306 :     return result;
                                541                 :                : }
                                542                 :                : 
                                543                 :                : /*
                                544                 :                :  * initialize a GiST entry with a decompressed version of key
                                545                 :                :  */
                                546                 :                : void
 7389 teodor@sigaev.ru          547                 :       27030918 : gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
                                548                 :                :                Datum k, Relation r, Page pg, OffsetNumber o,
                                549                 :                :                bool l, bool isNull)
                                550                 :                : {
 7010                           551         [ +  + ]:       27030918 :     if (!isNull)
                                552                 :                :     {
                                553                 :                :         GISTENTRY  *dep;
                                554                 :                : 
                                555                 :       26902116 :         gistentryinit(*e, k, r, pg, o, l);
                                556                 :                : 
                                557                 :                :         /* there may not be a decompress function in opclass */
 2909 tgl@sss.pgh.pa.us         558         [ +  + ]:       26902116 :         if (!OidIsValid(giststate->decompressFn[nkey].fn_oid))
                                559                 :       23840714 :             return;
                                560                 :                : 
                                561                 :                :         dep = (GISTENTRY *)
 5251                           562                 :        3061402 :             DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
                                563                 :                :                                               giststate->supportCollation[nkey],
                                564                 :                :                                               PointerGetDatum(e)));
                                565                 :                :         /* decompressFn may just return the given pointer */
 7389 teodor@sigaev.ru          566         [ +  + ]:        3061402 :         if (dep != e)
                                567                 :         357994 :             gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
                                568                 :                :                           dep->leafkey);
                                569                 :                :     }
                                570                 :                :     else
 7010                           571                 :         128802 :         gistentryinit(*e, (Datum) 0, r, pg, o, l);
                                572                 :                : }
                                573                 :                : 
                                574                 :                : IndexTuple
 7389                           575                 :         844711 : gistFormTuple(GISTSTATE *giststate, Relation r,
                                576                 :                :               const Datum *attdata, const bool *isnull, bool isleaf)
                                577                 :                : {
                                578                 :                :     Datum       compatt[INDEX_MAX_KEYS];
                                579                 :                :     IndexTuple  res;
                                580                 :                : 
 1815 heikki.linnakangas@i      581                 :         844711 :     gistCompressValues(giststate, r, attdata, isnull, isleaf, compatt);
                                582                 :                : 
                                583         [ +  + ]:         844710 :     res = index_form_tuple(isleaf ? giststate->leafTupdesc :
                                584                 :                :                            giststate->nonLeafTupdesc,
                                585                 :                :                            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                 :         844710 :     ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff);
                                592                 :         844710 :     return res;
                                593                 :                : }
                                594                 :                : 
                                595                 :                : void
                                596                 :         980662 : gistCompressValues(GISTSTATE *giststate, Relation r,
                                597                 :                :                    const Datum *attdata, const bool *isnull, bool isleaf, Datum *compatt)
                                598                 :                : {
                                599                 :                :     int         i;
                                600                 :                : 
                                601                 :                :     /*
                                602                 :                :      * Call the compress method on each attribute.
                                603                 :                :      */
 2372 akorotkov@postgresql      604         [ +  + ]:        2119211 :     for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
                                605                 :                :     {
 7389 teodor@sigaev.ru          606         [ +  + ]:        1138550 :         if (isnull[i])
                                607                 :           7782 :             compatt[i] = (Datum) 0;
                                608                 :                :         else
                                609                 :                :         {
                                610                 :                :             GISTENTRY   centry;
                                611                 :                :             GISTENTRY  *cep;
                                612                 :                : 
 3817 heikki.linnakangas@i      613                 :        1130768 :             gistentryinit(centry, attdata[i], r, NULL, (OffsetNumber) 0,
                                614                 :                :                           isleaf);
                                615                 :                :             /* there may not be a compress function in opclass */
 2909 tgl@sss.pgh.pa.us         616         [ +  + ]:        1130768 :             if (OidIsValid(giststate->compressFn[i].fn_oid))
                                617                 :                :                 cep = (GISTENTRY *)
                                618                 :         893035 :                     DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
                                619                 :                :                                                       giststate->supportCollation[i],
                                620                 :                :                                                       PointerGetDatum(&centry)));
                                621                 :                :             else
                                622                 :         237733 :                 cep = &centry;
 3817 heikki.linnakangas@i      623                 :        1130767 :             compatt[i] = cep->key;
                                624                 :                :         }
                                625                 :                :     }
                                626                 :                : 
 2372 akorotkov@postgresql      627         [ +  + ]:         980661 :     if (isleaf)
                                628                 :                :     {
                                629                 :                :         /*
                                630                 :                :          * Emplace each included attribute if any.
                                631                 :                :          */
                                632         [ +  + ]:         727599 :         for (; i < r->rd_att->natts; i++)
                                633                 :                :         {
                                634         [ +  + ]:         146570 :             if (isnull[i])
                                635                 :           1000 :                 compatt[i] = (Datum) 0;
                                636                 :                :             else
                                637                 :         145570 :                 compatt[i] = attdata[i];
                                638                 :                :         }
                                639                 :                :     }
 7389 teodor@sigaev.ru          640                 :         980661 : }
                                641                 :                : 
                                642                 :                : /*
                                643                 :                :  * initialize a GiST entry with fetched value in key field
                                644                 :                :  */
                                645                 :                : static Datum
 3817 heikki.linnakangas@i      646                 :          53417 : gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
                                647                 :                : {
                                648                 :                :     GISTENTRY   fentry;
                                649                 :                :     GISTENTRY  *fep;
                                650                 :                : 
                                651                 :          53417 :     gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
                                652                 :                : 
                                653                 :                :     fep = (GISTENTRY *)
                                654                 :          53417 :         DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
                                655                 :                :                                           giststate->supportCollation[nkey],
                                656                 :                :                                           PointerGetDatum(&fentry)));
                                657                 :                : 
                                658                 :                :     /* fetchFn set 'key', return it to the caller */
                                659                 :          53417 :     return fep->key;
                                660                 :                : }
                                661                 :                : 
                                662                 :                : /*
                                663                 :                :  * Fetch all keys in tuple.
                                664                 :                :  * Returns a new HeapTuple containing the originally-indexed data.
                                665                 :                :  */
                                666                 :                : HeapTuple
                                667                 :         265801 : gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple)
                                668                 :                : {
                                669                 :         265801 :     MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
                                670                 :                :     Datum       fetchatt[INDEX_MAX_KEYS];
                                671                 :                :     bool        isnull[INDEX_MAX_KEYS];
                                672                 :                :     int         i;
                                673                 :                : 
 2372 akorotkov@postgresql      674         [ +  + ]:         561777 :     for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
                                675                 :                :     {
                                676                 :                :         Datum       datum;
                                677                 :                : 
                                678                 :         295976 :         datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
                                679                 :                : 
 3817 heikki.linnakangas@i      680         [ +  + ]:         295976 :         if (giststate->fetchFn[i].fn_oid != InvalidOid)
                                681                 :                :         {
                                682         [ +  + ]:          53423 :             if (!isnull[i])
                                683                 :          53417 :                 fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
                                684                 :                :             else
                                685                 :              6 :                 fetchatt[i] = (Datum) 0;
                                686                 :                :         }
 2909 tgl@sss.pgh.pa.us         687         [ +  + ]:         242553 :         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         [ +  + ]:         212378 :             if (!isnull[i])
                                694                 :         210710 :                 fetchatt[i] = datum;
                                695                 :                :             else
                                696                 :           1668 :                 fetchatt[i] = (Datum) 0;
                                697                 :                :         }
                                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                 :                :              */
 3817 heikki.linnakangas@i      705                 :          30175 :             isnull[i] = true;
                                706                 :          30175 :             fetchatt[i] = (Datum) 0;
                                707                 :                :         }
                                708                 :                :     }
                                709                 :                : 
                                710                 :                :     /*
                                711                 :                :      * Get each included attribute.
                                712                 :                :      */
 2372 akorotkov@postgresql      713         [ -  + ]:         265801 :     for (; i < r->rd_att->natts; i++)
                                714                 :                :     {
 2372 akorotkov@postgresql      715                 :UBC           0 :         fetchatt[i] = index_getattr(tuple, i + 1, giststate->leafTupdesc,
                                716                 :                :                                     &isnull[i]);
                                717                 :                :     }
 3817 heikki.linnakangas@i      718                 :CBC      265801 :     MemoryContextSwitchTo(oldcxt);
                                719                 :                : 
 3113 tgl@sss.pgh.pa.us         720                 :         265801 :     return heap_form_tuple(giststate->fetchTupdesc, fetchatt, isnull);
                                721                 :                : }
                                722                 :                : 
                                723                 :                : float
 7389 teodor@sigaev.ru          724                 :       22583166 : gistpenalty(GISTSTATE *giststate, int attno,
                                725                 :                :             GISTENTRY *orig, bool isNullOrig,
                                726                 :                :             GISTENTRY *add, bool isNullAdd)
                                727                 :                : {
 6912 bruce@momjian.us          728                 :       22583166 :     float       penalty = 0.0;
                                729                 :                : 
 2943 peter_e@gmx.net           730         [ +  - ]:       22583166 :     if (giststate->penaltyFn[attno].fn_strict == false ||
                                731   [ +  +  +  + ]:       22583166 :         (isNullOrig == false && isNullAdd == false))
                                732                 :                :     {
 5251 tgl@sss.pgh.pa.us         733                 :       22443049 :         FunctionCall3Coll(&giststate->penaltyFn[attno],
                                734                 :                :                           giststate->supportCollation[attno],
                                735                 :                :                           PointerGetDatum(orig),
                                736                 :                :                           PointerGetDatum(add),
                                737                 :                :                           PointerGetDatum(&penalty));
                                738                 :                :         /* disallow negative or NaN penalty */
 5212                           739   [ +  +  -  + ]:       22443049 :         if (isnan(penalty) || penalty < 0.0)
                                740                 :           2275 :             penalty = 0.0;
                                741                 :                :     }
 6912 bruce@momjian.us          742   [ +  +  +  + ]:         140117 :     else if (isNullOrig && isNullAdd)
 7045 teodor@sigaev.ru          743                 :           9050 :         penalty = 0.0;
                                744                 :                :     else
                                745                 :                :     {
                                746                 :                :         /* try to prevent mixing null and non-null values */
 5032 tgl@sss.pgh.pa.us         747                 :         131067 :         penalty = get_float4_infinity();
                                748                 :                :     }
                                749                 :                : 
 7045 teodor@sigaev.ru          750                 :       22583166 :     return penalty;
                                751                 :                : }
                                752                 :                : 
                                753                 :                : /*
                                754                 :                :  * Initialize a new index page
                                755                 :                :  */
                                756                 :                : void
 1815 heikki.linnakangas@i      757                 :          15946 : gistinitpage(Page page, uint32 f)
                                758                 :                : {
                                759                 :                :     GISTPageOpaque opaque;
                                760                 :                : 
 1534 peter@eisentraut.org      761                 :          15946 :     PageInit(page, BLCKSZ, sizeof(GISTPageOpaqueData));
                                762                 :                : 
 7244 tgl@sss.pgh.pa.us         763                 :          15946 :     opaque = GistPageGetOpaque(page);
 6725                           764                 :          15946 :     opaque->rightlink = InvalidBlockNumber;
                                765                 :          15946 :     opaque->flags = f;
                                766                 :          15946 :     opaque->gist_page_id = GIST_PAGE_ID;
 7244                           767                 :          15946 : }
                                768                 :                : 
                                769                 :                : /*
                                770                 :                :  * Initialize a new index buffer
                                771                 :                :  */
                                772                 :                : void
 1815 heikki.linnakangas@i      773                 :          13952 : GISTInitBuffer(Buffer b, uint32 f)
                                774                 :                : {
                                775                 :                :     Page        page;
                                776                 :                : 
                                777                 :          13952 :     page = BufferGetPage(b);
                                778                 :          13952 :     gistinitpage(page, f);
                                779                 :          13952 : }
                                780                 :                : 
                                781                 :                : /*
                                782                 :                :  * Verify that a freshly-read page looks sane.
                                783                 :                :  */
                                784                 :                : void
 7244 tgl@sss.pgh.pa.us         785                 :        1038487 : gistcheckpage(Relation rel, Buffer buf)
                                786                 :                : {
 3426 kgrittn@postgresql.o      787                 :        1038487 :     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                 :                :      */
 7244 tgl@sss.pgh.pa.us         795         [ -  + ]:        1038487 :     if (PageIsNew(page))
 7244 tgl@sss.pgh.pa.us         796         [ #  # ]:UBC           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                 :                :      */
 6264 tgl@sss.pgh.pa.us         806         [ -  + ]:CBC     1038487 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
 7244 tgl@sss.pgh.pa.us         807         [ #  # ]:UBC           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.")));
 7244 tgl@sss.pgh.pa.us         813                 :CBC     1038487 : }
                                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
  889 andres@anarazel.de        824                 :          13034 : gistNewBuffer(Relation r, Relation heaprel)
                                825                 :                : {
                                826                 :                :     Buffer      buffer;
                                827                 :                : 
                                828                 :                :     /* First, try to get a page from FSM */
                                829                 :                :     for (;;)
 7289 bruce@momjian.us          830                 :UBC           0 :     {
 6185 heikki.linnakangas@i      831                 :CBC       13034 :         BlockNumber blkno = GetFreeIndexPage(r);
                                832                 :                : 
 7376 teodor@sigaev.ru          833         [ +  - ]:          13034 :         if (blkno == InvalidBlockNumber)
 7244 tgl@sss.pgh.pa.us         834                 :          13034 :             break;              /* nothing left in FSM */
                                835                 :                : 
 7376 teodor@sigaev.ru          836                 :UBC           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                 :                :          */
 7289 bruce@momjian.us          842         [ #  # ]:              0 :         if (ConditionalLockBuffer(buffer))
                                843                 :                :         {
 3426 kgrittn@postgresql.o      844                 :              0 :             Page        page = BufferGetPage(buffer);
                                845                 :                : 
                                846                 :                :             /*
                                847                 :                :              * If the page was never initialized, it's OK to use.
                                848                 :                :              */
 7244 tgl@sss.pgh.pa.us         849         [ #  # ]:              0 :             if (PageIsNew(page))
 2360 heikki.linnakangas@i      850                 :              0 :                 return buffer;
                                851                 :                : 
 7244 tgl@sss.pgh.pa.us         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                 :                :              */
 2360 heikki.linnakangas@i      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))
                                     #  #  #  #  #  
                                                 # ]
  889 andres@anarazel.de        867                 :              0 :                     gistXLogPageReuse(r, heaprel, blkno, GistPageGetDeleteXid(page));
                                868                 :                : 
 2360 heikki.linnakangas@i      869                 :              0 :                 return buffer;
                                870                 :                :             }
                                871                 :                : 
 7244 tgl@sss.pgh.pa.us         872                 :              0 :             LockBuffer(buffer, GIST_UNLOCK);
                                873                 :                :         }
                                874                 :                : 
                                875                 :                :         /* Can't use it, so release buffer and try again */
 7289 bruce@momjian.us          876                 :              0 :         ReleaseBuffer(buffer);
                                877                 :                :     }
                                878                 :                : 
                                879                 :                :     /* Must extend the file */
  745 tmunro@postgresql.or      880                 :CBC       13034 :     buffer = ExtendBufferedRel(BMR_REL(r), MAIN_FORKNUM, NULL,
                                881                 :                :                                EB_LOCK_FIRST);
                                882                 :                : 
 7383 teodor@sigaev.ru          883                 :          13034 :     return buffer;
                                884                 :                : }
                                885                 :                : 
                                886                 :                : /* Can this page be recycled yet? */
                                887                 :                : bool
 2360 heikki.linnakangas@i      888                 :           1489 : gistPageRecyclable(Page page)
                                889                 :                : {
 2236                           890         [ -  + ]:           1489 :     if (PageIsNew(page))
 2236 heikki.linnakangas@i      891                 :UBC           0 :         return true;
 2236 heikki.linnakangas@i      892         [ -  + ]:CBC        1489 :     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                 :                :          */
 2236 heikki.linnakangas@i      904                 :UBC           0 :         FullTransactionId deletexid_full = GistPageGetDeleteXid(page);
                                905                 :                : 
 1672 pg@bowt.ie                906                 :              0 :         return GlobalVisCheckRemovableFullXid(NULL, deletexid_full);
                                907                 :                :     }
 2236 heikki.linnakangas@i      908                 :CBC        1489 :     return false;
                                909                 :                : }
                                910                 :                : 
                                911                 :                : bytea *
 3520 tgl@sss.pgh.pa.us         912                 :             99 : 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                 :                : 
 2132 michael@paquier.xyz       919                 :             99 :     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
 3311 tgl@sss.pgh.pa.us         933                 :            234 : gistproperty(Oid index_oid, int attno,
                                934                 :                :              IndexAMProperty prop, const char *propname,
                                935                 :                :              bool *res, bool *isnull)
                                936                 :                : {
                                937                 :                :     Oid         opclass,
                                938                 :                :                 opfamily,
                                939                 :                :                 opcintype;
                                940                 :                :     int16       procno;
                                941                 :                : 
                                942                 :                :     /* Only answer column-level inquiries */
                                943         [ +  + ]:            234 :     if (attno == 0)
                                944                 :            147 :         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      [ +  +  + ]:             87 :     switch (prop)
                                958                 :                :     {
                                959                 :              6 :         case AMPROP_DISTANCE_ORDERABLE:
                                960                 :              6 :             procno = GIST_DISTANCE_PROC;
                                961                 :              6 :             break;
                                962                 :              6 :         case AMPROP_RETURNABLE:
                                963                 :              6 :             procno = GIST_FETCH_PROC;
                                964                 :              6 :             break;
                                965                 :             75 :         default:
                                966                 :             75 :             return false;
                                967                 :                :     }
                                968                 :                : 
                                969                 :                :     /* First we need to know the column's opclass. */
 2544 akorotkov@postgresql      970                 :             12 :     opclass = get_index_column_opclass(index_oid, attno);
                                971         [ -  + ]:             12 :     if (!OidIsValid(opclass))
                                972                 :                :     {
 3311 tgl@sss.pgh.pa.us         973                 :UBC           0 :         *isnull = true;
                                974                 :              0 :         return true;
                                975                 :                :     }
                                976                 :                : 
                                977                 :                :     /* Now look up the opclass family and input datatype. */
 2544 akorotkov@postgresql      978         [ -  + ]:CBC          12 :     if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
                                979                 :                :     {
 3311 tgl@sss.pgh.pa.us         980                 :UBC           0 :         *isnull = true;
                                981                 :              0 :         return true;
                                982                 :                :     }
                                983                 :                : 
                                984                 :                :     /* And now we can check whether the function is provided. */
                                985                 :                : 
 3311 tgl@sss.pgh.pa.us         986                 :CBC          12 :     *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                 :                :      */
 2909                           996   [ +  +  +  - ]:             12 :     if (prop == AMPROP_RETURNABLE && !*res)
                                997                 :                :     {
                                998                 :              6 :         *res = !SearchSysCacheExists4(AMPROCNUM,
                                999                 :                :                                       ObjectIdGetDatum(opfamily),
                               1000                 :                :                                       ObjectIdGetDatum(opcintype),
                               1001                 :                :                                       ObjectIdGetDatum(opcintype),
                               1002                 :                :                                       Int16GetDatum(GIST_COMPRESS_PROC));
                               1003                 :                :     }
                               1004                 :                : 
 2544 akorotkov@postgresql     1005                 :             12 :     *isnull = false;
                               1006                 :                : 
 3311 tgl@sss.pgh.pa.us        1007                 :             12 :     return true;
                               1008                 :                : }
                               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
 4590 heikki.linnakangas@i     1016                 :             42 : gistGetFakeLSN(Relation rel)
                               1017                 :                : {
                               1018         [ +  + ]:             42 :     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                 :              9 :         return counter++;
                               1027                 :                :     }
 1629 bruce@momjian.us         1028         [ -  + ]:             33 :     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;
 1981 noah@leadboat.com        1037                 :UBC           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 (!XLogRecPtrIsInvalid(lastlsn) && lastlsn == currlsn)
                               1044                 :              0 :             currlsn = gistXLogAssignLSN();
                               1045                 :                : 
                               1046                 :              0 :         lastlsn = currlsn;
                               1047                 :              0 :         return currlsn;
                               1048                 :                :     }
                               1049                 :                :     else
                               1050                 :                :     {
                               1051                 :                :         /*
                               1052                 :                :          * Unlogged relations are accessible from other backends, and survive
                               1053                 :                :          * (clean) restarts. GetFakeLSNForUnloggedRel() handles that for us.
                               1054                 :                :          */
 4590 heikki.linnakangas@i     1055         [ -  + ]:CBC          33 :         Assert(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED);
                               1056                 :             33 :         return GetFakeLSNForUnloggedRel();
                               1057                 :                :     }
                               1058                 :                : }
                               1059                 :                : 
                               1060                 :                : /*
                               1061                 :                :  * This is a stratnum translation support function for GiST opclasses that use
                               1062                 :                :  * the RT*StrategyNumber constants.
                               1063                 :                :  */
                               1064                 :                : Datum
   96 peter@eisentraut.org     1065                 :           1132 : gist_translate_cmptype_common(PG_FUNCTION_ARGS)
                               1066                 :                : {
  234                          1067                 :           1132 :     CompareType cmptype = PG_GETARG_INT32(0);
                               1068                 :                : 
                               1069   [ +  -  -  -  :           1132 :     switch (cmptype)
                                        -  +  +  - ]
                               1070                 :                :     {
                               1071                 :            442 :         case COMPARE_EQ:
                               1072                 :            442 :             PG_RETURN_UINT16(RTEqualStrategyNumber);
  234 peter@eisentraut.org     1073                 :UBC           0 :         case COMPARE_LT:
                               1074                 :              0 :             PG_RETURN_UINT16(RTLessStrategyNumber);
                               1075                 :              0 :         case COMPARE_LE:
                               1076                 :              0 :             PG_RETURN_UINT16(RTLessEqualStrategyNumber);
                               1077                 :              0 :         case COMPARE_GT:
                               1078                 :              0 :             PG_RETURN_UINT16(RTGreaterStrategyNumber);
                               1079                 :              0 :         case COMPARE_GE:
                               1080                 :              0 :             PG_RETURN_UINT16(RTGreaterEqualStrategyNumber);
  234 peter@eisentraut.org     1081                 :CBC         386 :         case COMPARE_OVERLAP:
                               1082                 :            386 :             PG_RETURN_UINT16(RTOverlapStrategyNumber);
                               1083                 :            304 :         case COMPARE_CONTAINED_BY:
                               1084                 :            304 :             PG_RETURN_UINT16(RTContainedByStrategyNumber);
  234 peter@eisentraut.org     1085                 :UBC           0 :         default:
                               1086                 :              0 :             PG_RETURN_UINT16(InvalidStrategy);
                               1087                 :                :     }
                               1088                 :                : }
                               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
  197 peter@eisentraut.org     1098                 :CBC        1129 : gisttranslatecmptype(CompareType cmptype, Oid opfamily)
                               1099                 :                : {
                               1100                 :                :     Oid         funcid;
                               1101                 :                :     Datum       result;
                               1102                 :                : 
                               1103                 :                :     /* Check whether the function is provided. */
   96                          1104                 :           1129 :     funcid = get_opfamily_proc(opfamily, ANYOID, ANYOID, GIST_TRANSLATE_CMPTYPE_PROC);
  354                          1105         [ -  + ]:           1129 :     if (!OidIsValid(funcid))
  354 peter@eisentraut.org     1106                 :UBC           0 :         return InvalidStrategy;
                               1107                 :                : 
                               1108                 :                :     /* Ask the translation function */
  234 peter@eisentraut.org     1109                 :CBC        1129 :     result = OidFunctionCall1Coll(funcid, InvalidOid, Int32GetDatum(cmptype));
  354                          1110                 :           1129 :     return DatumGetUInt16(result);
                               1111                 :                : }
        

Generated by: LCOV version 2.4-beta