LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginutil.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 92.2 % 282 260
Test Date: 2026-01-26 10:56:24 Functions: 92.9 % 14 13
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 63.5 % 104 66

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

Generated by: LCOV version 2.3.2-1