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

Generated by: LCOV version 1.13