LCOV - code coverage report
Current view: top level - src/backend/access/spgist - spgscan.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 93.7 % 554 519
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 23 23
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 71.1 % 305 217

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * spgscan.c
       4                 :             :  *        routines for scanning SP-GiST indexes
       5                 :             :  *
       6                 :             :  *
       7                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *                      src/backend/access/spgist/spgscan.c
      12                 :             :  *
      13                 :             :  *-------------------------------------------------------------------------
      14                 :             :  */
      15                 :             : 
      16                 :             : #include "postgres.h"
      17                 :             : 
      18                 :             : #include "access/genam.h"
      19                 :             : #include "access/relscan.h"
      20                 :             : #include "access/spgist_private.h"
      21                 :             : #include "executor/instrument_node.h"
      22                 :             : #include "miscadmin.h"
      23                 :             : #include "pgstat.h"
      24                 :             : #include "storage/bufmgr.h"
      25                 :             : #include "utils/datum.h"
      26                 :             : #include "utils/float.h"
      27                 :             : #include "utils/lsyscache.h"
      28                 :             : #include "utils/memutils.h"
      29                 :             : #include "utils/rel.h"
      30                 :             : 
      31                 :             : typedef void (*storeRes_func) (SpGistScanOpaque so, ItemPointer heapPtr,
      32                 :             :                                                            Datum leafValue, bool isNull,
      33                 :             :                                                            SpGistLeafTuple leafTuple, bool recheck,
      34                 :             :                                                            bool recheckDistances, double *distances);
      35                 :             : 
      36                 :             : /*
      37                 :             :  * Pairing heap comparison function for the SpGistSearchItem queue.
      38                 :             :  * KNN-searches currently only support NULLS LAST.  So, preserve this logic
      39                 :             :  * here.
      40                 :             :  */
      41                 :             : static int
      42                 :     2082729 : pairingheap_SpGistSearchItem_cmp(const pairingheap_node *a,
      43                 :             :                                                                  const pairingheap_node *b, void *arg)
      44                 :             : {
      45                 :     2082729 :         const SpGistSearchItem *sa = (const SpGistSearchItem *) a;
      46                 :     2082729 :         const SpGistSearchItem *sb = (const SpGistSearchItem *) b;
      47                 :     2082729 :         SpGistScanOpaque so = (SpGistScanOpaque) arg;
      48                 :     2082729 :         int                     i;
      49                 :             : 
      50         [ +  + ]:     2082729 :         if (sa->isNull)
      51                 :             :         {
      52         [ +  + ]:          97 :                 if (!sb->isNull)
      53                 :          90 :                         return -1;
      54                 :           7 :         }
      55         [ +  + ]:     2082632 :         else if (sb->isNull)
      56                 :             :         {
      57                 :          75 :                 return 1;
      58                 :             :         }
      59                 :             :         else
      60                 :             :         {
      61                 :             :                 /* Order according to distance comparison */
      62         [ +  + ]:     2123224 :                 for (i = 0; i < so->numberOfNonNullOrderBys; i++)
      63                 :             :                 {
      64   [ +  +  +  +  :     2017719 :                         if (isnan(sa->distances[i]) && isnan(sb->distances[i]))
          +  -  #  #  #  
                #  #  # ]
      65                 :           0 :                                 continue;               /* NaN == NaN */
      66   [ -  +  #  #  :      672573 :                         if (isnan(sa->distances[i]))
                   +  - ]
      67                 :           0 :                                 return -1;              /* NaN > number */
      68   [ -  +  #  #  :      672573 :                         if (isnan(sb->distances[i]))
                   +  - ]
      69                 :           0 :                                 return 1;               /* number < NaN */
      70         [ +  + ]:      672573 :                         if (sa->distances[i] != sb->distances[i])
      71                 :      631906 :                                 return (sa->distances[i] < sb->distances[i]) ? 1 : -1;
      72                 :       40667 :                 }
      73                 :             :         }
      74                 :             : 
      75                 :             :         /* Leaf items go before inner pages, to ensure a depth-first search */
      76   [ +  +  +  + ]:      105512 :         if (sa->isLeaf && !sb->isLeaf)
      77                 :         730 :                 return 1;
      78   [ +  +  +  + ]:      104782 :         if (!sa->isLeaf && sb->isLeaf)
      79                 :         824 :                 return -1;
      80                 :             : 
      81                 :      103958 :         return 0;
      82                 :      737583 : }
      83                 :             : 
      84                 :             : static void
      85                 :       77386 : spgFreeSearchItem(SpGistScanOpaque so, SpGistSearchItem *item)
      86                 :             : {
      87                 :             :         /* value is of type attType if isLeaf, else of type attLeafType */
      88                 :             :         /* (no, that is not backwards; yes, it's confusing) */
      89         [ +  + ]:       77386 :         if (!(item->isLeaf ? so->state.attType.attbyval :
      90         [ +  + ]:      126441 :                   so->state.attLeafType.attbyval) &&
      91                 :       49055 :                 DatumGetPointer(item->value) != NULL)
      92                 :       49055 :                 pfree(DatumGetPointer(item->value));
      93                 :             : 
      94         [ +  + ]:       77386 :         if (item->leafTuple)
      95                 :          10 :                 pfree(item->leafTuple);
      96                 :             : 
      97         [ +  + ]:       77386 :         if (item->traversalValue)
      98                 :        7412 :                 pfree(item->traversalValue);
      99                 :             : 
     100                 :       77386 :         pfree(item);
     101                 :       77386 : }
     102                 :             : 
     103                 :             : /*
     104                 :             :  * Add SpGistSearchItem to queue
     105                 :             :  *
     106                 :             :  * Called in queue context
     107                 :             :  */
     108                 :             : static void
     109                 :       77847 : spgAddSearchItemToQueue(SpGistScanOpaque so, SpGistSearchItem *item)
     110                 :             : {
     111                 :       77847 :         pairingheap_add(so->scanQueue, &item->phNode);
     112                 :       77847 : }
     113                 :             : 
     114                 :             : static SpGistSearchItem *
     115                 :       77847 : spgAllocSearchItem(SpGistScanOpaque so, bool isnull, double *distances)
     116                 :             : {
     117                 :             :         /* allocate distance array only for non-NULL items */
     118                 :      155694 :         SpGistSearchItem *item =
     119         [ +  + ]:       77847 :                 palloc(SizeOfSpGistSearchItem(isnull ? 0 : so->numberOfNonNullOrderBys));
     120                 :             : 
     121                 :       77847 :         item->isNull = isnull;
     122                 :             : 
     123   [ +  +  +  + ]:       77847 :         if (!isnull && so->numberOfNonNullOrderBys > 0)
     124                 :       63006 :                 memcpy(item->distances, distances,
     125                 :             :                            sizeof(item->distances[0]) * so->numberOfNonNullOrderBys);
     126                 :             : 
     127                 :      155694 :         return item;
     128                 :       77847 : }
     129                 :             : 
     130                 :             : static void
     131                 :         163 : spgAddStartItem(SpGistScanOpaque so, bool isnull)
     132                 :             : {
     133                 :         326 :         SpGistSearchItem *startEntry =
     134                 :         163 :                 spgAllocSearchItem(so, isnull, so->zeroDistances);
     135                 :             : 
     136                 :         326 :         ItemPointerSet(&startEntry->heapPtr,
     137                 :         163 :                                    isnull ? SPGIST_NULL_BLKNO : SPGIST_ROOT_BLKNO,
     138                 :             :                                    FirstOffsetNumber);
     139                 :         163 :         startEntry->isLeaf = false;
     140                 :         163 :         startEntry->level = 0;
     141                 :         163 :         startEntry->value = (Datum) 0;
     142                 :         163 :         startEntry->leafTuple = NULL;
     143                 :         163 :         startEntry->traversalValue = NULL;
     144                 :         163 :         startEntry->recheck = false;
     145                 :         163 :         startEntry->recheckDistances = false;
     146                 :             : 
     147                 :         163 :         spgAddSearchItemToQueue(so, startEntry);
     148                 :         163 : }
     149                 :             : 
     150                 :             : /*
     151                 :             :  * Initialize queue to search the root page, resetting
     152                 :             :  * any previously active scan
     153                 :             :  */
     154                 :             : static void
     155                 :         154 : resetSpGistScanOpaque(SpGistScanOpaque so)
     156                 :             : {
     157                 :         154 :         MemoryContext oldCtx;
     158                 :             : 
     159                 :         154 :         MemoryContextReset(so->traversalCxt);
     160                 :             : 
     161                 :         154 :         oldCtx = MemoryContextSwitchTo(so->traversalCxt);
     162                 :             : 
     163                 :             :         /* initialize queue only for distance-ordered scans */
     164                 :         154 :         so->scanQueue = pairingheap_allocate(pairingheap_SpGistSearchItem_cmp, so);
     165                 :             : 
     166         [ +  + ]:         154 :         if (so->searchNulls)
     167                 :             :                 /* Add a work item to scan the null index entries */
     168                 :          11 :                 spgAddStartItem(so, true);
     169                 :             : 
     170         [ +  + ]:         154 :         if (so->searchNonNulls)
     171                 :             :                 /* Add a work item to scan the non-null index entries */
     172                 :         152 :                 spgAddStartItem(so, false);
     173                 :             : 
     174                 :         154 :         MemoryContextSwitchTo(oldCtx);
     175                 :             : 
     176         [ +  + ]:         154 :         if (so->numberOfOrderBys > 0)
     177                 :             :         {
     178                 :             :                 /* Must pfree distances to avoid memory leak */
     179                 :          13 :                 int                     i;
     180                 :             : 
     181         [ +  + ]:          15 :                 for (i = 0; i < so->nPtrs; i++)
     182         [ -  + ]:           4 :                         if (so->distances[i])
     183                 :           2 :                                 pfree(so->distances[i]);
     184                 :          13 :         }
     185                 :             : 
     186         [ +  + ]:         154 :         if (so->want_itup)
     187                 :             :         {
     188                 :             :                 /* Must pfree reconstructed tuples to avoid memory leak */
     189                 :           4 :                 int                     i;
     190                 :             : 
     191         [ +  + ]:          15 :                 for (i = 0; i < so->nPtrs; i++)
     192                 :          11 :                         pfree(so->reconTups[i]);
     193                 :           4 :         }
     194                 :         154 :         so->iPtr = so->nPtrs = 0;
     195                 :         154 : }
     196                 :             : 
     197                 :             : /*
     198                 :             :  * Prepare scan keys in SpGistScanOpaque from caller-given scan keys
     199                 :             :  *
     200                 :             :  * Sets searchNulls, searchNonNulls, numberOfKeys, keyData fields of *so.
     201                 :             :  *
     202                 :             :  * The point here is to eliminate null-related considerations from what the
     203                 :             :  * opclass consistent functions need to deal with.  We assume all SPGiST-
     204                 :             :  * indexable operators are strict, so any null RHS value makes the scan
     205                 :             :  * condition unsatisfiable.  We also pull out any IS NULL/IS NOT NULL
     206                 :             :  * conditions; their effect is reflected into searchNulls/searchNonNulls.
     207                 :             :  */
     208                 :             : static void
     209                 :         154 : spgPrepareScanKeys(IndexScanDesc scan)
     210                 :             : {
     211                 :         154 :         SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
     212                 :         154 :         bool            qual_ok;
     213                 :         154 :         bool            haveIsNull;
     214                 :         154 :         bool            haveNotNull;
     215                 :         154 :         int                     nkeys;
     216                 :         154 :         int                     i;
     217                 :             : 
     218                 :         154 :         so->numberOfOrderBys = scan->numberOfOrderBys;
     219                 :         154 :         so->orderByData = scan->orderByData;
     220                 :             : 
     221         [ +  + ]:         154 :         if (so->numberOfOrderBys <= 0)
     222                 :         141 :                 so->numberOfNonNullOrderBys = 0;
     223                 :             :         else
     224                 :             :         {
     225                 :          13 :                 int                     j = 0;
     226                 :             : 
     227                 :             :                 /*
     228                 :             :                  * Remove all NULL keys, but remember their offsets in the original
     229                 :             :                  * array.
     230                 :             :                  */
     231         [ +  + ]:          29 :                 for (i = 0; i < scan->numberOfOrderBys; i++)
     232                 :             :                 {
     233                 :          16 :                         ScanKey         skey = &so->orderByData[i];
     234                 :             : 
     235         [ +  + ]:          16 :                         if (skey->sk_flags & SK_ISNULL)
     236                 :           1 :                                 so->nonNullOrderByOffsets[i] = -1;
     237                 :             :                         else
     238                 :             :                         {
     239         [ +  + ]:          15 :                                 if (i != j)
     240                 :           1 :                                         so->orderByData[j] = *skey;
     241                 :             : 
     242                 :          15 :                                 so->nonNullOrderByOffsets[i] = j++;
     243                 :             :                         }
     244                 :          16 :                 }
     245                 :             : 
     246                 :          13 :                 so->numberOfNonNullOrderBys = j;
     247                 :          13 :         }
     248                 :             : 
     249         [ +  + ]:         154 :         if (scan->numberOfKeys <= 0)
     250                 :             :         {
     251                 :             :                 /* If no quals, whole-index scan is required */
     252                 :           9 :                 so->searchNulls = true;
     253                 :           9 :                 so->searchNonNulls = true;
     254                 :           9 :                 so->numberOfKeys = 0;
     255                 :           9 :                 return;
     256                 :             :         }
     257                 :             : 
     258                 :             :         /* Examine the given quals */
     259                 :         145 :         qual_ok = true;
     260                 :         145 :         haveIsNull = haveNotNull = false;
     261                 :         145 :         nkeys = 0;
     262         [ +  + ]:         290 :         for (i = 0; i < scan->numberOfKeys; i++)
     263                 :             :         {
     264                 :         145 :                 ScanKey         skey = &scan->keyData[i];
     265                 :             : 
     266         [ +  + ]:         145 :                 if (skey->sk_flags & SK_SEARCHNULL)
     267                 :           2 :                         haveIsNull = true;
     268         [ +  + ]:         143 :                 else if (skey->sk_flags & SK_SEARCHNOTNULL)
     269                 :           4 :                         haveNotNull = true;
     270         [ -  + ]:         139 :                 else if (skey->sk_flags & SK_ISNULL)
     271                 :             :                 {
     272                 :             :                         /* ordinary qual with null argument - unsatisfiable */
     273                 :           0 :                         qual_ok = false;
     274                 :           0 :                         break;
     275                 :             :                 }
     276                 :             :                 else
     277                 :             :                 {
     278                 :             :                         /* ordinary qual, propagate into so->keyData */
     279                 :         139 :                         so->keyData[nkeys++] = *skey;
     280                 :             :                         /* this effectively creates a not-null requirement */
     281                 :         139 :                         haveNotNull = true;
     282                 :             :                 }
     283         [ -  + ]:         145 :         }
     284                 :             : 
     285                 :             :         /* IS NULL in combination with something else is unsatisfiable */
     286   [ +  +  +  - ]:         145 :         if (haveIsNull && haveNotNull)
     287                 :           0 :                 qual_ok = false;
     288                 :             : 
     289                 :             :         /* Emit results */
     290         [ +  - ]:         145 :         if (qual_ok)
     291                 :             :         {
     292                 :         145 :                 so->searchNulls = haveIsNull;
     293                 :         145 :                 so->searchNonNulls = haveNotNull;
     294                 :         145 :                 so->numberOfKeys = nkeys;
     295                 :         145 :         }
     296                 :             :         else
     297                 :             :         {
     298                 :           0 :                 so->searchNulls = false;
     299                 :           0 :                 so->searchNonNulls = false;
     300                 :           0 :                 so->numberOfKeys = 0;
     301                 :             :         }
     302                 :         154 : }
     303                 :             : 
     304                 :             : IndexScanDesc
     305                 :         150 : spgbeginscan(Relation rel, int keysz, int orderbysz)
     306                 :             : {
     307                 :         150 :         IndexScanDesc scan;
     308                 :         150 :         SpGistScanOpaque so;
     309                 :         150 :         int                     i;
     310                 :             : 
     311                 :         150 :         scan = RelationGetIndexScan(rel, keysz, orderbysz);
     312                 :             : 
     313                 :         150 :         so = palloc0_object(SpGistScanOpaqueData);
     314         [ +  + ]:         150 :         if (keysz > 0)
     315                 :         143 :                 so->keyData = palloc_array(ScanKeyData, keysz);
     316                 :             :         else
     317                 :           7 :                 so->keyData = NULL;
     318                 :         150 :         initSpGistState(&so->state, scan->indexRelation);
     319                 :             : 
     320                 :         150 :         so->tempCxt = AllocSetContextCreate(CurrentMemoryContext,
     321                 :             :                                                                                 "SP-GiST search temporary context",
     322                 :             :                                                                                 ALLOCSET_DEFAULT_SIZES);
     323                 :         150 :         so->traversalCxt = AllocSetContextCreate(CurrentMemoryContext,
     324                 :             :                                                                                          "SP-GiST traversal-value context",
     325                 :             :                                                                                          ALLOCSET_DEFAULT_SIZES);
     326                 :             : 
     327                 :             :         /*
     328                 :             :          * Set up reconTupDesc and xs_hitupdesc in case it's an index-only scan,
     329                 :             :          * making sure that the key column is shown as being of type attType.
     330                 :             :          * (It's rather annoying to do this work when it might be wasted, but for
     331                 :             :          * most opclasses we can re-use the index reldesc instead of making one.)
     332                 :             :          */
     333                 :         150 :         so->reconTupDesc = scan->xs_hitupdesc =
     334                 :         150 :                 getSpGistTupleDesc(rel, &so->state.attType);
     335                 :             : 
     336                 :             :         /* Allocate various arrays needed for order-by scans */
     337         [ +  + ]:         150 :         if (scan->numberOfOrderBys > 0)
     338                 :             :         {
     339                 :             :                 /* This will be filled in spgrescan, but allocate the space here */
     340                 :          11 :                 so->orderByTypes = palloc_array(Oid, scan->numberOfOrderBys);
     341                 :          11 :                 so->nonNullOrderByOffsets = palloc_array(int, scan->numberOfOrderBys);
     342                 :             : 
     343                 :             :                 /* These arrays have constant contents, so we can fill them now */
     344                 :          11 :                 so->zeroDistances = palloc_array(double, scan->numberOfOrderBys);
     345                 :          11 :                 so->infDistances = palloc_array(double, scan->numberOfOrderBys);
     346                 :             : 
     347         [ +  + ]:          23 :                 for (i = 0; i < scan->numberOfOrderBys; i++)
     348                 :             :                 {
     349                 :          12 :                         so->zeroDistances[i] = 0.0;
     350                 :          12 :                         so->infDistances[i] = get_float8_infinity();
     351                 :          12 :                 }
     352                 :             : 
     353                 :          11 :                 scan->xs_orderbyvals = palloc0_array(Datum, scan->numberOfOrderBys);
     354                 :          11 :                 scan->xs_orderbynulls = palloc_array(bool, scan->numberOfOrderBys);
     355                 :          11 :                 memset(scan->xs_orderbynulls, true,
     356                 :             :                            sizeof(bool) * scan->numberOfOrderBys);
     357                 :          11 :         }
     358                 :             : 
     359                 :         300 :         fmgr_info_copy(&so->innerConsistentFn,
     360                 :         150 :                                    index_getprocinfo(rel, 1, SPGIST_INNER_CONSISTENT_PROC),
     361                 :         150 :                                    CurrentMemoryContext);
     362                 :             : 
     363                 :         300 :         fmgr_info_copy(&so->leafConsistentFn,
     364                 :         150 :                                    index_getprocinfo(rel, 1, SPGIST_LEAF_CONSISTENT_PROC),
     365                 :         150 :                                    CurrentMemoryContext);
     366                 :             : 
     367                 :         150 :         so->indexCollation = rel->rd_indcollation[0];
     368                 :             : 
     369                 :         150 :         scan->opaque = so;
     370                 :             : 
     371                 :         300 :         return scan;
     372                 :         150 : }
     373                 :             : 
     374                 :             : void
     375                 :         154 : spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
     376                 :             :                   ScanKey orderbys, int norderbys)
     377                 :             : {
     378                 :         154 :         SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
     379                 :             : 
     380                 :             :         /* copy scankeys into local storage */
     381   [ +  -  +  + ]:         154 :         if (scankey && scan->numberOfKeys > 0)
     382                 :         145 :                 memcpy(scan->keyData, scankey, scan->numberOfKeys * sizeof(ScanKeyData));
     383                 :             : 
     384                 :             :         /* initialize order-by data if needed */
     385   [ +  +  +  + ]:         154 :         if (orderbys && scan->numberOfOrderBys > 0)
     386                 :             :         {
     387                 :          13 :                 int                     i;
     388                 :             : 
     389                 :          13 :                 memcpy(scan->orderByData, orderbys, scan->numberOfOrderBys * sizeof(ScanKeyData));
     390                 :             : 
     391         [ +  + ]:          29 :                 for (i = 0; i < scan->numberOfOrderBys; i++)
     392                 :             :                 {
     393                 :          16 :                         ScanKey         skey = &scan->orderByData[i];
     394                 :             : 
     395                 :             :                         /*
     396                 :             :                          * Look up the datatype returned by the original ordering
     397                 :             :                          * operator. SP-GiST always uses a float8 for the distance
     398                 :             :                          * function, but the ordering operator could be anything else.
     399                 :             :                          *
     400                 :             :                          * XXX: The distance function is only allowed to be lossy if the
     401                 :             :                          * ordering operator's result type is float4 or float8.  Otherwise
     402                 :             :                          * we don't know how to return the distance to the executor.  But
     403                 :             :                          * we cannot check that here, as we won't know if the distance
     404                 :             :                          * function is lossy until it returns *recheck = true for the
     405                 :             :                          * first time.
     406                 :             :                          */
     407                 :          16 :                         so->orderByTypes[i] = get_func_rettype(skey->sk_func.fn_oid);
     408                 :          16 :                 }
     409                 :          13 :         }
     410                 :             : 
     411                 :             :         /* preprocess scankeys, set up the representation in *so */
     412                 :         154 :         spgPrepareScanKeys(scan);
     413                 :             : 
     414                 :             :         /* set up starting queue entries */
     415                 :         154 :         resetSpGistScanOpaque(so);
     416                 :             : 
     417                 :             :         /* count an indexscan for stats */
     418   [ +  -  +  -  :         154 :         pgstat_count_index_scan(scan->indexRelation);
                   #  # ]
     419         [ +  - ]:         154 :         if (scan->instrument)
     420                 :         154 :                 scan->instrument->nsearches++;
     421                 :         154 : }
     422                 :             : 
     423                 :             : void
     424                 :         150 : spgendscan(IndexScanDesc scan)
     425                 :             : {
     426                 :         150 :         SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
     427                 :             : 
     428                 :         150 :         MemoryContextDelete(so->tempCxt);
     429                 :         150 :         MemoryContextDelete(so->traversalCxt);
     430                 :             : 
     431         [ +  + ]:         150 :         if (so->keyData)
     432                 :         143 :                 pfree(so->keyData);
     433                 :             : 
     434   [ +  -  +  + ]:         150 :         if (so->state.leafTupDesc &&
     435                 :         150 :                 so->state.leafTupDesc != RelationGetDescr(so->state.index))
     436                 :           1 :                 FreeTupleDesc(so->state.leafTupDesc);
     437                 :             : 
     438         [ -  + ]:         150 :         if (so->state.deadTupleStorage)
     439                 :         150 :                 pfree(so->state.deadTupleStorage);
     440                 :             : 
     441         [ +  + ]:         150 :         if (scan->numberOfOrderBys > 0)
     442                 :             :         {
     443                 :          11 :                 pfree(so->orderByTypes);
     444                 :          11 :                 pfree(so->nonNullOrderByOffsets);
     445                 :          11 :                 pfree(so->zeroDistances);
     446                 :          11 :                 pfree(so->infDistances);
     447                 :          11 :                 pfree(scan->xs_orderbyvals);
     448                 :          11 :                 pfree(scan->xs_orderbynulls);
     449                 :          11 :         }
     450                 :             : 
     451                 :         150 :         pfree(so);
     452                 :         150 : }
     453                 :             : 
     454                 :             : /*
     455                 :             :  * Leaf SpGistSearchItem constructor, called in queue context
     456                 :             :  */
     457                 :             : static SpGistSearchItem *
     458                 :       60985 : spgNewHeapItem(SpGistScanOpaque so, int level, SpGistLeafTuple leafTuple,
     459                 :             :                            Datum leafValue, bool recheck, bool recheckDistances,
     460                 :             :                            bool isnull, double *distances)
     461                 :             : {
     462                 :       60985 :         SpGistSearchItem *item = spgAllocSearchItem(so, isnull, distances);
     463                 :             : 
     464                 :       60985 :         item->level = level;
     465                 :       60985 :         item->heapPtr = leafTuple->heapPtr;
     466                 :             : 
     467                 :             :         /*
     468                 :             :          * If we need the reconstructed value, copy it to queue cxt out of tmp
     469                 :             :          * cxt.  Caution: the leaf_consistent method may not have supplied a value
     470                 :             :          * if we didn't ask it to, and mildly-broken methods might supply one of
     471                 :             :          * the wrong type.  The correct leafValue type is attType not leafType.
     472                 :             :          */
     473         [ +  + ]:       60985 :         if (so->want_itup)
     474                 :             :         {
     475         [ +  + ]:       46559 :                 item->value = isnull ? (Datum) 0 :
     476                 :       93106 :                         datumCopy(leafValue, so->state.attType.attbyval,
     477                 :       46553 :                                           so->state.attType.attlen);
     478                 :             : 
     479                 :             :                 /*
     480                 :             :                  * If we're going to need to reconstruct INCLUDE attributes, store the
     481                 :             :                  * whole leaf tuple so we can get the INCLUDE attributes out of it.
     482                 :             :                  */
     483         [ +  + ]:       46559 :                 if (so->state.leafTupDesc->natts > 1)
     484                 :             :                 {
     485                 :          34 :                         item->leafTuple = palloc(leafTuple->size);
     486                 :          34 :                         memcpy(item->leafTuple, leafTuple, leafTuple->size);
     487                 :          34 :                 }
     488                 :             :                 else
     489                 :       46525 :                         item->leafTuple = NULL;
     490                 :       46559 :         }
     491                 :             :         else
     492                 :             :         {
     493                 :       14426 :                 item->value = (Datum) 0;
     494                 :       14426 :                 item->leafTuple = NULL;
     495                 :             :         }
     496                 :       60985 :         item->traversalValue = NULL;
     497                 :       60985 :         item->isLeaf = true;
     498                 :       60985 :         item->recheck = recheck;
     499                 :       60985 :         item->recheckDistances = recheckDistances;
     500                 :             : 
     501                 :      121970 :         return item;
     502                 :       60985 : }
     503                 :             : 
     504                 :             : /*
     505                 :             :  * Test whether a leaf tuple satisfies all the scan keys
     506                 :             :  *
     507                 :             :  * *reportedSome is set to true if:
     508                 :             :  *              the scan is not ordered AND the item satisfies the scankeys
     509                 :             :  */
     510                 :             : static bool
     511                 :      440364 : spgLeafTest(SpGistScanOpaque so, SpGistSearchItem *item,
     512                 :             :                         SpGistLeafTuple leafTuple, bool isnull,
     513                 :             :                         bool *reportedSome, storeRes_func storeRes)
     514                 :             : {
     515                 :      440364 :         Datum           leafValue;
     516                 :      440364 :         double     *distances;
     517                 :      440364 :         bool            result;
     518                 :      440364 :         bool            recheck;
     519                 :      440364 :         bool            recheckDistances;
     520                 :             : 
     521         [ +  + ]:      440364 :         if (isnull)
     522                 :             :         {
     523                 :             :                 /* Should not have arrived on a nulls page unless nulls are wanted */
     524         [ +  - ]:          20 :                 Assert(so->searchNulls);
     525                 :          20 :                 leafValue = (Datum) 0;
     526                 :          20 :                 distances = NULL;
     527                 :          20 :                 recheck = false;
     528                 :          20 :                 recheckDistances = false;
     529                 :          20 :                 result = true;
     530                 :          20 :         }
     531                 :             :         else
     532                 :             :         {
     533                 :      440344 :                 spgLeafConsistentIn in;
     534                 :      440344 :                 spgLeafConsistentOut out;
     535                 :             : 
     536                 :             :                 /* use temp context for calling leaf_consistent */
     537                 :      440344 :                 MemoryContext oldCxt = MemoryContextSwitchTo(so->tempCxt);
     538                 :             : 
     539                 :      440344 :                 in.scankeys = so->keyData;
     540                 :      440344 :                 in.nkeys = so->numberOfKeys;
     541                 :      440344 :                 in.orderbys = so->orderByData;
     542                 :      440344 :                 in.norderbys = so->numberOfNonNullOrderBys;
     543         [ +  - ]:      440344 :                 Assert(!item->isLeaf);       /* else reconstructedValue would be wrong type */
     544                 :      440344 :                 in.reconstructedValue = item->value;
     545                 :      440344 :                 in.traversalValue = item->traversalValue;
     546                 :      440344 :                 in.level = item->level;
     547                 :      440344 :                 in.returnData = so->want_itup;
     548                 :      440344 :                 in.leafDatum = SGLTDATUM(leafTuple, &so->state);
     549                 :             : 
     550                 :      440344 :                 out.leafValue = (Datum) 0;
     551                 :      440344 :                 out.recheck = false;
     552                 :      440344 :                 out.distances = NULL;
     553                 :      440344 :                 out.recheckDistances = false;
     554                 :             : 
     555                 :      880688 :                 result = DatumGetBool(FunctionCall2Coll(&so->leafConsistentFn,
     556                 :      440344 :                                                                                                 so->indexCollation,
     557                 :      440344 :                                                                                                 PointerGetDatum(&in),
     558                 :      440344 :                                                                                                 PointerGetDatum(&out)));
     559                 :      440344 :                 recheck = out.recheck;
     560                 :      440344 :                 recheckDistances = out.recheckDistances;
     561                 :      440344 :                 leafValue = out.leafValue;
     562                 :      440344 :                 distances = out.distances;
     563                 :             : 
     564                 :      440344 :                 MemoryContextSwitchTo(oldCxt);
     565                 :      440344 :         }
     566                 :             : 
     567         [ +  + ]:      440364 :         if (result)
     568                 :             :         {
     569                 :             :                 /* item passes the scankeys */
     570         [ +  + ]:      343532 :                 if (so->numberOfNonNullOrderBys > 0)
     571                 :             :                 {
     572                 :             :                         /* the scan is ordered -> add the item to the queue */
     573                 :       60985 :                         MemoryContext oldCxt = MemoryContextSwitchTo(so->traversalCxt);
     574                 :      121970 :                         SpGistSearchItem *heapItem = spgNewHeapItem(so, item->level,
     575                 :       60985 :                                                                                                                 leafTuple,
     576                 :       60985 :                                                                                                                 leafValue,
     577                 :       60985 :                                                                                                                 recheck,
     578                 :       60985 :                                                                                                                 recheckDistances,
     579                 :       60985 :                                                                                                                 isnull,
     580                 :       60985 :                                                                                                                 distances);
     581                 :             : 
     582                 :       60985 :                         spgAddSearchItemToQueue(so, heapItem);
     583                 :             : 
     584                 :       60985 :                         MemoryContextSwitchTo(oldCxt);
     585                 :       60985 :                 }
     586                 :             :                 else
     587                 :             :                 {
     588                 :             :                         /* non-ordered scan, so report the item right away */
     589         [ +  - ]:      282547 :                         Assert(!recheckDistances);
     590                 :      565094 :                         storeRes(so, &leafTuple->heapPtr, leafValue, isnull,
     591                 :      282547 :                                          leafTuple, recheck, false, NULL);
     592                 :      282547 :                         *reportedSome = true;
     593                 :             :                 }
     594                 :      343532 :         }
     595                 :             : 
     596                 :      880728 :         return result;
     597                 :      440364 : }
     598                 :             : 
     599                 :             : /* A bundle initializer for inner_consistent methods */
     600                 :             : static void
     601                 :        4203 : spgInitInnerConsistentIn(spgInnerConsistentIn *in,
     602                 :             :                                                  SpGistScanOpaque so,
     603                 :             :                                                  SpGistSearchItem *item,
     604                 :             :                                                  SpGistInnerTuple innerTuple)
     605                 :             : {
     606                 :        4203 :         in->scankeys = so->keyData;
     607                 :        4203 :         in->orderbys = so->orderByData;
     608                 :        4203 :         in->nkeys = so->numberOfKeys;
     609                 :        4203 :         in->norderbys = so->numberOfNonNullOrderBys;
     610         [ +  - ]:        4203 :         Assert(!item->isLeaf);               /* else reconstructedValue would be wrong type */
     611                 :        4203 :         in->reconstructedValue = item->value;
     612                 :        4203 :         in->traversalMemoryContext = so->traversalCxt;
     613                 :        4203 :         in->traversalValue = item->traversalValue;
     614                 :        4203 :         in->level = item->level;
     615                 :        4203 :         in->returnData = so->want_itup;
     616                 :        4203 :         in->allTheSame = innerTuple->allTheSame;
     617                 :        4203 :         in->hasPrefix = (innerTuple->prefixSize > 0);
     618   [ +  +  +  + ]:        4203 :         in->prefixDatum = SGITDATUM(innerTuple, &so->state);
     619                 :        4203 :         in->nNodes = innerTuple->nNodes;
     620                 :        4203 :         in->nodeLabels = spgExtractNodeLabels(&so->state, innerTuple);
     621                 :        4203 : }
     622                 :             : 
     623                 :             : static SpGistSearchItem *
     624                 :       16699 : spgMakeInnerItem(SpGistScanOpaque so,
     625                 :             :                                  SpGistSearchItem *parentItem,
     626                 :             :                                  SpGistNodeTuple tuple,
     627                 :             :                                  spgInnerConsistentOut *out, int i, bool isnull,
     628                 :             :                                  double *distances)
     629                 :             : {
     630                 :       16699 :         SpGistSearchItem *item = spgAllocSearchItem(so, isnull, distances);
     631                 :             : 
     632                 :       16699 :         item->heapPtr = tuple->t_tid;
     633         [ +  + ]:       16699 :         item->level = out->levelAdds ? parentItem->level + out->levelAdds[i]
     634                 :        8833 :                 : parentItem->level;
     635                 :             : 
     636                 :             :         /* Must copy value out of temp context */
     637                 :             :         /* (recall that reconstructed values are of type leafType) */
     638         [ +  + ]:       16699 :         item->value = out->reconstructedValues
     639                 :        5856 :                 ? datumCopy(out->reconstructedValues[i],
     640                 :        2928 :                                         so->state.attLeafType.attbyval,
     641                 :        2928 :                                         so->state.attLeafType.attlen)
     642                 :             :                 : (Datum) 0;
     643                 :             : 
     644                 :       16699 :         item->leafTuple = NULL;
     645                 :             : 
     646                 :             :         /*
     647                 :             :          * Elements of out.traversalValues should be allocated in
     648                 :             :          * in.traversalMemoryContext, which is actually a long lived context of
     649                 :             :          * index scan.
     650                 :             :          */
     651                 :       16699 :         item->traversalValue =
     652         [ +  + ]:       16699 :                 out->traversalValues ? out->traversalValues[i] : NULL;
     653                 :             : 
     654                 :       16699 :         item->isLeaf = false;
     655                 :       16699 :         item->recheck = false;
     656                 :       16699 :         item->recheckDistances = false;
     657                 :             : 
     658                 :       33398 :         return item;
     659                 :       16699 : }
     660                 :             : 
     661                 :             : static void
     662                 :        4203 : spgInnerTest(SpGistScanOpaque so, SpGistSearchItem *item,
     663                 :             :                          SpGistInnerTuple innerTuple, bool isnull)
     664                 :             : {
     665                 :        4203 :         MemoryContext oldCxt = MemoryContextSwitchTo(so->tempCxt);
     666                 :        4203 :         spgInnerConsistentOut out;
     667                 :        4203 :         int                     nNodes = innerTuple->nNodes;
     668                 :        4203 :         int                     i;
     669                 :             : 
     670                 :        4203 :         memset(&out, 0, sizeof(out));
     671                 :             : 
     672         [ -  + ]:        4203 :         if (!isnull)
     673                 :             :         {
     674                 :        4203 :                 spgInnerConsistentIn in;
     675                 :             : 
     676                 :        4203 :                 spgInitInnerConsistentIn(&in, so, item, innerTuple);
     677                 :             : 
     678                 :             :                 /* use user-defined inner consistent method */
     679                 :        8406 :                 FunctionCall2Coll(&so->innerConsistentFn,
     680                 :        4203 :                                                   so->indexCollation,
     681                 :        4203 :                                                   PointerGetDatum(&in),
     682                 :        4203 :                                                   PointerGetDatum(&out));
     683                 :        4203 :         }
     684                 :             :         else
     685                 :             :         {
     686                 :             :                 /* force all children to be visited */
     687                 :           0 :                 out.nNodes = nNodes;
     688                 :           0 :                 out.nodeNumbers = palloc_array(int, nNodes);
     689         [ #  # ]:           0 :                 for (i = 0; i < nNodes; i++)
     690                 :           0 :                         out.nodeNumbers[i] = i;
     691                 :             :         }
     692                 :             : 
     693                 :             :         /* If allTheSame, they should all or none of them match */
     694   [ +  +  +  -  :        4203 :         if (innerTuple->allTheSame && out.nNodes != 0 && out.nNodes != nNodes)
                   +  - ]
     695   [ #  #  #  # ]:           0 :                 elog(ERROR, "inconsistent inner_consistent results for allTheSame inner tuple");
     696                 :             : 
     697         [ -  + ]:        4203 :         if (out.nNodes)
     698                 :             :         {
     699                 :             :                 /* collect node pointers */
     700                 :        4203 :                 SpGistNodeTuple node;
     701                 :        4203 :                 SpGistNodeTuple *nodes = palloc_array(SpGistNodeTuple, nNodes);
     702                 :             : 
     703         [ +  + ]:       38780 :                 SGITITERATE(innerTuple, i, node)
     704                 :             :                 {
     705                 :       34577 :                         nodes[i] = node;
     706                 :       34577 :                 }
     707                 :             : 
     708                 :        4203 :                 MemoryContextSwitchTo(so->traversalCxt);
     709                 :             : 
     710         [ +  + ]:       32081 :                 for (i = 0; i < out.nNodes; i++)
     711                 :             :                 {
     712                 :       27878 :                         int                     nodeN = out.nodeNumbers[i];
     713                 :       27878 :                         SpGistSearchItem *innerItem;
     714                 :       27878 :                         double     *distances;
     715                 :             : 
     716         [ +  - ]:       27878 :                         Assert(nodeN >= 0 && nodeN < nNodes);
     717                 :             : 
     718                 :       27878 :                         node = nodes[nodeN];
     719                 :             : 
     720         [ +  + ]:       27878 :                         if (!ItemPointerIsValid(&node->t_tid))
     721                 :       11179 :                                 continue;
     722                 :             : 
     723                 :             :                         /*
     724                 :             :                          * Use infinity distances if innerConsistentFn() failed to return
     725                 :             :                          * them or if is a NULL item (their distances are really unused).
     726                 :             :                          */
     727         [ +  + ]:       16699 :                         distances = out.distances ? out.distances[i] : so->infDistances;
     728                 :             : 
     729                 :       33398 :                         innerItem = spgMakeInnerItem(so, item, node, &out, i, isnull,
     730                 :       16699 :                                                                                  distances);
     731                 :             : 
     732                 :       16699 :                         spgAddSearchItemToQueue(so, innerItem);
     733      [ -  +  + ]:       27878 :                 }
     734                 :        4203 :         }
     735                 :             : 
     736                 :        4203 :         MemoryContextSwitchTo(oldCxt);
     737                 :        4203 : }
     738                 :             : 
     739                 :             : /* Returns a next item in an (ordered) scan or null if the index is exhausted */
     740                 :             : static SpGistSearchItem *
     741                 :       77533 : spgGetNextQueueItem(SpGistScanOpaque so)
     742                 :             : {
     743         [ +  + ]:       77533 :         if (pairingheap_is_empty(so->scanQueue))
     744                 :         147 :                 return NULL;                    /* Done when both heaps are empty */
     745                 :             : 
     746                 :             :         /* Return item; caller is responsible to pfree it */
     747                 :       77386 :         return (SpGistSearchItem *) pairingheap_remove_first(so->scanQueue);
     748                 :       77533 : }
     749                 :             : 
     750                 :             : enum SpGistSpecialOffsetNumbers
     751                 :             : {
     752                 :             :         SpGistBreakOffsetNumber = InvalidOffsetNumber,
     753                 :             :         SpGistRedirectOffsetNumber = MaxOffsetNumber + 1,
     754                 :             :         SpGistErrorOffsetNumber = MaxOffsetNumber + 2,
     755                 :             : };
     756                 :             : 
     757                 :             : static OffsetNumber
     758                 :      440364 : spgTestLeafTuple(SpGistScanOpaque so,
     759                 :             :                                  SpGistSearchItem *item,
     760                 :             :                                  Page page, OffsetNumber offset,
     761                 :             :                                  bool isnull, bool isroot,
     762                 :             :                                  bool *reportedSome,
     763                 :             :                                  storeRes_func storeRes)
     764                 :             : {
     765                 :      880728 :         SpGistLeafTuple leafTuple = (SpGistLeafTuple)
     766                 :      440364 :                 PageGetItem(page, PageGetItemId(page, offset));
     767                 :             : 
     768         [ -  + ]:      440364 :         if (leafTuple->tupstate != SPGIST_LIVE)
     769                 :             :         {
     770         [ #  # ]:           0 :                 if (!isroot)                    /* all tuples on root should be live */
     771                 :             :                 {
     772         [ #  # ]:           0 :                         if (leafTuple->tupstate == SPGIST_REDIRECT)
     773                 :             :                         {
     774                 :             :                                 /* redirection tuple should be first in chain */
     775         [ #  # ]:           0 :                                 Assert(offset == ItemPointerGetOffsetNumber(&item->heapPtr));
     776                 :             :                                 /* transfer attention to redirect point */
     777                 :           0 :                                 item->heapPtr = ((SpGistDeadTuple) leafTuple)->pointer;
     778         [ #  # ]:           0 :                                 Assert(ItemPointerGetBlockNumber(&item->heapPtr) != SPGIST_METAPAGE_BLKNO);
     779                 :           0 :                                 return SpGistRedirectOffsetNumber;
     780                 :             :                         }
     781                 :             : 
     782         [ #  # ]:           0 :                         if (leafTuple->tupstate == SPGIST_DEAD)
     783                 :             :                         {
     784                 :             :                                 /* dead tuple should be first in chain */
     785         [ #  # ]:           0 :                                 Assert(offset == ItemPointerGetOffsetNumber(&item->heapPtr));
     786                 :             :                                 /* No live entries on this page */
     787         [ #  # ]:           0 :                                 Assert(SGLT_GET_NEXTOFFSET(leafTuple) == InvalidOffsetNumber);
     788                 :           0 :                                 return SpGistBreakOffsetNumber;
     789                 :             :                         }
     790                 :           0 :                 }
     791                 :             : 
     792                 :             :                 /* We should not arrive at a placeholder */
     793   [ #  #  #  # ]:           0 :                 elog(ERROR, "unexpected SPGiST tuple state: %d", leafTuple->tupstate);
     794                 :           0 :                 return SpGistErrorOffsetNumber;
     795                 :             :         }
     796                 :             : 
     797         [ +  - ]:      440364 :         Assert(ItemPointerIsValid(&leafTuple->heapPtr));
     798                 :             : 
     799                 :      440364 :         spgLeafTest(so, item, leafTuple, isnull, reportedSome, storeRes);
     800                 :             : 
     801                 :      440364 :         return SGLT_GET_NEXTOFFSET(leafTuple);
     802                 :      440364 : }
     803                 :             : 
     804                 :             : /*
     805                 :             :  * Walk the tree and report all tuples passing the scan quals to the storeRes
     806                 :             :  * subroutine.
     807                 :             :  *
     808                 :             :  * If scanWholeIndex is true, we'll do just that.  If not, we'll stop at the
     809                 :             :  * next page boundary once we have reported at least one tuple.
     810                 :             :  */
     811                 :             : static void
     812                 :       62915 : spgWalk(Relation index, SpGistScanOpaque so, bool scanWholeIndex,
     813                 :             :                 storeRes_func storeRes)
     814                 :             : {
     815                 :       62915 :         Buffer          buffer = InvalidBuffer;
     816                 :       62915 :         bool            reportedSome = false;
     817                 :             : 
     818   [ +  +  +  + ]:      140301 :         while (scanWholeIndex || !reportedSome)
     819                 :             :         {
     820                 :       77533 :                 SpGistSearchItem *item = spgGetNextQueueItem(so);
     821                 :             : 
     822         [ +  + ]:       77533 :                 if (item == NULL)
     823                 :         147 :                         break;                          /* No more items in queue -> done */
     824                 :             : 
     825                 :             : redirect:
     826                 :             :                 /* Check for interrupts, just in case of infinite loop */
     827         [ +  - ]:       77386 :                 CHECK_FOR_INTERRUPTS();
     828                 :             : 
     829         [ +  + ]:       77386 :                 if (item->isLeaf)
     830                 :             :                 {
     831                 :             :                         /* We store heap items in the queue only in case of ordered search */
     832         [ -  + ]:       60559 :                         Assert(so->numberOfNonNullOrderBys > 0);
     833                 :      121118 :                         storeRes(so, &item->heapPtr, item->value, item->isNull,
     834                 :       60559 :                                          item->leafTuple, item->recheck,
     835                 :       60559 :                                          item->recheckDistances, item->distances);
     836                 :       60559 :                         reportedSome = true;
     837                 :       60559 :                 }
     838                 :             :                 else
     839                 :             :                 {
     840                 :       16827 :                         BlockNumber blkno = ItemPointerGetBlockNumber(&item->heapPtr);
     841                 :       16827 :                         OffsetNumber offset = ItemPointerGetOffsetNumber(&item->heapPtr);
     842                 :       16827 :                         Page            page;
     843                 :       16827 :                         bool            isnull;
     844                 :             : 
     845         [ +  + ]:       16827 :                         if (buffer == InvalidBuffer)
     846                 :             :                         {
     847                 :        3345 :                                 buffer = ReadBuffer(index, blkno);
     848                 :        3345 :                                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
     849                 :        3345 :                         }
     850         [ +  + ]:       13482 :                         else if (blkno != BufferGetBlockNumber(buffer))
     851                 :             :                         {
     852                 :        9287 :                                 UnlockReleaseBuffer(buffer);
     853                 :        9287 :                                 buffer = ReadBuffer(index, blkno);
     854                 :        9287 :                                 LockBuffer(buffer, BUFFER_LOCK_SHARE);
     855                 :        9287 :                         }
     856                 :             : 
     857                 :             :                         /* else new pointer points to the same page, no work needed */
     858                 :             : 
     859                 :       16827 :                         page = BufferGetPage(buffer);
     860                 :             : 
     861                 :       16827 :                         isnull = SpGistPageStoresNulls(page) ? true : false;
     862                 :             : 
     863         [ +  + ]:       16827 :                         if (SpGistPageIsLeaf(page))
     864                 :             :                         {
     865                 :             :                                 /* Page is a leaf - that is, all its tuples are heap items */
     866                 :       12624 :                                 OffsetNumber max = PageGetMaxOffsetNumber(page);
     867                 :             : 
     868   [ +  +  +  + ]:       12624 :                                 if (SpGistBlockIsRoot(blkno))
     869                 :             :                                 {
     870                 :             :                                         /* When root is a leaf, examine all its tuples */
     871         [ +  + ]:        1021 :                                         for (offset = FirstOffsetNumber; offset <= max; offset++)
     872                 :        1976 :                                                 (void) spgTestLeafTuple(so, item, page, offset,
     873                 :         988 :                                                                                                 isnull, true,
     874                 :         988 :                                                                                                 &reportedSome, storeRes);
     875                 :          33 :                                 }
     876                 :             :                                 else
     877                 :             :                                 {
     878                 :             :                                         /* Normal case: just examine the chain we arrived at */
     879         [ +  + ]:      451967 :                                         while (offset != InvalidOffsetNumber)
     880                 :             :                                         {
     881         [ +  - ]:      439376 :                                                 Assert(offset >= FirstOffsetNumber && offset <= max);
     882                 :      878752 :                                                 offset = spgTestLeafTuple(so, item, page, offset,
     883                 :      439376 :                                                                                                   isnull, false,
     884                 :      439376 :                                                                                                   &reportedSome, storeRes);
     885         [ -  + ]:      439376 :                                                 if (offset == SpGistRedirectOffsetNumber)
     886                 :           0 :                                                         goto redirect;
     887                 :             :                                         }
     888                 :             :                                 }
     889         [ -  + ]:       12624 :                         }
     890                 :             :                         else                            /* page is inner */
     891                 :             :                         {
     892                 :        8406 :                                 SpGistInnerTuple innerTuple = (SpGistInnerTuple)
     893                 :        4203 :                                         PageGetItem(page, PageGetItemId(page, offset));
     894                 :             : 
     895         [ +  - ]:        4203 :                                 if (innerTuple->tupstate != SPGIST_LIVE)
     896                 :             :                                 {
     897         [ #  # ]:           0 :                                         if (innerTuple->tupstate == SPGIST_REDIRECT)
     898                 :             :                                         {
     899                 :             :                                                 /* transfer attention to redirect point */
     900                 :           0 :                                                 item->heapPtr = ((SpGistDeadTuple) innerTuple)->pointer;
     901         [ #  # ]:           0 :                                                 Assert(ItemPointerGetBlockNumber(&item->heapPtr) !=
     902                 :             :                                                            SPGIST_METAPAGE_BLKNO);
     903                 :           0 :                                                 goto redirect;
     904                 :             :                                         }
     905   [ #  #  #  # ]:           0 :                                         elog(ERROR, "unexpected SPGiST tuple state: %d",
     906                 :             :                                                  innerTuple->tupstate);
     907                 :           0 :                                 }
     908                 :             : 
     909                 :        4203 :                                 spgInnerTest(so, item, innerTuple, isnull);
     910         [ -  + ]:        4203 :                         }
     911         [ +  - ]:       16827 :                 }
     912                 :             : 
     913                 :             :                 /* done with this scan item */
     914                 :       77386 :                 spgFreeSearchItem(so, item);
     915                 :             :                 /* clear temp context before proceeding to the next one */
     916                 :       77386 :                 MemoryContextReset(so->tempCxt);
     917         [ +  + ]:       77533 :         }
     918                 :             : 
     919         [ +  + ]:       62915 :         if (buffer != InvalidBuffer)
     920                 :        3345 :                 UnlockReleaseBuffer(buffer);
     921                 :       62915 : }
     922                 :             : 
     923                 :             : 
     924                 :             : /* storeRes subroutine for getbitmap case */
     925                 :             : static void
     926                 :      175369 : storeBitmap(SpGistScanOpaque so, ItemPointer heapPtr,
     927                 :             :                         Datum leafValue, bool isnull,
     928                 :             :                         SpGistLeafTuple leafTuple, bool recheck,
     929                 :             :                         bool recheckDistances, double *distances)
     930                 :             : {
     931         [ +  - ]:      175369 :         Assert(!recheckDistances && !distances);
     932                 :      175369 :         tbm_add_tuples(so->tbm, heapPtr, 1, recheck);
     933                 :      175369 :         so->ntids++;
     934                 :      175369 : }
     935                 :             : 
     936                 :             : int64
     937                 :          58 : spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
     938                 :             : {
     939                 :          58 :         SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
     940                 :             : 
     941                 :             :         /* Copy want_itup to *so so we don't need to pass it around separately */
     942                 :          58 :         so->want_itup = false;
     943                 :             : 
     944                 :          58 :         so->tbm = tbm;
     945                 :          58 :         so->ntids = 0;
     946                 :             : 
     947                 :          58 :         spgWalk(scan->indexRelation, so, true, storeBitmap);
     948                 :             : 
     949                 :         116 :         return so->ntids;
     950                 :          58 : }
     951                 :             : 
     952                 :             : /* storeRes subroutine for gettuple case */
     953                 :             : static void
     954                 :      167737 : storeGettuple(SpGistScanOpaque so, ItemPointer heapPtr,
     955                 :             :                           Datum leafValue, bool isnull,
     956                 :             :                           SpGistLeafTuple leafTuple, bool recheck,
     957                 :             :                           bool recheckDistances, double *nonNullDistances)
     958                 :             : {
     959         [ +  - ]:      167737 :         Assert(so->nPtrs < MaxIndexTuplesPerPage);
     960                 :      167737 :         so->heapPtrs[so->nPtrs] = *heapPtr;
     961                 :      167737 :         so->recheck[so->nPtrs] = recheck;
     962                 :      167737 :         so->recheckDistances[so->nPtrs] = recheckDistances;
     963                 :             : 
     964         [ +  + ]:      167737 :         if (so->numberOfOrderBys > 0)
     965                 :             :         {
     966   [ +  +  +  - ]:       60559 :                 if (isnull || so->numberOfNonNullOrderBys <= 0)
     967                 :           8 :                         so->distances[so->nPtrs] = NULL;
     968                 :             :                 else
     969                 :             :                 {
     970                 :       60551 :                         IndexOrderByDistance *distances = palloc_array(IndexOrderByDistance,
     971                 :             :                                                                                                                    so->numberOfOrderBys);
     972                 :       60551 :                         int                     i;
     973                 :             : 
     974         [ +  + ]:      121105 :                         for (i = 0; i < so->numberOfOrderBys; i++)
     975                 :             :                         {
     976                 :       60554 :                                 int                     offset = so->nonNullOrderByOffsets[i];
     977                 :             : 
     978         [ +  + ]:       60554 :                                 if (offset >= 0)
     979                 :             :                                 {
     980                 :             :                                         /* Copy non-NULL distance value */
     981                 :       60553 :                                         distances[i].value = nonNullDistances[offset];
     982                 :       60553 :                                         distances[i].isnull = false;
     983                 :       60553 :                                 }
     984                 :             :                                 else
     985                 :             :                                 {
     986                 :             :                                         /* Set distance's NULL flag. */
     987                 :           1 :                                         distances[i].value = 0.0;
     988                 :           1 :                                         distances[i].isnull = true;
     989                 :             :                                 }
     990                 :       60554 :                         }
     991                 :             : 
     992                 :       60551 :                         so->distances[so->nPtrs] = distances;
     993                 :       60551 :                 }
     994                 :       60559 :         }
     995                 :             : 
     996         [ +  + ]:      167737 :         if (so->want_itup)
     997                 :             :         {
     998                 :             :                 /*
     999                 :             :                  * Reconstruct index data.  We have to copy the datum out of the temp
    1000                 :             :                  * context anyway, so we may as well create the tuple here.
    1001                 :             :                  */
    1002                 :      153231 :                 Datum           leafDatums[INDEX_MAX_KEYS];
    1003                 :      153231 :                 bool            leafIsnulls[INDEX_MAX_KEYS];
    1004                 :             : 
    1005                 :             :                 /* We only need to deform the old tuple if it has INCLUDE attributes */
    1006         [ +  + ]:      153231 :                 if (so->state.leafTupDesc->natts > 1)
    1007                 :          20 :                         spgDeformLeafTuple(leafTuple, so->state.leafTupDesc,
    1008                 :          10 :                                                            leafDatums, leafIsnulls, isnull);
    1009                 :             : 
    1010                 :      153231 :                 leafDatums[spgKeyColumn] = leafValue;
    1011                 :      153231 :                 leafIsnulls[spgKeyColumn] = isnull;
    1012                 :             : 
    1013                 :      306462 :                 so->reconTups[so->nPtrs] = heap_form_tuple(so->reconTupDesc,
    1014                 :      153231 :                                                                                                    leafDatums,
    1015                 :      153231 :                                                                                                    leafIsnulls);
    1016                 :      153231 :         }
    1017                 :      167737 :         so->nPtrs++;
    1018                 :      167737 : }
    1019                 :             : 
    1020                 :             : bool
    1021                 :      167810 : spggettuple(IndexScanDesc scan, ScanDirection dir)
    1022                 :             : {
    1023                 :      167810 :         SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
    1024                 :             : 
    1025         [ +  - ]:      167810 :         if (dir != ForwardScanDirection)
    1026   [ #  #  #  # ]:           0 :                 elog(ERROR, "SP-GiST only supports forward scan direction");
    1027                 :             : 
    1028                 :             :         /* Copy want_itup to *so so we don't need to pass it around separately */
    1029                 :      167810 :         so->want_itup = scan->xs_want_itup;
    1030                 :             : 
    1031                 :      230578 :         for (;;)
    1032                 :             :         {
    1033         [ +  + ]:      230578 :                 if (so->iPtr < so->nPtrs)
    1034                 :             :                 {
    1035                 :             :                         /* continuing to return reported tuples */
    1036                 :      167721 :                         scan->xs_heaptid = so->heapPtrs[so->iPtr];
    1037                 :      167721 :                         scan->xs_recheck = so->recheck[so->iPtr];
    1038                 :      167721 :                         scan->xs_hitup = so->reconTups[so->iPtr];
    1039                 :             : 
    1040         [ +  + ]:      167721 :                         if (so->numberOfOrderBys > 0)
    1041                 :      121118 :                                 index_store_float8_orderby_distances(scan, so->orderByTypes,
    1042                 :       60559 :                                                                                                          so->distances[so->iPtr],
    1043                 :       60559 :                                                                                                          so->recheckDistances[so->iPtr]);
    1044                 :      167721 :                         so->iPtr++;
    1045                 :      167721 :                         return true;
    1046                 :             :                 }
    1047                 :             : 
    1048         [ +  + ]:       62857 :                 if (so->numberOfOrderBys > 0)
    1049                 :             :                 {
    1050                 :             :                         /* Must pfree distances to avoid memory leak */
    1051                 :       60568 :                         int                     i;
    1052                 :             : 
    1053         [ +  + ]:      121123 :                         for (i = 0; i < so->nPtrs; i++)
    1054         [ +  + ]:      121102 :                                 if (so->distances[i])
    1055                 :       60547 :                                         pfree(so->distances[i]);
    1056                 :       60568 :                 }
    1057                 :             : 
    1058         [ +  + ]:       62857 :                 if (so->want_itup)
    1059                 :             :                 {
    1060                 :             :                         /* Must pfree reconstructed tuples to avoid memory leak */
    1061                 :       48405 :                         int                     i;
    1062                 :             : 
    1063         [ +  + ]:      201613 :                         for (i = 0; i < so->nPtrs; i++)
    1064                 :      153208 :                                 pfree(so->reconTups[i]);
    1065                 :       48405 :                 }
    1066                 :       62857 :                 so->iPtr = so->nPtrs = 0;
    1067                 :             : 
    1068                 :       62857 :                 spgWalk(scan->indexRelation, so, false, storeGettuple);
    1069                 :             : 
    1070         [ +  + ]:       62857 :                 if (so->nPtrs == 0)
    1071                 :          89 :                         break;                          /* must have completed scan */
    1072                 :             :         }
    1073                 :             : 
    1074                 :          89 :         return false;
    1075                 :      167810 : }
    1076                 :             : 
    1077                 :             : bool
    1078                 :         305 : spgcanreturn(Relation index, int attno)
    1079                 :             : {
    1080                 :         305 :         SpGistCache *cache;
    1081                 :             : 
    1082                 :             :         /* INCLUDE attributes can always be fetched for index-only scans */
    1083         [ +  + ]:         305 :         if (attno > 1)
    1084                 :           2 :                 return true;
    1085                 :             : 
    1086                 :             :         /* We can do it if the opclass config function says so */
    1087                 :         303 :         cache = spgGetCache(index);
    1088                 :             : 
    1089                 :         303 :         return cache->config.canReturnData;
    1090                 :         305 : }
        

Generated by: LCOV version 2.3.2-1