LCOV - code coverage report
Current view: top level - src/backend/utils/cache - catcache.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 89.7 % 841 754
Test Date: 2026-01-26 10:56:24 Functions: 89.3 % 56 50
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 68.3 % 407 278

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * catcache.c
       4                 :             :  *        System catalog cache for tuples matching a key.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *        src/backend/utils/cache/catcache.c
      12                 :             :  *
      13                 :             :  *-------------------------------------------------------------------------
      14                 :             :  */
      15                 :             : #include "postgres.h"
      16                 :             : 
      17                 :             : #include "access/genam.h"
      18                 :             : #include "access/heaptoast.h"
      19                 :             : #include "access/relscan.h"
      20                 :             : #include "access/table.h"
      21                 :             : #include "access/xact.h"
      22                 :             : #include "catalog/catalog.h"
      23                 :             : #include "catalog/pg_collation.h"
      24                 :             : #include "catalog/pg_type.h"
      25                 :             : #include "common/hashfn.h"
      26                 :             : #include "common/pg_prng.h"
      27                 :             : #include "miscadmin.h"
      28                 :             : #include "port/pg_bitutils.h"
      29                 :             : #ifdef CATCACHE_STATS
      30                 :             : #include "storage/ipc.h"              /* for on_proc_exit */
      31                 :             : #endif
      32                 :             : #include "storage/lmgr.h"
      33                 :             : #include "utils/builtins.h"
      34                 :             : #include "utils/catcache.h"
      35                 :             : #include "utils/datum.h"
      36                 :             : #include "utils/fmgroids.h"
      37                 :             : #include "utils/injection_point.h"
      38                 :             : #include "utils/inval.h"
      39                 :             : #include "utils/memutils.h"
      40                 :             : #include "utils/rel.h"
      41                 :             : #include "utils/resowner.h"
      42                 :             : #include "utils/syscache.h"
      43                 :             : 
      44                 :             : /*
      45                 :             :  * If a catcache invalidation is processed while we are in the middle of
      46                 :             :  * creating a catcache entry (or list), it might apply to the entry we're
      47                 :             :  * creating, making it invalid before it's been inserted to the catcache.  To
      48                 :             :  * catch such cases, we have a stack of "create-in-progress" entries.  Cache
      49                 :             :  * invalidation marks any matching entries in the stack as dead, in addition
      50                 :             :  * to the actual CatCTup and CatCList entries.
      51                 :             :  */
      52                 :             : typedef struct CatCInProgress
      53                 :             : {
      54                 :             :         CatCache   *cache;                      /* cache that the entry belongs to */
      55                 :             :         uint32          hash_value;             /* hash of the entry; ignored for lists */
      56                 :             :         bool            list;                   /* is it a list entry? */
      57                 :             :         bool            dead;                   /* set when the entry is invalidated */
      58                 :             :         struct CatCInProgress *next;
      59                 :             : } CatCInProgress;
      60                 :             : 
      61                 :             : static CatCInProgress *catcache_in_progress_stack = NULL;
      62                 :             : 
      63                 :             :  /* #define CACHEDEBUG */       /* turns DEBUG elogs on */
      64                 :             : 
      65                 :             : /*
      66                 :             :  * Given a hash value and the size of the hash table, find the bucket
      67                 :             :  * in which the hash value belongs. Since the hash table must contain
      68                 :             :  * a power-of-2 number of elements, this is a simple bitmask.
      69                 :             :  */
      70                 :             : #define HASH_INDEX(h, sz) ((Index) ((h) & ((sz) - 1)))
      71                 :             : 
      72                 :             : 
      73                 :             : /*
      74                 :             :  *              variables, macros and other stuff
      75                 :             :  */
      76                 :             : 
      77                 :             : #ifdef CACHEDEBUG
      78                 :             : #define CACHE_elog(...)                         elog(__VA_ARGS__)
      79                 :             : #else
      80                 :             : #define CACHE_elog(...)
      81                 :             : #endif
      82                 :             : 
      83                 :             : /* Cache management header --- pointer is NULL until created */
      84                 :             : static CatCacheHeader *CacheHdr = NULL;
      85                 :             : 
      86                 :             : static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
      87                 :             :                                                                                            int nkeys,
      88                 :             :                                                                                            Datum v1, Datum v2,
      89                 :             :                                                                                            Datum v3, Datum v4);
      90                 :             : 
      91                 :             : static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
      92                 :             :                                                                                                 int nkeys,
      93                 :             :                                                                                                 uint32 hashValue,
      94                 :             :                                                                                                 Index hashIndex,
      95                 :             :                                                                                                 Datum v1, Datum v2,
      96                 :             :                                                                                                 Datum v3, Datum v4);
      97                 :             : 
      98                 :             : static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
      99                 :             :                                                                                    Datum v1, Datum v2, Datum v3, Datum v4);
     100                 :             : static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys,
     101                 :             :                                                                                                 HeapTuple tuple);
     102                 :             : static inline bool CatalogCacheCompareTuple(const CatCache *cache, int nkeys,
     103                 :             :                                                                                         const Datum *cachekeys,
     104                 :             :                                                                                         const Datum *searchkeys);
     105                 :             : 
     106                 :             : #ifdef CATCACHE_STATS
     107                 :             : static void CatCachePrintStats(int code, Datum arg);
     108                 :             : #endif
     109                 :             : static void CatCacheRemoveCTup(CatCache *cache, CatCTup *ct);
     110                 :             : static void CatCacheRemoveCList(CatCache *cache, CatCList *cl);
     111                 :             : static void RehashCatCache(CatCache *cp);
     112                 :             : static void RehashCatCacheLists(CatCache *cp);
     113                 :             : static void CatalogCacheInitializeCache(CatCache *cache);
     114                 :             : static CatCTup *CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
     115                 :             :                                                                                 Datum *arguments,
     116                 :             :                                                                                 uint32 hashValue, Index hashIndex);
     117                 :             : 
     118                 :             : static void ReleaseCatCacheWithOwner(HeapTuple tuple, ResourceOwner resowner);
     119                 :             : static void ReleaseCatCacheListWithOwner(CatCList *list, ResourceOwner resowner);
     120                 :             : static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, const int *attnos,
     121                 :             :                                                          const Datum *keys);
     122                 :             : static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, const int *attnos,
     123                 :             :                                                          const Datum *srckeys, Datum *dstkeys);
     124                 :             : 
     125                 :             : 
     126                 :             : /*
     127                 :             :  *                                      internal support functions
     128                 :             :  */
     129                 :             : 
     130                 :             : /* ResourceOwner callbacks to hold catcache references */
     131                 :             : 
     132                 :             : static void ResOwnerReleaseCatCache(Datum res);
     133                 :             : static char *ResOwnerPrintCatCache(Datum res);
     134                 :             : static void ResOwnerReleaseCatCacheList(Datum res);
     135                 :             : static char *ResOwnerPrintCatCacheList(Datum res);
     136                 :             : 
     137                 :             : static const ResourceOwnerDesc catcache_resowner_desc =
     138                 :             : {
     139                 :             :         /* catcache references */
     140                 :             :         .name = "catcache reference",
     141                 :             :         .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
     142                 :             :         .release_priority = RELEASE_PRIO_CATCACHE_REFS,
     143                 :             :         .ReleaseResource = ResOwnerReleaseCatCache,
     144                 :             :         .DebugPrint = ResOwnerPrintCatCache
     145                 :             : };
     146                 :             : 
     147                 :             : static const ResourceOwnerDesc catlistref_resowner_desc =
     148                 :             : {
     149                 :             :         /* catcache-list pins */
     150                 :             :         .name = "catcache list reference",
     151                 :             :         .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
     152                 :             :         .release_priority = RELEASE_PRIO_CATCACHE_LIST_REFS,
     153                 :             :         .ReleaseResource = ResOwnerReleaseCatCacheList,
     154                 :             :         .DebugPrint = ResOwnerPrintCatCacheList
     155                 :             : };
     156                 :             : 
     157                 :             : /* Convenience wrappers over ResourceOwnerRemember/Forget */
     158                 :             : static inline void
     159                 :     9950240 : ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
     160                 :             : {
     161                 :     9950240 :         ResourceOwnerRemember(owner, PointerGetDatum(tuple), &catcache_resowner_desc);
     162                 :     9950240 : }
     163                 :             : static inline void
     164                 :     9948436 : ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
     165                 :             : {
     166                 :     9948436 :         ResourceOwnerForget(owner, PointerGetDatum(tuple), &catcache_resowner_desc);
     167                 :     9948436 : }
     168                 :             : static inline void
     169                 :      378012 : ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
     170                 :             : {
     171                 :      378012 :         ResourceOwnerRemember(owner, PointerGetDatum(list), &catlistref_resowner_desc);
     172                 :      378012 : }
     173                 :             : static inline void
     174                 :           0 : ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
     175                 :             : {
     176                 :           0 :         ResourceOwnerForget(owner, PointerGetDatum(list), &catlistref_resowner_desc);
     177                 :           0 : }
     178                 :             : 
     179                 :             : 
     180                 :             : /*
     181                 :             :  * Hash and equality functions for system types that are used as cache key
     182                 :             :  * fields.  In some cases, we just call the regular SQL-callable functions for
     183                 :             :  * the appropriate data type, but that tends to be a little slow, and the
     184                 :             :  * speed of these functions is performance-critical.  Therefore, for data
     185                 :             :  * types that frequently occur as catcache keys, we hard-code the logic here.
     186                 :             :  * Avoiding the overhead of DirectFunctionCallN(...) is a substantial win, and
     187                 :             :  * in certain cases (like int4) we can adopt a faster hash algorithm as well.
     188                 :             :  */
     189                 :             : 
     190                 :             : static bool
     191                 :      685853 : chareqfast(Datum a, Datum b)
     192                 :             : {
     193                 :      685853 :         return DatumGetChar(a) == DatumGetChar(b);
     194                 :             : }
     195                 :             : 
     196                 :             : static uint32
     197                 :      725632 : charhashfast(Datum datum)
     198                 :             : {
     199                 :      725632 :         return murmurhash32((int32) DatumGetChar(datum));
     200                 :             : }
     201                 :             : 
     202                 :             : static bool
     203                 :      353158 : nameeqfast(Datum a, Datum b)
     204                 :             : {
     205                 :      353158 :         char       *ca = NameStr(*DatumGetName(a));
     206                 :      353158 :         char       *cb = NameStr(*DatumGetName(b));
     207                 :             : 
     208                 :      706316 :         return strncmp(ca, cb, NAMEDATALEN) == 0;
     209                 :      353158 : }
     210                 :             : 
     211                 :             : static uint32
     212                 :      709472 : namehashfast(Datum datum)
     213                 :             : {
     214                 :      709472 :         char       *key = NameStr(*DatumGetName(datum));
     215                 :             : 
     216                 :     1418944 :         return hash_bytes((unsigned char *) key, strlen(key));
     217                 :      709472 : }
     218                 :             : 
     219                 :             : static bool
     220                 :      912160 : int2eqfast(Datum a, Datum b)
     221                 :             : {
     222                 :      912160 :         return DatumGetInt16(a) == DatumGetInt16(b);
     223                 :             : }
     224                 :             : 
     225                 :             : static uint32
     226                 :     1117951 : int2hashfast(Datum datum)
     227                 :             : {
     228                 :     1117951 :         return murmurhash32((int32) DatumGetInt16(datum));
     229                 :             : }
     230                 :             : 
     231                 :             : static bool
     232                 :    11692689 : int4eqfast(Datum a, Datum b)
     233                 :             : {
     234                 :    11692689 :         return DatumGetInt32(a) == DatumGetInt32(b);
     235                 :             : }
     236                 :             : 
     237                 :             : static uint32
     238                 :    12752871 : int4hashfast(Datum datum)
     239                 :             : {
     240                 :    12752871 :         return murmurhash32((int32) DatumGetInt32(datum));
     241                 :             : }
     242                 :             : 
     243                 :             : static bool
     244                 :           1 : texteqfast(Datum a, Datum b)
     245                 :             : {
     246                 :             :         /*
     247                 :             :          * The use of DEFAULT_COLLATION_OID is fairly arbitrary here.  We just
     248                 :             :          * want to take the fast "deterministic" path in texteq().
     249                 :             :          */
     250                 :           1 :         return DatumGetBool(DirectFunctionCall2Coll(texteq, DEFAULT_COLLATION_OID, a, b));
     251                 :             : }
     252                 :             : 
     253                 :             : static uint32
     254                 :          41 : texthashfast(Datum datum)
     255                 :             : {
     256                 :             :         /* analogously here as in texteqfast() */
     257                 :          41 :         return DatumGetInt32(DirectFunctionCall1Coll(hashtext, DEFAULT_COLLATION_OID, datum));
     258                 :             : }
     259                 :             : 
     260                 :             : static bool
     261                 :         138 : oidvectoreqfast(Datum a, Datum b)
     262                 :             : {
     263                 :         138 :         return DatumGetBool(DirectFunctionCall2(oidvectoreq, a, b));
     264                 :             : }
     265                 :             : 
     266                 :             : static uint32
     267                 :       29450 : oidvectorhashfast(Datum datum)
     268                 :             : {
     269                 :       29450 :         return DatumGetInt32(DirectFunctionCall1(hashoidvector, datum));
     270                 :             : }
     271                 :             : 
     272                 :             : /* Lookup support functions for a type. */
     273                 :             : static void
     274                 :       27336 : GetCCHashEqFuncs(Oid keytype, CCHashFN *hashfunc, RegProcedure *eqfunc, CCFastEqualFN *fasteqfunc)
     275                 :             : {
     276   [ +  +  +  +  :       27336 :         switch (keytype)
             +  +  +  +  
                      - ]
     277                 :             :         {
     278                 :             :                 case BOOLOID:
     279                 :         301 :                         *hashfunc = charhashfast;
     280                 :         301 :                         *fasteqfunc = chareqfast;
     281                 :         301 :                         *eqfunc = F_BOOLEQ;
     282                 :         301 :                         break;
     283                 :             :                 case CHAROID:
     284                 :         767 :                         *hashfunc = charhashfast;
     285                 :         767 :                         *fasteqfunc = chareqfast;
     286                 :         767 :                         *eqfunc = F_CHAREQ;
     287                 :         767 :                         break;
     288                 :             :                 case NAMEOID:
     289                 :        4532 :                         *hashfunc = namehashfast;
     290                 :        4532 :                         *fasteqfunc = nameeqfast;
     291                 :        4532 :                         *eqfunc = F_NAMEEQ;
     292                 :        4532 :                         break;
     293                 :             :                 case INT2OID:
     294                 :        1779 :                         *hashfunc = int2hashfast;
     295                 :        1779 :                         *fasteqfunc = int2eqfast;
     296                 :        1779 :                         *eqfunc = F_INT2EQ;
     297                 :        1779 :                         break;
     298                 :             :                 case INT4OID:
     299                 :         201 :                         *hashfunc = int4hashfast;
     300                 :         201 :                         *fasteqfunc = int4eqfast;
     301                 :         201 :                         *eqfunc = F_INT4EQ;
     302                 :         201 :                         break;
     303                 :             :                 case TEXTOID:
     304                 :          46 :                         *hashfunc = texthashfast;
     305                 :          46 :                         *fasteqfunc = texteqfast;
     306                 :          46 :                         *eqfunc = F_TEXTEQ;
     307                 :          46 :                         break;
     308                 :             :                 case OIDOID:
     309                 :             :                 case REGPROCOID:
     310                 :             :                 case REGPROCEDUREOID:
     311                 :             :                 case REGOPEROID:
     312                 :             :                 case REGOPERATOROID:
     313                 :             :                 case REGCLASSOID:
     314                 :             :                 case REGTYPEOID:
     315                 :             :                 case REGCOLLATIONOID:
     316                 :             :                 case REGCONFIGOID:
     317                 :             :                 case REGDICTIONARYOID:
     318                 :             :                 case REGROLEOID:
     319                 :             :                 case REGNAMESPACEOID:
     320                 :             :                 case REGDATABASEOID:
     321                 :       19411 :                         *hashfunc = int4hashfast;
     322                 :       19411 :                         *fasteqfunc = int4eqfast;
     323                 :       19411 :                         *eqfunc = F_OIDEQ;
     324                 :       19411 :                         break;
     325                 :             :                 case OIDVECTOROID:
     326                 :         299 :                         *hashfunc = oidvectorhashfast;
     327                 :         299 :                         *fasteqfunc = oidvectoreqfast;
     328                 :         299 :                         *eqfunc = F_OIDVECTOREQ;
     329                 :         299 :                         break;
     330                 :             :                 default:
     331   [ #  #  #  # ]:           0 :                         elog(FATAL, "type %u not supported as catcache key", keytype);
     332                 :           0 :                         *hashfunc = NULL;       /* keep compiler quiet */
     333                 :             : 
     334                 :           0 :                         *eqfunc = InvalidOid;
     335                 :           0 :                         break;
     336                 :             :         }
     337                 :       27336 : }
     338                 :             : 
     339                 :             : /*
     340                 :             :  *              CatalogCacheComputeHashValue
     341                 :             :  *
     342                 :             :  * Compute the hash value associated with a given set of lookup keys
     343                 :             :  */
     344                 :             : static uint32
     345                 :    11479393 : CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
     346                 :             :                                                          Datum v1, Datum v2, Datum v3, Datum v4)
     347                 :             : {
     348                 :    11479393 :         uint32          hashValue = 0;
     349                 :    11479393 :         uint32          oneHash;
     350                 :    11479393 :         CCHashFN   *cc_hashfunc = cache->cc_hashfunc;
     351                 :             : 
     352                 :             :         CACHE_elog(DEBUG2, "CatalogCacheComputeHashValue %s %d %p",
     353                 :             :                            cache->cc_relname, nkeys, cache);
     354                 :             : 
     355   [ +  +  +  +  :    11479393 :         switch (nkeys)
                      - ]
     356                 :             :         {
     357                 :             :                 case 4:
     358                 :      450151 :                         oneHash = (cc_hashfunc[3]) (v4);
     359                 :      450151 :                         hashValue ^= pg_rotate_left32(oneHash, 24);
     360                 :             :                         /* FALLTHROUGH */
     361                 :             :                 case 3:
     362                 :     1222124 :                         oneHash = (cc_hashfunc[2]) (v3);
     363                 :     1222124 :                         hashValue ^= pg_rotate_left32(oneHash, 16);
     364                 :             :                         /* FALLTHROUGH */
     365                 :             :                 case 2:
     366                 :     2183749 :                         oneHash = (cc_hashfunc[1]) (v2);
     367                 :     2183749 :                         hashValue ^= pg_rotate_left32(oneHash, 8);
     368                 :             :                         /* FALLTHROUGH */
     369                 :             :                 case 1:
     370                 :    11479393 :                         oneHash = (cc_hashfunc[0]) (v1);
     371                 :    11479393 :                         hashValue ^= oneHash;
     372                 :    11479393 :                         break;
     373                 :             :                 default:
     374   [ #  #  #  # ]:           0 :                         elog(FATAL, "wrong number of hash keys: %d", nkeys);
     375                 :           0 :                         break;
     376                 :             :         }
     377                 :             : 
     378                 :    22958786 :         return hashValue;
     379                 :    11479393 : }
     380                 :             : 
     381                 :             : /*
     382                 :             :  *              CatalogCacheComputeTupleHashValue
     383                 :             :  *
     384                 :             :  * Compute the hash value associated with a given tuple to be cached
     385                 :             :  */
     386                 :             : static uint32
     387                 :      510528 : CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys, HeapTuple tuple)
     388                 :             : {
     389                 :     1531584 :         Datum           v1 = 0,
     390                 :      510528 :                                 v2 = 0,
     391                 :      510528 :                                 v3 = 0,
     392                 :      510528 :                                 v4 = 0;
     393                 :      510528 :         bool            isNull = false;
     394                 :      510528 :         int                *cc_keyno = cache->cc_keyno;
     395                 :      510528 :         TupleDesc       cc_tupdesc = cache->cc_tupdesc;
     396                 :             : 
     397                 :             :         /* Now extract key fields from tuple, insert into scankey */
     398   [ +  +  +  +  :      510528 :         switch (nkeys)
                      - ]
     399                 :             :         {
     400                 :             :                 case 4:
     401                 :       70848 :                         v4 = fastgetattr(tuple,
     402                 :       35424 :                                                          cc_keyno[3],
     403                 :       35424 :                                                          cc_tupdesc,
     404                 :             :                                                          &isNull);
     405         [ +  - ]:       35424 :                         Assert(!isNull);
     406                 :             :                         /* FALLTHROUGH */
     407                 :             :                 case 3:
     408                 :      164290 :                         v3 = fastgetattr(tuple,
     409                 :       82145 :                                                          cc_keyno[2],
     410                 :       82145 :                                                          cc_tupdesc,
     411                 :             :                                                          &isNull);
     412         [ +  - ]:       82145 :                         Assert(!isNull);
     413                 :             :                         /* FALLTHROUGH */
     414                 :             :                 case 2:
     415                 :      812434 :                         v2 = fastgetattr(tuple,
     416                 :      406217 :                                                          cc_keyno[1],
     417                 :      406217 :                                                          cc_tupdesc,
     418                 :             :                                                          &isNull);
     419         [ +  - ]:      406217 :                         Assert(!isNull);
     420                 :             :                         /* FALLTHROUGH */
     421                 :             :                 case 1:
     422                 :     1021056 :                         v1 = fastgetattr(tuple,
     423                 :      510528 :                                                          cc_keyno[0],
     424                 :      510528 :                                                          cc_tupdesc,
     425                 :             :                                                          &isNull);
     426         [ +  - ]:      510528 :                         Assert(!isNull);
     427                 :      510528 :                         break;
     428                 :             :                 default:
     429   [ #  #  #  # ]:           0 :                         elog(FATAL, "wrong number of hash keys: %d", nkeys);
     430                 :           0 :                         break;
     431                 :             :         }
     432                 :             : 
     433                 :     1021056 :         return CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
     434                 :      510528 : }
     435                 :             : 
     436                 :             : /*
     437                 :             :  *              CatalogCacheCompareTuple
     438                 :             :  *
     439                 :             :  * Compare a tuple to the passed arguments.
     440                 :             :  */
     441                 :             : static inline bool
     442                 :    10555683 : CatalogCacheCompareTuple(const CatCache *cache, int nkeys,
     443                 :             :                                                  const Datum *cachekeys,
     444                 :             :                                                  const Datum *searchkeys)
     445                 :             : {
     446                 :    10555683 :         const CCFastEqualFN *cc_fastequal = cache->cc_fastequal;
     447                 :    10555683 :         int                     i;
     448                 :             : 
     449         [ +  + ]:    24199682 :         for (i = 0; i < nkeys; i++)
     450                 :             :         {
     451         [ -  + ]:    13643999 :                 if (!(cc_fastequal[i]) (cachekeys[i], searchkeys[i]))
     452                 :           0 :                         return false;
     453                 :    13643999 :         }
     454                 :    10555683 :         return true;
     455                 :    10555683 : }
     456                 :             : 
     457                 :             : 
     458                 :             : #ifdef CATCACHE_STATS
     459                 :             : 
     460                 :             : static void
     461                 :             : CatCachePrintStats(int code, Datum arg)
     462                 :             : {
     463                 :             :         slist_iter      iter;
     464                 :             :         uint64          cc_searches = 0;
     465                 :             :         uint64          cc_hits = 0;
     466                 :             :         uint64          cc_neg_hits = 0;
     467                 :             :         uint64          cc_newloads = 0;
     468                 :             :         uint64          cc_invals = 0;
     469                 :             :         uint64          cc_nlists = 0;
     470                 :             :         uint64          cc_lsearches = 0;
     471                 :             :         uint64          cc_lhits = 0;
     472                 :             : 
     473                 :             :         slist_foreach(iter, &CacheHdr->ch_caches)
     474                 :             :         {
     475                 :             :                 CatCache   *cache = slist_container(CatCache, cc_next, iter.cur);
     476                 :             : 
     477                 :             :                 if (cache->cc_ntup == 0 && cache->cc_searches == 0)
     478                 :             :                         continue;                       /* don't print unused caches */
     479                 :             :                 elog(DEBUG2, "catcache %s/%u: %d tup, %" PRIu64 " srch, %" PRIu64 "+%"
     480                 :             :                          PRIu64 "=%" PRIu64 " hits, %" PRIu64 "+%" PRIu64 "=%"
     481                 :             :                          PRIu64 " loads, %" PRIu64 " invals, %d lists, %" PRIu64
     482                 :             :                          " lsrch, %" PRIu64 " lhits",
     483                 :             :                          cache->cc_relname,
     484                 :             :                          cache->cc_indexoid,
     485                 :             :                          cache->cc_ntup,
     486                 :             :                          cache->cc_searches,
     487                 :             :                          cache->cc_hits,
     488                 :             :                          cache->cc_neg_hits,
     489                 :             :                          cache->cc_hits + cache->cc_neg_hits,
     490                 :             :                          cache->cc_newloads,
     491                 :             :                          cache->cc_searches - cache->cc_hits - cache->cc_neg_hits - cache->cc_newloads,
     492                 :             :                          cache->cc_searches - cache->cc_hits - cache->cc_neg_hits,
     493                 :             :                          cache->cc_invals,
     494                 :             :                          cache->cc_nlist,
     495                 :             :                          cache->cc_lsearches,
     496                 :             :                          cache->cc_lhits);
     497                 :             :                 cc_searches += cache->cc_searches;
     498                 :             :                 cc_hits += cache->cc_hits;
     499                 :             :                 cc_neg_hits += cache->cc_neg_hits;
     500                 :             :                 cc_newloads += cache->cc_newloads;
     501                 :             :                 cc_invals += cache->cc_invals;
     502                 :             :                 cc_nlists += cache->cc_nlist;
     503                 :             :                 cc_lsearches += cache->cc_lsearches;
     504                 :             :                 cc_lhits += cache->cc_lhits;
     505                 :             :         }
     506                 :             :         elog(DEBUG2, "catcache totals: %d tup, %" PRIu64 " srch, %" PRIu64 "+%"
     507                 :             :                  PRIu64 "=%" PRIu64 " hits, %" PRIu64 "+%" PRIu64 "=%" PRIu64
     508                 :             :                  " loads, %" PRIu64 " invals, %" PRIu64 " lists, %" PRIu64
     509                 :             :                  " lsrch, %" PRIu64 " lhits",
     510                 :             :                  CacheHdr->ch_ntup,
     511                 :             :                  cc_searches,
     512                 :             :                  cc_hits,
     513                 :             :                  cc_neg_hits,
     514                 :             :                  cc_hits + cc_neg_hits,
     515                 :             :                  cc_newloads,
     516                 :             :                  cc_searches - cc_hits - cc_neg_hits - cc_newloads,
     517                 :             :                  cc_searches - cc_hits - cc_neg_hits,
     518                 :             :                  cc_invals,
     519                 :             :                  cc_nlists,
     520                 :             :                  cc_lsearches,
     521                 :             :                  cc_lhits);
     522                 :             : }
     523                 :             : #endif                                                  /* CATCACHE_STATS */
     524                 :             : 
     525                 :             : 
     526                 :             : /*
     527                 :             :  *              CatCacheRemoveCTup
     528                 :             :  *
     529                 :             :  * Unlink and delete the given cache entry
     530                 :             :  *
     531                 :             :  * NB: if it is a member of a CatCList, the CatCList is deleted too.
     532                 :             :  * Both the cache entry and the list had better have zero refcount.
     533                 :             :  */
     534                 :             : static void
     535                 :      139429 : CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
     536                 :             : {
     537         [ +  - ]:      139429 :         Assert(ct->refcount == 0);
     538         [ +  - ]:      139429 :         Assert(ct->my_cache == cache);
     539                 :             : 
     540         [ -  + ]:      139429 :         if (ct->c_list)
     541                 :             :         {
     542                 :             :                 /*
     543                 :             :                  * The cleanest way to handle this is to call CatCacheRemoveCList,
     544                 :             :                  * which will recurse back to me, and the recursive call will do the
     545                 :             :                  * work.  Set the "dead" flag to make sure it does recurse.
     546                 :             :                  */
     547                 :           0 :                 ct->dead = true;
     548                 :           0 :                 CatCacheRemoveCList(cache, ct->c_list);
     549                 :           0 :                 return;                                 /* nothing left to do */
     550                 :             :         }
     551                 :             : 
     552                 :             :         /* delink from linked list */
     553                 :      139429 :         dlist_delete(&ct->cache_elem);
     554                 :             : 
     555                 :             :         /*
     556                 :             :          * Free keys when we're dealing with a negative entry, normal entries just
     557                 :             :          * point into tuple, allocated together with the CatCTup.
     558                 :             :          */
     559         [ +  + ]:      139429 :         if (ct->negative)
     560                 :       70876 :                 CatCacheFreeKeys(cache->cc_tupdesc, cache->cc_nkeys,
     561                 :       35438 :                                                  cache->cc_keyno, ct->keys);
     562                 :             : 
     563                 :      139429 :         pfree(ct);
     564                 :             : 
     565                 :      139429 :         --cache->cc_ntup;
     566                 :      139429 :         --CacheHdr->ch_ntup;
     567                 :      139429 : }
     568                 :             : 
     569                 :             : /*
     570                 :             :  *              CatCacheRemoveCList
     571                 :             :  *
     572                 :             :  * Unlink and delete the given cache list entry
     573                 :             :  *
     574                 :             :  * NB: any dead member entries that become unreferenced are deleted too.
     575                 :             :  */
     576                 :             : static void
     577                 :       13023 : CatCacheRemoveCList(CatCache *cache, CatCList *cl)
     578                 :             : {
     579                 :       13023 :         int                     i;
     580                 :             : 
     581         [ +  - ]:       13023 :         Assert(cl->refcount == 0);
     582         [ +  - ]:       13023 :         Assert(cl->my_cache == cache);
     583                 :             : 
     584                 :             :         /* delink from member tuples */
     585         [ +  + ]:       50988 :         for (i = cl->n_members; --i >= 0;)
     586                 :             :         {
     587                 :       37965 :                 CatCTup    *ct = cl->members[i];
     588                 :             : 
     589         [ +  - ]:       37965 :                 Assert(ct->c_list == cl);
     590                 :       37965 :                 ct->c_list = NULL;
     591                 :             :                 /* if the member is dead and now has no references, remove it */
     592                 :             :                 if (
     593                 :             : #ifndef CATCACHE_FORCE_RELEASE
     594   [ +  +  -  + ]:       37965 :                         ct->dead &&
     595                 :             : #endif
     596                 :          24 :                         ct->refcount == 0)
     597                 :          24 :                         CatCacheRemoveCTup(cache, ct);
     598                 :       37965 :         }
     599                 :             : 
     600                 :             :         /* delink from linked list */
     601                 :       13023 :         dlist_delete(&cl->cache_elem);
     602                 :             : 
     603                 :             :         /* free associated column data */
     604                 :       26046 :         CatCacheFreeKeys(cache->cc_tupdesc, cl->nkeys,
     605                 :       13023 :                                          cache->cc_keyno, cl->keys);
     606                 :             : 
     607                 :       13023 :         pfree(cl);
     608                 :             : 
     609                 :       13023 :         --cache->cc_nlist;
     610                 :       13023 : }
     611                 :             : 
     612                 :             : 
     613                 :             : /*
     614                 :             :  *      CatCacheInvalidate
     615                 :             :  *
     616                 :             :  *      Invalidate entries in the specified cache, given a hash value.
     617                 :             :  *
     618                 :             :  *      We delete cache entries that match the hash value, whether positive
     619                 :             :  *      or negative.  We don't care whether the invalidation is the result
     620                 :             :  *      of a tuple insertion or a deletion.
     621                 :             :  *
     622                 :             :  *      We used to try to match positive cache entries by TID, but that is
     623                 :             :  *      unsafe after a VACUUM FULL on a system catalog: an inval event could
     624                 :             :  *      be queued before VACUUM FULL, and then processed afterwards, when the
     625                 :             :  *      target tuple that has to be invalidated has a different TID than it
     626                 :             :  *      did when the event was created.  So now we just compare hash values and
     627                 :             :  *      accept the small risk of unnecessary invalidations due to false matches.
     628                 :             :  *
     629                 :             :  *      This routine is only quasi-public: it should only be used by inval.c.
     630                 :             :  */
     631                 :             : void
     632                 :     2421680 : CatCacheInvalidate(CatCache *cache, uint32 hashValue)
     633                 :             : {
     634                 :     2421680 :         Index           hashIndex;
     635                 :     2421680 :         dlist_mutable_iter iter;
     636                 :             : 
     637                 :             :         CACHE_elog(DEBUG2, "CatCacheInvalidate: called");
     638                 :             : 
     639                 :             :         /*
     640                 :             :          * We don't bother to check whether the cache has finished initialization
     641                 :             :          * yet; if not, there will be no entries in it so no problem.
     642                 :             :          */
     643                 :             : 
     644                 :             :         /*
     645                 :             :          * Invalidate *all* CatCLists in this cache; it's too hard to tell which
     646                 :             :          * searches might still be correct, so just zap 'em all.
     647                 :             :          */
     648         [ +  + ]:     2668480 :         for (int i = 0; i < cache->cc_nlbuckets; i++)
     649                 :             :         {
     650                 :      246800 :                 dlist_head *bucket = &cache->cc_lbucket[i];
     651                 :             : 
     652   [ +  +  +  + ]:      259010 :                 dlist_foreach_modify(iter, bucket)
     653                 :             :                 {
     654                 :       12210 :                         CatCList   *cl = dlist_container(CatCList, cache_elem, iter.cur);
     655                 :             : 
     656         [ +  + ]:       12210 :                         if (cl->refcount > 0)
     657                 :          24 :                                 cl->dead = true;
     658                 :             :                         else
     659                 :       12186 :                                 CatCacheRemoveCList(cache, cl);
     660                 :       12210 :                 }
     661                 :      246800 :         }
     662                 :             : 
     663                 :             :         /*
     664                 :             :          * inspect the proper hash bucket for tuple matches
     665                 :             :          */
     666                 :     2421680 :         hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
     667   [ +  +  +  + ]:     3393467 :         dlist_foreach_modify(iter, &cache->cc_bucket[hashIndex])
     668                 :             :         {
     669                 :      971787 :                 CatCTup    *ct = dlist_container(CatCTup, cache_elem, iter.cur);
     670                 :             : 
     671         [ +  + ]:      971787 :                 if (hashValue == ct->hash_value)
     672                 :             :                 {
     673   [ +  +  +  - ]:      112189 :                         if (ct->refcount > 0 ||
     674         [ +  + ]:      112069 :                                 (ct->c_list && ct->c_list->refcount > 0))
     675                 :             :                         {
     676                 :         120 :                                 ct->dead = true;
     677                 :             :                                 /* list, if any, was marked dead above */
     678   [ +  +  +  - ]:         120 :                                 Assert(ct->c_list == NULL || ct->c_list->dead);
     679                 :         120 :                         }
     680                 :             :                         else
     681                 :      112045 :                                 CatCacheRemoveCTup(cache, ct);
     682                 :             :                         CACHE_elog(DEBUG2, "CatCacheInvalidate: invalidated");
     683                 :             : #ifdef CATCACHE_STATS
     684                 :             :                         cache->cc_invals++;
     685                 :             : #endif
     686                 :             :                         /* could be multiple matches, so keep looking! */
     687                 :      112165 :                 }
     688                 :      971787 :         }
     689                 :             : 
     690                 :             :         /* Also invalidate any entries that are being built */
     691         [ +  + ]:     2447369 :         for (CatCInProgress *e = catcache_in_progress_stack; e != NULL; e = e->next)
     692                 :             :         {
     693         [ +  + ]:       25689 :                 if (e->cache == cache)
     694                 :             :                 {
     695   [ +  +  -  + ]:         103 :                         if (e->list || e->hash_value == hashValue)
     696                 :         101 :                                 e->dead = true;
     697                 :         103 :                 }
     698                 :       25689 :         }
     699                 :     2421680 : }
     700                 :             : 
     701                 :             : /* ----------------------------------------------------------------
     702                 :             :  *                                         public functions
     703                 :             :  * ----------------------------------------------------------------
     704                 :             :  */
     705                 :             : 
     706                 :             : 
     707                 :             : /*
     708                 :             :  * Standard routine for creating cache context if it doesn't exist yet
     709                 :             :  *
     710                 :             :  * There are a lot of places (probably far more than necessary) that check
     711                 :             :  * whether CacheMemoryContext exists yet and want to create it if not.
     712                 :             :  * We centralize knowledge of exactly how to create it here.
     713                 :             :  */
     714                 :             : void
     715                 :         798 : CreateCacheMemoryContext(void)
     716                 :             : {
     717                 :             :         /*
     718                 :             :          * Purely for paranoia, check that context doesn't exist; caller probably
     719                 :             :          * did so already.
     720                 :             :          */
     721         [ -  + ]:         798 :         if (!CacheMemoryContext)
     722                 :         798 :                 CacheMemoryContext = AllocSetContextCreate(TopMemoryContext,
     723                 :             :                                                                                                    "CacheMemoryContext",
     724                 :             :                                                                                                    ALLOCSET_DEFAULT_SIZES);
     725                 :         798 : }
     726                 :             : 
     727                 :             : 
     728                 :             : /*
     729                 :             :  *              ResetCatalogCache
     730                 :             :  *
     731                 :             :  * Reset one catalog cache to empty.
     732                 :             :  *
     733                 :             :  * This is not very efficient if the target cache is nearly empty.
     734                 :             :  * However, it shouldn't need to be efficient; we don't invoke it often.
     735                 :             :  *
     736                 :             :  * If 'debug_discard' is true, we are being called as part of
     737                 :             :  * debug_discard_caches.  In that case, the cache is not reset for
     738                 :             :  * correctness, but just to get more testing of cache invalidation.  We skip
     739                 :             :  * resetting in-progress build entries in that case, or we'd never make any
     740                 :             :  * progress.
     741                 :             :  */
     742                 :             : static void
     743                 :       45015 : ResetCatalogCache(CatCache *cache, bool debug_discard)
     744                 :             : {
     745                 :       45015 :         dlist_mutable_iter iter;
     746                 :       45015 :         int                     i;
     747                 :             : 
     748                 :             :         /* Remove each list in this cache, or at least mark it dead */
     749         [ +  + ]:       48327 :         for (i = 0; i < cache->cc_nlbuckets; i++)
     750                 :             :         {
     751                 :        3312 :                 dlist_head *bucket = &cache->cc_lbucket[i];
     752                 :             : 
     753   [ +  +  +  + ]:        4148 :                 dlist_foreach_modify(iter, bucket)
     754                 :             :                 {
     755                 :         836 :                         CatCList   *cl = dlist_container(CatCList, cache_elem, iter.cur);
     756                 :             : 
     757         [ -  + ]:         836 :                         if (cl->refcount > 0)
     758                 :           0 :                                 cl->dead = true;
     759                 :             :                         else
     760                 :         836 :                                 CatCacheRemoveCList(cache, cl);
     761                 :         836 :                 }
     762                 :        3312 :         }
     763                 :             : 
     764                 :             :         /* Remove each tuple in this cache, or at least mark it dead */
     765         [ +  + ]:     1343365 :         for (i = 0; i < cache->cc_nbuckets; i++)
     766                 :             :         {
     767                 :     1298350 :                 dlist_head *bucket = &cache->cc_bucket[i];
     768                 :             : 
     769   [ +  +  +  + ]:     1325616 :                 dlist_foreach_modify(iter, bucket)
     770                 :             :                 {
     771                 :       27266 :                         CatCTup    *ct = dlist_container(CatCTup, cache_elem, iter.cur);
     772                 :             : 
     773   [ +  -  #  # ]:       27266 :                         if (ct->refcount > 0 ||
     774         [ -  + ]:       27266 :                                 (ct->c_list && ct->c_list->refcount > 0))
     775                 :             :                         {
     776                 :           0 :                                 ct->dead = true;
     777                 :             :                                 /* list, if any, was marked dead above */
     778   [ #  #  #  # ]:           0 :                                 Assert(ct->c_list == NULL || ct->c_list->dead);
     779                 :           0 :                         }
     780                 :             :                         else
     781                 :       27266 :                                 CatCacheRemoveCTup(cache, ct);
     782                 :             : #ifdef CATCACHE_STATS
     783                 :             :                         cache->cc_invals++;
     784                 :             : #endif
     785                 :       27266 :                 }
     786                 :     1298350 :         }
     787                 :             : 
     788                 :             :         /* Also invalidate any entries that are being built */
     789         [ -  + ]:       45015 :         if (!debug_discard)
     790                 :             :         {
     791         [ +  + ]:       45185 :                 for (CatCInProgress *e = catcache_in_progress_stack; e != NULL; e = e->next)
     792                 :             :                 {
     793         [ +  + ]:         170 :                         if (e->cache == cache)
     794                 :           2 :                                 e->dead = true;
     795                 :         170 :                 }
     796                 :       45015 :         }
     797                 :       45015 : }
     798                 :             : 
     799                 :             : /*
     800                 :             :  *              ResetCatalogCaches
     801                 :             :  *
     802                 :             :  * Reset all caches when a shared cache inval event forces it
     803                 :             :  */
     804                 :             : void
     805                 :           0 : ResetCatalogCaches(void)
     806                 :             : {
     807                 :           0 :         ResetCatalogCachesExt(false);
     808                 :           0 : }
     809                 :             : 
     810                 :             : void
     811                 :         529 : ResetCatalogCachesExt(bool debug_discard)
     812                 :             : {
     813                 :         529 :         slist_iter      iter;
     814                 :             : 
     815                 :             :         CACHE_elog(DEBUG2, "ResetCatalogCaches called");
     816                 :             : 
     817         [ +  + ]:       45494 :         slist_foreach(iter, &CacheHdr->ch_caches)
     818                 :             :         {
     819                 :       44965 :                 CatCache   *cache = slist_container(CatCache, cc_next, iter.cur);
     820                 :             : 
     821                 :       44965 :                 ResetCatalogCache(cache, debug_discard);
     822                 :       44965 :         }
     823                 :             : 
     824                 :             :         CACHE_elog(DEBUG2, "end of ResetCatalogCaches call");
     825                 :         529 : }
     826                 :             : 
     827                 :             : /*
     828                 :             :  *              CatalogCacheFlushCatalog
     829                 :             :  *
     830                 :             :  *      Flush all catcache entries that came from the specified system catalog.
     831                 :             :  *      This is needed after VACUUM FULL/CLUSTER on the catalog, since the
     832                 :             :  *      tuples very likely now have different TIDs than before.  (At one point
     833                 :             :  *      we also tried to force re-execution of CatalogCacheInitializeCache for
     834                 :             :  *      the cache(s) on that catalog.  This is a bad idea since it leads to all
     835                 :             :  *      kinds of trouble if a cache flush occurs while loading cache entries.
     836                 :             :  *      We now avoid the need to do it by copying cc_tupdesc out of the relcache,
     837                 :             :  *      rather than relying on the relcache to keep a tupdesc for us.  Of course
     838                 :             :  *      this assumes the tupdesc of a cacheable system table will not change...)
     839                 :             :  */
     840                 :             : void
     841                 :          46 : CatalogCacheFlushCatalog(Oid catId)
     842                 :             : {
     843                 :          46 :         slist_iter      iter;
     844                 :             : 
     845                 :             :         CACHE_elog(DEBUG2, "CatalogCacheFlushCatalog called for %u", catId);
     846                 :             : 
     847         [ +  + ]:        3956 :         slist_foreach(iter, &CacheHdr->ch_caches)
     848                 :             :         {
     849                 :        3910 :                 CatCache   *cache = slist_container(CatCache, cc_next, iter.cur);
     850                 :             : 
     851                 :             :                 /* Does this cache store tuples of the target catalog? */
     852         [ +  + ]:        3910 :                 if (cache->cc_reloid == catId)
     853                 :             :                 {
     854                 :             :                         /* Yes, so flush all its contents */
     855                 :          50 :                         ResetCatalogCache(cache, false);
     856                 :             : 
     857                 :             :                         /* Tell inval.c to call syscache callbacks for this cache */
     858                 :          50 :                         CallSyscacheCallbacks(cache->id, 0);
     859                 :          50 :                 }
     860                 :        3910 :         }
     861                 :             : 
     862                 :             :         CACHE_elog(DEBUG2, "end of CatalogCacheFlushCatalog call");
     863                 :          46 : }
     864                 :             : 
     865                 :             : /*
     866                 :             :  *              InitCatCache
     867                 :             :  *
     868                 :             :  *      This allocates and initializes a cache for a system catalog relation.
     869                 :             :  *      Actually, the cache is only partially initialized to avoid opening the
     870                 :             :  *      relation.  The relation will be opened and the rest of the cache
     871                 :             :  *      structure initialized on the first access.
     872                 :             :  */
     873                 :             : #ifdef CACHEDEBUG
     874                 :             : #define InitCatCache_DEBUG2 \
     875                 :             : do { \
     876                 :             :         elog(DEBUG2, "InitCatCache: rel=%u ind=%u id=%d nkeys=%d size=%d", \
     877                 :             :                  cp->cc_reloid, cp->cc_indexoid, cp->id, \
     878                 :             :                  cp->cc_nkeys, cp->cc_nbuckets); \
     879                 :             : } while(0)
     880                 :             : #else
     881                 :             : #define InitCatCache_DEBUG2
     882                 :             : #endif
     883                 :             : 
     884                 :             : CatCache *
     885                 :       67830 : InitCatCache(int id,
     886                 :             :                          Oid reloid,
     887                 :             :                          Oid indexoid,
     888                 :             :                          int nkeys,
     889                 :             :                          const int *key,
     890                 :             :                          int nbuckets)
     891                 :             : {
     892                 :       67830 :         CatCache   *cp;
     893                 :       67830 :         MemoryContext oldcxt;
     894                 :       67830 :         int                     i;
     895                 :             : 
     896                 :             :         /*
     897                 :             :          * nbuckets is the initial number of hash buckets to use in this catcache.
     898                 :             :          * It will be enlarged later if it becomes too full.
     899                 :             :          *
     900                 :             :          * nbuckets must be a power of two.  We check this via Assert rather than
     901                 :             :          * a full runtime check because the values will be coming from constant
     902                 :             :          * tables.
     903                 :             :          *
     904                 :             :          * If you're confused by the power-of-two check, see comments in
     905                 :             :          * bitmapset.c for an explanation.
     906                 :             :          */
     907         [ +  - ]:       67830 :         Assert(nbuckets > 0 && (nbuckets & -nbuckets) == nbuckets);
     908                 :             : 
     909                 :             :         /*
     910                 :             :          * first switch to the cache context so our allocations do not vanish at
     911                 :             :          * the end of a transaction
     912                 :             :          */
     913         [ +  - ]:       67830 :         if (!CacheMemoryContext)
     914                 :           0 :                 CreateCacheMemoryContext();
     915                 :             : 
     916                 :       67830 :         oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
     917                 :             : 
     918                 :             :         /*
     919                 :             :          * if first time through, initialize the cache group header
     920                 :             :          */
     921         [ +  + ]:       67830 :         if (CacheHdr == NULL)
     922                 :             :         {
     923                 :         798 :                 CacheHdr = palloc_object(CatCacheHeader);
     924                 :         798 :                 slist_init(&CacheHdr->ch_caches);
     925                 :         798 :                 CacheHdr->ch_ntup = 0;
     926                 :             : #ifdef CATCACHE_STATS
     927                 :             :                 /* set up to dump stats at backend exit */
     928                 :             :                 on_proc_exit(CatCachePrintStats, 0);
     929                 :             : #endif
     930                 :         798 :         }
     931                 :             : 
     932                 :             :         /*
     933                 :             :          * Allocate a new cache structure, aligning to a cacheline boundary
     934                 :             :          *
     935                 :             :          * Note: we rely on zeroing to initialize all the dlist headers correctly
     936                 :             :          */
     937                 :       67830 :         cp = (CatCache *) palloc_aligned(sizeof(CatCache), PG_CACHE_LINE_SIZE,
     938                 :             :                                                                          MCXT_ALLOC_ZERO);
     939                 :       67830 :         cp->cc_bucket = palloc0(nbuckets * sizeof(dlist_head));
     940                 :             : 
     941                 :             :         /*
     942                 :             :          * Many catcaches never receive any list searches.  Therefore, we don't
     943                 :             :          * allocate the cc_lbuckets till we get a list search.
     944                 :             :          */
     945                 :       67830 :         cp->cc_lbucket = NULL;
     946                 :             : 
     947                 :             :         /*
     948                 :             :          * initialize the cache's relation information for the relation
     949                 :             :          * corresponding to this cache, and initialize some of the new cache's
     950                 :             :          * other internal fields.  But don't open the relation yet.
     951                 :             :          */
     952                 :       67830 :         cp->id = id;
     953                 :       67830 :         cp->cc_relname = "(not known yet)";
     954                 :       67830 :         cp->cc_reloid = reloid;
     955                 :       67830 :         cp->cc_indexoid = indexoid;
     956                 :       67830 :         cp->cc_relisshared = false; /* temporary */
     957                 :       67830 :         cp->cc_tupdesc = (TupleDesc) NULL;
     958                 :       67830 :         cp->cc_ntup = 0;
     959                 :       67830 :         cp->cc_nlist = 0;
     960                 :       67830 :         cp->cc_nbuckets = nbuckets;
     961                 :       67830 :         cp->cc_nlbuckets = 0;
     962                 :       67830 :         cp->cc_nkeys = nkeys;
     963         [ +  + ]:      177156 :         for (i = 0; i < nkeys; ++i)
     964                 :             :         {
     965         [ +  - ]:      109326 :                 Assert(AttributeNumberIsValid(key[i]));
     966                 :      109326 :                 cp->cc_keyno[i] = key[i];
     967                 :      109326 :         }
     968                 :             : 
     969                 :             :         /*
     970                 :             :          * new cache is initialized as far as we can go for now. print some
     971                 :             :          * debugging information, if appropriate.
     972                 :             :          */
     973                 :             :         InitCatCache_DEBUG2;
     974                 :             : 
     975                 :             :         /*
     976                 :             :          * add completed cache to top of group header's list
     977                 :             :          */
     978                 :       67830 :         slist_push_head(&CacheHdr->ch_caches, &cp->cc_next);
     979                 :             : 
     980                 :             :         /*
     981                 :             :          * back to the old context before we return...
     982                 :             :          */
     983                 :       67830 :         MemoryContextSwitchTo(oldcxt);
     984                 :             : 
     985                 :      135660 :         return cp;
     986                 :       67830 : }
     987                 :             : 
     988                 :             : /*
     989                 :             :  * Enlarge a catcache, doubling the number of buckets.
     990                 :             :  */
     991                 :             : static void
     992                 :           0 : RehashCatCache(CatCache *cp)
     993                 :             : {
     994                 :           0 :         dlist_head *newbucket;
     995                 :           0 :         int                     newnbuckets;
     996                 :           0 :         int                     i;
     997                 :             : 
     998   [ #  #  #  # ]:           0 :         elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
     999                 :             :                  cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
    1000                 :             : 
    1001                 :             :         /* Allocate a new, larger, hash table. */
    1002                 :           0 :         newnbuckets = cp->cc_nbuckets * 2;
    1003                 :           0 :         newbucket = (dlist_head *) MemoryContextAllocZero(CacheMemoryContext, newnbuckets * sizeof(dlist_head));
    1004                 :             : 
    1005                 :             :         /* Move all entries from old hash table to new. */
    1006         [ #  # ]:           0 :         for (i = 0; i < cp->cc_nbuckets; i++)
    1007                 :             :         {
    1008                 :           0 :                 dlist_mutable_iter iter;
    1009                 :             : 
    1010   [ #  #  #  # ]:           0 :                 dlist_foreach_modify(iter, &cp->cc_bucket[i])
    1011                 :             :                 {
    1012                 :           0 :                         CatCTup    *ct = dlist_container(CatCTup, cache_elem, iter.cur);
    1013                 :           0 :                         int                     hashIndex = HASH_INDEX(ct->hash_value, newnbuckets);
    1014                 :             : 
    1015                 :           0 :                         dlist_delete(iter.cur);
    1016                 :             : 
    1017                 :             :                         /*
    1018                 :             :                          * Note that each item is pushed at the tail of the new bucket,
    1019                 :             :                          * not its head.  This is consistent with the SearchCatCache*()
    1020                 :             :                          * routines, where matching entries are moved at the front of the
    1021                 :             :                          * list to speed subsequent searches.
    1022                 :             :                          */
    1023                 :           0 :                         dlist_push_tail(&newbucket[hashIndex], &ct->cache_elem);
    1024                 :           0 :                 }
    1025                 :           0 :         }
    1026                 :             : 
    1027                 :             :         /* Switch to the new array. */
    1028                 :           0 :         pfree(cp->cc_bucket);
    1029                 :           0 :         cp->cc_nbuckets = newnbuckets;
    1030                 :           0 :         cp->cc_bucket = newbucket;
    1031                 :           0 : }
    1032                 :             : 
    1033                 :             : /*
    1034                 :             :  * Enlarge a catcache's list storage, doubling the number of buckets.
    1035                 :             :  */
    1036                 :             : static void
    1037                 :          84 : RehashCatCacheLists(CatCache *cp)
    1038                 :             : {
    1039                 :          84 :         dlist_head *newbucket;
    1040                 :          84 :         int                     newnbuckets;
    1041                 :          84 :         int                     i;
    1042                 :             : 
    1043   [ -  +  -  + ]:          84 :         elog(DEBUG1, "rehashing catalog cache id %d for %s; %d lists, %d buckets",
    1044                 :             :                  cp->id, cp->cc_relname, cp->cc_nlist, cp->cc_nlbuckets);
    1045                 :             : 
    1046                 :             :         /* Allocate a new, larger, hash table. */
    1047                 :          84 :         newnbuckets = cp->cc_nlbuckets * 2;
    1048                 :          84 :         newbucket = (dlist_head *) MemoryContextAllocZero(CacheMemoryContext, newnbuckets * sizeof(dlist_head));
    1049                 :             : 
    1050                 :             :         /* Move all entries from old hash table to new. */
    1051         [ +  + ]:        2292 :         for (i = 0; i < cp->cc_nlbuckets; i++)
    1052                 :             :         {
    1053                 :        2208 :                 dlist_mutable_iter iter;
    1054                 :             : 
    1055   [ +  +  +  + ]:        6708 :                 dlist_foreach_modify(iter, &cp->cc_lbucket[i])
    1056                 :             :                 {
    1057                 :        4500 :                         CatCList   *cl = dlist_container(CatCList, cache_elem, iter.cur);
    1058                 :        4500 :                         int                     hashIndex = HASH_INDEX(cl->hash_value, newnbuckets);
    1059                 :             : 
    1060                 :        4500 :                         dlist_delete(iter.cur);
    1061                 :             : 
    1062                 :             :                         /*
    1063                 :             :                          * Note that each item is pushed at the tail of the new bucket,
    1064                 :             :                          * not its head.  This is consistent with the SearchCatCache*()
    1065                 :             :                          * routines, where matching entries are moved at the front of the
    1066                 :             :                          * list to speed subsequent searches.
    1067                 :             :                          */
    1068                 :        4500 :                         dlist_push_tail(&newbucket[hashIndex], &cl->cache_elem);
    1069                 :        4500 :                 }
    1070                 :        2208 :         }
    1071                 :             : 
    1072                 :             :         /* Switch to the new array. */
    1073                 :          84 :         pfree(cp->cc_lbucket);
    1074                 :          84 :         cp->cc_nlbuckets = newnbuckets;
    1075                 :          84 :         cp->cc_lbucket = newbucket;
    1076                 :          84 : }
    1077                 :             : 
    1078                 :             : /*
    1079                 :             :  *              ConditionalCatalogCacheInitializeCache
    1080                 :             :  *
    1081                 :             :  * Call CatalogCacheInitializeCache() if not yet done.
    1082                 :             :  */
    1083                 :             : pg_attribute_always_inline
    1084                 :             : static void
    1085                 :    11376759 : ConditionalCatalogCacheInitializeCache(CatCache *cache)
    1086                 :             : {
    1087                 :             : #ifdef USE_ASSERT_CHECKING
    1088                 :             :         /*
    1089                 :             :          * TypeCacheRelCallback() runs outside transactions and relies on TYPEOID
    1090                 :             :          * for hashing.  This isn't ideal.  Since lookup_type_cache() both
    1091                 :             :          * registers the callback and searches TYPEOID, reaching trouble likely
    1092                 :             :          * requires OOM at an unlucky moment.
    1093                 :             :          *
    1094                 :             :          * InvalidateAttoptCacheCallback() runs outside transactions and likewise
    1095                 :             :          * relies on ATTNUM.  InitPostgres() initializes ATTNUM, so it's reliable.
    1096                 :             :          */
    1097   [ +  +  +  + ]:    11376759 :         if (!(cache->id == TYPEOID || cache->id == ATTNUM) ||
    1098                 :    11376759 :                 IsTransactionState())
    1099                 :    11376220 :                 AssertCouldGetRelation();
    1100                 :             :         else
    1101         [ +  - ]:         539 :                 Assert(cache->cc_tupdesc != NULL);
    1102                 :             : #endif
    1103                 :             : 
    1104         [ +  + ]:    11376759 :         if (unlikely(cache->cc_tupdesc == NULL))
    1105                 :       17440 :                 CatalogCacheInitializeCache(cache);
    1106                 :    11376759 : }
    1107                 :             : 
    1108                 :             : /*
    1109                 :             :  *              CatalogCacheInitializeCache
    1110                 :             :  *
    1111                 :             :  * This function does final initialization of a catcache: obtain the tuple
    1112                 :             :  * descriptor and set up the hash and equality function links.
    1113                 :             :  */
    1114                 :             : #ifdef CACHEDEBUG
    1115                 :             : #define CatalogCacheInitializeCache_DEBUG1 \
    1116                 :             :         elog(DEBUG2, "CatalogCacheInitializeCache: cache @%p rel=%u", cache, \
    1117                 :             :                  cache->cc_reloid)
    1118                 :             : 
    1119                 :             : #define CatalogCacheInitializeCache_DEBUG2 \
    1120                 :             : do { \
    1121                 :             :                 if (cache->cc_keyno[i] > 0) { \
    1122                 :             :                         elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d, %u", \
    1123                 :             :                                 i+1, cache->cc_nkeys, cache->cc_keyno[i], \
    1124                 :             :                                  TupleDescAttr(tupdesc, cache->cc_keyno[i] - 1)->atttypid); \
    1125                 :             :                 } else { \
    1126                 :             :                         elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d", \
    1127                 :             :                                 i+1, cache->cc_nkeys, cache->cc_keyno[i]); \
    1128                 :             :                 } \
    1129                 :             : } while(0)
    1130                 :             : #else
    1131                 :             : #define CatalogCacheInitializeCache_DEBUG1
    1132                 :             : #define CatalogCacheInitializeCache_DEBUG2
    1133                 :             : #endif
    1134                 :             : 
    1135                 :             : static void
    1136                 :       17440 : CatalogCacheInitializeCache(CatCache *cache)
    1137                 :             : {
    1138                 :       17440 :         Relation        relation;
    1139                 :       17440 :         MemoryContext oldcxt;
    1140                 :       17440 :         TupleDesc       tupdesc;
    1141                 :       17440 :         int                     i;
    1142                 :             : 
    1143                 :             :         CatalogCacheInitializeCache_DEBUG1;
    1144                 :             : 
    1145                 :       17440 :         relation = table_open(cache->cc_reloid, AccessShareLock);
    1146                 :             : 
    1147                 :             :         /*
    1148                 :             :          * switch to the cache context so our allocations do not vanish at the end
    1149                 :             :          * of a transaction
    1150                 :             :          */
    1151         [ +  - ]:       17440 :         Assert(CacheMemoryContext != NULL);
    1152                 :             : 
    1153                 :       17440 :         oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
    1154                 :             : 
    1155                 :             :         /*
    1156                 :             :          * copy the relcache's tuple descriptor to permanent cache storage
    1157                 :             :          */
    1158                 :       17440 :         tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
    1159                 :             : 
    1160                 :             :         /*
    1161                 :             :          * save the relation's name and relisshared flag, too (cc_relname is used
    1162                 :             :          * only for debugging purposes)
    1163                 :             :          */
    1164                 :       17440 :         cache->cc_relname = pstrdup(RelationGetRelationName(relation));
    1165                 :       17440 :         cache->cc_relisshared = RelationGetForm(relation)->relisshared;
    1166                 :             : 
    1167                 :             :         /*
    1168                 :             :          * return to the caller's memory context and close the rel
    1169                 :             :          */
    1170                 :       17440 :         MemoryContextSwitchTo(oldcxt);
    1171                 :             : 
    1172                 :       17440 :         table_close(relation, AccessShareLock);
    1173                 :             : 
    1174                 :             :         CACHE_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys",
    1175                 :             :                            cache->cc_relname, cache->cc_nkeys);
    1176                 :             : 
    1177                 :             :         /*
    1178                 :             :          * initialize cache's key information
    1179                 :             :          */
    1180         [ +  + ]:       44776 :         for (i = 0; i < cache->cc_nkeys; ++i)
    1181                 :             :         {
    1182                 :       27336 :                 Oid                     keytype;
    1183                 :       27336 :                 RegProcedure eqfunc;
    1184                 :             : 
    1185                 :             :                 CatalogCacheInitializeCache_DEBUG2;
    1186                 :             : 
    1187         [ +  - ]:       27336 :                 if (cache->cc_keyno[i] > 0)
    1188                 :             :                 {
    1189                 :       54672 :                         Form_pg_attribute attr = TupleDescAttr(tupdesc,
    1190                 :       27336 :                                                                                                    cache->cc_keyno[i] - 1);
    1191                 :             : 
    1192                 :       27336 :                         keytype = attr->atttypid;
    1193                 :             :                         /* cache key columns should always be NOT NULL */
    1194         [ +  - ]:       27336 :                         Assert(attr->attnotnull);
    1195                 :       27336 :                 }
    1196                 :             :                 else
    1197                 :             :                 {
    1198         [ #  # ]:           0 :                         if (cache->cc_keyno[i] < 0)
    1199   [ #  #  #  # ]:           0 :                                 elog(FATAL, "sys attributes are not supported in caches");
    1200                 :           0 :                         keytype = OIDOID;
    1201                 :             :                 }
    1202                 :             : 
    1203                 :       54672 :                 GetCCHashEqFuncs(keytype,
    1204                 :       27336 :                                                  &cache->cc_hashfunc[i],
    1205                 :             :                                                  &eqfunc,
    1206                 :       27336 :                                                  &cache->cc_fastequal[i]);
    1207                 :             : 
    1208                 :             :                 /*
    1209                 :             :                  * Do equality-function lookup (we assume this won't need a catalog
    1210                 :             :                  * lookup for any supported type)
    1211                 :             :                  */
    1212                 :       54672 :                 fmgr_info_cxt(eqfunc,
    1213                 :       27336 :                                           &cache->cc_skey[i].sk_func,
    1214                 :       27336 :                                           CacheMemoryContext);
    1215                 :             : 
    1216                 :             :                 /* Initialize sk_attno suitably for HeapKeyTest() and heap scans */
    1217                 :       27336 :                 cache->cc_skey[i].sk_attno = cache->cc_keyno[i];
    1218                 :             : 
    1219                 :             :                 /* Fill in sk_strategy as well --- always standard equality */
    1220                 :       27336 :                 cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber;
    1221                 :       27336 :                 cache->cc_skey[i].sk_subtype = InvalidOid;
    1222                 :             :                 /* If a catcache key requires a collation, it must be C collation */
    1223                 :       27336 :                 cache->cc_skey[i].sk_collation = C_COLLATION_OID;
    1224                 :             : 
    1225                 :             :                 CACHE_elog(DEBUG2, "CatalogCacheInitializeCache %s %d %p",
    1226                 :             :                                    cache->cc_relname, i, cache);
    1227                 :       27336 :         }
    1228                 :             : 
    1229                 :             :         /*
    1230                 :             :          * mark this cache fully initialized
    1231                 :             :          */
    1232                 :       17440 :         cache->cc_tupdesc = tupdesc;
    1233                 :       17440 : }
    1234                 :             : 
    1235                 :             : /*
    1236                 :             :  * InitCatCachePhase2 -- external interface for CatalogCacheInitializeCache
    1237                 :             :  *
    1238                 :             :  * One reason to call this routine is to ensure that the relcache has
    1239                 :             :  * created entries for all the catalogs and indexes referenced by catcaches.
    1240                 :             :  * Therefore, provide an option to open the index as well as fixing the
    1241                 :             :  * cache itself.  An exception is the indexes on pg_am, which we don't use
    1242                 :             :  * (cf. IndexScanOK).
    1243                 :             :  */
    1244                 :             : void
    1245                 :        2667 : InitCatCachePhase2(CatCache *cache, bool touch_index)
    1246                 :             : {
    1247                 :        2667 :         ConditionalCatalogCacheInitializeCache(cache);
    1248                 :             : 
    1249         [ +  + ]:        2667 :         if (touch_index &&
    1250   [ +  +  +  + ]:        1870 :                 cache->id != AMOID &&
    1251                 :        1848 :                 cache->id != AMNAME)
    1252                 :             :         {
    1253                 :        1826 :                 Relation        idesc;
    1254                 :             : 
    1255                 :             :                 /*
    1256                 :             :                  * We must lock the underlying catalog before opening the index to
    1257                 :             :                  * avoid deadlock, since index_open could possibly result in reading
    1258                 :             :                  * this same catalog, and if anyone else is exclusive-locking this
    1259                 :             :                  * catalog and index they'll be doing it in that order.
    1260                 :             :                  */
    1261                 :        1826 :                 LockRelationOid(cache->cc_reloid, AccessShareLock);
    1262                 :        1826 :                 idesc = index_open(cache->cc_indexoid, AccessShareLock);
    1263                 :             : 
    1264                 :             :                 /*
    1265                 :             :                  * While we've got the index open, let's check that it's unique (and
    1266                 :             :                  * not just deferrable-unique, thank you very much).  This is just to
    1267                 :             :                  * catch thinkos in definitions of new catcaches, so we don't worry
    1268                 :             :                  * about the pg_am indexes not getting tested.
    1269                 :             :                  */
    1270         [ +  - ]:        1826 :                 Assert(idesc->rd_index->indisunique &&
    1271                 :             :                            idesc->rd_index->indimmediate);
    1272                 :             : 
    1273                 :        1826 :                 index_close(idesc, AccessShareLock);
    1274                 :        1826 :                 UnlockRelationOid(cache->cc_reloid, AccessShareLock);
    1275                 :        1826 :         }
    1276                 :        2667 : }
    1277                 :             : 
    1278                 :             : 
    1279                 :             : /*
    1280                 :             :  *              IndexScanOK
    1281                 :             :  *
    1282                 :             :  *              This function checks for tuples that will be fetched by
    1283                 :             :  *              IndexSupportInitialize() during relcache initialization for
    1284                 :             :  *              certain system indexes that support critical syscaches.
    1285                 :             :  *              We can't use an indexscan to fetch these, else we'll get into
    1286                 :             :  *              infinite recursion.  A plain heap scan will work, however.
    1287                 :             :  *              Once we have completed relcache initialization (signaled by
    1288                 :             :  *              criticalRelcachesBuilt), we don't have to worry anymore.
    1289                 :             :  *
    1290                 :             :  *              Similarly, during backend startup we have to be able to use the
    1291                 :             :  *              pg_authid, pg_auth_members and pg_database syscaches for
    1292                 :             :  *              authentication even if we don't yet have relcache entries for those
    1293                 :             :  *              catalogs' indexes.
    1294                 :             :  */
    1295                 :             : static bool
    1296                 :      326013 : IndexScanOK(CatCache *cache)
    1297                 :             : {
    1298   [ +  +  +  + ]:      326013 :         switch (cache->id)
    1299                 :             :         {
    1300                 :             :                 case INDEXRELID:
    1301                 :             : 
    1302                 :             :                         /*
    1303                 :             :                          * Rather than tracking exactly which indexes have to be loaded
    1304                 :             :                          * before we can use indexscans (which changes from time to time),
    1305                 :             :                          * just force all pg_index searches to be heap scans until we've
    1306                 :             :                          * built the critical relcaches.
    1307                 :             :                          */
    1308         [ +  + ]:       19794 :                         if (!criticalRelcachesBuilt)
    1309                 :         306 :                                 return false;
    1310                 :       19488 :                         break;
    1311                 :             : 
    1312                 :             :                 case AMOID:
    1313                 :             :                 case AMNAME:
    1314                 :             : 
    1315                 :             :                         /*
    1316                 :             :                          * Always do heap scans in pg_am, because it's so small there's
    1317                 :             :                          * not much point in an indexscan anyway.  We *must* do this when
    1318                 :             :                          * initially building critical relcache entries, but we might as
    1319                 :             :                          * well just always do it.
    1320                 :             :                          */
    1321                 :        2739 :                         return false;
    1322                 :             : 
    1323                 :             :                 case AUTHNAME:
    1324                 :             :                 case AUTHOID:
    1325                 :             :                 case AUTHMEMMEMROLE:
    1326                 :             :                 case DATABASEOID:
    1327                 :             : 
    1328                 :             :                         /*
    1329                 :             :                          * Protect authentication lookups occurring before relcache has
    1330                 :             :                          * collected entries for shared indexes.
    1331                 :             :                          */
    1332         [ +  + ]:        4087 :                         if (!criticalSharedRelcachesBuilt)
    1333                 :          36 :                                 return false;
    1334                 :        4051 :                         break;
    1335                 :             : 
    1336                 :             :                 default:
    1337                 :      299393 :                         break;
    1338                 :             :         }
    1339                 :             : 
    1340                 :             :         /* Normal case, allow index scan */
    1341                 :      322932 :         return true;
    1342                 :      326013 : }
    1343                 :             : 
    1344                 :             : /*
    1345                 :             :  *      SearchCatCache
    1346                 :             :  *
    1347                 :             :  *              This call searches a system cache for a tuple, opening the relation
    1348                 :             :  *              if necessary (on the first access to a particular cache).
    1349                 :             :  *
    1350                 :             :  *              The result is NULL if not found, or a pointer to a HeapTuple in
    1351                 :             :  *              the cache.  The caller must not modify the tuple, and must call
    1352                 :             :  *              ReleaseCatCache() when done with it.
    1353                 :             :  *
    1354                 :             :  * The search key values should be expressed as Datums of the key columns'
    1355                 :             :  * datatype(s).  (Pass zeroes for any unused parameters.)  As a special
    1356                 :             :  * exception, the passed-in key for a NAME column can be just a C string;
    1357                 :             :  * the caller need not go to the trouble of converting it to a fully
    1358                 :             :  * null-padded NAME.
    1359                 :             :  */
    1360                 :             : HeapTuple
    1361                 :      458689 : SearchCatCache(CatCache *cache,
    1362                 :             :                            Datum v1,
    1363                 :             :                            Datum v2,
    1364                 :             :                            Datum v3,
    1365                 :             :                            Datum v4)
    1366                 :             : {
    1367                 :      458689 :         return SearchCatCacheInternal(cache, cache->cc_nkeys, v1, v2, v3, v4);
    1368                 :             : }
    1369                 :             : 
    1370                 :             : 
    1371                 :             : /*
    1372                 :             :  * SearchCatCacheN() are SearchCatCache() versions for a specific number of
    1373                 :             :  * arguments. The compiler can inline the body and unroll loops, making them a
    1374                 :             :  * bit faster than SearchCatCache().
    1375                 :             :  */
    1376                 :             : 
    1377                 :             : HeapTuple
    1378                 :     8624561 : SearchCatCache1(CatCache *cache,
    1379                 :             :                                 Datum v1)
    1380                 :             : {
    1381                 :     8624561 :         return SearchCatCacheInternal(cache, 1, v1, 0, 0, 0);
    1382                 :             : }
    1383                 :             : 
    1384                 :             : 
    1385                 :             : HeapTuple
    1386                 :      343840 : SearchCatCache2(CatCache *cache,
    1387                 :             :                                 Datum v1, Datum v2)
    1388                 :             : {
    1389                 :      343840 :         return SearchCatCacheInternal(cache, 2, v1, v2, 0, 0);
    1390                 :             : }
    1391                 :             : 
    1392                 :             : 
    1393                 :             : HeapTuple
    1394                 :      661646 : SearchCatCache3(CatCache *cache,
    1395                 :             :                                 Datum v1, Datum v2, Datum v3)
    1396                 :             : {
    1397                 :      661646 :         return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
    1398                 :             : }
    1399                 :             : 
    1400                 :             : 
    1401                 :             : HeapTuple
    1402                 :      414677 : SearchCatCache4(CatCache *cache,
    1403                 :             :                                 Datum v1, Datum v2, Datum v3, Datum v4)
    1404                 :             : {
    1405                 :      414677 :         return SearchCatCacheInternal(cache, 4, v1, v2, v3, v4);
    1406                 :             : }
    1407                 :             : 
    1408                 :             : /*
    1409                 :             :  * Work-horse for SearchCatCache/SearchCatCacheN.
    1410                 :             :  */
    1411                 :             : static inline HeapTuple
    1412                 :    10503413 : SearchCatCacheInternal(CatCache *cache,
    1413                 :             :                                            int nkeys,
    1414                 :             :                                            Datum v1,
    1415                 :             :                                            Datum v2,
    1416                 :             :                                            Datum v3,
    1417                 :             :                                            Datum v4)
    1418                 :             : {
    1419                 :    10503413 :         Datum           arguments[CATCACHE_MAXKEYS];
    1420                 :    10503413 :         uint32          hashValue;
    1421                 :    10503413 :         Index           hashIndex;
    1422                 :    10503413 :         dlist_iter      iter;
    1423                 :    10503413 :         dlist_head *bucket;
    1424                 :    10503413 :         CatCTup    *ct;
    1425                 :             : 
    1426         [ +  - ]:    10503413 :         Assert(cache->cc_nkeys == nkeys);
    1427                 :             : 
    1428                 :             :         /*
    1429                 :             :          * one-time startup overhead for each cache
    1430                 :             :          */
    1431                 :    10503413 :         ConditionalCatalogCacheInitializeCache(cache);
    1432                 :             : 
    1433                 :             : #ifdef CATCACHE_STATS
    1434                 :             :         cache->cc_searches++;
    1435                 :             : #endif
    1436                 :             : 
    1437                 :             :         /* Initialize local parameter array */
    1438                 :    10503413 :         arguments[0] = v1;
    1439                 :    10503413 :         arguments[1] = v2;
    1440                 :    10503413 :         arguments[2] = v3;
    1441                 :    10503413 :         arguments[3] = v4;
    1442                 :             : 
    1443                 :             :         /*
    1444                 :             :          * find the hash bucket in which to look for the tuple
    1445                 :             :          */
    1446                 :    10503413 :         hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
    1447                 :    10503413 :         hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
    1448                 :             : 
    1449                 :             :         /*
    1450                 :             :          * scan the hash bucket until we find a match or exhaust our tuples
    1451                 :             :          *
    1452                 :             :          * Note: it's okay to use dlist_foreach here, even though we modify the
    1453                 :             :          * dlist within the loop, because we don't continue the loop afterwards.
    1454                 :             :          */
    1455                 :    10503413 :         bucket = &cache->cc_bucket[hashIndex];
    1456   [ +  +  +  + ]:    10983728 :         dlist_foreach(iter, bucket)
    1457                 :             :         {
    1458                 :    10679636 :                 ct = dlist_container(CatCTup, cache_elem, iter.cur);
    1459                 :             : 
    1460         [ -  + ]:    10679636 :                 if (ct->dead)
    1461                 :           0 :                         continue;                       /* ignore dead entries */
    1462                 :             : 
    1463         [ +  + ]:    10679636 :                 if (ct->hash_value != hashValue)
    1464                 :      480315 :                         continue;                       /* quickly skip entry if wrong hash val */
    1465                 :             : 
    1466         [ -  + ]:    10199321 :                 if (!CatalogCacheCompareTuple(cache, nkeys, ct->keys, arguments))
    1467                 :           0 :                         continue;
    1468                 :             : 
    1469                 :             :                 /*
    1470                 :             :                  * We found a match in the cache.  Move it to the front of the list
    1471                 :             :                  * for its hashbucket, in order to speed subsequent searches.  (The
    1472                 :             :                  * most frequently accessed elements in any hashbucket will tend to be
    1473                 :             :                  * near the front of the hashbucket's list.)
    1474                 :             :                  */
    1475                 :    10199321 :                 dlist_move_head(bucket, &ct->cache_elem);
    1476                 :             : 
    1477                 :             :                 /*
    1478                 :             :                  * If it's a positive entry, bump its refcount and return it. If it's
    1479                 :             :                  * negative, we can report failure to the caller.
    1480                 :             :                  */
    1481         [ +  + ]:    10199321 :                 if (!ct->negative)
    1482                 :             :                 {
    1483                 :     9745104 :                         ResourceOwnerEnlarge(CurrentResourceOwner);
    1484                 :     9745104 :                         ct->refcount++;
    1485                 :     9745104 :                         ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
    1486                 :             : 
    1487                 :             :                         CACHE_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",
    1488                 :             :                                            cache->cc_relname, hashIndex);
    1489                 :             : 
    1490                 :             : #ifdef CATCACHE_STATS
    1491                 :             :                         cache->cc_hits++;
    1492                 :             : #endif
    1493                 :             : 
    1494                 :     9745104 :                         return &ct->tuple;
    1495                 :             :                 }
    1496                 :             :                 else
    1497                 :             :                 {
    1498                 :             :                         CACHE_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d",
    1499                 :             :                                            cache->cc_relname, hashIndex);
    1500                 :             : 
    1501                 :             : #ifdef CATCACHE_STATS
    1502                 :             :                         cache->cc_neg_hits++;
    1503                 :             : #endif
    1504                 :             : 
    1505                 :      454217 :                         return NULL;
    1506                 :             :                 }
    1507                 :             :         }
    1508                 :             : 
    1509                 :      304092 :         return SearchCatCacheMiss(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);
    1510                 :    10503413 : }
    1511                 :             : 
    1512                 :             : /*
    1513                 :             :  * Search the actual catalogs, rather than the cache.
    1514                 :             :  *
    1515                 :             :  * This is kept separate from SearchCatCacheInternal() to keep the fast-path
    1516                 :             :  * as small as possible.  To avoid that effort being undone by a helpful
    1517                 :             :  * compiler, try to explicitly forbid inlining.
    1518                 :             :  */
    1519                 :             : static pg_noinline HeapTuple
    1520                 :      304092 : SearchCatCacheMiss(CatCache *cache,
    1521                 :             :                                    int nkeys,
    1522                 :             :                                    uint32 hashValue,
    1523                 :             :                                    Index hashIndex,
    1524                 :             :                                    Datum v1,
    1525                 :             :                                    Datum v2,
    1526                 :             :                                    Datum v3,
    1527                 :             :                                    Datum v4)
    1528                 :             : {
    1529                 :      304092 :         ScanKeyData cur_skey[CATCACHE_MAXKEYS];
    1530                 :      304092 :         Relation        relation;
    1531                 :      304092 :         SysScanDesc scandesc;
    1532                 :      304092 :         HeapTuple       ntp;
    1533                 :      304092 :         CatCTup    *ct;
    1534                 :      304092 :         bool            stale;
    1535                 :      304092 :         Datum           arguments[CATCACHE_MAXKEYS];
    1536                 :             : 
    1537                 :             :         /* Initialize local parameter array */
    1538                 :      304092 :         arguments[0] = v1;
    1539                 :      304092 :         arguments[1] = v2;
    1540                 :      304092 :         arguments[2] = v3;
    1541                 :      304092 :         arguments[3] = v4;
    1542                 :             : 
    1543                 :             :         /*
    1544                 :             :          * Tuple was not found in cache, so we have to try to retrieve it directly
    1545                 :             :          * from the relation.  If found, we will add it to the cache; if not
    1546                 :             :          * found, we will add a negative cache entry instead.
    1547                 :             :          *
    1548                 :             :          * NOTE: it is possible for recursive cache lookups to occur while reading
    1549                 :             :          * the relation --- for example, due to shared-cache-inval messages being
    1550                 :             :          * processed during table_open().  This is OK.  It's even possible for one
    1551                 :             :          * of those lookups to find and enter the very same tuple we are trying to
    1552                 :             :          * fetch here.  If that happens, we will enter a second copy of the tuple
    1553                 :             :          * into the cache.  The first copy will never be referenced again, and
    1554                 :             :          * will eventually age out of the cache, so there's no functional problem.
    1555                 :             :          * This case is rare enough that it's not worth expending extra cycles to
    1556                 :             :          * detect.
    1557                 :             :          *
    1558                 :             :          * Another case, which we *must* handle, is that the tuple could become
    1559                 :             :          * outdated during CatalogCacheCreateEntry's attempt to detoast it (since
    1560                 :             :          * AcceptInvalidationMessages can run during TOAST table access).  We do
    1561                 :             :          * not want to return already-stale catcache entries, so we loop around
    1562                 :             :          * and do the table scan again if that happens.
    1563                 :             :          */
    1564                 :      304092 :         relation = table_open(cache->cc_reloid, AccessShareLock);
    1565                 :             : 
    1566                 :             :         /*
    1567                 :             :          * Ok, need to make a lookup in the relation, copy the scankey and fill
    1568                 :             :          * out any per-call fields.
    1569                 :             :          */
    1570                 :      304092 :         memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * nkeys);
    1571                 :      304092 :         cur_skey[0].sk_argument = v1;
    1572                 :      304092 :         cur_skey[1].sk_argument = v2;
    1573                 :      304092 :         cur_skey[2].sk_argument = v3;
    1574                 :      304092 :         cur_skey[3].sk_argument = v4;
    1575                 :             : 
    1576                 :      304092 :         do
    1577                 :             :         {
    1578                 :      608616 :                 scandesc = systable_beginscan(relation,
    1579                 :      304308 :                                                                           cache->cc_indexoid,
    1580                 :      304308 :                                                                           IndexScanOK(cache),
    1581                 :             :                                                                           NULL,
    1582                 :      304308 :                                                                           nkeys,
    1583                 :      304308 :                                                                           cur_skey);
    1584                 :             : 
    1585                 :      304308 :                 ct = NULL;
    1586                 :      304308 :                 stale = false;
    1587                 :             : 
    1588         [ +  + ]:      304308 :                 while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
    1589                 :             :                 {
    1590                 :      410704 :                         ct = CatalogCacheCreateEntry(cache, ntp, NULL,
    1591                 :      205352 :                                                                                  hashValue, hashIndex);
    1592                 :             :                         /* upon failure, we must start the scan over */
    1593         [ +  + ]:      205352 :                         if (ct == NULL)
    1594                 :             :                         {
    1595                 :         216 :                                 stale = true;
    1596                 :         216 :                                 break;
    1597                 :             :                         }
    1598                 :             :                         /* immediately set the refcount to 1 */
    1599                 :      205136 :                         ResourceOwnerEnlarge(CurrentResourceOwner);
    1600                 :      205136 :                         ct->refcount++;
    1601                 :      205136 :                         ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
    1602                 :      205136 :                         break;                          /* assume only one match */
    1603                 :             :                 }
    1604                 :             : 
    1605                 :      304308 :                 systable_endscan(scandesc);
    1606         [ +  + ]:      304308 :         } while (stale);
    1607                 :             : 
    1608                 :      304092 :         table_close(relation, AccessShareLock);
    1609                 :             : 
    1610                 :             :         /*
    1611                 :             :          * If tuple was not found, we need to build a negative cache entry
    1612                 :             :          * containing a fake tuple.  The fake tuple has the correct key columns,
    1613                 :             :          * but nulls everywhere else.
    1614                 :             :          *
    1615                 :             :          * In bootstrap mode, we don't build negative entries, because the cache
    1616                 :             :          * invalidation mechanism isn't alive and can't clear them if the tuple
    1617                 :             :          * gets created later.  (Bootstrap doesn't do UPDATEs, so it doesn't need
    1618                 :             :          * cache inval for that.)
    1619                 :             :          */
    1620         [ +  + ]:      304092 :         if (ct == NULL)
    1621                 :             :         {
    1622         [ +  + ]:       98956 :                 if (IsBootstrapProcessingMode())
    1623                 :         540 :                         return NULL;
    1624                 :             : 
    1625                 :      196832 :                 ct = CatalogCacheCreateEntry(cache, NULL, arguments,
    1626                 :       98416 :                                                                          hashValue, hashIndex);
    1627                 :             : 
    1628                 :             :                 /* Creating a negative cache entry shouldn't fail */
    1629         [ +  - ]:       98416 :                 Assert(ct != NULL);
    1630                 :             : 
    1631                 :             :                 CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
    1632                 :             :                                    cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
    1633                 :             :                 CACHE_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d",
    1634                 :             :                                    cache->cc_relname, hashIndex);
    1635                 :             : 
    1636                 :             :                 /*
    1637                 :             :                  * We are not returning the negative entry to the caller, so leave its
    1638                 :             :                  * refcount zero.
    1639                 :             :                  */
    1640                 :             : 
    1641                 :       98416 :                 return NULL;
    1642                 :             :         }
    1643                 :             : 
    1644                 :             :         CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
    1645                 :             :                            cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
    1646                 :             :         CACHE_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d",
    1647                 :             :                            cache->cc_relname, hashIndex);
    1648                 :             : 
    1649                 :             : #ifdef CATCACHE_STATS
    1650                 :             :         cache->cc_newloads++;
    1651                 :             : #endif
    1652                 :             : 
    1653                 :      205136 :         return &ct->tuple;
    1654                 :      304092 : }
    1655                 :             : 
    1656                 :             : /*
    1657                 :             :  *      ReleaseCatCache
    1658                 :             :  *
    1659                 :             :  *      Decrement the reference count of a catcache entry (releasing the
    1660                 :             :  *      hold grabbed by a successful SearchCatCache).
    1661                 :             :  *
    1662                 :             :  *      NOTE: if compiled with -DCATCACHE_FORCE_RELEASE then catcache entries
    1663                 :             :  *      will be freed as soon as their refcount goes to zero.  In combination
    1664                 :             :  *      with aset.c's CLOBBER_FREED_MEMORY option, this provides a good test
    1665                 :             :  *      to catch references to already-released catcache entries.
    1666                 :             :  */
    1667                 :             : void
    1668                 :     9948436 : ReleaseCatCache(HeapTuple tuple)
    1669                 :             : {
    1670                 :     9948436 :         ReleaseCatCacheWithOwner(tuple, CurrentResourceOwner);
    1671                 :     9948436 : }
    1672                 :             : 
    1673                 :             : static void
    1674                 :     9950240 : ReleaseCatCacheWithOwner(HeapTuple tuple, ResourceOwner resowner)
    1675                 :             : {
    1676                 :     9950240 :         CatCTup    *ct = (CatCTup *) (((char *) tuple) -
    1677                 :             :                                                                   offsetof(CatCTup, tuple));
    1678                 :             : 
    1679                 :             :         /* Safety checks to ensure we were handed a cache entry */
    1680         [ +  - ]:     9950240 :         Assert(ct->ct_magic == CT_MAGIC);
    1681         [ +  - ]:     9950240 :         Assert(ct->refcount > 0);
    1682                 :             : 
    1683                 :     9950240 :         ct->refcount--;
    1684         [ +  + ]:     9950240 :         if (resowner)
    1685                 :     9948436 :                 ResourceOwnerForgetCatCacheRef(resowner, &ct->tuple);
    1686                 :             : 
    1687                 :             :         if (
    1688                 :             : #ifndef CATCACHE_FORCE_RELEASE
    1689         [ +  + ]:     9950240 :                 ct->dead &&
    1690                 :             : #endif
    1691   [ +  +  #  # ]:         109 :                 ct->refcount == 0 &&
    1692         [ -  + ]:          94 :                 (ct->c_list == NULL || ct->c_list->refcount == 0))
    1693                 :          94 :                 CatCacheRemoveCTup(ct->my_cache, ct);
    1694                 :     9950240 : }
    1695                 :             : 
    1696                 :             : 
    1697                 :             : /*
    1698                 :             :  *      GetCatCacheHashValue
    1699                 :             :  *
    1700                 :             :  *              Compute the hash value for a given set of search keys.
    1701                 :             :  *
    1702                 :             :  * The reason for exposing this as part of the API is that the hash value is
    1703                 :             :  * exposed in cache invalidation operations, so there are places outside the
    1704                 :             :  * catcache code that need to be able to compute the hash values.
    1705                 :             :  */
    1706                 :             : uint32
    1707                 :       87440 : GetCatCacheHashValue(CatCache *cache,
    1708                 :             :                                          Datum v1,
    1709                 :             :                                          Datum v2,
    1710                 :             :                                          Datum v3,
    1711                 :             :                                          Datum v4)
    1712                 :             : {
    1713                 :             :         /*
    1714                 :             :          * one-time startup overhead for each cache
    1715                 :             :          */
    1716                 :       87440 :         ConditionalCatalogCacheInitializeCache(cache);
    1717                 :             : 
    1718                 :             :         /*
    1719                 :             :          * calculate the hash value
    1720                 :             :          */
    1721                 :       87440 :         return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, v1, v2, v3, v4);
    1722                 :             : }
    1723                 :             : 
    1724                 :             : 
    1725                 :             : /*
    1726                 :             :  *      SearchCatCacheList
    1727                 :             :  *
    1728                 :             :  *              Generate a list of all tuples matching a partial key (that is,
    1729                 :             :  *              a key specifying just the first K of the cache's N key columns).
    1730                 :             :  *
    1731                 :             :  *              It doesn't make any sense to specify all of the cache's key columns
    1732                 :             :  *              here: since the key is unique, there could be at most one match, so
    1733                 :             :  *              you ought to use SearchCatCache() instead.  Hence this function takes
    1734                 :             :  *              one fewer Datum argument than SearchCatCache() does.
    1735                 :             :  *
    1736                 :             :  *              The caller must not modify the list object or the pointed-to tuples,
    1737                 :             :  *              and must call ReleaseCatCacheList() when done with the list.
    1738                 :             :  */
    1739                 :             : CatCList *
    1740                 :      378012 : SearchCatCacheList(CatCache *cache,
    1741                 :             :                                    int nkeys,
    1742                 :             :                                    Datum v1,
    1743                 :             :                                    Datum v2,
    1744                 :             :                                    Datum v3)
    1745                 :             : {
    1746                 :      378012 :         Datum           v4 = 0;                 /* dummy last-column value */
    1747                 :      378012 :         Datum           arguments[CATCACHE_MAXKEYS];
    1748                 :      378012 :         uint32          lHashValue;
    1749                 :      378012 :         Index           lHashIndex;
    1750                 :      378012 :         dlist_iter      iter;
    1751                 :      378012 :         dlist_head *lbucket;
    1752                 :      378012 :         CatCList   *cl;
    1753                 :      378012 :         CatCTup    *ct;
    1754                 :      378012 :         List       *volatile ctlist;
    1755                 :      378012 :         ListCell   *ctlist_item;
    1756                 :      378012 :         int                     nmembers;
    1757                 :      378012 :         bool            ordered;
    1758                 :      378012 :         HeapTuple       ntp;
    1759                 :      378012 :         MemoryContext oldcxt;
    1760                 :      378012 :         int                     i;
    1761                 :      378012 :         CatCInProgress *save_in_progress;
    1762                 :      378012 :         CatCInProgress in_progress_ent;
    1763                 :             : 
    1764                 :             :         /*
    1765                 :             :          * one-time startup overhead for each cache
    1766                 :             :          */
    1767                 :      378012 :         ConditionalCatalogCacheInitializeCache(cache);
    1768                 :             : 
    1769         [ +  - ]:      378012 :         Assert(nkeys > 0 && nkeys < cache->cc_nkeys);
    1770                 :             : 
    1771                 :             : #ifdef CATCACHE_STATS
    1772                 :             :         cache->cc_lsearches++;
    1773                 :             : #endif
    1774                 :             : 
    1775                 :             :         /* Initialize local parameter array */
    1776                 :      378012 :         arguments[0] = v1;
    1777                 :      378012 :         arguments[1] = v2;
    1778                 :      378012 :         arguments[2] = v3;
    1779                 :      378012 :         arguments[3] = v4;
    1780                 :             : 
    1781                 :             :         /*
    1782                 :             :          * If we haven't previously done a list search in this cache, create the
    1783                 :             :          * bucket header array; otherwise, consider whether it's time to enlarge
    1784                 :             :          * it.
    1785                 :             :          */
    1786         [ +  + ]:      378012 :         if (cache->cc_lbucket == NULL)
    1787                 :             :         {
    1788                 :             :                 /* Arbitrary initial size --- must be a power of 2 */
    1789                 :        1230 :                 int                     nbuckets = 16;
    1790                 :             : 
    1791                 :        1230 :                 cache->cc_lbucket = (dlist_head *)
    1792                 :        2460 :                         MemoryContextAllocZero(CacheMemoryContext,
    1793                 :        1230 :                                                                    nbuckets * sizeof(dlist_head));
    1794                 :             :                 /* Don't set cc_nlbuckets if we get OOM allocating cc_lbucket */
    1795                 :        1230 :                 cache->cc_nlbuckets = nbuckets;
    1796                 :        1230 :         }
    1797                 :             :         else
    1798                 :             :         {
    1799                 :             :                 /*
    1800                 :             :                  * If the hash table has become too full, enlarge the buckets array.
    1801                 :             :                  * Quite arbitrarily, we enlarge when fill factor > 2.
    1802                 :             :                  */
    1803         [ +  + ]:      376782 :                 if (cache->cc_nlist > cache->cc_nlbuckets * 2)
    1804                 :          84 :                         RehashCatCacheLists(cache);
    1805                 :             :         }
    1806                 :             : 
    1807                 :             :         /*
    1808                 :             :          * Find the hash bucket in which to look for the CatCList.
    1809                 :             :          */
    1810                 :      378012 :         lHashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
    1811                 :      378012 :         lHashIndex = HASH_INDEX(lHashValue, cache->cc_nlbuckets);
    1812                 :             : 
    1813                 :             :         /*
    1814                 :             :          * scan the items until we find a match or exhaust our list
    1815                 :             :          *
    1816                 :             :          * Note: it's okay to use dlist_foreach here, even though we modify the
    1817                 :             :          * dlist within the loop, because we don't continue the loop afterwards.
    1818                 :             :          */
    1819                 :      378012 :         lbucket = &cache->cc_lbucket[lHashIndex];
    1820   [ +  +  +  + ]:      411694 :         dlist_foreach(iter, lbucket)
    1821                 :             :         {
    1822                 :      390044 :                 cl = dlist_container(CatCList, cache_elem, iter.cur);
    1823                 :             : 
    1824         [ -  + ]:      390044 :                 if (cl->dead)
    1825                 :           0 :                         continue;                       /* ignore dead entries */
    1826                 :             : 
    1827         [ +  + ]:      390044 :                 if (cl->hash_value != lHashValue)
    1828                 :       33682 :                         continue;                       /* quickly skip entry if wrong hash val */
    1829                 :             : 
    1830                 :             :                 /*
    1831                 :             :                  * see if the cached list matches our key.
    1832                 :             :                  */
    1833         [ -  + ]:      356362 :                 if (cl->nkeys != nkeys)
    1834                 :           0 :                         continue;
    1835                 :             : 
    1836         [ -  + ]:      356362 :                 if (!CatalogCacheCompareTuple(cache, nkeys, cl->keys, arguments))
    1837                 :           0 :                         continue;
    1838                 :             : 
    1839                 :             :                 /*
    1840                 :             :                  * We found a matching list.  Move the list to the front of the list
    1841                 :             :                  * for its hashbucket, so as to speed subsequent searches.  (We do not
    1842                 :             :                  * move the members to the fronts of their hashbucket lists, however,
    1843                 :             :                  * since there's no point in that unless they are searched for
    1844                 :             :                  * individually.)
    1845                 :             :                  */
    1846                 :      356362 :                 dlist_move_head(lbucket, &cl->cache_elem);
    1847                 :             : 
    1848                 :             :                 /* Bump the list's refcount and return it */
    1849                 :      356362 :                 ResourceOwnerEnlarge(CurrentResourceOwner);
    1850                 :      356362 :                 cl->refcount++;
    1851                 :      356362 :                 ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
    1852                 :             : 
    1853                 :             :                 CACHE_elog(DEBUG2, "SearchCatCacheList(%s): found list",
    1854                 :             :                                    cache->cc_relname);
    1855                 :             : 
    1856                 :             : #ifdef CATCACHE_STATS
    1857                 :             :                 cache->cc_lhits++;
    1858                 :             : #endif
    1859                 :             : 
    1860                 :      356362 :                 return cl;
    1861                 :             :         }
    1862                 :             : 
    1863                 :             :         /*
    1864                 :             :          * List was not found in cache, so we have to build it by reading the
    1865                 :             :          * relation.  For each matching tuple found in the relation, use an
    1866                 :             :          * existing cache entry if possible, else build a new one.
    1867                 :             :          *
    1868                 :             :          * We have to bump the member refcounts temporarily to ensure they won't
    1869                 :             :          * get dropped from the cache while loading other members. We use a PG_TRY
    1870                 :             :          * block to ensure we can undo those refcounts if we get an error before
    1871                 :             :          * we finish constructing the CatCList.  ctlist must be valid throughout
    1872                 :             :          * the PG_TRY block.
    1873                 :             :          */
    1874                 :       21650 :         ctlist = NIL;
    1875                 :             : 
    1876                 :             :         /*
    1877                 :             :          * Cache invalidation can happen while we're building the list.
    1878                 :             :          * CatalogCacheCreateEntry() handles concurrent invalidation of individual
    1879                 :             :          * tuples, but it's also possible that a new entry is concurrently added
    1880                 :             :          * that should be part of the list we're building.  Register an
    1881                 :             :          * "in-progress" entry that will receive the invalidation, until we have
    1882                 :             :          * built the final list entry.
    1883                 :             :          */
    1884                 :       21650 :         save_in_progress = catcache_in_progress_stack;
    1885                 :       21650 :         in_progress_ent.next = catcache_in_progress_stack;
    1886                 :       21650 :         in_progress_ent.cache = cache;
    1887                 :       21650 :         in_progress_ent.hash_value = lHashValue;
    1888                 :       21650 :         in_progress_ent.list = true;
    1889                 :       21650 :         in_progress_ent.dead = false;
    1890                 :       21650 :         catcache_in_progress_stack = &in_progress_ent;
    1891                 :             : 
    1892         [ +  - ]:       21650 :         PG_TRY();
    1893                 :             :         {
    1894                 :       21650 :                 ScanKeyData cur_skey[CATCACHE_MAXKEYS];
    1895                 :       21650 :                 Relation        relation;
    1896                 :       21650 :                 SysScanDesc scandesc;
    1897                 :       21650 :                 bool            first_iter = true;
    1898                 :             : 
    1899                 :       21650 :                 relation = table_open(cache->cc_reloid, AccessShareLock);
    1900                 :             : 
    1901                 :             :                 /*
    1902                 :             :                  * Ok, need to make a lookup in the relation, copy the scankey and
    1903                 :             :                  * fill out any per-call fields.
    1904                 :             :                  */
    1905                 :       21650 :                 memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * cache->cc_nkeys);
    1906                 :       21650 :                 cur_skey[0].sk_argument = v1;
    1907                 :       21650 :                 cur_skey[1].sk_argument = v2;
    1908                 :       21650 :                 cur_skey[2].sk_argument = v3;
    1909                 :       21650 :                 cur_skey[3].sk_argument = v4;
    1910                 :             : 
    1911                 :             :                 /*
    1912                 :             :                  * Scan the table for matching entries.  If an invalidation arrives
    1913                 :             :                  * mid-build, we will loop back here to retry.
    1914                 :             :                  */
    1915                 :       21650 :                 do
    1916                 :             :                 {
    1917                 :             :                         /*
    1918                 :             :                          * If we are retrying, release refcounts on any items created on
    1919                 :             :                          * the previous iteration.  We dare not try to free them if
    1920                 :             :                          * they're now unreferenced, since an error while doing that would
    1921                 :             :                          * result in the PG_CATCH below doing extra refcount decrements.
    1922                 :             :                          * Besides, we'll likely re-adopt those items in the next
    1923                 :             :                          * iteration, so it's not worth complicating matters to try to get
    1924                 :             :                          * rid of them.
    1925                 :             :                          */
    1926   [ +  +  +  +  :       22519 :                         foreach(ctlist_item, ctlist)
                   +  + ]
    1927                 :             :                         {
    1928                 :         814 :                                 ct = (CatCTup *) lfirst(ctlist_item);
    1929         [ +  - ]:         814 :                                 Assert(ct->c_list == NULL);
    1930         [ +  - ]:         814 :                                 Assert(ct->refcount > 0);
    1931                 :         814 :                                 ct->refcount--;
    1932                 :         814 :                         }
    1933                 :             :                         /* Reset ctlist in preparation for new try */
    1934                 :       21705 :                         ctlist = NIL;
    1935                 :       21705 :                         in_progress_ent.dead = false;
    1936                 :             : 
    1937                 :       43410 :                         scandesc = systable_beginscan(relation,
    1938                 :       21705 :                                                                                   cache->cc_indexoid,
    1939                 :       21705 :                                                                                   IndexScanOK(cache),
    1940                 :             :                                                                                   NULL,
    1941                 :       21705 :                                                                                   nkeys,
    1942                 :       21705 :                                                                                   cur_skey);
    1943                 :             : 
    1944                 :             :                         /* The list will be ordered iff we are doing an index scan */
    1945                 :       21705 :                         ordered = (scandesc->irel != NULL);
    1946                 :             : 
    1947                 :             :                         /* Injection point to help testing the recursive invalidation case */
    1948         [ +  + ]:       21705 :                         if (first_iter)
    1949                 :             :                         {
    1950                 :             :                                 INJECTION_POINT("catcache-list-miss-systable-scan-started", NULL);
    1951                 :       21650 :                                 first_iter = false;
    1952                 :       21650 :                         }
    1953                 :             : 
    1954   [ +  +  +  + ]:      168322 :                         while (HeapTupleIsValid(ntp = systable_getnext(scandesc)) &&
    1955                 :       73336 :                                    !in_progress_ent.dead)
    1956                 :             :                         {
    1957                 :       73334 :                                 uint32          hashValue;
    1958                 :       73334 :                                 Index           hashIndex;
    1959                 :       73334 :                                 bool            found = false;
    1960                 :       73334 :                                 dlist_head *bucket;
    1961                 :             : 
    1962                 :             :                                 /*
    1963                 :             :                                  * See if there's an entry for this tuple already.
    1964                 :             :                                  */
    1965                 :       73334 :                                 ct = NULL;
    1966                 :       73334 :                                 hashValue = CatalogCacheComputeTupleHashValue(cache, cache->cc_nkeys, ntp);
    1967                 :       73334 :                                 hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
    1968                 :             : 
    1969                 :       73334 :                                 bucket = &cache->cc_bucket[hashIndex];
    1970   [ +  +  +  + ]:      103892 :                                 dlist_foreach(iter, bucket)
    1971                 :             :                                 {
    1972                 :       52684 :                                         ct = dlist_container(CatCTup, cache_elem, iter.cur);
    1973                 :             : 
    1974   [ +  -  +  + ]:       52684 :                                         if (ct->dead || ct->negative)
    1975                 :          91 :                                                 continue;       /* ignore dead and negative entries */
    1976                 :             : 
    1977         [ +  + ]:       52593 :                                         if (ct->hash_value != hashValue)
    1978                 :       28848 :                                                 continue;       /* quickly skip entry if wrong hash val */
    1979                 :             : 
    1980         [ +  - ]:       23745 :                                         if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self)))
    1981                 :           0 :                                                 continue;       /* not same tuple */
    1982                 :             : 
    1983                 :             :                                         /*
    1984                 :             :                                          * Found a match, but can't use it if it belongs to
    1985                 :             :                                          * another list already
    1986                 :             :                                          */
    1987         [ +  + ]:       23745 :                                         if (ct->c_list)
    1988                 :        1619 :                                                 continue;
    1989                 :             : 
    1990                 :       22126 :                                         found = true;
    1991                 :       22126 :                                         break;          /* A-OK */
    1992                 :             :                                 }
    1993                 :             : 
    1994         [ +  + ]:       73334 :                                 if (!found)
    1995                 :             :                                 {
    1996                 :             :                                         /* We didn't find a usable entry, so make a new one */
    1997                 :      102416 :                                         ct = CatalogCacheCreateEntry(cache, ntp, NULL,
    1998                 :       51208 :                                                                                                  hashValue, hashIndex);
    1999                 :             : 
    2000                 :             :                                         /* upon failure, we must start the scan over */
    2001         [ +  + ]:       51208 :                                         if (ct == NULL)
    2002                 :             :                                         {
    2003                 :          53 :                                                 in_progress_ent.dead = true;
    2004                 :          53 :                                                 break;
    2005                 :             :                                         }
    2006                 :       51155 :                                 }
    2007                 :             : 
    2008                 :             :                                 /* Careful here: add entry to ctlist, then bump its refcount */
    2009                 :             :                                 /* This way leaves state correct if lappend runs out of memory */
    2010                 :       73281 :                                 ctlist = lappend(ctlist, ct);
    2011                 :       73281 :                                 ct->refcount++;
    2012      [ -  +  + ]:       73334 :                         }
    2013                 :             : 
    2014                 :       21705 :                         systable_endscan(scandesc);
    2015         [ +  + ]:       21705 :                 } while (in_progress_ent.dead);
    2016                 :             : 
    2017                 :       21650 :                 table_close(relation, AccessShareLock);
    2018                 :             : 
    2019                 :             :                 /* Make sure the resource owner has room to remember this entry. */
    2020                 :       21650 :                 ResourceOwnerEnlarge(CurrentResourceOwner);
    2021                 :             : 
    2022                 :             :                 /* Now we can build the CatCList entry. */
    2023                 :       21650 :                 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
    2024                 :       21650 :                 nmembers = list_length(ctlist);
    2025                 :       21650 :                 cl = (CatCList *)
    2026                 :       21650 :                         palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *));
    2027                 :             : 
    2028                 :             :                 /* Extract key values */
    2029                 :       43300 :                 CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno,
    2030                 :       21650 :                                                  arguments, cl->keys);
    2031                 :       21650 :                 MemoryContextSwitchTo(oldcxt);
    2032                 :             : 
    2033                 :             :                 /*
    2034                 :             :                  * We are now past the last thing that could trigger an elog before we
    2035                 :             :                  * have finished building the CatCList and remembering it in the
    2036                 :             :                  * resource owner.  So it's OK to fall out of the PG_TRY, and indeed
    2037                 :             :                  * we'd better do so before we start marking the members as belonging
    2038                 :             :                  * to the list.
    2039                 :             :                  */
    2040                 :       21650 :         }
    2041                 :       21650 :         PG_CATCH();
    2042                 :             :         {
    2043         [ #  # ]:           0 :                 Assert(catcache_in_progress_stack == &in_progress_ent);
    2044                 :           0 :                 catcache_in_progress_stack = save_in_progress;
    2045                 :             : 
    2046   [ #  #  #  #  :           0 :                 foreach(ctlist_item, ctlist)
                   #  # ]
    2047                 :             :                 {
    2048                 :           0 :                         ct = (CatCTup *) lfirst(ctlist_item);
    2049         [ #  # ]:           0 :                         Assert(ct->c_list == NULL);
    2050         [ #  # ]:           0 :                         Assert(ct->refcount > 0);
    2051                 :           0 :                         ct->refcount--;
    2052                 :             :                         if (
    2053                 :             : #ifndef CATCACHE_FORCE_RELEASE
    2054         [ #  # ]:           0 :                                 ct->dead &&
    2055                 :             : #endif
    2056   [ #  #  #  # ]:           0 :                                 ct->refcount == 0 &&
    2057         [ #  # ]:           0 :                                 (ct->c_list == NULL || ct->c_list->refcount == 0))
    2058                 :           0 :                                 CatCacheRemoveCTup(cache, ct);
    2059                 :           0 :                 }
    2060                 :             : 
    2061                 :           0 :                 PG_RE_THROW();
    2062                 :             :         }
    2063         [ +  - ]:       21650 :         PG_END_TRY();
    2064         [ +  - ]:       21650 :         Assert(catcache_in_progress_stack == &in_progress_ent);
    2065                 :       21650 :         catcache_in_progress_stack = save_in_progress;
    2066                 :             : 
    2067                 :       21650 :         cl->cl_magic = CL_MAGIC;
    2068                 :       21650 :         cl->my_cache = cache;
    2069                 :       21650 :         cl->refcount = 0;                    /* for the moment */
    2070                 :       21650 :         cl->dead = false;
    2071                 :       21650 :         cl->ordered = ordered;
    2072                 :       21650 :         cl->nkeys = nkeys;
    2073                 :       21650 :         cl->hash_value = lHashValue;
    2074                 :       21650 :         cl->n_members = nmembers;
    2075                 :             : 
    2076                 :       21650 :         i = 0;
    2077   [ +  +  +  +  :       94117 :         foreach(ctlist_item, ctlist)
                   +  + ]
    2078                 :             :         {
    2079                 :       72467 :                 cl->members[i++] = ct = (CatCTup *) lfirst(ctlist_item);
    2080         [ +  - ]:       72467 :                 Assert(ct->c_list == NULL);
    2081                 :       72467 :                 ct->c_list = cl;
    2082                 :             :                 /* release the temporary refcount on the member */
    2083         [ +  - ]:       72467 :                 Assert(ct->refcount > 0);
    2084                 :       72467 :                 ct->refcount--;
    2085                 :             :                 /* mark list dead if any members already dead */
    2086         [ +  - ]:       72467 :                 if (ct->dead)
    2087                 :           0 :                         cl->dead = true;
    2088                 :       72467 :         }
    2089         [ +  - ]:       21650 :         Assert(i == nmembers);
    2090                 :             : 
    2091                 :             :         /*
    2092                 :             :          * Add the CatCList to the appropriate bucket, and count it.
    2093                 :             :          */
    2094                 :       21650 :         dlist_push_head(lbucket, &cl->cache_elem);
    2095                 :             : 
    2096                 :       21650 :         cache->cc_nlist++;
    2097                 :             : 
    2098                 :             :         /* Finally, bump the list's refcount and return it */
    2099                 :       21650 :         cl->refcount++;
    2100                 :       21650 :         ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
    2101                 :             : 
    2102                 :             :         CACHE_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",
    2103                 :             :                            cache->cc_relname, nmembers);
    2104                 :             : 
    2105                 :       21650 :         return cl;
    2106                 :      378012 : }
    2107                 :             : 
    2108                 :             : /*
    2109                 :             :  *      ReleaseCatCacheList
    2110                 :             :  *
    2111                 :             :  *      Decrement the reference count of a catcache list.
    2112                 :             :  */
    2113                 :             : void
    2114                 :      378006 : ReleaseCatCacheList(CatCList *list)
    2115                 :             : {
    2116                 :      378006 :         ReleaseCatCacheListWithOwner(list, CurrentResourceOwner);
    2117                 :      378006 : }
    2118                 :             : 
    2119                 :             : static void
    2120                 :      378012 : ReleaseCatCacheListWithOwner(CatCList *list, ResourceOwner resowner)
    2121                 :             : {
    2122                 :             :         /* Safety checks to ensure we were handed a cache entry */
    2123         [ +  - ]:      378012 :         Assert(list->cl_magic == CL_MAGIC);
    2124         [ +  - ]:      378012 :         Assert(list->refcount > 0);
    2125                 :      378012 :         list->refcount--;
    2126         [ +  + ]:      378012 :         if (resowner)
    2127                 :      378006 :                 ResourceOwnerForgetCatCacheListRef(resowner, list);
    2128                 :             : 
    2129                 :             :         if (
    2130                 :             : #ifndef CATCACHE_FORCE_RELEASE
    2131   [ +  +  -  + ]:      378012 :                 list->dead &&
    2132                 :             : #endif
    2133                 :           1 :                 list->refcount == 0)
    2134                 :           1 :                 CatCacheRemoveCList(list->my_cache, list);
    2135                 :      378012 : }
    2136                 :             : 
    2137                 :             : 
    2138                 :             : /*
    2139                 :             :  * CatalogCacheCreateEntry
    2140                 :             :  *              Create a new CatCTup entry, copying the given HeapTuple and other
    2141                 :             :  *              supplied data into it.  The new entry initially has refcount 0.
    2142                 :             :  *
    2143                 :             :  * To create a normal cache entry, ntp must be the HeapTuple just fetched
    2144                 :             :  * from scandesc, and "arguments" is not used.  To create a negative cache
    2145                 :             :  * entry, pass NULL for ntp; then "arguments" is the cache keys to use.
    2146                 :             :  * In either case, hashValue/hashIndex are the hash values computed from
    2147                 :             :  * the cache keys.
    2148                 :             :  *
    2149                 :             :  * Returns NULL if we attempt to detoast the tuple and observe that it
    2150                 :             :  * became stale.  (This cannot happen for a negative entry.)  Caller must
    2151                 :             :  * retry the tuple lookup in that case.
    2152                 :             :  */
    2153                 :             : static CatCTup *
    2154                 :      354976 : CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
    2155                 :             :                                                 uint32 hashValue, Index hashIndex)
    2156                 :             : {
    2157                 :      354976 :         CatCTup    *ct;
    2158                 :      354976 :         MemoryContext oldcxt;
    2159                 :             : 
    2160         [ +  + ]:      354976 :         if (ntp)
    2161                 :             :         {
    2162                 :      256560 :                 int                     i;
    2163                 :      256560 :                 HeapTuple       dtp = NULL;
    2164                 :             : 
    2165                 :             :                 /*
    2166                 :             :                  * The invalidation of the in-progress entry essentially never happens
    2167                 :             :                  * during our regression tests, and there's no easy way to force it to
    2168                 :             :                  * fail for testing purposes.  To ensure we have test coverage for the
    2169                 :             :                  * retry paths in our callers, make debug builds randomly fail about
    2170                 :             :                  * 0.1% of the times through this code path, even when there's no
    2171                 :             :                  * toasted fields.
    2172                 :             :                  */
    2173                 :             : #ifdef USE_ASSERT_CHECKING
    2174         [ +  + ]:      256560 :                 if (pg_prng_uint32(&pg_global_prng_state) <= (PG_UINT32_MAX / 1000))
    2175                 :         269 :                         return NULL;
    2176                 :             : #endif
    2177                 :             : 
    2178                 :             :                 /*
    2179                 :             :                  * If there are any out-of-line toasted fields in the tuple, expand
    2180                 :             :                  * them in-line.  This saves cycles during later use of the catcache
    2181                 :             :                  * entry, and also protects us against the possibility of the toast
    2182                 :             :                  * tuples being freed before we attempt to fetch them, in case of
    2183                 :             :                  * something using a slightly stale catcache entry.
    2184                 :             :                  */
    2185         [ +  + ]:      256291 :                 if (HeapTupleHasExternal(ntp))
    2186                 :             :                 {
    2187                 :         183 :                         CatCInProgress *save_in_progress;
    2188                 :         183 :                         CatCInProgress in_progress_ent;
    2189                 :             : 
    2190                 :             :                         /*
    2191                 :             :                          * The tuple could become stale while we are doing toast table
    2192                 :             :                          * access (since AcceptInvalidationMessages can run then).  The
    2193                 :             :                          * invalidation will mark our in-progress entry as dead.
    2194                 :             :                          */
    2195                 :         183 :                         save_in_progress = catcache_in_progress_stack;
    2196                 :         183 :                         in_progress_ent.next = catcache_in_progress_stack;
    2197                 :         183 :                         in_progress_ent.cache = cache;
    2198                 :         183 :                         in_progress_ent.hash_value = hashValue;
    2199                 :         183 :                         in_progress_ent.list = false;
    2200                 :         183 :                         in_progress_ent.dead = false;
    2201                 :         183 :                         catcache_in_progress_stack = &in_progress_ent;
    2202                 :             : 
    2203         [ -  + ]:         183 :                         PG_TRY();
    2204                 :             :                         {
    2205                 :         183 :                                 dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc);
    2206                 :             :                         }
    2207                 :         183 :                         PG_FINALLY();
    2208                 :             :                         {
    2209         [ +  - ]:         183 :                                 Assert(catcache_in_progress_stack == &in_progress_ent);
    2210                 :         183 :                                 catcache_in_progress_stack = save_in_progress;
    2211                 :             :                         }
    2212         [ +  - ]:         183 :                         PG_END_TRY();
    2213                 :             : 
    2214         [ -  + ]:         183 :                         if (in_progress_ent.dead)
    2215                 :             :                         {
    2216                 :           0 :                                 heap_freetuple(dtp);
    2217                 :           0 :                                 return NULL;
    2218                 :             :                         }
    2219         [ -  + ]:         183 :                 }
    2220                 :             :                 else
    2221                 :      256108 :                         dtp = ntp;
    2222                 :             : 
    2223                 :             :                 /* Allocate memory for CatCTup and the cached tuple in one go */
    2224                 :      256291 :                 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
    2225                 :             : 
    2226                 :      256291 :                 ct = (CatCTup *) palloc(sizeof(CatCTup) +
    2227                 :      256291 :                                                                 MAXIMUM_ALIGNOF + dtp->t_len);
    2228                 :      256291 :                 ct->tuple.t_len = dtp->t_len;
    2229                 :      256291 :                 ct->tuple.t_self = dtp->t_self;
    2230                 :      256291 :                 ct->tuple.t_tableOid = dtp->t_tableOid;
    2231                 :      256291 :                 ct->tuple.t_data = (HeapTupleHeader)
    2232                 :      256291 :                         MAXALIGN(((char *) ct) + sizeof(CatCTup));
    2233                 :             :                 /* copy tuple contents */
    2234                 :      256291 :                 memcpy((char *) ct->tuple.t_data,
    2235                 :             :                            (const char *) dtp->t_data,
    2236                 :             :                            dtp->t_len);
    2237                 :      256291 :                 MemoryContextSwitchTo(oldcxt);
    2238                 :             : 
    2239         [ +  + ]:      256291 :                 if (dtp != ntp)
    2240                 :         183 :                         heap_freetuple(dtp);
    2241                 :             : 
    2242                 :             :                 /* extract keys - they'll point into the tuple if not by-value */
    2243         [ +  + ]:      753980 :                 for (i = 0; i < cache->cc_nkeys; i++)
    2244                 :             :                 {
    2245                 :      497689 :                         Datum           atp;
    2246                 :      497689 :                         bool            isnull;
    2247                 :             : 
    2248                 :      995378 :                         atp = heap_getattr(&ct->tuple,
    2249                 :      497689 :                                                            cache->cc_keyno[i],
    2250                 :      497689 :                                                            cache->cc_tupdesc,
    2251                 :             :                                                            &isnull);
    2252         [ +  - ]:      497689 :                         Assert(!isnull);
    2253                 :      497689 :                         ct->keys[i] = atp;
    2254                 :      497689 :                 }
    2255         [ +  + ]:      256560 :         }
    2256                 :             :         else
    2257                 :             :         {
    2258                 :             :                 /* Set up keys for a negative cache entry */
    2259                 :       98416 :                 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
    2260                 :       98416 :                 ct = palloc_object(CatCTup);
    2261                 :             : 
    2262                 :             :                 /*
    2263                 :             :                  * Store keys - they'll point into separately allocated memory if not
    2264                 :             :                  * by-value.
    2265                 :             :                  */
    2266                 :      196832 :                 CatCacheCopyKeys(cache->cc_tupdesc, cache->cc_nkeys, cache->cc_keyno,
    2267                 :       98416 :                                                  arguments, ct->keys);
    2268                 :       98416 :                 MemoryContextSwitchTo(oldcxt);
    2269                 :             :         }
    2270                 :             : 
    2271                 :             :         /*
    2272                 :             :          * Finish initializing the CatCTup header, and add it to the cache's
    2273                 :             :          * linked list and counts.
    2274                 :             :          */
    2275                 :      354707 :         ct->ct_magic = CT_MAGIC;
    2276                 :      354707 :         ct->my_cache = cache;
    2277                 :      354707 :         ct->c_list = NULL;
    2278                 :      354707 :         ct->refcount = 0;                    /* for the moment */
    2279                 :      354707 :         ct->dead = false;
    2280                 :      354707 :         ct->negative = (ntp == NULL);
    2281                 :      354707 :         ct->hash_value = hashValue;
    2282                 :             : 
    2283                 :      354707 :         dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
    2284                 :             : 
    2285                 :      354707 :         cache->cc_ntup++;
    2286                 :      354707 :         CacheHdr->ch_ntup++;
    2287                 :             : 
    2288                 :             :         /*
    2289                 :             :          * If the hash table has become too full, enlarge the buckets array. Quite
    2290                 :             :          * arbitrarily, we enlarge when fill factor > 2.
    2291                 :             :          */
    2292         [ +  + ]:      354707 :         if (cache->cc_ntup > cache->cc_nbuckets * 2)
    2293                 :         312 :                 RehashCatCache(cache);
    2294                 :             : 
    2295                 :      354707 :         return ct;
    2296                 :      354976 : }
    2297                 :             : 
    2298                 :             : /*
    2299                 :             :  * Helper routine that frees keys stored in the keys array.
    2300                 :             :  */
    2301                 :             : static void
    2302                 :       48461 : CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, const int *attnos, const Datum *keys)
    2303                 :             : {
    2304                 :       48461 :         int                     i;
    2305                 :             : 
    2306         [ +  + ]:      140784 :         for (i = 0; i < nkeys; i++)
    2307                 :             :         {
    2308                 :       92323 :                 int                     attnum = attnos[i];
    2309                 :             : 
    2310                 :             :                 /* system attribute are not supported in caches */
    2311         [ +  - ]:       92323 :                 Assert(attnum > 0);
    2312                 :             : 
    2313         [ +  + ]:       92323 :                 if (!TupleDescCompactAttr(tupdesc, attnum - 1)->attbyval)
    2314                 :       40787 :                         pfree(DatumGetPointer(keys[i]));
    2315                 :       92323 :         }
    2316                 :       48461 : }
    2317                 :             : 
    2318                 :             : /*
    2319                 :             :  * Helper routine that copies the keys in the srckeys array into the dstkeys
    2320                 :             :  * one, guaranteeing that the datums are fully allocated in the current memory
    2321                 :             :  * context.
    2322                 :             :  */
    2323                 :             : static void
    2324                 :      120066 : CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, const int *attnos,
    2325                 :             :                                  const Datum *srckeys, Datum *dstkeys)
    2326                 :             : {
    2327                 :      120066 :         int                     i;
    2328                 :             : 
    2329                 :             :         /*
    2330                 :             :          * XXX: memory and lookup performance could possibly be improved by
    2331                 :             :          * storing all keys in one allocation.
    2332                 :             :          */
    2333                 :             : 
    2334         [ +  + ]:      368333 :         for (i = 0; i < nkeys; i++)
    2335                 :             :         {
    2336                 :      248267 :                 int                     attnum = attnos[i];
    2337                 :      248267 :                 Form_pg_attribute att = TupleDescAttr(tupdesc, attnum - 1);
    2338                 :      248267 :                 Datum           src = srckeys[i];
    2339                 :      248267 :                 NameData        srcname;
    2340                 :             : 
    2341                 :             :                 /*
    2342                 :             :                  * Must be careful in case the caller passed a C string where a NAME
    2343                 :             :                  * is wanted: convert the given argument to a correctly padded NAME.
    2344                 :             :                  * Otherwise the memcpy() done by datumCopy() could fall off the end
    2345                 :             :                  * of memory.
    2346                 :             :                  */
    2347         [ +  + ]:      248267 :                 if (att->atttypid == NAMEOID)
    2348                 :             :                 {
    2349                 :       64112 :                         namestrcpy(&srcname, DatumGetCString(src));
    2350                 :       64112 :                         src = NameGetDatum(&srcname);
    2351                 :       64112 :                 }
    2352                 :             : 
    2353                 :      496534 :                 dstkeys[i] = datumCopy(src,
    2354                 :      248267 :                                                            att->attbyval,
    2355                 :      248267 :                                                            att->attlen);
    2356                 :      248267 :         }
    2357                 :      120066 : }
    2358                 :             : 
    2359                 :             : /*
    2360                 :             :  *      PrepareToInvalidateCacheTuple()
    2361                 :             :  *
    2362                 :             :  *      This is part of a rather subtle chain of events, so pay attention:
    2363                 :             :  *
    2364                 :             :  *      When a tuple is inserted or deleted, it cannot be flushed from the
    2365                 :             :  *      catcaches immediately, for reasons explained at the top of cache/inval.c.
    2366                 :             :  *      Instead we have to add entry(s) for the tuple to a list of pending tuple
    2367                 :             :  *      invalidations that will be done at the end of the command or transaction.
    2368                 :             :  *
    2369                 :             :  *      The lists of tuples that need to be flushed are kept by inval.c.  This
    2370                 :             :  *      routine is a helper routine for inval.c.  Given a tuple belonging to
    2371                 :             :  *      the specified relation, find all catcaches it could be in, compute the
    2372                 :             :  *      correct hash value for each such catcache, and call the specified
    2373                 :             :  *      function to record the cache id and hash value in inval.c's lists.
    2374                 :             :  *      SysCacheInvalidate will be called later, if appropriate,
    2375                 :             :  *      using the recorded information.
    2376                 :             :  *
    2377                 :             :  *      For an insert or delete, tuple is the target tuple and newtuple is NULL.
    2378                 :             :  *      For an update, we are called just once, with tuple being the old tuple
    2379                 :             :  *      version and newtuple the new version.  We should make two list entries
    2380                 :             :  *      if the tuple's hash value changed, but only one if it didn't.
    2381                 :             :  *
    2382                 :             :  *      Note that it is irrelevant whether the given tuple is actually loaded
    2383                 :             :  *      into the catcache at the moment.  Even if it's not there now, it might
    2384                 :             :  *      be by the end of the command, or there might be a matching negative entry
    2385                 :             :  *      to flush --- or other backends' caches might have such entries --- so
    2386                 :             :  *      we have to make list entries to flush it later.
    2387                 :             :  *
    2388                 :             :  *      Also note that it's not an error if there are no catcaches for the
    2389                 :             :  *      specified relation.  inval.c doesn't know exactly which rels have
    2390                 :             :  *      catcaches --- it will call this routine for any tuple that's in a
    2391                 :             :  *      system relation.
    2392                 :             :  */
    2393                 :             : void
    2394                 :      226678 : PrepareToInvalidateCacheTuple(Relation relation,
    2395                 :             :                                                           HeapTuple tuple,
    2396                 :             :                                                           HeapTuple newtuple,
    2397                 :             :                                                           void (*function) (int, uint32, Oid, void *),
    2398                 :             :                                                           void *context)
    2399                 :             : {
    2400                 :      226678 :         slist_iter      iter;
    2401                 :      226678 :         Oid                     reloid;
    2402                 :             : 
    2403                 :             :         CACHE_elog(DEBUG2, "PrepareToInvalidateCacheTuple: called");
    2404                 :             : 
    2405                 :             :         /*
    2406                 :             :          * sanity checks
    2407                 :             :          */
    2408         [ +  - ]:      226678 :         Assert(RelationIsValid(relation));
    2409         [ +  - ]:      226678 :         Assert(HeapTupleIsValid(tuple));
    2410         [ +  - ]:      226678 :         Assert(function);
    2411         [ +  - ]:      226678 :         Assert(CacheHdr != NULL);
    2412                 :             : 
    2413                 :      226678 :         reloid = RelationGetRelid(relation);
    2414                 :             : 
    2415                 :             :         /* ----------------
    2416                 :             :          *      for each cache
    2417                 :             :          *         if the cache contains tuples from the specified relation
    2418                 :             :          *                 compute the tuple's hash value(s) in this cache,
    2419                 :             :          *                 and call the passed function to register the information.
    2420                 :             :          * ----------------
    2421                 :             :          */
    2422                 :             : 
    2423         [ +  + ]:    19494308 :         slist_foreach(iter, &CacheHdr->ch_caches)
    2424                 :             :         {
    2425                 :    19267630 :                 CatCache   *ccp = slist_container(CatCache, cc_next, iter.cur);
    2426                 :    19267630 :                 uint32          hashvalue;
    2427                 :    19267630 :                 Oid                     dbid;
    2428                 :             : 
    2429         [ +  + ]:    19267630 :                 if (ccp->cc_reloid != reloid)
    2430                 :    18862403 :                         continue;
    2431                 :             : 
    2432                 :             :                 /* Just in case cache hasn't finished initialization yet... */
    2433                 :      405227 :                 ConditionalCatalogCacheInitializeCache(ccp);
    2434                 :             : 
    2435                 :      405227 :                 hashvalue = CatalogCacheComputeTupleHashValue(ccp, ccp->cc_nkeys, tuple);
    2436         [ +  + ]:      405227 :                 dbid = ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId;
    2437                 :             : 
    2438                 :      405227 :                 (*function) (ccp->id, hashvalue, dbid, context);
    2439                 :             : 
    2440         [ +  + ]:      405227 :                 if (newtuple)
    2441                 :             :                 {
    2442                 :       31967 :                         uint32          newhashvalue;
    2443                 :             : 
    2444                 :       31967 :                         newhashvalue = CatalogCacheComputeTupleHashValue(ccp, ccp->cc_nkeys, newtuple);
    2445                 :             : 
    2446         [ +  + ]:       31967 :                         if (newhashvalue != hashvalue)
    2447                 :         870 :                                 (*function) (ccp->id, newhashvalue, dbid, context);
    2448                 :       31967 :                 }
    2449      [ -  +  + ]:    19267630 :         }
    2450                 :      226678 : }
    2451                 :             : 
    2452                 :             : /* ResourceOwner callbacks */
    2453                 :             : 
    2454                 :             : static void
    2455                 :        1804 : ResOwnerReleaseCatCache(Datum res)
    2456                 :             : {
    2457                 :        1804 :         ReleaseCatCacheWithOwner((HeapTuple) DatumGetPointer(res), NULL);
    2458                 :        1804 : }
    2459                 :             : 
    2460                 :             : static char *
    2461                 :           0 : ResOwnerPrintCatCache(Datum res)
    2462                 :             : {
    2463                 :           0 :         HeapTuple       tuple = (HeapTuple) DatumGetPointer(res);
    2464                 :           0 :         CatCTup    *ct = (CatCTup *) (((char *) tuple) -
    2465                 :             :                                                                   offsetof(CatCTup, tuple));
    2466                 :             : 
    2467                 :             :         /* Safety check to ensure we were handed a cache entry */
    2468         [ #  # ]:           0 :         Assert(ct->ct_magic == CT_MAGIC);
    2469                 :             : 
    2470                 :           0 :         return psprintf("cache %s (%d), tuple %u/%u has count %d",
    2471                 :           0 :                                         ct->my_cache->cc_relname, ct->my_cache->id,
    2472                 :           0 :                                         ItemPointerGetBlockNumber(&(tuple->t_self)),
    2473                 :           0 :                                         ItemPointerGetOffsetNumber(&(tuple->t_self)),
    2474                 :           0 :                                         ct->refcount);
    2475                 :           0 : }
    2476                 :             : 
    2477                 :             : static void
    2478                 :           0 : ResOwnerReleaseCatCacheList(Datum res)
    2479                 :             : {
    2480                 :           0 :         ReleaseCatCacheListWithOwner((CatCList *) DatumGetPointer(res), NULL);
    2481                 :           0 : }
    2482                 :             : 
    2483                 :             : static char *
    2484                 :           0 : ResOwnerPrintCatCacheList(Datum res)
    2485                 :             : {
    2486                 :           0 :         CatCList   *list = (CatCList *) DatumGetPointer(res);
    2487                 :             : 
    2488                 :           0 :         return psprintf("cache %s (%d), list %p has count %d",
    2489                 :           0 :                                         list->my_cache->cc_relname, list->my_cache->id,
    2490                 :           0 :                                         list, list->refcount);
    2491                 :           0 : }
        

Generated by: LCOV version 2.3.2-1