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

Generated by: LCOV version 1.14