LCOV - differential code coverage report
Current view: top level - src/backend/access/gin - ginscan.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 94.1 % 188 177 11 177
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 8 8 8
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a