LCOV - code coverage report
Current view: top level - src/backend/utils/cache - catcache.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19beta1 Lines: 91.6 % 658 603
Test Date: 2026-06-27 15:16:46 Functions: 94.6 % 56 53
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1