LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginscan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 167 180 92.8 %
Date: 2019-11-15 22:06:47 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          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-2019, 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        1006 : ginbeginscan(Relation rel, int nkeys, int norderbys)
      26             : {
      27             :     IndexScanDesc scan;
      28             :     GinScanOpaque so;
      29             : 
      30             :     /* no order by operators allowed */
      31             :     Assert(norderbys == 0);
      32             : 
      33        1006 :     scan = RelationGetIndexScan(rel, nkeys, norderbys);
      34             : 
      35             :     /* allocate private workspace */
      36        1006 :     so = (GinScanOpaque) palloc(sizeof(GinScanOpaqueData));
      37        1006 :     so->keys = NULL;
      38        1006 :     so->nkeys = 0;
      39        1006 :     so->tempCtx = AllocSetContextCreate(CurrentMemoryContext,
      40             :                                         "Gin scan temporary context",
      41             :                                         ALLOCSET_DEFAULT_SIZES);
      42        1006 :     so->keyCtx = AllocSetContextCreate(CurrentMemoryContext,
      43             :                                        "Gin scan key context",
      44             :                                        ALLOCSET_DEFAULT_SIZES);
      45        1006 :     initGinState(&so->ginstate, scan->indexRelation);
      46             : 
      47        1006 :     scan->opaque = so;
      48             : 
      49        1006 :     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        2600 : ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum,
      58             :                  StrategyNumber strategy, int32 searchMode,
      59             :                  Datum queryKey, GinNullCategory queryCategory,
      60             :                  bool isPartialMatch, Pointer extra_data)
      61             : {
      62        2600 :     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        2600 :     if (extra_data == NULL)
      73             :     {
      74        3814 :         for (i = 0; i < so->totalentries; i++)
      75             :         {
      76        2586 :             GinScanEntry prevEntry = so->entries[i];
      77             : 
      78        5016 :             if (prevEntry->extra_data == NULL &&
      79        4860 :                 prevEntry->isPartialMatch == isPartialMatch &&
      80        4798 :                 prevEntry->strategy == strategy &&
      81        4736 :                 prevEntry->searchMode == searchMode &&
      82        4736 :                 prevEntry->attnum == attnum &&
      83        4736 :                 ginCompareEntries(ginstate, attnum,
      84             :                                   prevEntry->queryKey,
      85        2368 :                                   prevEntry->queryCategory,
      86             :                                   queryKey,
      87             :                                   queryCategory) == 0)
      88             :             {
      89             :                 /* Successful match */
      90           0 :                 return prevEntry;
      91             :             }
      92             :         }
      93             :     }
      94             : 
      95             :     /* Nope, create a new entry */
      96        2600 :     scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData));
      97        2600 :     scanEntry->queryKey = queryKey;
      98        2600 :     scanEntry->queryCategory = queryCategory;
      99        2600 :     scanEntry->isPartialMatch = isPartialMatch;
     100        2600 :     scanEntry->extra_data = extra_data;
     101        2600 :     scanEntry->strategy = strategy;
     102        2600 :     scanEntry->searchMode = searchMode;
     103        2600 :     scanEntry->attnum = attnum;
     104             : 
     105        2600 :     scanEntry->buffer = InvalidBuffer;
     106        2600 :     ItemPointerSetMin(&scanEntry->curItem);
     107        2600 :     scanEntry->matchBitmap = NULL;
     108        2600 :     scanEntry->matchIterator = NULL;
     109        2600 :     scanEntry->matchResult = NULL;
     110        2600 :     scanEntry->list = NULL;
     111        2600 :     scanEntry->nlist = 0;
     112        2600 :     scanEntry->offset = InvalidOffsetNumber;
     113        2600 :     scanEntry->isFinished = false;
     114        2600 :     scanEntry->reduceResult = false;
     115             : 
     116             :     /* Add it to so's array */
     117        2600 :     if (so->totalentries >= so->allocentries)
     118             :     {
     119           6 :         so->allocentries *= 2;
     120           6 :         so->entries = (GinScanEntry *)
     121           6 :             repalloc(so->entries, so->allocentries * sizeof(GinScanEntry));
     122             :     }
     123        2600 :     so->entries[so->totalentries++] = scanEntry;
     124             : 
     125        2600 :     return scanEntry;
     126             : }
     127             : 
     128             : /*
     129             :  * Initialize the next GinScanKey using the output from the extractQueryFn
     130             :  */
     131             : static void
     132        1010 : ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
     133             :                StrategyNumber strategy, int32 searchMode,
     134             :                Datum query, uint32 nQueryValues,
     135             :                Datum *queryValues, GinNullCategory *queryCategories,
     136             :                bool *partial_matches, Pointer *extra_data)
     137             : {
     138        1010 :     GinScanKey  key = &(so->keys[so->nkeys++]);
     139        1010 :     GinState   *ginstate = &so->ginstate;
     140        1010 :     uint32      nUserQueryValues = nQueryValues;
     141             :     uint32      i;
     142             : 
     143             :     /* Non-default search modes add one "hidden" entry to each key */
     144        1010 :     if (searchMode != GIN_SEARCH_MODE_DEFAULT)
     145         110 :         nQueryValues++;
     146        1010 :     key->nentries = nQueryValues;
     147        1010 :     key->nuserentries = nUserQueryValues;
     148             : 
     149        1010 :     key->scanEntry = (GinScanEntry *) palloc(sizeof(GinScanEntry) * nQueryValues);
     150        1010 :     key->entryRes = (GinTernaryValue *) palloc0(sizeof(GinTernaryValue) * nQueryValues);
     151             : 
     152        1010 :     key->query = query;
     153        1010 :     key->queryValues = queryValues;
     154        1010 :     key->queryCategories = queryCategories;
     155        1010 :     key->extra_data = extra_data;
     156        1010 :     key->strategy = strategy;
     157        1010 :     key->searchMode = searchMode;
     158        1010 :     key->attnum = attnum;
     159             : 
     160        1010 :     ItemPointerSetMin(&key->curItem);
     161        1010 :     key->curItemMatches = false;
     162        1010 :     key->recheckCurItem = false;
     163        1010 :     key->isFinished = false;
     164        1010 :     key->nrequired = 0;
     165        1010 :     key->nadditional = 0;
     166        1010 :     key->requiredEntries = NULL;
     167        1010 :     key->additionalEntries = NULL;
     168             : 
     169        1010 :     ginInitConsistentFunction(ginstate, key);
     170             : 
     171        3610 :     for (i = 0; i < nQueryValues; i++)
     172             :     {
     173             :         Datum       queryKey;
     174             :         GinNullCategory queryCategory;
     175             :         bool        isPartialMatch;
     176             :         Pointer     this_extra;
     177             : 
     178        2600 :         if (i < nUserQueryValues)
     179             :         {
     180             :             /* set up normal entry using extractQueryFn's outputs */
     181        2490 :             queryKey = queryValues[i];
     182        2490 :             queryCategory = queryCategories[i];
     183        2490 :             isPartialMatch =
     184        2490 :                 (ginstate->canPartialMatch[attnum - 1] && partial_matches)
     185        2490 :                 ? partial_matches[i] : false;
     186        2490 :             this_extra = (extra_data) ? extra_data[i] : NULL;
     187             :         }
     188             :         else
     189             :         {
     190             :             /* set up hidden entry */
     191         110 :             queryKey = (Datum) 0;
     192         110 :             switch (searchMode)
     193             :             {
     194             :                 case GIN_SEARCH_MODE_INCLUDE_EMPTY:
     195          30 :                     queryCategory = GIN_CAT_EMPTY_ITEM;
     196          30 :                     break;
     197             :                 case GIN_SEARCH_MODE_ALL:
     198          80 :                     queryCategory = GIN_CAT_EMPTY_QUERY;
     199          80 :                     break;
     200             :                 case GIN_SEARCH_MODE_EVERYTHING:
     201           0 :                     queryCategory = GIN_CAT_EMPTY_QUERY;
     202           0 :                     break;
     203             :                 default:
     204           0 :                     elog(ERROR, "unexpected searchMode: %d", searchMode);
     205             :                     queryCategory = 0;  /* keep compiler quiet */
     206             :                     break;
     207             :             }
     208         110 :             isPartialMatch = false;
     209         110 :             this_extra = NULL;
     210             : 
     211             :             /*
     212             :              * We set the strategy to a fixed value so that ginFillScanEntry
     213             :              * can combine these entries for different scan keys.  This is
     214             :              * safe because the strategy value in the entry struct is only
     215             :              * used for partial-match cases.  It's OK to overwrite our local
     216             :              * variable here because this is the last loop iteration.
     217             :              */
     218         110 :             strategy = InvalidStrategy;
     219             :         }
     220             : 
     221        2600 :         key->scanEntry[i] = ginFillScanEntry(so, attnum,
     222             :                                              strategy, searchMode,
     223             :                                              queryKey, queryCategory,
     224             :                                              isPartialMatch, this_extra);
     225             :     }
     226        1010 : }
     227             : 
     228             : /*
     229             :  * Release current scan keys, if any.
     230             :  */
     231             : void
     232        3026 : ginFreeScanKeys(GinScanOpaque so)
     233             : {
     234             :     uint32      i;
     235             : 
     236        3026 :     if (so->keys == NULL)
     237        2016 :         return;
     238             : 
     239        3610 :     for (i = 0; i < so->totalentries; i++)
     240             :     {
     241        2600 :         GinScanEntry entry = so->entries[i];
     242             : 
     243        2600 :         if (entry->buffer != InvalidBuffer)
     244           0 :             ReleaseBuffer(entry->buffer);
     245        2600 :         if (entry->list)
     246        1446 :             pfree(entry->list);
     247        2600 :         if (entry->matchIterator)
     248           0 :             tbm_end_iterate(entry->matchIterator);
     249        2600 :         if (entry->matchBitmap)
     250         328 :             tbm_free(entry->matchBitmap);
     251             :     }
     252             : 
     253        1010 :     MemoryContextResetAndDeleteChildren(so->keyCtx);
     254             : 
     255        1010 :     so->keys = NULL;
     256        1010 :     so->nkeys = 0;
     257        1010 :     so->entries = NULL;
     258        1010 :     so->totalentries = 0;
     259             : }
     260             : 
     261             : void
     262        1010 : ginNewScanKey(IndexScanDesc scan)
     263             : {
     264        1010 :     ScanKey     scankey = scan->keyData;
     265        1010 :     GinScanOpaque so = (GinScanOpaque) scan->opaque;
     266             :     int         i;
     267        1010 :     bool        hasNullQuery = false;
     268             :     MemoryContext oldCtx;
     269             : 
     270             :     /*
     271             :      * Allocate all the scan key information in the key context. (If
     272             :      * extractQuery leaks anything there, it won't be reset until the end of
     273             :      * scan or rescan, but that's OK.)
     274             :      */
     275        1010 :     oldCtx = MemoryContextSwitchTo(so->keyCtx);
     276             : 
     277             :     /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
     278        1010 :     so->keys = (GinScanKey)
     279        1010 :         palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData));
     280        1010 :     so->nkeys = 0;
     281             : 
     282             :     /* initialize expansible array of GinScanEntry pointers */
     283        1010 :     so->totalentries = 0;
     284        1010 :     so->allocentries = 32;
     285        1010 :     so->entries = (GinScanEntry *)
     286        1010 :         palloc(so->allocentries * sizeof(GinScanEntry));
     287             : 
     288        1010 :     so->isVoidRes = false;
     289             : 
     290        4040 :     for (i = 0; i < scan->numberOfKeys; i++)
     291             :     {
     292        1018 :         ScanKey     skey = &scankey[i];
     293             :         Datum      *queryValues;
     294        1018 :         int32       nQueryValues = 0;
     295        1018 :         bool       *partial_matches = NULL;
     296        1018 :         Pointer    *extra_data = NULL;
     297        1018 :         bool       *nullFlags = NULL;
     298             :         GinNullCategory *categories;
     299        1018 :         int32       searchMode = GIN_SEARCH_MODE_DEFAULT;
     300             : 
     301             :         /*
     302             :          * We assume that GIN-indexable operators are strict, so a null query
     303             :          * argument means an unsatisfiable query.
     304             :          */
     305        1018 :         if (skey->sk_flags & SK_ISNULL)
     306             :         {
     307           0 :             so->isVoidRes = true;
     308           8 :             break;
     309             :         }
     310             : 
     311             :         /* OK to call the extractQueryFn */
     312        1018 :         queryValues = (Datum *)
     313        1018 :             DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
     314             :                                               so->ginstate.supportCollation[skey->sk_attno - 1],
     315             :                                               skey->sk_argument,
     316             :                                               PointerGetDatum(&nQueryValues),
     317             :                                               UInt16GetDatum(skey->sk_strategy),
     318             :                                               PointerGetDatum(&partial_matches),
     319             :                                               PointerGetDatum(&extra_data),
     320             :                                               PointerGetDatum(&nullFlags),
     321             :                                               PointerGetDatum(&searchMode)));
     322             : 
     323             :         /*
     324             :          * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
     325             :          * in particular we don't allow extractQueryFn to select
     326             :          * GIN_SEARCH_MODE_EVERYTHING.
     327             :          */
     328        2036 :         if (searchMode < GIN_SEARCH_MODE_DEFAULT ||
     329        1018 :             searchMode > GIN_SEARCH_MODE_ALL)
     330           0 :             searchMode = GIN_SEARCH_MODE_ALL;
     331             : 
     332             :         /* Non-default modes require the index to have placeholders */
     333        1018 :         if (searchMode != GIN_SEARCH_MODE_DEFAULT)
     334         110 :             hasNullQuery = true;
     335             : 
     336             :         /*
     337             :          * In default mode, no keys means an unsatisfiable query.
     338             :          */
     339        1018 :         if (queryValues == NULL || nQueryValues <= 0)
     340             :         {
     341         100 :             if (searchMode == GIN_SEARCH_MODE_DEFAULT)
     342             :             {
     343           8 :                 so->isVoidRes = true;
     344           8 :                 break;
     345             :             }
     346          92 :             nQueryValues = 0;   /* ensure sane value */
     347             :         }
     348             : 
     349             :         /*
     350             :          * Create GinNullCategory representation.  If the extractQueryFn
     351             :          * didn't create a nullFlags array, we assume everything is non-null.
     352             :          * While at it, detect whether any null keys are present.
     353             :          */
     354        1010 :         categories = (GinNullCategory *) palloc0(nQueryValues * sizeof(GinNullCategory));
     355        1010 :         if (nullFlags)
     356             :         {
     357             :             int32       j;
     358             : 
     359         424 :             for (j = 0; j < nQueryValues; j++)
     360             :             {
     361         226 :                 if (nullFlags[j])
     362             :                 {
     363           0 :                     categories[j] = GIN_CAT_NULL_KEY;
     364           0 :                     hasNullQuery = true;
     365             :                 }
     366             :             }
     367             :         }
     368             : 
     369        2020 :         ginFillScanKey(so, skey->sk_attno,
     370        1010 :                        skey->sk_strategy, searchMode,
     371             :                        skey->sk_argument, nQueryValues,
     372             :                        queryValues, categories,
     373             :                        partial_matches, extra_data);
     374             :     }
     375             : 
     376             :     /*
     377             :      * If there are no regular scan keys, generate an EVERYTHING scankey to
     378             :      * drive a full-index scan.
     379             :      */
     380        1010 :     if (so->nkeys == 0 && !so->isVoidRes)
     381             :     {
     382           0 :         hasNullQuery = true;
     383           0 :         ginFillScanKey(so, FirstOffsetNumber,
     384             :                        InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING,
     385             :                        (Datum) 0, 0,
     386             :                        NULL, NULL, NULL, NULL);
     387             :     }
     388             : 
     389             :     /*
     390             :      * If the index is version 0, it may be missing null and placeholder
     391             :      * entries, which would render searches for nulls and full-index scans
     392             :      * unreliable.  Throw an error if so.
     393             :      */
     394        1010 :     if (hasNullQuery && !so->isVoidRes)
     395             :     {
     396             :         GinStatsData ginStats;
     397             : 
     398         110 :         ginGetStats(scan->indexRelation, &ginStats);
     399         110 :         if (ginStats.ginVersion < 1)
     400           0 :             ereport(ERROR,
     401             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     402             :                      errmsg("old GIN indexes do not support whole-index scans nor searches for nulls"),
     403             :                      errhint("To fix this, do REINDEX INDEX \"%s\".",
     404             :                              RelationGetRelationName(scan->indexRelation))));
     405             :     }
     406             : 
     407        1010 :     MemoryContextSwitchTo(oldCtx);
     408             : 
     409        1010 :     pgstat_count_index_scan(scan->indexRelation);
     410        1010 : }
     411             : 
     412             : void
     413        1010 : ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
     414             :           ScanKey orderbys, int norderbys)
     415             : {
     416        1010 :     GinScanOpaque so = (GinScanOpaque) scan->opaque;
     417             : 
     418        1010 :     ginFreeScanKeys(so);
     419             : 
     420        1010 :     if (scankey && scan->numberOfKeys > 0)
     421             :     {
     422        1010 :         memmove(scan->keyData, scankey,
     423        1010 :                 scan->numberOfKeys * sizeof(ScanKeyData));
     424             :     }
     425        1010 : }
     426             : 
     427             : 
     428             : void
     429        1006 : ginendscan(IndexScanDesc scan)
     430             : {
     431        1006 :     GinScanOpaque so = (GinScanOpaque) scan->opaque;
     432             : 
     433        1006 :     ginFreeScanKeys(so);
     434             : 
     435        1006 :     MemoryContextDelete(so->tempCtx);
     436        1006 :     MemoryContextDelete(so->keyCtx);
     437             : 
     438        1006 :     pfree(so);
     439        1006 : }

Generated by: LCOV version 1.13