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

Generated by: LCOV version 1.11