LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginutil.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 248 258 96.1 %
Date: 2020-05-25 05:06:35 Functions: 15 15 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * ginutil.c
       4             :  *    Utility routines for the Postgres inverted index access method.
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *          src/backend/access/gin/ginutil.c
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/gin_private.h"
      18             : #include "access/ginxlog.h"
      19             : #include "access/reloptions.h"
      20             : #include "access/xloginsert.h"
      21             : #include "catalog/pg_collation.h"
      22             : #include "catalog/pg_type.h"
      23             : #include "commands/vacuum.h"
      24             : #include "miscadmin.h"
      25             : #include "storage/indexfsm.h"
      26             : #include "storage/lmgr.h"
      27             : #include "storage/predicate.h"
      28             : #include "utils/builtins.h"
      29             : #include "utils/index_selfuncs.h"
      30             : #include "utils/typcache.h"
      31             : 
      32             : 
      33             : /*
      34             :  * GIN handler function: return IndexAmRoutine with access method parameters
      35             :  * and callbacks.
      36             :  */
      37             : Datum
      38        1304 : ginhandler(PG_FUNCTION_ARGS)
      39             : {
      40        1304 :     IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
      41             : 
      42        1304 :     amroutine->amstrategies = 0;
      43        1304 :     amroutine->amsupport = GINNProcs;
      44        1304 :     amroutine->amoptsprocnum = GIN_OPTIONS_PROC;
      45        1304 :     amroutine->amcanorder = false;
      46        1304 :     amroutine->amcanorderbyop = false;
      47        1304 :     amroutine->amcanbackward = false;
      48        1304 :     amroutine->amcanunique = false;
      49        1304 :     amroutine->amcanmulticol = true;
      50        1304 :     amroutine->amoptionalkey = true;
      51        1304 :     amroutine->amsearcharray = false;
      52        1304 :     amroutine->amsearchnulls = false;
      53        1304 :     amroutine->amstorage = true;
      54        1304 :     amroutine->amclusterable = false;
      55        1304 :     amroutine->ampredlocks = true;
      56        1304 :     amroutine->amcanparallel = false;
      57        1304 :     amroutine->amcaninclude = false;
      58        1304 :     amroutine->amusemaintenanceworkmem = true;
      59        1304 :     amroutine->amparallelvacuumoptions =
      60             :         VACUUM_OPTION_PARALLEL_BULKDEL | VACUUM_OPTION_PARALLEL_CLEANUP;
      61        1304 :     amroutine->amkeytype = InvalidOid;
      62             : 
      63        1304 :     amroutine->ambuild = ginbuild;
      64        1304 :     amroutine->ambuildempty = ginbuildempty;
      65        1304 :     amroutine->aminsert = gininsert;
      66        1304 :     amroutine->ambulkdelete = ginbulkdelete;
      67        1304 :     amroutine->amvacuumcleanup = ginvacuumcleanup;
      68        1304 :     amroutine->amcanreturn = NULL;
      69        1304 :     amroutine->amcostestimate = gincostestimate;
      70        1304 :     amroutine->amoptions = ginoptions;
      71        1304 :     amroutine->amproperty = NULL;
      72        1304 :     amroutine->ambuildphasename = NULL;
      73        1304 :     amroutine->amvalidate = ginvalidate;
      74        1304 :     amroutine->ambeginscan = ginbeginscan;
      75        1304 :     amroutine->amrescan = ginrescan;
      76        1304 :     amroutine->amgettuple = NULL;
      77        1304 :     amroutine->amgetbitmap = gingetbitmap;
      78        1304 :     amroutine->amendscan = ginendscan;
      79        1304 :     amroutine->ammarkpos = NULL;
      80        1304 :     amroutine->amrestrpos = NULL;
      81        1304 :     amroutine->amestimateparallelscan = NULL;
      82        1304 :     amroutine->aminitparallelscan = NULL;
      83        1304 :     amroutine->amparallelrescan = NULL;
      84             : 
      85        1304 :     PG_RETURN_POINTER(amroutine);
      86             : }
      87             : 
      88             : /*
      89             :  * initGinState: fill in an empty GinState struct to describe the index
      90             :  *
      91             :  * Note: assorted subsidiary data is allocated in the CurrentMemoryContext.
      92             :  */
      93             : void
      94        1532 : initGinState(GinState *state, Relation index)
      95             : {
      96        1532 :     TupleDesc   origTupdesc = RelationGetDescr(index);
      97             :     int         i;
      98             : 
      99        1532 :     MemSet(state, 0, sizeof(GinState));
     100             : 
     101        1532 :     state->index = index;
     102        1532 :     state->oneCol = (origTupdesc->natts == 1) ? true : false;
     103        1532 :     state->origTupdesc = origTupdesc;
     104             : 
     105        3202 :     for (i = 0; i < origTupdesc->natts; i++)
     106             :     {
     107        1670 :         Form_pg_attribute attr = TupleDescAttr(origTupdesc, i);
     108             : 
     109        1670 :         if (state->oneCol)
     110        1394 :             state->tupdesc[i] = state->origTupdesc;
     111             :         else
     112             :         {
     113         276 :             state->tupdesc[i] = CreateTemplateTupleDesc(2);
     114             : 
     115         276 :             TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL,
     116             :                                INT2OID, -1, 0);
     117         276 :             TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL,
     118             :                                attr->atttypid,
     119             :                                attr->atttypmod,
     120             :                                attr->attndims);
     121         276 :             TupleDescInitEntryCollation(state->tupdesc[i], (AttrNumber) 2,
     122             :                                         attr->attcollation);
     123             :         }
     124             : 
     125             :         /*
     126             :          * If the compare proc isn't specified in the opclass definition, look
     127             :          * up the index key type's default btree comparator.
     128             :          */
     129        1670 :         if (index_getprocid(index, i + 1, GIN_COMPARE_PROC) != InvalidOid)
     130             :         {
     131         994 :             fmgr_info_copy(&(state->compareFn[i]),
     132         994 :                            index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
     133             :                            CurrentMemoryContext);
     134             :         }
     135             :         else
     136             :         {
     137             :             TypeCacheEntry *typentry;
     138             : 
     139         676 :             typentry = lookup_type_cache(attr->atttypid,
     140             :                                          TYPECACHE_CMP_PROC_FINFO);
     141         676 :             if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
     142           0 :                 ereport(ERROR,
     143             :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
     144             :                          errmsg("could not identify a comparison function for type %s",
     145             :                                 format_type_be(attr->atttypid))));
     146         676 :             fmgr_info_copy(&(state->compareFn[i]),
     147             :                            &(typentry->cmp_proc_finfo),
     148             :                            CurrentMemoryContext);
     149             :         }
     150             : 
     151             :         /* Opclass must always provide extract procs */
     152        1670 :         fmgr_info_copy(&(state->extractValueFn[i]),
     153        1670 :                        index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC),
     154             :                        CurrentMemoryContext);
     155        1670 :         fmgr_info_copy(&(state->extractQueryFn[i]),
     156        1670 :                        index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC),
     157             :                        CurrentMemoryContext);
     158             : 
     159             :         /*
     160             :          * Check opclass capability to do tri-state or binary logic consistent
     161             :          * check.
     162             :          */
     163        1670 :         if (index_getprocid(index, i + 1, GIN_TRICONSISTENT_PROC) != InvalidOid)
     164             :         {
     165        1270 :             fmgr_info_copy(&(state->triConsistentFn[i]),
     166        1270 :                            index_getprocinfo(index, i + 1, GIN_TRICONSISTENT_PROC),
     167             :                            CurrentMemoryContext);
     168             :         }
     169             : 
     170        1670 :         if (index_getprocid(index, i + 1, GIN_CONSISTENT_PROC) != InvalidOid)
     171             :         {
     172        1670 :             fmgr_info_copy(&(state->consistentFn[i]),
     173        1670 :                            index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC),
     174             :                            CurrentMemoryContext);
     175             :         }
     176             : 
     177        1670 :         if (state->consistentFn[i].fn_oid == InvalidOid &&
     178           0 :             state->triConsistentFn[i].fn_oid == InvalidOid)
     179             :         {
     180           0 :             elog(ERROR, "missing GIN support function (%d or %d) for attribute %d of index \"%s\"",
     181             :                  GIN_CONSISTENT_PROC, GIN_TRICONSISTENT_PROC,
     182             :                  i + 1, RelationGetRelationName(index));
     183             :         }
     184             : 
     185             :         /*
     186             :          * Check opclass capability to do partial match.
     187             :          */
     188        1670 :         if (index_getprocid(index, i + 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid)
     189             :         {
     190         504 :             fmgr_info_copy(&(state->comparePartialFn[i]),
     191         504 :                            index_getprocinfo(index, i + 1, GIN_COMPARE_PARTIAL_PROC),
     192             :                            CurrentMemoryContext);
     193         504 :             state->canPartialMatch[i] = true;
     194             :         }
     195             :         else
     196             :         {
     197        1166 :             state->canPartialMatch[i] = false;
     198             :         }
     199             : 
     200             :         /*
     201             :          * If the index column has a specified collation, we should honor that
     202             :          * while doing comparisons.  However, we may have a collatable storage
     203             :          * type for a noncollatable indexed data type (for instance, hstore
     204             :          * uses text index entries).  If there's no index collation then
     205             :          * specify default collation in case the support functions need
     206             :          * collation.  This is harmless if the support functions don't care
     207             :          * about collation, so we just do it unconditionally.  (We could
     208             :          * alternatively call get_typcollation, but that seems like expensive
     209             :          * overkill --- there aren't going to be any cases where a GIN storage
     210             :          * type has a nondefault collation.)
     211             :          */
     212        1670 :         if (OidIsValid(index->rd_indcollation[i]))
     213         268 :             state->supportCollation[i] = index->rd_indcollation[i];
     214             :         else
     215        1402 :             state->supportCollation[i] = DEFAULT_COLLATION_OID;
     216             :     }
     217        1532 : }
     218             : 
     219             : /*
     220             :  * Extract attribute (column) number of stored entry from GIN tuple
     221             :  */
     222             : OffsetNumber
     223     2974248 : gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
     224             : {
     225             :     OffsetNumber colN;
     226             : 
     227     2974248 :     if (ginstate->oneCol)
     228             :     {
     229             :         /* column number is not stored explicitly */
     230     2954604 :         colN = FirstOffsetNumber;
     231             :     }
     232             :     else
     233             :     {
     234             :         Datum       res;
     235             :         bool        isnull;
     236             : 
     237             :         /*
     238             :          * First attribute is always int16, so we can safely use any tuple
     239             :          * descriptor to obtain first attribute of tuple
     240             :          */
     241       19644 :         res = index_getattr(tuple, FirstOffsetNumber, ginstate->tupdesc[0],
     242             :                             &isnull);
     243             :         Assert(!isnull);
     244             : 
     245       19644 :         colN = DatumGetUInt16(res);
     246             :         Assert(colN >= FirstOffsetNumber && colN <= ginstate->origTupdesc->natts);
     247             :     }
     248             : 
     249     2974248 :     return colN;
     250             : }
     251             : 
     252             : /*
     253             :  * Extract stored datum (and possible null category) from GIN tuple
     254             :  */
     255             : Datum
     256     2963884 : gintuple_get_key(GinState *ginstate, IndexTuple tuple,
     257             :                  GinNullCategory *category)
     258             : {
     259             :     Datum       res;
     260             :     bool        isnull;
     261             : 
     262     2963884 :     if (ginstate->oneCol)
     263             :     {
     264             :         /*
     265             :          * Single column index doesn't store attribute numbers in tuples
     266             :          */
     267     2954548 :         res = index_getattr(tuple, FirstOffsetNumber, ginstate->origTupdesc,
     268             :                             &isnull);
     269             :     }
     270             :     else
     271             :     {
     272             :         /*
     273             :          * Since the datum type depends on which index column it's from, we
     274             :          * must be careful to use the right tuple descriptor here.
     275             :          */
     276        9336 :         OffsetNumber colN = gintuple_get_attrnum(ginstate, tuple);
     277             : 
     278        9336 :         res = index_getattr(tuple, OffsetNumberNext(FirstOffsetNumber),
     279             :                             ginstate->tupdesc[colN - 1],
     280             :                             &isnull);
     281             :     }
     282             : 
     283     2963884 :     if (isnull)
     284        1064 :         *category = GinGetNullCategory(tuple, ginstate);
     285             :     else
     286     2962820 :         *category = GIN_CAT_NORM_KEY;
     287             : 
     288     2963884 :     return res;
     289             : }
     290             : 
     291             : /*
     292             :  * Allocate a new page (either by recycling, or by extending the index file)
     293             :  * The returned buffer is already pinned and exclusive-locked
     294             :  * Caller is responsible for initializing the page by calling GinInitBuffer
     295             :  */
     296             : Buffer
     297        2872 : GinNewBuffer(Relation index)
     298             : {
     299             :     Buffer      buffer;
     300             :     bool        needLock;
     301             : 
     302             :     /* First, try to get a page from FSM */
     303             :     for (;;)
     304           0 :     {
     305        2872 :         BlockNumber blkno = GetFreeIndexPage(index);
     306             : 
     307        2872 :         if (blkno == InvalidBlockNumber)
     308        2804 :             break;
     309             : 
     310          68 :         buffer = ReadBuffer(index, blkno);
     311             : 
     312             :         /*
     313             :          * We have to guard against the possibility that someone else already
     314             :          * recycled this page; the buffer may be locked if so.
     315             :          */
     316          68 :         if (ConditionalLockBuffer(buffer))
     317             :         {
     318          68 :             if (GinPageIsRecyclable(BufferGetPage(buffer)))
     319          68 :                 return buffer;  /* OK to use */
     320             : 
     321           0 :             LockBuffer(buffer, GIN_UNLOCK);
     322             :         }
     323             : 
     324             :         /* Can't use it, so release buffer and try again */
     325           0 :         ReleaseBuffer(buffer);
     326             :     }
     327             : 
     328             :     /* Must extend the file */
     329        2804 :     needLock = !RELATION_IS_LOCAL(index);
     330        2804 :     if (needLock)
     331        1252 :         LockRelationForExtension(index, ExclusiveLock);
     332             : 
     333        2804 :     buffer = ReadBuffer(index, P_NEW);
     334        2804 :     LockBuffer(buffer, GIN_EXCLUSIVE);
     335             : 
     336        2804 :     if (needLock)
     337        1252 :         UnlockRelationForExtension(index, ExclusiveLock);
     338             : 
     339        2804 :     return buffer;
     340             : }
     341             : 
     342             : void
     343        4326 : GinInitPage(Page page, uint32 f, Size pageSize)
     344             : {
     345             :     GinPageOpaque opaque;
     346             : 
     347        4326 :     PageInit(page, pageSize, sizeof(GinPageOpaqueData));
     348             : 
     349        4326 :     opaque = GinPageGetOpaque(page);
     350        4326 :     memset(opaque, 0, sizeof(GinPageOpaqueData));
     351        4326 :     opaque->flags = f;
     352        4326 :     opaque->rightlink = InvalidBlockNumber;
     353        4326 : }
     354             : 
     355             : void
     356         934 : GinInitBuffer(Buffer b, uint32 f)
     357             : {
     358         934 :     GinInitPage(BufferGetPage(b), f, BufferGetPageSize(b));
     359         934 : }
     360             : 
     361             : void
     362         208 : GinInitMetabuffer(Buffer b)
     363             : {
     364             :     GinMetaPageData *metadata;
     365         208 :     Page        page = BufferGetPage(b);
     366             : 
     367         208 :     GinInitPage(page, GIN_META, BufferGetPageSize(b));
     368             : 
     369         208 :     metadata = GinPageGetMeta(page);
     370             : 
     371         208 :     metadata->head = metadata->tail = InvalidBlockNumber;
     372         208 :     metadata->tailFreeSize = 0;
     373         208 :     metadata->nPendingPages = 0;
     374         208 :     metadata->nPendingHeapTuples = 0;
     375         208 :     metadata->nTotalPages = 0;
     376         208 :     metadata->nEntryPages = 0;
     377         208 :     metadata->nDataPages = 0;
     378         208 :     metadata->nEntries = 0;
     379         208 :     metadata->ginVersion = GIN_CURRENT_VERSION;
     380             : 
     381             :     /*
     382             :      * Set pd_lower just past the end of the metadata.  This is essential,
     383             :      * because without doing so, metadata will be lost if xlog.c compresses
     384             :      * the page.
     385             :      */
     386         208 :     ((PageHeader) page)->pd_lower =
     387         208 :         ((char *) metadata + sizeof(GinMetaPageData)) - (char *) page;
     388         208 : }
     389             : 
     390             : /*
     391             :  * Compare two keys of the same index column
     392             :  */
     393             : int
     394    12549152 : ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
     395             :                   Datum a, GinNullCategory categorya,
     396             :                   Datum b, GinNullCategory categoryb)
     397             : {
     398             :     /* if not of same null category, sort by that first */
     399    12549152 :     if (categorya != categoryb)
     400       17480 :         return (categorya < categoryb) ? -1 : 1;
     401             : 
     402             :     /* all null items in same category are equal */
     403    12531672 :     if (categorya != GIN_CAT_NORM_KEY)
     404        5752 :         return 0;
     405             : 
     406             :     /* both not null, so safe to call the compareFn */
     407    12525920 :     return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
     408             :                                            ginstate->supportCollation[attnum - 1],
     409             :                                            a, b));
     410             : }
     411             : 
     412             : /*
     413             :  * Compare two keys of possibly different index columns
     414             :  */
     415             : int
     416    12549516 : ginCompareAttEntries(GinState *ginstate,
     417             :                      OffsetNumber attnuma, Datum a, GinNullCategory categorya,
     418             :                      OffsetNumber attnumb, Datum b, GinNullCategory categoryb)
     419             : {
     420             :     /* attribute number is the first sort key */
     421    12549516 :     if (attnuma != attnumb)
     422        3752 :         return (attnuma < attnumb) ? -1 : 1;
     423             : 
     424    12545764 :     return ginCompareEntries(ginstate, attnuma, a, categorya, b, categoryb);
     425             : }
     426             : 
     427             : 
     428             : /*
     429             :  * Support for sorting key datums in ginExtractEntries
     430             :  *
     431             :  * Note: we only have to worry about null and not-null keys here;
     432             :  * ginExtractEntries never generates more than one placeholder null,
     433             :  * so it doesn't have to sort those.
     434             :  */
     435             : typedef struct
     436             : {
     437             :     Datum       datum;
     438             :     bool        isnull;
     439             : } keyEntryData;
     440             : 
     441             : typedef struct
     442             : {
     443             :     FmgrInfo   *cmpDatumFunc;
     444             :     Oid         collation;
     445             :     bool        haveDups;
     446             : } cmpEntriesArg;
     447             : 
     448             : static int
     449      828562 : cmpEntries(const void *a, const void *b, void *arg)
     450             : {
     451      828562 :     const keyEntryData *aa = (const keyEntryData *) a;
     452      828562 :     const keyEntryData *bb = (const keyEntryData *) b;
     453      828562 :     cmpEntriesArg *data = (cmpEntriesArg *) arg;
     454             :     int         res;
     455             : 
     456      828562 :     if (aa->isnull)
     457             :     {
     458           0 :         if (bb->isnull)
     459           0 :             res = 0;            /* NULL "=" NULL */
     460             :         else
     461           0 :             res = 1;            /* NULL ">" not-NULL */
     462             :     }
     463      828562 :     else if (bb->isnull)
     464           0 :         res = -1;               /* not-NULL "<" NULL */
     465             :     else
     466      828562 :         res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
     467             :                                               data->collation,
     468             :                                               aa->datum, bb->datum));
     469             : 
     470             :     /*
     471             :      * Detect if we have any duplicates.  If there are equal keys, qsort must
     472             :      * compare them at some point, else it wouldn't know whether one should go
     473             :      * before or after the other.
     474             :      */
     475      828562 :     if (res == 0)
     476       21362 :         data->haveDups = true;
     477             : 
     478      828562 :     return res;
     479             : }
     480             : 
     481             : 
     482             : /*
     483             :  * Extract the index key values from an indexable item
     484             :  *
     485             :  * The resulting key values are sorted, and any duplicates are removed.
     486             :  * This avoids generating redundant index entries.
     487             :  */
     488             : Datum *
     489      962736 : ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
     490             :                   Datum value, bool isNull,
     491             :                   int32 *nentries, GinNullCategory **categories)
     492             : {
     493             :     Datum      *entries;
     494             :     bool       *nullFlags;
     495             :     int32       i;
     496             : 
     497             :     /*
     498             :      * We don't call the extractValueFn on a null item.  Instead generate a
     499             :      * placeholder.
     500             :      */
     501      962736 :     if (isNull)
     502             :     {
     503        4566 :         *nentries = 1;
     504        4566 :         entries = (Datum *) palloc(sizeof(Datum));
     505        4566 :         entries[0] = (Datum) 0;
     506        4566 :         *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory));
     507        4566 :         (*categories)[0] = GIN_CAT_NULL_ITEM;
     508        4566 :         return entries;
     509             :     }
     510             : 
     511             :     /* OK, call the opclass's extractValueFn */
     512      958170 :     nullFlags = NULL;           /* in case extractValue doesn't set it */
     513      958170 :     entries = (Datum *)
     514      958170 :         DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
     515             :                                           ginstate->supportCollation[attnum - 1],
     516             :                                           value,
     517             :                                           PointerGetDatum(nentries),
     518             :                                           PointerGetDatum(&nullFlags)));
     519             : 
     520             :     /*
     521             :      * Generate a placeholder if the item contained no keys.
     522             :      */
     523      958170 :     if (entries == NULL || *nentries <= 0)
     524             :     {
     525        1264 :         *nentries = 1;
     526        1264 :         entries = (Datum *) palloc(sizeof(Datum));
     527        1264 :         entries[0] = (Datum) 0;
     528        1264 :         *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory));
     529        1264 :         (*categories)[0] = GIN_CAT_EMPTY_ITEM;
     530        1264 :         return entries;
     531             :     }
     532             : 
     533             :     /*
     534             :      * If the extractValueFn didn't create a nullFlags array, create one,
     535             :      * assuming that everything's non-null.
     536             :      */
     537      956906 :     if (nullFlags == NULL)
     538      216124 :         nullFlags = (bool *) palloc0(*nentries * sizeof(bool));
     539             : 
     540             :     /*
     541             :      * If there's more than one key, sort and unique-ify.
     542             :      *
     543             :      * XXX Using qsort here is notationally painful, and the overhead is
     544             :      * pretty bad too.  For small numbers of keys it'd likely be better to use
     545             :      * a simple insertion sort.
     546             :      */
     547      956906 :     if (*nentries > 1)
     548             :     {
     549             :         keyEntryData *keydata;
     550             :         cmpEntriesArg arg;
     551             : 
     552      198818 :         keydata = (keyEntryData *) palloc(*nentries * sizeof(keyEntryData));
     553     1005340 :         for (i = 0; i < *nentries; i++)
     554             :         {
     555      806522 :             keydata[i].datum = entries[i];
     556      806522 :             keydata[i].isnull = nullFlags[i];
     557             :         }
     558             : 
     559      198818 :         arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
     560      198818 :         arg.collation = ginstate->supportCollation[attnum - 1];
     561      198818 :         arg.haveDups = false;
     562      198818 :         qsort_arg(keydata, *nentries, sizeof(keyEntryData),
     563             :                   cmpEntries, (void *) &arg);
     564             : 
     565      198818 :         if (arg.haveDups)
     566             :         {
     567             :             /* there are duplicates, must get rid of 'em */
     568             :             int32       j;
     569             : 
     570       10326 :             entries[0] = keydata[0].datum;
     571       10326 :             nullFlags[0] = keydata[0].isnull;
     572       10326 :             j = 1;
     573       48362 :             for (i = 1; i < *nentries; i++)
     574             :             {
     575       38036 :                 if (cmpEntries(&keydata[i - 1], &keydata[i], &arg) != 0)
     576             :                 {
     577       27420 :                     entries[j] = keydata[i].datum;
     578       27420 :                     nullFlags[j] = keydata[i].isnull;
     579       27420 :                     j++;
     580             :                 }
     581             :             }
     582       10326 :             *nentries = j;
     583             :         }
     584             :         else
     585             :         {
     586             :             /* easy, no duplicates */
     587      946652 :             for (i = 0; i < *nentries; i++)
     588             :             {
     589      758160 :                 entries[i] = keydata[i].datum;
     590      758160 :                 nullFlags[i] = keydata[i].isnull;
     591             :             }
     592             :         }
     593             : 
     594      198818 :         pfree(keydata);
     595             :     }
     596             : 
     597             :     /*
     598             :      * Create GinNullCategory representation from nullFlags.
     599             :      */
     600      956906 :     *categories = (GinNullCategory *) palloc0(*nentries * sizeof(GinNullCategory));
     601     2510900 :     for (i = 0; i < *nentries; i++)
     602     1553994 :         (*categories)[i] = (nullFlags[i] ? GIN_CAT_NULL_KEY : GIN_CAT_NORM_KEY);
     603             : 
     604      956906 :     return entries;
     605             : }
     606             : 
     607             : bytea *
     608         476 : ginoptions(Datum reloptions, bool validate)
     609             : {
     610             :     static const relopt_parse_elt tab[] = {
     611             :         {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
     612             :         {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,
     613             :                                                              pendingListCleanupSize)}
     614             :     };
     615             : 
     616         476 :     return (bytea *) build_reloptions(reloptions, validate,
     617             :                                       RELOPT_KIND_GIN,
     618             :                                       sizeof(GinOptions),
     619             :                                       tab, lengthof(tab));
     620             : }
     621             : 
     622             : /*
     623             :  * Fetch index's statistical data into *stats
     624             :  *
     625             :  * Note: in the result, nPendingPages can be trusted to be up-to-date,
     626             :  * as can ginVersion; but the other fields are as of the last VACUUM.
     627             :  */
     628             : void
     629        1552 : ginGetStats(Relation index, GinStatsData *stats)
     630             : {
     631             :     Buffer      metabuffer;
     632             :     Page        metapage;
     633             :     GinMetaPageData *metadata;
     634             : 
     635        1552 :     metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
     636        1552 :     LockBuffer(metabuffer, GIN_SHARE);
     637        1552 :     metapage = BufferGetPage(metabuffer);
     638        1552 :     metadata = GinPageGetMeta(metapage);
     639             : 
     640        1552 :     stats->nPendingPages = metadata->nPendingPages;
     641        1552 :     stats->nTotalPages = metadata->nTotalPages;
     642        1552 :     stats->nEntryPages = metadata->nEntryPages;
     643        1552 :     stats->nDataPages = metadata->nDataPages;
     644        1552 :     stats->nEntries = metadata->nEntries;
     645        1552 :     stats->ginVersion = metadata->ginVersion;
     646             : 
     647        1552 :     UnlockReleaseBuffer(metabuffer);
     648        1552 : }
     649             : 
     650             : /*
     651             :  * Write the given statistics to the index's metapage
     652             :  *
     653             :  * Note: nPendingPages and ginVersion are *not* copied over
     654             :  */
     655             : void
     656         240 : ginUpdateStats(Relation index, const GinStatsData *stats, bool is_build)
     657             : {
     658             :     Buffer      metabuffer;
     659             :     Page        metapage;
     660             :     GinMetaPageData *metadata;
     661             : 
     662         240 :     metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
     663         240 :     LockBuffer(metabuffer, GIN_EXCLUSIVE);
     664         240 :     metapage = BufferGetPage(metabuffer);
     665         240 :     metadata = GinPageGetMeta(metapage);
     666             : 
     667         240 :     START_CRIT_SECTION();
     668             : 
     669         240 :     metadata->nTotalPages = stats->nTotalPages;
     670         240 :     metadata->nEntryPages = stats->nEntryPages;
     671         240 :     metadata->nDataPages = stats->nDataPages;
     672         240 :     metadata->nEntries = stats->nEntries;
     673             : 
     674             :     /*
     675             :      * Set pd_lower just past the end of the metadata.  This is essential,
     676             :      * because without doing so, metadata will be lost if xlog.c compresses
     677             :      * the page.  (We must do this here because pre-v11 versions of PG did not
     678             :      * set the metapage's pd_lower correctly, so a pg_upgraded index might
     679             :      * contain the wrong value.)
     680             :      */
     681         240 :     ((PageHeader) metapage)->pd_lower =
     682         240 :         ((char *) metadata + sizeof(GinMetaPageData)) - (char *) metapage;
     683             : 
     684         240 :     MarkBufferDirty(metabuffer);
     685             : 
     686         240 :     if (RelationNeedsWAL(index) && !is_build)
     687             :     {
     688             :         XLogRecPtr  recptr;
     689             :         ginxlogUpdateMeta data;
     690             : 
     691          32 :         data.node = index->rd_node;
     692          32 :         data.ntuples = 0;
     693          32 :         data.newRightlink = data.prevTail = InvalidBlockNumber;
     694          32 :         memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
     695             : 
     696          32 :         XLogBeginInsert();
     697          32 :         XLogRegisterData((char *) &data, sizeof(ginxlogUpdateMeta));
     698          32 :         XLogRegisterBuffer(0, metabuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
     699             : 
     700          32 :         recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE);
     701          32 :         PageSetLSN(metapage, recptr);
     702             :     }
     703             : 
     704         240 :     UnlockReleaseBuffer(metabuffer);
     705             : 
     706         240 :     END_CRIT_SECTION();
     707         240 : }

Generated by: LCOV version 1.13