LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginscan.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 93.5 % 263 246
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 73.4 % 124 91

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * ginscan.c
       4                 :             :  *        routines to manage scans of inverted index relations
       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/gin/ginscan.c
      12                 :             :  *-------------------------------------------------------------------------
      13                 :             :  */
      14                 :             : 
      15                 :             : #include "postgres.h"
      16                 :             : 
      17                 :             : #include "access/gin_private.h"
      18                 :             : #include "access/relscan.h"
      19                 :             : #include "executor/instrument_node.h"
      20                 :             : #include "pgstat.h"
      21                 :             : #include "utils/memutils.h"
      22                 :             : #include "utils/rel.h"
      23                 :             : 
      24                 :             : 
      25                 :             : IndexScanDesc
      26                 :         173 : ginbeginscan(Relation rel, int nkeys, int norderbys)
      27                 :             : {
      28                 :         173 :         IndexScanDesc scan;
      29                 :         173 :         GinScanOpaque so;
      30                 :             : 
      31                 :             :         /* no order by operators allowed */
      32         [ +  - ]:         173 :         Assert(norderbys == 0);
      33                 :             : 
      34                 :         173 :         scan = RelationGetIndexScan(rel, nkeys, norderbys);
      35                 :             : 
      36                 :             :         /* allocate private workspace */
      37                 :         173 :         so = (GinScanOpaque) palloc_object(GinScanOpaqueData);
      38                 :         173 :         so->keys = NULL;
      39                 :         173 :         so->nkeys = 0;
      40                 :         173 :         so->tempCtx = AllocSetContextCreate(CurrentMemoryContext,
      41                 :             :                                                                                 "Gin scan temporary context",
      42                 :             :                                                                                 ALLOCSET_DEFAULT_SIZES);
      43                 :         173 :         so->keyCtx = AllocSetContextCreate(CurrentMemoryContext,
      44                 :             :                                                                            "Gin scan key context",
      45                 :             :                                                                            ALLOCSET_DEFAULT_SIZES);
      46                 :         173 :         initGinState(&so->ginstate, scan->indexRelation);
      47                 :             : 
      48                 :         173 :         scan->opaque = so;
      49                 :             : 
      50                 :         346 :         return scan;
      51                 :         173 : }
      52                 :             : 
      53                 :             : /*
      54                 :             :  * Create a new GinScanEntry, unless an equivalent one already exists,
      55                 :             :  * in which case just return it
      56                 :             :  */
      57                 :             : static GinScanEntry
      58                 :         289 : ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum,
      59                 :             :                                  StrategyNumber strategy, int32 searchMode,
      60                 :             :                                  Datum queryKey, GinNullCategory queryCategory,
      61                 :             :                                  bool isPartialMatch, Pointer extra_data)
      62                 :             : {
      63                 :         289 :         GinState   *ginstate = &so->ginstate;
      64                 :         289 :         GinScanEntry scanEntry;
      65                 :         289 :         uint32          i;
      66                 :             : 
      67                 :             :         /*
      68                 :             :          * Look for an existing equivalent entry.
      69                 :             :          *
      70                 :             :          * Entries with non-null extra_data are never considered identical, since
      71                 :             :          * we can't know exactly what the opclass might be doing with that.
      72                 :             :          *
      73                 :             :          * Also, give up de-duplication once we have 100 entries.  That avoids
      74                 :             :          * spending O(N^2) time on probably-fruitless de-duplication of large
      75                 :             :          * search-key sets.  The threshold of 100 is arbitrary but matches
      76                 :             :          * predtest.c's threshold for what's a large array.
      77                 :             :          */
      78   [ +  +  -  + ]:         289 :         if (extra_data == NULL && so->totalentries < 100)
      79                 :             :         {
      80         [ +  + ]:         330 :                 for (i = 0; i < so->totalentries; i++)
      81                 :             :                 {
      82                 :         140 :                         GinScanEntry prevEntry = so->entries[i];
      83                 :             : 
      84         [ +  + ]:         140 :                         if (prevEntry->extra_data == NULL &&
      85         [ +  - ]:          90 :                                 prevEntry->isPartialMatch == isPartialMatch &&
      86         [ +  + ]:          90 :                                 prevEntry->strategy == strategy &&
      87         [ +  - ]:          69 :                                 prevEntry->searchMode == searchMode &&
      88   [ +  +  +  - ]:          69 :                                 prevEntry->attnum == attnum &&
      89                 :         130 :                                 ginCompareEntries(ginstate, attnum,
      90                 :          65 :                                                                   prevEntry->queryKey,
      91                 :          65 :                                                                   prevEntry->queryCategory,
      92                 :          65 :                                                                   queryKey,
      93                 :         130 :                                                                   queryCategory) == 0)
      94                 :             :                         {
      95                 :             :                                 /* Successful match */
      96                 :           0 :                                 return prevEntry;
      97                 :             :                         }
      98         [ -  + ]:         140 :                 }
      99                 :         190 :         }
     100                 :             : 
     101                 :             :         /* Nope, create a new entry */
     102                 :         289 :         scanEntry = palloc_object(GinScanEntryData);
     103                 :         289 :         scanEntry->queryKey = queryKey;
     104                 :         289 :         scanEntry->queryCategory = queryCategory;
     105                 :         289 :         scanEntry->isPartialMatch = isPartialMatch;
     106                 :         289 :         scanEntry->extra_data = extra_data;
     107                 :         289 :         scanEntry->strategy = strategy;
     108                 :         289 :         scanEntry->searchMode = searchMode;
     109                 :         289 :         scanEntry->attnum = attnum;
     110                 :             : 
     111                 :         289 :         scanEntry->buffer = InvalidBuffer;
     112                 :         289 :         ItemPointerSetMin(&scanEntry->curItem);
     113                 :         289 :         scanEntry->matchBitmap = NULL;
     114                 :         289 :         scanEntry->matchIterator = NULL;
     115                 :         289 :         scanEntry->matchResult.blockno = InvalidBlockNumber;
     116                 :         289 :         scanEntry->matchNtuples = -1;
     117                 :         289 :         scanEntry->list = NULL;
     118                 :         289 :         scanEntry->nlist = 0;
     119                 :         289 :         scanEntry->offset = InvalidOffsetNumber;
     120                 :         289 :         scanEntry->isFinished = false;
     121                 :         289 :         scanEntry->reduceResult = false;
     122                 :             : 
     123                 :             :         /* Add it to so's array */
     124         [ +  - ]:         289 :         if (so->totalentries >= so->allocentries)
     125                 :             :         {
     126                 :           0 :                 so->allocentries *= 2;
     127                 :           0 :                 so->entries = repalloc_array(so->entries, GinScanEntry, so->allocentries);
     128                 :           0 :         }
     129                 :         289 :         so->entries[so->totalentries++] = scanEntry;
     130                 :             : 
     131                 :         289 :         return scanEntry;
     132                 :         289 : }
     133                 :             : 
     134                 :             : /*
     135                 :             :  * Append hidden scan entry of given category to the scan key.
     136                 :             :  *
     137                 :             :  * NB: this had better be called at most once per scan key, since
     138                 :             :  * ginFillScanKey leaves room for only one hidden entry.  Currently,
     139                 :             :  * it seems sufficiently clear that this is true that we don't bother
     140                 :             :  * with any cross-check logic.
     141                 :             :  */
     142                 :             : static void
     143                 :          51 : ginScanKeyAddHiddenEntry(GinScanOpaque so, GinScanKey key,
     144                 :             :                                                  GinNullCategory queryCategory)
     145                 :             : {
     146                 :          51 :         int                     i = key->nentries++;
     147                 :             : 
     148                 :             :         /* strategy is of no interest because this is not a partial-match item */
     149                 :         102 :         key->scanEntry[i] = ginFillScanEntry(so, key->attnum,
     150                 :          51 :                                                                                  InvalidStrategy, key->searchMode,
     151                 :          51 :                                                                                  (Datum) 0, queryCategory,
     152                 :             :                                                                                  false, NULL);
     153                 :          51 : }
     154                 :             : 
     155                 :             : /*
     156                 :             :  * Initialize the next GinScanKey using the output from the extractQueryFn
     157                 :             :  */
     158                 :             : static void
     159                 :         192 : ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
     160                 :             :                            StrategyNumber strategy, int32 searchMode,
     161                 :             :                            Datum query, uint32 nQueryValues,
     162                 :             :                            Datum *queryValues, GinNullCategory *queryCategories,
     163                 :             :                            bool *partial_matches, Pointer *extra_data)
     164                 :             : {
     165                 :         192 :         GinScanKey      key = &(so->keys[so->nkeys++]);
     166                 :         192 :         GinState   *ginstate = &so->ginstate;
     167                 :         192 :         uint32          i;
     168                 :             : 
     169                 :         192 :         key->nentries = nQueryValues;
     170                 :         192 :         key->nuserentries = nQueryValues;
     171                 :             : 
     172                 :             :         /* Allocate one extra array slot for possible "hidden" entry */
     173                 :         192 :         key->scanEntry = palloc_array(GinScanEntry, nQueryValues + 1);
     174                 :         192 :         key->entryRes = palloc0_array(GinTernaryValue, nQueryValues + 1);
     175                 :             : 
     176                 :         192 :         key->query = query;
     177                 :         192 :         key->queryValues = queryValues;
     178                 :         192 :         key->queryCategories = queryCategories;
     179                 :         192 :         key->extra_data = extra_data;
     180                 :         192 :         key->strategy = strategy;
     181                 :         192 :         key->searchMode = searchMode;
     182                 :         192 :         key->attnum = attnum;
     183                 :             : 
     184                 :             :         /*
     185                 :             :          * Initially, scan keys of GIN_SEARCH_MODE_ALL mode are marked
     186                 :             :          * excludeOnly.  This might get changed later.
     187                 :             :          */
     188                 :         192 :         key->excludeOnly = (searchMode == GIN_SEARCH_MODE_ALL);
     189                 :             : 
     190                 :         192 :         ItemPointerSetMin(&key->curItem);
     191                 :         192 :         key->curItemMatches = false;
     192                 :         192 :         key->recheckCurItem = false;
     193                 :         192 :         key->isFinished = false;
     194                 :         192 :         key->nrequired = 0;
     195                 :         192 :         key->nadditional = 0;
     196                 :         192 :         key->requiredEntries = NULL;
     197                 :         192 :         key->additionalEntries = NULL;
     198                 :             : 
     199                 :         192 :         ginInitConsistentFunction(ginstate, key);
     200                 :             : 
     201                 :             :         /* Set up normal scan entries using extractQueryFn's outputs */
     202         [ +  + ]:         430 :         for (i = 0; i < nQueryValues; i++)
     203                 :             :         {
     204                 :         238 :                 Datum           queryKey;
     205                 :         238 :                 GinNullCategory queryCategory;
     206                 :         238 :                 bool            isPartialMatch;
     207                 :         238 :                 Pointer         this_extra;
     208                 :             : 
     209                 :         238 :                 queryKey = queryValues[i];
     210                 :         238 :                 queryCategory = queryCategories[i];
     211                 :         238 :                 isPartialMatch =
     212   [ +  +  -  + ]:         238 :                         (ginstate->canPartialMatch[attnum - 1] && partial_matches)
     213                 :          59 :                         ? partial_matches[i] : false;
     214         [ +  + ]:         238 :                 this_extra = (extra_data) ? extra_data[i] : NULL;
     215                 :             : 
     216                 :         476 :                 key->scanEntry[i] = ginFillScanEntry(so, attnum,
     217                 :         238 :                                                                                          strategy, searchMode,
     218                 :         238 :                                                                                          queryKey, queryCategory,
     219                 :         238 :                                                                                          isPartialMatch, this_extra);
     220                 :         238 :         }
     221                 :             : 
     222                 :             :         /*
     223                 :             :          * For GIN_SEARCH_MODE_INCLUDE_EMPTY and GIN_SEARCH_MODE_EVERYTHING search
     224                 :             :          * modes, we add the "hidden" entry immediately.  GIN_SEARCH_MODE_ALL is
     225                 :             :          * handled later, since we might be able to omit the hidden entry for it.
     226                 :             :          */
     227         [ +  + ]:         192 :         if (searchMode == GIN_SEARCH_MODE_INCLUDE_EMPTY)
     228                 :           7 :                 ginScanKeyAddHiddenEntry(so, key, GIN_CAT_EMPTY_ITEM);
     229         [ +  - ]:         185 :         else if (searchMode == GIN_SEARCH_MODE_EVERYTHING)
     230                 :           0 :                 ginScanKeyAddHiddenEntry(so, key, GIN_CAT_EMPTY_QUERY);
     231                 :         192 : }
     232                 :             : 
     233                 :             : /*
     234                 :             :  * Release current scan keys, if any.
     235                 :             :  */
     236                 :             : void
     237                 :         521 : ginFreeScanKeys(GinScanOpaque so)
     238                 :             : {
     239                 :         521 :         uint32          i;
     240                 :             : 
     241         [ +  + ]:         521 :         if (so->keys == NULL)
     242                 :         347 :                 return;
     243                 :             : 
     244         [ +  + ]:         463 :         for (i = 0; i < so->totalentries; i++)
     245                 :             :         {
     246                 :         289 :                 GinScanEntry entry = so->entries[i];
     247                 :             : 
     248         [ +  - ]:         289 :                 if (entry->buffer != InvalidBuffer)
     249                 :           0 :                         ReleaseBuffer(entry->buffer);
     250         [ +  + ]:         289 :                 if (entry->list)
     251                 :         200 :                         pfree(entry->list);
     252         [ +  - ]:         289 :                 if (entry->matchIterator)
     253                 :           0 :                         tbm_end_private_iterate(entry->matchIterator);
     254         [ +  + ]:         289 :                 if (entry->matchBitmap)
     255                 :          46 :                         tbm_free(entry->matchBitmap);
     256                 :         289 :         }
     257                 :             : 
     258                 :         174 :         MemoryContextReset(so->keyCtx);
     259                 :             : 
     260                 :         174 :         so->keys = NULL;
     261                 :         174 :         so->nkeys = 0;
     262                 :         174 :         so->entries = NULL;
     263                 :         174 :         so->totalentries = 0;
     264         [ -  + ]:         521 : }
     265                 :             : 
     266                 :             : void
     267                 :         174 : ginNewScanKey(IndexScanDesc scan)
     268                 :             : {
     269                 :         174 :         ScanKey         scankey = scan->keyData;
     270                 :         174 :         GinScanOpaque so = (GinScanOpaque) scan->opaque;
     271                 :         174 :         int                     i;
     272                 :         174 :         int                     numExcludeOnly;
     273                 :         174 :         bool            hasNullQuery = false;
     274                 :         174 :         bool            attrHasNormalScan[INDEX_MAX_KEYS] = {false};
     275                 :         174 :         MemoryContext oldCtx;
     276                 :             : 
     277                 :             :         /*
     278                 :             :          * Allocate all the scan key information in the key context. (If
     279                 :             :          * extractQuery leaks anything there, it won't be reset until the end of
     280                 :             :          * scan or rescan, but that's OK.)
     281                 :             :          */
     282                 :         174 :         oldCtx = MemoryContextSwitchTo(so->keyCtx);
     283                 :             : 
     284                 :             :         /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
     285                 :         174 :         so->keys = (GinScanKey)
     286         [ +  + ]:         174 :                 palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData));
     287                 :         174 :         so->nkeys = 0;
     288                 :             : 
     289                 :             :         /* initialize expansible array of GinScanEntry pointers */
     290                 :         174 :         so->totalentries = 0;
     291                 :         174 :         so->allocentries = 32;
     292                 :         174 :         so->entries = (GinScanEntry *)
     293                 :         174 :                 palloc(so->allocentries * sizeof(GinScanEntry));
     294                 :             : 
     295                 :         174 :         so->isVoidRes = false;
     296                 :             : 
     297         [ +  + ]:         366 :         for (i = 0; i < scan->numberOfKeys; i++)
     298                 :             :         {
     299                 :         194 :                 ScanKey         skey = &scankey[i];
     300                 :         194 :                 Datum      *queryValues;
     301                 :         194 :                 int32           nQueryValues = 0;
     302                 :         194 :                 bool       *partial_matches = NULL;
     303                 :         194 :                 Pointer    *extra_data = NULL;
     304                 :         194 :                 bool       *nullFlags = NULL;
     305                 :         194 :                 GinNullCategory *categories;
     306                 :         194 :                 int32           searchMode = GIN_SEARCH_MODE_DEFAULT;
     307                 :             : 
     308                 :             :                 /*
     309                 :             :                  * We assume that GIN-indexable operators are strict, so a null query
     310                 :             :                  * argument means an unsatisfiable query.
     311                 :             :                  */
     312         [ -  + ]:         194 :                 if (skey->sk_flags & SK_ISNULL)
     313                 :             :                 {
     314                 :           0 :                         so->isVoidRes = true;
     315                 :           0 :                         break;
     316                 :             :                 }
     317                 :             : 
     318                 :             :                 /* OK to call the extractQueryFn */
     319                 :         194 :                 queryValues = (Datum *)
     320                 :         388 :                         DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
     321                 :         194 :                                                                                           so->ginstate.supportCollation[skey->sk_attno - 1],
     322                 :         194 :                                                                                           skey->sk_argument,
     323                 :         194 :                                                                                           PointerGetDatum(&nQueryValues),
     324                 :         194 :                                                                                           UInt16GetDatum(skey->sk_strategy),
     325                 :         194 :                                                                                           PointerGetDatum(&partial_matches),
     326                 :         194 :                                                                                           PointerGetDatum(&extra_data),
     327                 :         194 :                                                                                           PointerGetDatum(&nullFlags),
     328                 :         194 :                                                                                           PointerGetDatum(&searchMode)));
     329                 :             : 
     330                 :             :                 /*
     331                 :             :                  * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
     332                 :             :                  * in particular we don't allow extractQueryFn to select
     333                 :             :                  * GIN_SEARCH_MODE_EVERYTHING.
     334                 :             :                  */
     335   [ +  -  -  + ]:         194 :                 if (searchMode < GIN_SEARCH_MODE_DEFAULT ||
     336                 :         194 :                         searchMode > GIN_SEARCH_MODE_ALL)
     337                 :           0 :                         searchMode = GIN_SEARCH_MODE_ALL;
     338                 :             : 
     339                 :             :                 /* Non-default modes require the index to have placeholders */
     340         [ +  + ]:         194 :                 if (searchMode != GIN_SEARCH_MODE_DEFAULT)
     341                 :          56 :                         hasNullQuery = true;
     342                 :             : 
     343                 :             :                 /*
     344                 :             :                  * In default mode, no keys means an unsatisfiable query.
     345                 :             :                  */
     346   [ +  +  +  + ]:         194 :                 if (queryValues == NULL || nQueryValues <= 0)
     347                 :             :                 {
     348         [ +  + ]:          47 :                         if (searchMode == GIN_SEARCH_MODE_DEFAULT)
     349                 :             :                         {
     350                 :           2 :                                 so->isVoidRes = true;
     351                 :           2 :                                 break;
     352                 :             :                         }
     353                 :          45 :                         nQueryValues = 0;       /* ensure sane value */
     354                 :          45 :                 }
     355                 :             : 
     356                 :             :                 /*
     357                 :             :                  * Create GinNullCategory representation.  If the extractQueryFn
     358                 :             :                  * didn't create a nullFlags array, we assume everything is non-null.
     359                 :             :                  * While at it, detect whether any null keys are present.
     360                 :             :                  */
     361                 :         192 :                 categories = (GinNullCategory *) palloc0(nQueryValues * sizeof(GinNullCategory));
     362         [ +  + ]:         192 :                 if (nullFlags)
     363                 :             :                 {
     364                 :          83 :                         int32           j;
     365                 :             : 
     366         [ +  + ]:         150 :                         for (j = 0; j < nQueryValues; j++)
     367                 :             :                         {
     368         [ +  - ]:          67 :                                 if (nullFlags[j])
     369                 :             :                                 {
     370                 :           0 :                                         categories[j] = GIN_CAT_NULL_KEY;
     371                 :           0 :                                         hasNullQuery = true;
     372                 :           0 :                                 }
     373                 :          67 :                         }
     374                 :          83 :                 }
     375                 :             : 
     376                 :         384 :                 ginFillScanKey(so, skey->sk_attno,
     377                 :         192 :                                            skey->sk_strategy, searchMode,
     378                 :         192 :                                            skey->sk_argument, nQueryValues,
     379                 :         192 :                                            queryValues, categories,
     380                 :         192 :                                            partial_matches, extra_data);
     381                 :             : 
     382                 :             :                 /* Remember if we had any non-excludeOnly keys */
     383         [ +  + ]:         192 :                 if (searchMode != GIN_SEARCH_MODE_ALL)
     384                 :         143 :                         attrHasNormalScan[skey->sk_attno - 1] = true;
     385         [ +  + ]:         194 :         }
     386                 :             : 
     387                 :             :         /*
     388                 :             :          * Processing GIN_SEARCH_MODE_ALL scan keys requires us to make a second
     389                 :             :          * pass over the scan keys.  Above we marked each such scan key as
     390                 :             :          * excludeOnly.  If the involved column has any normal (not excludeOnly)
     391                 :             :          * scan key as well, then we can leave it like that.  Otherwise, one
     392                 :             :          * excludeOnly scan key must receive a GIN_CAT_EMPTY_QUERY hidden entry
     393                 :             :          * and be set to normal (excludeOnly = false).
     394                 :             :          */
     395                 :         174 :         numExcludeOnly = 0;
     396         [ +  + ]:         366 :         for (i = 0; i < so->nkeys; i++)
     397                 :             :         {
     398                 :         192 :                 GinScanKey      key = &so->keys[i];
     399                 :             : 
     400         [ +  + ]:         192 :                 if (key->searchMode != GIN_SEARCH_MODE_ALL)
     401                 :         143 :                         continue;
     402                 :             : 
     403         [ +  + ]:          49 :                 if (!attrHasNormalScan[key->attnum - 1])
     404                 :             :                 {
     405                 :          44 :                         key->excludeOnly = false;
     406                 :          44 :                         ginScanKeyAddHiddenEntry(so, key, GIN_CAT_EMPTY_QUERY);
     407                 :          44 :                         attrHasNormalScan[key->attnum - 1] = true;
     408                 :          44 :                 }
     409                 :             :                 else
     410                 :           5 :                         numExcludeOnly++;
     411         [ +  + ]:         192 :         }
     412                 :             : 
     413                 :             :         /*
     414                 :             :          * If we left any excludeOnly scan keys as-is, move them to the end of the
     415                 :             :          * scan key array: they must appear after normal key(s).
     416                 :             :          */
     417         [ +  + ]:         174 :         if (numExcludeOnly > 0)
     418                 :             :         {
     419                 :           5 :                 GinScanKey      tmpkeys;
     420                 :           5 :                 int                     iNormalKey;
     421                 :           5 :                 int                     iExcludeOnly;
     422                 :             : 
     423                 :             :                 /* We'd better have made at least one normal key */
     424         [ +  - ]:           5 :                 Assert(numExcludeOnly < so->nkeys);
     425                 :             :                 /* Make a temporary array to hold the re-ordered scan keys */
     426                 :           5 :                 tmpkeys = (GinScanKey) palloc(so->nkeys * sizeof(GinScanKeyData));
     427                 :             :                 /* Re-order the keys ... */
     428                 :           5 :                 iNormalKey = 0;
     429                 :           5 :                 iExcludeOnly = so->nkeys - numExcludeOnly;
     430         [ +  + ]:          19 :                 for (i = 0; i < so->nkeys; i++)
     431                 :             :                 {
     432                 :          14 :                         GinScanKey      key = &so->keys[i];
     433                 :             : 
     434         [ +  + ]:          14 :                         if (key->excludeOnly)
     435                 :             :                         {
     436                 :           5 :                                 memcpy(tmpkeys + iExcludeOnly, key, sizeof(GinScanKeyData));
     437                 :           5 :                                 iExcludeOnly++;
     438                 :           5 :                         }
     439                 :             :                         else
     440                 :             :                         {
     441                 :           9 :                                 memcpy(tmpkeys + iNormalKey, key, sizeof(GinScanKeyData));
     442                 :           9 :                                 iNormalKey++;
     443                 :             :                         }
     444                 :          14 :                 }
     445         [ +  - ]:           5 :                 Assert(iNormalKey == so->nkeys - numExcludeOnly);
     446         [ +  - ]:           5 :                 Assert(iExcludeOnly == so->nkeys);
     447                 :             :                 /* ... and copy them back to so->keys[] */
     448                 :           5 :                 memcpy(so->keys, tmpkeys, so->nkeys * sizeof(GinScanKeyData));
     449                 :           5 :                 pfree(tmpkeys);
     450                 :           5 :         }
     451                 :             : 
     452                 :             :         /*
     453                 :             :          * If there are no regular scan keys, generate an EVERYTHING scankey to
     454                 :             :          * drive a full-index scan.
     455                 :             :          */
     456   [ +  +  +  - ]:         174 :         if (so->nkeys == 0 && !so->isVoidRes)
     457                 :             :         {
     458                 :           0 :                 hasNullQuery = true;
     459                 :           0 :                 ginFillScanKey(so, FirstOffsetNumber,
     460                 :             :                                            InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING,
     461                 :             :                                            (Datum) 0, 0,
     462                 :             :                                            NULL, NULL, NULL, NULL);
     463                 :           0 :         }
     464                 :             : 
     465                 :             :         /*
     466                 :             :          * If the index is version 0, it may be missing null and placeholder
     467                 :             :          * entries, which would render searches for nulls and full-index scans
     468                 :             :          * unreliable.  Throw an error if so.
     469                 :             :          */
     470   [ +  +  -  + ]:         174 :         if (hasNullQuery && !so->isVoidRes)
     471                 :             :         {
     472                 :          50 :                 GinStatsData ginStats;
     473                 :             : 
     474                 :          50 :                 ginGetStats(scan->indexRelation, &ginStats);
     475         [ +  - ]:          50 :                 if (ginStats.ginVersion < 1)
     476   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     477                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     478                 :             :                                          errmsg("old GIN indexes do not support whole-index scans nor searches for nulls"),
     479                 :             :                                          errhint("To fix this, do REINDEX INDEX \"%s\".",
     480                 :             :                                                          RelationGetRelationName(scan->indexRelation))));
     481                 :          50 :         }
     482                 :             : 
     483                 :         174 :         MemoryContextSwitchTo(oldCtx);
     484                 :             : 
     485   [ +  -  +  -  :         174 :         pgstat_count_index_scan(scan->indexRelation);
                   #  # ]
     486         [ -  + ]:         174 :         if (scan->instrument)
     487                 :         174 :                 scan->instrument->nsearches++;
     488                 :         174 : }
     489                 :             : 
     490                 :             : void
     491                 :         174 : ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
     492                 :             :                   ScanKey orderbys, int norderbys)
     493                 :             : {
     494                 :         174 :         GinScanOpaque so = (GinScanOpaque) scan->opaque;
     495                 :             : 
     496                 :         174 :         ginFreeScanKeys(so);
     497                 :             : 
     498   [ +  -  -  + ]:         174 :         if (scankey && scan->numberOfKeys > 0)
     499                 :         174 :                 memcpy(scan->keyData, scankey, scan->numberOfKeys * sizeof(ScanKeyData));
     500                 :         174 : }
     501                 :             : 
     502                 :             : 
     503                 :             : void
     504                 :         173 : ginendscan(IndexScanDesc scan)
     505                 :             : {
     506                 :         173 :         GinScanOpaque so = (GinScanOpaque) scan->opaque;
     507                 :             : 
     508                 :         173 :         ginFreeScanKeys(so);
     509                 :             : 
     510                 :         173 :         MemoryContextDelete(so->tempCtx);
     511                 :         173 :         MemoryContextDelete(so->keyCtx);
     512                 :             : 
     513                 :         173 :         pfree(so);
     514                 :         173 : }
        

Generated by: LCOV version 2.3.2-1