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

Generated by: LCOV version 1.14