LCOV - differential code coverage report
Current view: top level - src/backend/access/gin - ginutil.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 96.1 % 259 249 4 3 3 2 130 3 114 5 126 5
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 15 15 14 1 13 1
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a