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

Generated by: LCOV version 1.16