LCOV - code coverage report
Current view: top level - src/backend/utils/cache - catcache.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 534 576 92.7 %
Date: 2023-12-05 09:10:49 Functions: 51 53 96.2 %
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-2023, 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/sysattr.h"
      21             : #include "access/table.h"
      22             : #include "access/xact.h"
      23             : #include "catalog/pg_collation.h"
      24             : #include "catalog/pg_operator.h"
      25             : #include "catalog/pg_type.h"
      26             : #include "common/hashfn.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/inval.h"
      38             : #include "utils/memutils.h"
      39             : #include "utils/rel.h"
      40             : #include "utils/resowner.h"
      41             : #include "utils/syscache.h"
      42             : 
      43             : 
      44             :  /* #define CACHEDEBUG */   /* turns DEBUG elogs on */
      45             : 
      46             : /*
      47             :  * Given a hash value and the size of the hash table, find the bucket
      48             :  * in which the hash value belongs. Since the hash table must contain
      49             :  * a power-of-2 number of elements, this is a simple bitmask.
      50             :  */
      51             : #define HASH_INDEX(h, sz) ((Index) ((h) & ((sz) - 1)))
      52             : 
      53             : 
      54             : /*
      55             :  *      variables, macros and other stuff
      56             :  */
      57             : 
      58             : #ifdef CACHEDEBUG
      59             : #define CACHE_elog(...)             elog(__VA_ARGS__)
      60             : #else
      61             : #define CACHE_elog(...)
      62             : #endif
      63             : 
      64             : /* Cache management header --- pointer is NULL until created */
      65             : static CatCacheHeader *CacheHdr = NULL;
      66             : 
      67             : static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
      68             :                                                int nkeys,
      69             :                                                Datum v1, Datum v2,
      70             :                                                Datum v3, Datum v4);
      71             : 
      72             : static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
      73             :                                                 int nkeys,
      74             :                                                 uint32 hashValue,
      75             :                                                 Index hashIndex,
      76             :                                                 Datum v1, Datum v2,
      77             :                                                 Datum v3, Datum v4);
      78             : 
      79             : static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
      80             :                                            Datum v1, Datum v2, Datum v3, Datum v4);
      81             : static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys,
      82             :                                                 HeapTuple tuple);
      83             : static inline bool CatalogCacheCompareTuple(const CatCache *cache, int nkeys,
      84             :                                             const Datum *cachekeys,
      85             :                                             const Datum *searchkeys);
      86             : 
      87             : #ifdef CATCACHE_STATS
      88             : static void CatCachePrintStats(int code, Datum arg);
      89             : #endif
      90             : static void CatCacheRemoveCTup(CatCache *cache, CatCTup *ct);
      91             : static void CatCacheRemoveCList(CatCache *cache, CatCList *cl);
      92             : static void CatalogCacheInitializeCache(CatCache *cache);
      93             : static CatCTup *CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
      94             :                                         Datum *arguments,
      95             :                                         uint32 hashValue, Index hashIndex,
      96             :                                         bool negative);
      97             : 
      98             : static void ReleaseCatCacheWithOwner(HeapTuple tuple, ResourceOwner resowner);
      99             : static void ReleaseCatCacheListWithOwner(CatCList *list, ResourceOwner resowner);
     100             : static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
     101             :                              Datum *keys);
     102             : static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
     103             :                              Datum *srckeys, Datum *dstkeys);
     104             : 
     105             : 
     106             : /*
     107             :  *                  internal support functions
     108             :  */
     109             : 
     110             : /* ResourceOwner callbacks to hold catcache references */
     111             : 
     112             : static void ResOwnerReleaseCatCache(Datum res);
     113             : static char *ResOwnerPrintCatCache(Datum res);
     114             : static void ResOwnerReleaseCatCacheList(Datum res);
     115             : static char *ResOwnerPrintCatCacheList(Datum res);
     116             : 
     117             : static const ResourceOwnerDesc catcache_resowner_desc =
     118             : {
     119             :     /* catcache references */
     120             :     .name = "catcache reference",
     121             :     .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
     122             :     .release_priority = RELEASE_PRIO_CATCACHE_REFS,
     123             :     .ReleaseResource = ResOwnerReleaseCatCache,
     124             :     .DebugPrint = ResOwnerPrintCatCache
     125             : };
     126             : 
     127             : static const ResourceOwnerDesc catlistref_resowner_desc =
     128             : {
     129             :     /* catcache-list pins */
     130             :     .name = "catcache list reference",
     131             :     .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
     132             :     .release_priority = RELEASE_PRIO_CATCACHE_LIST_REFS,
     133             :     .ReleaseResource = ResOwnerReleaseCatCacheList,
     134             :     .DebugPrint = ResOwnerPrintCatCacheList
     135             : };
     136             : 
     137             : /* Convenience wrappers over ResourceOwnerRemember/Forget */
     138             : static inline void
     139    64201642 : ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
     140             : {
     141    64201642 :     ResourceOwnerRemember(owner, PointerGetDatum(tuple), &catcache_resowner_desc);
     142    64201642 : }
     143             : static inline void
     144    64191700 : ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
     145             : {
     146    64191700 :     ResourceOwnerForget(owner, PointerGetDatum(tuple), &catcache_resowner_desc);
     147    64191700 : }
     148             : static inline void
     149     2741802 : ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
     150             : {
     151     2741802 :     ResourceOwnerRemember(owner, PointerGetDatum(list), &catlistref_resowner_desc);
     152     2741802 : }
     153             : static inline void
     154     2741766 : ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
     155             : {
     156     2741766 :     ResourceOwnerForget(owner, PointerGetDatum(list), &catlistref_resowner_desc);
     157     2741766 : }
     158             : 
     159             : 
     160             : /*
     161             :  * Hash and equality functions for system types that are used as cache key
     162             :  * fields.  In some cases, we just call the regular SQL-callable functions for
     163             :  * the appropriate data type, but that tends to be a little slow, and the
     164             :  * speed of these functions is performance-critical.  Therefore, for data
     165             :  * types that frequently occur as catcache keys, we hard-code the logic here.
     166             :  * Avoiding the overhead of DirectFunctionCallN(...) is a substantial win, and
     167             :  * in certain cases (like int4) we can adopt a faster hash algorithm as well.
     168             :  */
     169             : 
     170             : static bool
     171     4167890 : chareqfast(Datum a, Datum b)
     172             : {
     173     4167890 :     return DatumGetChar(a) == DatumGetChar(b);
     174             : }
     175             : 
     176             : static uint32
     177     4763516 : charhashfast(Datum datum)
     178             : {
     179     4763516 :     return murmurhash32((int32) DatumGetChar(datum));
     180             : }
     181             : 
     182             : static bool
     183     3095620 : nameeqfast(Datum a, Datum b)
     184             : {
     185     3095620 :     char       *ca = NameStr(*DatumGetName(a));
     186     3095620 :     char       *cb = NameStr(*DatumGetName(b));
     187             : 
     188     3095620 :     return strncmp(ca, cb, NAMEDATALEN) == 0;
     189             : }
     190             : 
     191             : static uint32
     192     6825106 : namehashfast(Datum datum)
     193             : {
     194     6825106 :     char       *key = NameStr(*DatumGetName(datum));
     195             : 
     196     6825106 :     return hash_any((unsigned char *) key, strlen(key));
     197             : }
     198             : 
     199             : static bool
     200     6320022 : int2eqfast(Datum a, Datum b)
     201             : {
     202     6320022 :     return DatumGetInt16(a) == DatumGetInt16(b);
     203             : }
     204             : 
     205             : static uint32
     206     8576486 : int2hashfast(Datum datum)
     207             : {
     208     8576486 :     return murmurhash32((int32) DatumGetInt16(datum));
     209             : }
     210             : 
     211             : static bool
     212    75276354 : int4eqfast(Datum a, Datum b)
     213             : {
     214    75276354 :     return DatumGetInt32(a) == DatumGetInt32(b);
     215             : }
     216             : 
     217             : static uint32
     218    87142292 : int4hashfast(Datum datum)
     219             : {
     220    87142292 :     return murmurhash32((int32) DatumGetInt32(datum));
     221             : }
     222             : 
     223             : static bool
     224         166 : texteqfast(Datum a, Datum b)
     225             : {
     226             :     /*
     227             :      * The use of DEFAULT_COLLATION_OID is fairly arbitrary here.  We just
     228             :      * want to take the fast "deterministic" path in texteq().
     229             :      */
     230         166 :     return DatumGetBool(DirectFunctionCall2Coll(texteq, DEFAULT_COLLATION_OID, a, b));
     231             : }
     232             : 
     233             : static uint32
     234        3094 : texthashfast(Datum datum)
     235             : {
     236             :     /* analogously here as in texteqfast() */
     237        3094 :     return DatumGetInt32(DirectFunctionCall1Coll(hashtext, DEFAULT_COLLATION_OID, datum));
     238             : }
     239             : 
     240             : static bool
     241        2156 : oidvectoreqfast(Datum a, Datum b)
     242             : {
     243        2156 :     return DatumGetBool(DirectFunctionCall2(oidvectoreq, a, b));
     244             : }
     245             : 
     246             : static uint32
     247      291336 : oidvectorhashfast(Datum datum)
     248             : {
     249      291336 :     return DatumGetInt32(DirectFunctionCall1(hashoidvector, datum));
     250             : }
     251             : 
     252             : /* Lookup support functions for a type. */
     253             : static void
     254      815928 : GetCCHashEqFuncs(Oid keytype, CCHashFN *hashfunc, RegProcedure *eqfunc, CCFastEqualFN *fasteqfunc)
     255             : {
     256      815928 :     switch (keytype)
     257             :     {
     258       11098 :         case BOOLOID:
     259       11098 :             *hashfunc = charhashfast;
     260       11098 :             *fasteqfunc = chareqfast;
     261       11098 :             *eqfunc = F_BOOLEQ;
     262       11098 :             break;
     263       15200 :         case CHAROID:
     264       15200 :             *hashfunc = charhashfast;
     265       15200 :             *fasteqfunc = chareqfast;
     266       15200 :             *eqfunc = F_CHAREQ;
     267       15200 :             break;
     268      154106 :         case NAMEOID:
     269      154106 :             *hashfunc = namehashfast;
     270      154106 :             *fasteqfunc = nameeqfast;
     271      154106 :             *eqfunc = F_NAMEEQ;
     272      154106 :             break;
     273       49808 :         case INT2OID:
     274       49808 :             *hashfunc = int2hashfast;
     275       49808 :             *fasteqfunc = int2eqfast;
     276       49808 :             *eqfunc = F_INT2EQ;
     277       49808 :             break;
     278       10446 :         case INT4OID:
     279       10446 :             *hashfunc = int4hashfast;
     280       10446 :             *fasteqfunc = int4eqfast;
     281       10446 :             *eqfunc = F_INT4EQ;
     282       10446 :             break;
     283        4756 :         case TEXTOID:
     284        4756 :             *hashfunc = texthashfast;
     285        4756 :             *fasteqfunc = texteqfast;
     286        4756 :             *eqfunc = F_TEXTEQ;
     287        4756 :             break;
     288      558644 :         case OIDOID:
     289             :         case REGPROCOID:
     290             :         case REGPROCEDUREOID:
     291             :         case REGOPEROID:
     292             :         case REGOPERATOROID:
     293             :         case REGCLASSOID:
     294             :         case REGTYPEOID:
     295             :         case REGCOLLATIONOID:
     296             :         case REGCONFIGOID:
     297             :         case REGDICTIONARYOID:
     298             :         case REGROLEOID:
     299             :         case REGNAMESPACEOID:
     300      558644 :             *hashfunc = int4hashfast;
     301      558644 :             *fasteqfunc = int4eqfast;
     302      558644 :             *eqfunc = F_OIDEQ;
     303      558644 :             break;
     304       11870 :         case OIDVECTOROID:
     305       11870 :             *hashfunc = oidvectorhashfast;
     306       11870 :             *fasteqfunc = oidvectoreqfast;
     307       11870 :             *eqfunc = F_OIDVECTOREQ;
     308       11870 :             break;
     309           0 :         default:
     310           0 :             elog(FATAL, "type %u not supported as catcache key", keytype);
     311             :             *hashfunc = NULL;   /* keep compiler quiet */
     312             : 
     313             :             *eqfunc = InvalidOid;
     314             :             break;
     315             :     }
     316      815928 : }
     317             : 
     318             : /*
     319             :  *      CatalogCacheComputeHashValue
     320             :  *
     321             :  * Compute the hash value associated with a given set of lookup keys
     322             :  */
     323             : static uint32
     324    76837494 : CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
     325             :                              Datum v1, Datum v2, Datum v3, Datum v4)
     326             : {
     327    76837494 :     uint32      hashValue = 0;
     328             :     uint32      oneHash;
     329    76837494 :     CCHashFN   *cc_hashfunc = cache->cc_hashfunc;
     330             : 
     331             :     CACHE_elog(DEBUG2, "CatalogCacheComputeHashValue %s %d %p",
     332             :                cache->cc_relname, nkeys, cache);
     333             : 
     334    76837494 :     switch (nkeys)
     335             :     {
     336     3557398 :         case 4:
     337     3557398 :             oneHash = (cc_hashfunc[3]) (v4);
     338     3557398 :             hashValue ^= pg_rotate_left32(oneHash, 24);
     339             :             /* FALLTHROUGH */
     340     8905788 :         case 3:
     341     8905788 :             oneHash = (cc_hashfunc[2]) (v3);
     342     8905788 :             hashValue ^= pg_rotate_left32(oneHash, 16);
     343             :             /* FALLTHROUGH */
     344    18301150 :         case 2:
     345    18301150 :             oneHash = (cc_hashfunc[1]) (v2);
     346    18301150 :             hashValue ^= pg_rotate_left32(oneHash, 8);
     347             :             /* FALLTHROUGH */
     348    76837494 :         case 1:
     349    76837494 :             oneHash = (cc_hashfunc[0]) (v1);
     350    76837494 :             hashValue ^= oneHash;
     351    76837494 :             break;
     352           0 :         default:
     353           0 :             elog(FATAL, "wrong number of hash keys: %d", nkeys);
     354             :             break;
     355             :     }
     356             : 
     357    76837494 :     return hashValue;
     358             : }
     359             : 
     360             : /*
     361             :  *      CatalogCacheComputeTupleHashValue
     362             :  *
     363             :  * Compute the hash value associated with a given tuple to be cached
     364             :  */
     365             : static uint32
     366     5296416 : CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys, HeapTuple tuple)
     367             : {
     368     5296416 :     Datum       v1 = 0,
     369     5296416 :                 v2 = 0,
     370     5296416 :                 v3 = 0,
     371     5296416 :                 v4 = 0;
     372     5296416 :     bool        isNull = false;
     373     5296416 :     int        *cc_keyno = cache->cc_keyno;
     374     5296416 :     TupleDesc   cc_tupdesc = cache->cc_tupdesc;
     375             : 
     376             :     /* Now extract key fields from tuple, insert into scankey */
     377     5296416 :     switch (nkeys)
     378             :     {
     379      371110 :         case 4:
     380      371110 :             v4 = fastgetattr(tuple,
     381      371110 :                              cc_keyno[3],
     382             :                              cc_tupdesc,
     383             :                              &isNull);
     384             :             Assert(!isNull);
     385             :             /* FALLTHROUGH */
     386      972608 :         case 3:
     387      972608 :             v3 = fastgetattr(tuple,
     388      972608 :                              cc_keyno[2],
     389             :                              cc_tupdesc,
     390             :                              &isNull);
     391             :             Assert(!isNull);
     392             :             /* FALLTHROUGH */
     393     3972282 :         case 2:
     394     3972282 :             v2 = fastgetattr(tuple,
     395     3972282 :                              cc_keyno[1],
     396             :                              cc_tupdesc,
     397             :                              &isNull);
     398             :             Assert(!isNull);
     399             :             /* FALLTHROUGH */
     400     5296416 :         case 1:
     401     5296416 :             v1 = fastgetattr(tuple,
     402             :                              cc_keyno[0],
     403             :                              cc_tupdesc,
     404             :                              &isNull);
     405             :             Assert(!isNull);
     406     5296416 :             break;
     407           0 :         default:
     408           0 :             elog(FATAL, "wrong number of hash keys: %d", nkeys);
     409             :             break;
     410             :     }
     411             : 
     412     5296416 :     return CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
     413             : }
     414             : 
     415             : /*
     416             :  *      CatalogCacheCompareTuple
     417             :  *
     418             :  * Compare a tuple to the passed arguments.
     419             :  */
     420             : static inline bool
     421    66896290 : CatalogCacheCompareTuple(const CatCache *cache, int nkeys,
     422             :                          const Datum *cachekeys,
     423             :                          const Datum *searchkeys)
     424             : {
     425    66896290 :     const CCFastEqualFN *cc_fastequal = cache->cc_fastequal;
     426             :     int         i;
     427             : 
     428   155758498 :     for (i = 0; i < nkeys; i++)
     429             :     {
     430    88862208 :         if (!(cc_fastequal[i]) (cachekeys[i], searchkeys[i]))
     431           0 :             return false;
     432             :     }
     433    66896290 :     return true;
     434             : }
     435             : 
     436             : 
     437             : #ifdef CATCACHE_STATS
     438             : 
     439             : static void
     440             : CatCachePrintStats(int code, Datum arg)
     441             : {
     442             :     slist_iter  iter;
     443             :     long        cc_searches = 0;
     444             :     long        cc_hits = 0;
     445             :     long        cc_neg_hits = 0;
     446             :     long        cc_newloads = 0;
     447             :     long        cc_invals = 0;
     448             :     long        cc_lsearches = 0;
     449             :     long        cc_lhits = 0;
     450             : 
     451             :     slist_foreach(iter, &CacheHdr->ch_caches)
     452             :     {
     453             :         CatCache   *cache = slist_container(CatCache, cc_next, iter.cur);
     454             : 
     455             :         if (cache->cc_ntup == 0 && cache->cc_searches == 0)
     456             :             continue;           /* don't print unused caches */
     457             :         elog(DEBUG2, "catcache %s/%u: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld lsrch, %ld lhits",
     458             :              cache->cc_relname,
     459             :              cache->cc_indexoid,
     460             :              cache->cc_ntup,
     461             :              cache->cc_searches,
     462             :              cache->cc_hits,
     463             :              cache->cc_neg_hits,
     464             :              cache->cc_hits + cache->cc_neg_hits,
     465             :              cache->cc_newloads,
     466             :              cache->cc_searches - cache->cc_hits - cache->cc_neg_hits - cache->cc_newloads,
     467             :              cache->cc_searches - cache->cc_hits - cache->cc_neg_hits,
     468             :              cache->cc_invals,
     469             :              cache->cc_lsearches,
     470             :              cache->cc_lhits);
     471             :         cc_searches += cache->cc_searches;
     472             :         cc_hits += cache->cc_hits;
     473             :         cc_neg_hits += cache->cc_neg_hits;
     474             :         cc_newloads += cache->cc_newloads;
     475             :         cc_invals += cache->cc_invals;
     476             :         cc_lsearches += cache->cc_lsearches;
     477             :         cc_lhits += cache->cc_lhits;
     478             :     }
     479             :     elog(DEBUG2, "catcache totals: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld lsrch, %ld lhits",
     480             :          CacheHdr->ch_ntup,
     481             :          cc_searches,
     482             :          cc_hits,
     483             :          cc_neg_hits,
     484             :          cc_hits + cc_neg_hits,
     485             :          cc_newloads,
     486             :          cc_searches - cc_hits - cc_neg_hits - cc_newloads,
     487             :          cc_searches - cc_hits - cc_neg_hits,
     488             :          cc_invals,
     489             :          cc_lsearches,
     490             :          cc_lhits);
     491             : }
     492             : #endif                          /* CATCACHE_STATS */
     493             : 
     494             : 
     495             : /*
     496             :  *      CatCacheRemoveCTup
     497             :  *
     498             :  * Unlink and delete the given cache entry
     499             :  *
     500             :  * NB: if it is a member of a CatCList, the CatCList is deleted too.
     501             :  * Both the cache entry and the list had better have zero refcount.
     502             :  */
     503             : static void
     504     1224860 : CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
     505             : {
     506             :     Assert(ct->refcount == 0);
     507             :     Assert(ct->my_cache == cache);
     508             : 
     509     1224860 :     if (ct->c_list)
     510             :     {
     511             :         /*
     512             :          * The cleanest way to handle this is to call CatCacheRemoveCList,
     513             :          * which will recurse back to me, and the recursive call will do the
     514             :          * work.  Set the "dead" flag to make sure it does recurse.
     515             :          */
     516           0 :         ct->dead = true;
     517           0 :         CatCacheRemoveCList(cache, ct->c_list);
     518           0 :         return;                 /* nothing left to do */
     519             :     }
     520             : 
     521             :     /* delink from linked list */
     522     1224860 :     dlist_delete(&ct->cache_elem);
     523             : 
     524             :     /*
     525             :      * Free keys when we're dealing with a negative entry, normal entries just
     526             :      * point into tuple, allocated together with the CatCTup.
     527             :      */
     528     1224860 :     if (ct->negative)
     529      327526 :         CatCacheFreeKeys(cache->cc_tupdesc, cache->cc_nkeys,
     530      327526 :                          cache->cc_keyno, ct->keys);
     531             : 
     532     1224860 :     pfree(ct);
     533             : 
     534     1224860 :     --cache->cc_ntup;
     535     1224860 :     --CacheHdr->ch_ntup;
     536             : }
     537             : 
     538             : /*
     539             :  *      CatCacheRemoveCList
     540             :  *
     541             :  * Unlink and delete the given cache list entry
     542             :  *
     543             :  * NB: any dead member entries that become unreferenced are deleted too.
     544             :  */
     545             : static void
     546      100422 : CatCacheRemoveCList(CatCache *cache, CatCList *cl)
     547             : {
     548             :     int         i;
     549             : 
     550             :     Assert(cl->refcount == 0);
     551             :     Assert(cl->my_cache == cache);
     552             : 
     553             :     /* delink from member tuples */
     554      336452 :     for (i = cl->n_members; --i >= 0;)
     555             :     {
     556      236030 :         CatCTup    *ct = cl->members[i];
     557             : 
     558             :         Assert(ct->c_list == cl);
     559      236030 :         ct->c_list = NULL;
     560             :         /* if the member is dead and now has no references, remove it */
     561      236030 :         if (
     562             : #ifndef CATCACHE_FORCE_RELEASE
     563      236030 :             ct->dead &&
     564             : #endif
     565         144 :             ct->refcount == 0)
     566         144 :             CatCacheRemoveCTup(cache, ct);
     567             :     }
     568             : 
     569             :     /* delink from linked list */
     570      100422 :     dlist_delete(&cl->cache_elem);
     571             : 
     572             :     /* free associated column data */
     573      100422 :     CatCacheFreeKeys(cache->cc_tupdesc, cl->nkeys,
     574      100422 :                      cache->cc_keyno, cl->keys);
     575             : 
     576      100422 :     pfree(cl);
     577      100422 : }
     578             : 
     579             : 
     580             : /*
     581             :  *  CatCacheInvalidate
     582             :  *
     583             :  *  Invalidate entries in the specified cache, given a hash value.
     584             :  *
     585             :  *  We delete cache entries that match the hash value, whether positive
     586             :  *  or negative.  We don't care whether the invalidation is the result
     587             :  *  of a tuple insertion or a deletion.
     588             :  *
     589             :  *  We used to try to match positive cache entries by TID, but that is
     590             :  *  unsafe after a VACUUM FULL on a system catalog: an inval event could
     591             :  *  be queued before VACUUM FULL, and then processed afterwards, when the
     592             :  *  target tuple that has to be invalidated has a different TID than it
     593             :  *  did when the event was created.  So now we just compare hash values and
     594             :  *  accept the small risk of unnecessary invalidations due to false matches.
     595             :  *
     596             :  *  This routine is only quasi-public: it should only be used by inval.c.
     597             :  */
     598             : void
     599    16091480 : CatCacheInvalidate(CatCache *cache, uint32 hashValue)
     600             : {
     601             :     Index       hashIndex;
     602             :     dlist_mutable_iter iter;
     603             : 
     604             :     CACHE_elog(DEBUG2, "CatCacheInvalidate: called");
     605             : 
     606             :     /*
     607             :      * We don't bother to check whether the cache has finished initialization
     608             :      * yet; if not, there will be no entries in it so no problem.
     609             :      */
     610             : 
     611             :     /*
     612             :      * Invalidate *all* CatCLists in this cache; it's too hard to tell which
     613             :      * searches might still be correct, so just zap 'em all.
     614             :      */
     615    16188220 :     dlist_foreach_modify(iter, &cache->cc_lists)
     616             :     {
     617       96740 :         CatCList   *cl = dlist_container(CatCList, cache_elem, iter.cur);
     618             : 
     619       96740 :         if (cl->refcount > 0)
     620         144 :             cl->dead = true;
     621             :         else
     622       96596 :             CatCacheRemoveCList(cache, cl);
     623             :     }
     624             : 
     625             :     /*
     626             :      * inspect the proper hash bucket for tuple matches
     627             :      */
     628    16091480 :     hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
     629    21639932 :     dlist_foreach_modify(iter, &cache->cc_bucket[hashIndex])
     630             :     {
     631     5548452 :         CatCTup    *ct = dlist_container(CatCTup, cache_elem, iter.cur);
     632             : 
     633     5548452 :         if (hashValue == ct->hash_value)
     634             :         {
     635     1079438 :             if (ct->refcount > 0 ||
     636     1078392 :                 (ct->c_list && ct->c_list->refcount > 0))
     637             :             {
     638        1190 :                 ct->dead = true;
     639             :                 /* list, if any, was marked dead above */
     640        1190 :                 Assert(ct->c_list == NULL || ct->c_list->dead);
     641             :             }
     642             :             else
     643     1078248 :                 CatCacheRemoveCTup(cache, ct);
     644             :             CACHE_elog(DEBUG2, "CatCacheInvalidate: invalidated");
     645             : #ifdef CATCACHE_STATS
     646             :             cache->cc_invals++;
     647             : #endif
     648             :             /* could be multiple matches, so keep looking! */
     649             :         }
     650             :     }
     651    16091480 : }
     652             : 
     653             : /* ----------------------------------------------------------------
     654             :  *                     public functions
     655             :  * ----------------------------------------------------------------
     656             :  */
     657             : 
     658             : 
     659             : /*
     660             :  * Standard routine for creating cache context if it doesn't exist yet
     661             :  *
     662             :  * There are a lot of places (probably far more than necessary) that check
     663             :  * whether CacheMemoryContext exists yet and want to create it if not.
     664             :  * We centralize knowledge of exactly how to create it here.
     665             :  */
     666             : void
     667       24268 : CreateCacheMemoryContext(void)
     668             : {
     669             :     /*
     670             :      * Purely for paranoia, check that context doesn't exist; caller probably
     671             :      * did so already.
     672             :      */
     673       24268 :     if (!CacheMemoryContext)
     674       24268 :         CacheMemoryContext = AllocSetContextCreate(TopMemoryContext,
     675             :                                                    "CacheMemoryContext",
     676             :                                                    ALLOCSET_DEFAULT_SIZES);
     677       24268 : }
     678             : 
     679             : 
     680             : /*
     681             :  *      ResetCatalogCache
     682             :  *
     683             :  * Reset one catalog cache to empty.
     684             :  *
     685             :  * This is not very efficient if the target cache is nearly empty.
     686             :  * However, it shouldn't need to be efficient; we don't invoke it often.
     687             :  */
     688             : static void
     689      321012 : ResetCatalogCache(CatCache *cache)
     690             : {
     691             :     dlist_mutable_iter iter;
     692             :     int         i;
     693             : 
     694             :     /* Remove each list in this cache, or at least mark it dead */
     695      324832 :     dlist_foreach_modify(iter, &cache->cc_lists)
     696             :     {
     697        3820 :         CatCList   *cl = dlist_container(CatCList, cache_elem, iter.cur);
     698             : 
     699        3820 :         if (cl->refcount > 0)
     700           0 :             cl->dead = true;
     701             :         else
     702        3820 :             CatCacheRemoveCList(cache, cl);
     703             :     }
     704             : 
     705             :     /* Remove each tuple in this cache, or at least mark it dead */
     706     9787720 :     for (i = 0; i < cache->cc_nbuckets; i++)
     707             :     {
     708     9466708 :         dlist_head *bucket = &cache->cc_bucket[i];
     709             : 
     710     9612136 :         dlist_foreach_modify(iter, bucket)
     711             :         {
     712      145428 :             CatCTup    *ct = dlist_container(CatCTup, cache_elem, iter.cur);
     713             : 
     714      145428 :             if (ct->refcount > 0 ||
     715      145426 :                 (ct->c_list && ct->c_list->refcount > 0))
     716             :             {
     717           2 :                 ct->dead = true;
     718             :                 /* list, if any, was marked dead above */
     719           2 :                 Assert(ct->c_list == NULL || ct->c_list->dead);
     720             :             }
     721             :             else
     722      145426 :                 CatCacheRemoveCTup(cache, ct);
     723             : #ifdef CATCACHE_STATS
     724             :             cache->cc_invals++;
     725             : #endif
     726             :         }
     727             :     }
     728      321012 : }
     729             : 
     730             : /*
     731             :  *      ResetCatalogCaches
     732             :  *
     733             :  * Reset all caches when a shared cache inval event forces it
     734             :  */
     735             : void
     736        3858 : ResetCatalogCaches(void)
     737             : {
     738             :     slist_iter  iter;
     739             : 
     740             :     CACHE_elog(DEBUG2, "ResetCatalogCaches called");
     741             : 
     742      324072 :     slist_foreach(iter, &CacheHdr->ch_caches)
     743             :     {
     744      320214 :         CatCache   *cache = slist_container(CatCache, cc_next, iter.cur);
     745             : 
     746      320214 :         ResetCatalogCache(cache);
     747             :     }
     748             : 
     749             :     CACHE_elog(DEBUG2, "end of ResetCatalogCaches call");
     750        3858 : }
     751             : 
     752             : /*
     753             :  *      CatalogCacheFlushCatalog
     754             :  *
     755             :  *  Flush all catcache entries that came from the specified system catalog.
     756             :  *  This is needed after VACUUM FULL/CLUSTER on the catalog, since the
     757             :  *  tuples very likely now have different TIDs than before.  (At one point
     758             :  *  we also tried to force re-execution of CatalogCacheInitializeCache for
     759             :  *  the cache(s) on that catalog.  This is a bad idea since it leads to all
     760             :  *  kinds of trouble if a cache flush occurs while loading cache entries.
     761             :  *  We now avoid the need to do it by copying cc_tupdesc out of the relcache,
     762             :  *  rather than relying on the relcache to keep a tupdesc for us.  Of course
     763             :  *  this assumes the tupdesc of a cachable system table will not change...)
     764             :  */
     765             : void
     766         644 : CatalogCacheFlushCatalog(Oid catId)
     767             : {
     768             :     slist_iter  iter;
     769             : 
     770             :     CACHE_elog(DEBUG2, "CatalogCacheFlushCatalog called for %u", catId);
     771             : 
     772       54096 :     slist_foreach(iter, &CacheHdr->ch_caches)
     773             :     {
     774       53452 :         CatCache   *cache = slist_container(CatCache, cc_next, iter.cur);
     775             : 
     776             :         /* Does this cache store tuples of the target catalog? */
     777       53452 :         if (cache->cc_reloid == catId)
     778             :         {
     779             :             /* Yes, so flush all its contents */
     780         798 :             ResetCatalogCache(cache);
     781             : 
     782             :             /* Tell inval.c to call syscache callbacks for this cache */
     783         798 :             CallSyscacheCallbacks(cache->id, 0);
     784             :         }
     785             :     }
     786             : 
     787             :     CACHE_elog(DEBUG2, "end of CatalogCacheFlushCatalog call");
     788         644 : }
     789             : 
     790             : /*
     791             :  *      InitCatCache
     792             :  *
     793             :  *  This allocates and initializes a cache for a system catalog relation.
     794             :  *  Actually, the cache is only partially initialized to avoid opening the
     795             :  *  relation.  The relation will be opened and the rest of the cache
     796             :  *  structure initialized on the first access.
     797             :  */
     798             : #ifdef CACHEDEBUG
     799             : #define InitCatCache_DEBUG2 \
     800             : do { \
     801             :     elog(DEBUG2, "InitCatCache: rel=%u ind=%u id=%d nkeys=%d size=%d", \
     802             :          cp->cc_reloid, cp->cc_indexoid, cp->id, \
     803             :          cp->cc_nkeys, cp->cc_nbuckets); \
     804             : } while(0)
     805             : #else
     806             : #define InitCatCache_DEBUG2
     807             : #endif
     808             : 
     809             : CatCache *
     810     2014244 : InitCatCache(int id,
     811             :              Oid reloid,
     812             :              Oid indexoid,
     813             :              int nkeys,
     814             :              const int *key,
     815             :              int nbuckets)
     816             : {
     817             :     CatCache   *cp;
     818             :     MemoryContext oldcxt;
     819             :     int         i;
     820             : 
     821             :     /*
     822             :      * nbuckets is the initial number of hash buckets to use in this catcache.
     823             :      * It will be enlarged later if it becomes too full.
     824             :      *
     825             :      * nbuckets must be a power of two.  We check this via Assert rather than
     826             :      * a full runtime check because the values will be coming from constant
     827             :      * tables.
     828             :      *
     829             :      * If you're confused by the power-of-two check, see comments in
     830             :      * bitmapset.c for an explanation.
     831             :      */
     832             :     Assert(nbuckets > 0 && (nbuckets & -nbuckets) == nbuckets);
     833             : 
     834             :     /*
     835             :      * first switch to the cache context so our allocations do not vanish at
     836             :      * the end of a transaction
     837             :      */
     838     2014244 :     if (!CacheMemoryContext)
     839           0 :         CreateCacheMemoryContext();
     840             : 
     841     2014244 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
     842             : 
     843             :     /*
     844             :      * if first time through, initialize the cache group header
     845             :      */
     846     2014244 :     if (CacheHdr == NULL)
     847             :     {
     848       24268 :         CacheHdr = (CatCacheHeader *) palloc(sizeof(CatCacheHeader));
     849       24268 :         slist_init(&CacheHdr->ch_caches);
     850       24268 :         CacheHdr->ch_ntup = 0;
     851             : #ifdef CATCACHE_STATS
     852             :         /* set up to dump stats at backend exit */
     853             :         on_proc_exit(CatCachePrintStats, 0);
     854             : #endif
     855             :     }
     856             : 
     857             :     /*
     858             :      * Allocate a new cache structure, aligning to a cacheline boundary
     859             :      *
     860             :      * Note: we rely on zeroing to initialize all the dlist headers correctly
     861             :      */
     862     2014244 :     cp = (CatCache *) palloc_aligned(sizeof(CatCache), PG_CACHE_LINE_SIZE,
     863             :                                      MCXT_ALLOC_ZERO);
     864     2014244 :     cp->cc_bucket = palloc0(nbuckets * sizeof(dlist_head));
     865             : 
     866             :     /*
     867             :      * initialize the cache's relation information for the relation
     868             :      * corresponding to this cache, and initialize some of the new cache's
     869             :      * other internal fields.  But don't open the relation yet.
     870             :      */
     871     2014244 :     cp->id = id;
     872     2014244 :     cp->cc_relname = "(not known yet)";
     873     2014244 :     cp->cc_reloid = reloid;
     874     2014244 :     cp->cc_indexoid = indexoid;
     875     2014244 :     cp->cc_relisshared = false; /* temporary */
     876     2014244 :     cp->cc_tupdesc = (TupleDesc) NULL;
     877     2014244 :     cp->cc_ntup = 0;
     878     2014244 :     cp->cc_nbuckets = nbuckets;
     879     2014244 :     cp->cc_nkeys = nkeys;
     880     5290424 :     for (i = 0; i < nkeys; ++i)
     881             :     {
     882             :         Assert(AttributeNumberIsValid(key[i]));
     883     3276180 :         cp->cc_keyno[i] = key[i];
     884             :     }
     885             : 
     886             :     /*
     887             :      * new cache is initialized as far as we can go for now. print some
     888             :      * debugging information, if appropriate.
     889             :      */
     890             :     InitCatCache_DEBUG2;
     891             : 
     892             :     /*
     893             :      * add completed cache to top of group header's list
     894             :      */
     895     2014244 :     slist_push_head(&CacheHdr->ch_caches, &cp->cc_next);
     896             : 
     897             :     /*
     898             :      * back to the old context before we return...
     899             :      */
     900     2014244 :     MemoryContextSwitchTo(oldcxt);
     901             : 
     902     2014244 :     return cp;
     903             : }
     904             : 
     905             : /*
     906             :  * Enlarge a catcache, doubling the number of buckets.
     907             :  */
     908             : static void
     909        4294 : RehashCatCache(CatCache *cp)
     910             : {
     911             :     dlist_head *newbucket;
     912             :     int         newnbuckets;
     913             :     int         i;
     914             : 
     915        4294 :     elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
     916             :          cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
     917             : 
     918             :     /* Allocate a new, larger, hash table. */
     919        4294 :     newnbuckets = cp->cc_nbuckets * 2;
     920        4294 :     newbucket = (dlist_head *) MemoryContextAllocZero(CacheMemoryContext, newnbuckets * sizeof(dlist_head));
     921             : 
     922             :     /* Move all entries from old hash table to new. */
     923      361126 :     for (i = 0; i < cp->cc_nbuckets; i++)
     924             :     {
     925             :         dlist_mutable_iter iter;
     926             : 
     927     1074790 :         dlist_foreach_modify(iter, &cp->cc_bucket[i])
     928             :         {
     929      717958 :             CatCTup    *ct = dlist_container(CatCTup, cache_elem, iter.cur);
     930      717958 :             int         hashIndex = HASH_INDEX(ct->hash_value, newnbuckets);
     931             : 
     932      717958 :             dlist_delete(iter.cur);
     933      717958 :             dlist_push_head(&newbucket[hashIndex], &ct->cache_elem);
     934             :         }
     935             :     }
     936             : 
     937             :     /* Switch to the new array. */
     938        4294 :     pfree(cp->cc_bucket);
     939        4294 :     cp->cc_nbuckets = newnbuckets;
     940        4294 :     cp->cc_bucket = newbucket;
     941        4294 : }
     942             : 
     943             : /*
     944             :  *      CatalogCacheInitializeCache
     945             :  *
     946             :  * This function does final initialization of a catcache: obtain the tuple
     947             :  * descriptor and set up the hash and equality function links.  We assume
     948             :  * that the relcache entry can be opened at this point!
     949             :  */
     950             : #ifdef CACHEDEBUG
     951             : #define CatalogCacheInitializeCache_DEBUG1 \
     952             :     elog(DEBUG2, "CatalogCacheInitializeCache: cache @%p rel=%u", cache, \
     953             :          cache->cc_reloid)
     954             : 
     955             : #define CatalogCacheInitializeCache_DEBUG2 \
     956             : do { \
     957             :         if (cache->cc_keyno[i] > 0) { \
     958             :             elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d, %u", \
     959             :                 i+1, cache->cc_nkeys, cache->cc_keyno[i], \
     960             :                  TupleDescAttr(tupdesc, cache->cc_keyno[i] - 1)->atttypid); \
     961             :         } else { \
     962             :             elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d", \
     963             :                 i+1, cache->cc_nkeys, cache->cc_keyno[i]); \
     964             :         } \
     965             : } while(0)
     966             : #else
     967             : #define CatalogCacheInitializeCache_DEBUG1
     968             : #define CatalogCacheInitializeCache_DEBUG2
     969             : #endif
     970             : 
     971             : static void
     972      511350 : CatalogCacheInitializeCache(CatCache *cache)
     973             : {
     974             :     Relation    relation;
     975             :     MemoryContext oldcxt;
     976             :     TupleDesc   tupdesc;
     977             :     int         i;
     978             : 
     979             :     CatalogCacheInitializeCache_DEBUG1;
     980             : 
     981      511350 :     relation = table_open(cache->cc_reloid, AccessShareLock);
     982             : 
     983             :     /*
     984             :      * switch to the cache context so our allocations do not vanish at the end
     985             :      * of a transaction
     986             :      */
     987             :     Assert(CacheMemoryContext != NULL);
     988             : 
     989      511346 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
     990             : 
     991             :     /*
     992             :      * copy the relcache's tuple descriptor to permanent cache storage
     993             :      */
     994      511346 :     tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
     995             : 
     996             :     /*
     997             :      * save the relation's name and relisshared flag, too (cc_relname is used
     998             :      * only for debugging purposes)
     999             :      */
    1000      511346 :     cache->cc_relname = pstrdup(RelationGetRelationName(relation));
    1001      511346 :     cache->cc_relisshared = RelationGetForm(relation)->relisshared;
    1002             : 
    1003             :     /*
    1004             :      * return to the caller's memory context and close the rel
    1005             :      */
    1006      511346 :     MemoryContextSwitchTo(oldcxt);
    1007             : 
    1008      511346 :     table_close(relation, AccessShareLock);
    1009             : 
    1010             :     CACHE_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys",
    1011             :                cache->cc_relname, cache->cc_nkeys);
    1012             : 
    1013             :     /*
    1014             :      * initialize cache's key information
    1015             :      */
    1016     1327274 :     for (i = 0; i < cache->cc_nkeys; ++i)
    1017             :     {
    1018             :         Oid         keytype;
    1019             :         RegProcedure eqfunc;
    1020             : 
    1021             :         CatalogCacheInitializeCache_DEBUG2;
    1022             : 
    1023      815928 :         if (cache->cc_keyno[i] > 0)
    1024             :         {
    1025      815928 :             Form_pg_attribute attr = TupleDescAttr(tupdesc,
    1026             :                                                    cache->cc_keyno[i] - 1);
    1027             : 
    1028      815928 :             keytype = attr->atttypid;
    1029             :             /* cache key columns should always be NOT NULL */
    1030             :             Assert(attr->attnotnull);
    1031             :         }
    1032             :         else
    1033             :         {
    1034           0 :             if (cache->cc_keyno[i] < 0)
    1035           0 :                 elog(FATAL, "sys attributes are not supported in caches");
    1036           0 :             keytype = OIDOID;
    1037             :         }
    1038             : 
    1039      815928 :         GetCCHashEqFuncs(keytype,
    1040             :                          &cache->cc_hashfunc[i],
    1041             :                          &eqfunc,
    1042             :                          &cache->cc_fastequal[i]);
    1043             : 
    1044             :         /*
    1045             :          * Do equality-function lookup (we assume this won't need a catalog
    1046             :          * lookup for any supported type)
    1047             :          */
    1048      815928 :         fmgr_info_cxt(eqfunc,
    1049             :                       &cache->cc_skey[i].sk_func,
    1050             :                       CacheMemoryContext);
    1051             : 
    1052             :         /* Initialize sk_attno suitably for HeapKeyTest() and heap scans */
    1053      815928 :         cache->cc_skey[i].sk_attno = cache->cc_keyno[i];
    1054             : 
    1055             :         /* Fill in sk_strategy as well --- always standard equality */
    1056      815928 :         cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber;
    1057      815928 :         cache->cc_skey[i].sk_subtype = InvalidOid;
    1058             :         /* If a catcache key requires a collation, it must be C collation */
    1059      815928 :         cache->cc_skey[i].sk_collation = C_COLLATION_OID;
    1060             : 
    1061             :         CACHE_elog(DEBUG2, "CatalogCacheInitializeCache %s %d %p",
    1062             :                    cache->cc_relname, i, cache);
    1063             :     }
    1064             : 
    1065             :     /*
    1066             :      * mark this cache fully initialized
    1067             :      */
    1068      511346 :     cache->cc_tupdesc = tupdesc;
    1069      511346 : }
    1070             : 
    1071             : /*
    1072             :  * InitCatCachePhase2 -- external interface for CatalogCacheInitializeCache
    1073             :  *
    1074             :  * One reason to call this routine is to ensure that the relcache has
    1075             :  * created entries for all the catalogs and indexes referenced by catcaches.
    1076             :  * Therefore, provide an option to open the index as well as fixing the
    1077             :  * cache itself.  An exception is the indexes on pg_am, which we don't use
    1078             :  * (cf. IndexScanOK).
    1079             :  */
    1080             : void
    1081      182068 : InitCatCachePhase2(CatCache *cache, bool touch_index)
    1082             : {
    1083      182068 :     if (cache->cc_tupdesc == NULL)
    1084      168818 :         CatalogCacheInitializeCache(cache);
    1085             : 
    1086      182064 :     if (touch_index &&
    1087      158980 :         cache->id != AMOID &&
    1088      157062 :         cache->id != AMNAME)
    1089             :     {
    1090             :         Relation    idesc;
    1091             : 
    1092             :         /*
    1093             :          * We must lock the underlying catalog before opening the index to
    1094             :          * avoid deadlock, since index_open could possibly result in reading
    1095             :          * this same catalog, and if anyone else is exclusive-locking this
    1096             :          * catalog and index they'll be doing it in that order.
    1097             :          */
    1098      155144 :         LockRelationOid(cache->cc_reloid, AccessShareLock);
    1099      155144 :         idesc = index_open(cache->cc_indexoid, AccessShareLock);
    1100             : 
    1101             :         /*
    1102             :          * While we've got the index open, let's check that it's unique (and
    1103             :          * not just deferrable-unique, thank you very much).  This is just to
    1104             :          * catch thinkos in definitions of new catcaches, so we don't worry
    1105             :          * about the pg_am indexes not getting tested.
    1106             :          */
    1107             :         Assert(idesc->rd_index->indisunique &&
    1108             :                idesc->rd_index->indimmediate);
    1109             : 
    1110      155142 :         index_close(idesc, AccessShareLock);
    1111      155142 :         UnlockRelationOid(cache->cc_reloid, AccessShareLock);
    1112             :     }
    1113      182062 : }
    1114             : 
    1115             : 
    1116             : /*
    1117             :  *      IndexScanOK
    1118             :  *
    1119             :  *      This function checks for tuples that will be fetched by
    1120             :  *      IndexSupportInitialize() during relcache initialization for
    1121             :  *      certain system indexes that support critical syscaches.
    1122             :  *      We can't use an indexscan to fetch these, else we'll get into
    1123             :  *      infinite recursion.  A plain heap scan will work, however.
    1124             :  *      Once we have completed relcache initialization (signaled by
    1125             :  *      criticalRelcachesBuilt), we don't have to worry anymore.
    1126             :  *
    1127             :  *      Similarly, during backend startup we have to be able to use the
    1128             :  *      pg_authid, pg_auth_members and pg_database syscaches for
    1129             :  *      authentication even if we don't yet have relcache entries for those
    1130             :  *      catalogs' indexes.
    1131             :  */
    1132             : static bool
    1133     4511772 : IndexScanOK(CatCache *cache, ScanKey cur_skey)
    1134             : {
    1135     4511772 :     switch (cache->id)
    1136             :     {
    1137      379646 :         case INDEXRELID:
    1138             : 
    1139             :             /*
    1140             :              * Rather than tracking exactly which indexes have to be loaded
    1141             :              * before we can use indexscans (which changes from time to time),
    1142             :              * just force all pg_index searches to be heap scans until we've
    1143             :              * built the critical relcaches.
    1144             :              */
    1145      379646 :             if (!criticalRelcachesBuilt)
    1146       23400 :                 return false;
    1147      356246 :             break;
    1148             : 
    1149       45696 :         case AMOID:
    1150             :         case AMNAME:
    1151             : 
    1152             :             /*
    1153             :              * Always do heap scans in pg_am, because it's so small there's
    1154             :              * not much point in an indexscan anyway.  We *must* do this when
    1155             :              * initially building critical relcache entries, but we might as
    1156             :              * well just always do it.
    1157             :              */
    1158       45696 :             return false;
    1159             : 
    1160       84648 :         case AUTHNAME:
    1161             :         case AUTHOID:
    1162             :         case AUTHMEMMEMROLE:
    1163             :         case DATABASEOID:
    1164             : 
    1165             :             /*
    1166             :              * Protect authentication lookups occurring before relcache has
    1167             :              * collected entries for shared indexes.
    1168             :              */
    1169       84648 :             if (!criticalSharedRelcachesBuilt)
    1170        3144 :                 return false;
    1171       81504 :             break;
    1172             : 
    1173     4001782 :         default:
    1174     4001782 :             break;
    1175             :     }
    1176             : 
    1177             :     /* Normal case, allow index scan */
    1178     4439532 :     return true;
    1179             : }
    1180             : 
    1181             : /*
    1182             :  *  SearchCatCache
    1183             :  *
    1184             :  *      This call searches a system cache for a tuple, opening the relation
    1185             :  *      if necessary (on the first access to a particular cache).
    1186             :  *
    1187             :  *      The result is NULL if not found, or a pointer to a HeapTuple in
    1188             :  *      the cache.  The caller must not modify the tuple, and must call
    1189             :  *      ReleaseCatCache() when done with it.
    1190             :  *
    1191             :  * The search key values should be expressed as Datums of the key columns'
    1192             :  * datatype(s).  (Pass zeroes for any unused parameters.)  As a special
    1193             :  * exception, the passed-in key for a NAME column can be just a C string;
    1194             :  * the caller need not go to the trouble of converting it to a fully
    1195             :  * null-padded NAME.
    1196             :  */
    1197             : HeapTuple
    1198     4303464 : SearchCatCache(CatCache *cache,
    1199             :                Datum v1,
    1200             :                Datum v2,
    1201             :                Datum v3,
    1202             :                Datum v4)
    1203             : {
    1204     4303464 :     return SearchCatCacheInternal(cache, cache->cc_nkeys, v1, v2, v3, v4);
    1205             : }
    1206             : 
    1207             : 
    1208             : /*
    1209             :  * SearchCatCacheN() are SearchCatCache() versions for a specific number of
    1210             :  * arguments. The compiler can inline the body and unroll loops, making them a
    1211             :  * bit faster than SearchCatCache().
    1212             :  */
    1213             : 
    1214             : HeapTuple
    1215    53052894 : SearchCatCache1(CatCache *cache,
    1216             :                 Datum v1)
    1217             : {
    1218    53052894 :     return SearchCatCacheInternal(cache, 1, v1, 0, 0, 0);
    1219             : }
    1220             : 
    1221             : 
    1222             : HeapTuple
    1223     4017998 : SearchCatCache2(CatCache *cache,
    1224             :                 Datum v1, Datum v2)
    1225             : {
    1226     4017998 :     return SearchCatCacheInternal(cache, 2, v1, v2, 0, 0);
    1227             : }
    1228             : 
    1229             : 
    1230             : HeapTuple
    1231     4106462 : SearchCatCache3(CatCache *cache,
    1232             :                 Datum v1, Datum v2, Datum v3)
    1233             : {
    1234     4106462 :     return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
    1235             : }
    1236             : 
    1237             : 
    1238             : HeapTuple
    1239     3185442 : SearchCatCache4(CatCache *cache,
    1240             :                 Datum v1, Datum v2, Datum v3, Datum v4)
    1241             : {
    1242     3185442 :     return SearchCatCacheInternal(cache, 4, v1, v2, v3, v4);
    1243             : }
    1244             : 
    1245             : /*
    1246             :  * Work-horse for SearchCatCache/SearchCatCacheN.
    1247             :  */
    1248             : static inline HeapTuple
    1249    68666260 : SearchCatCacheInternal(CatCache *cache,
    1250             :                        int nkeys,
    1251             :                        Datum v1,
    1252             :                        Datum v2,
    1253             :                        Datum v3,
    1254             :                        Datum v4)
    1255             : {
    1256             :     Datum       arguments[CATCACHE_MAXKEYS];
    1257             :     uint32      hashValue;
    1258             :     Index       hashIndex;
    1259             :     dlist_iter  iter;
    1260             :     dlist_head *bucket;
    1261             :     CatCTup    *ct;
    1262             : 
    1263             :     /* Make sure we're in an xact, even if this ends up being a cache hit */
    1264             :     Assert(IsTransactionState());
    1265             : 
    1266             :     Assert(cache->cc_nkeys == nkeys);
    1267             : 
    1268             :     /*
    1269             :      * one-time startup overhead for each cache
    1270             :      */
    1271    68666260 :     if (unlikely(cache->cc_tupdesc == NULL))
    1272      286496 :         CatalogCacheInitializeCache(cache);
    1273             : 
    1274             : #ifdef CATCACHE_STATS
    1275             :     cache->cc_searches++;
    1276             : #endif
    1277             : 
    1278             :     /* Initialize local parameter array */
    1279    68666260 :     arguments[0] = v1;
    1280    68666260 :     arguments[1] = v2;
    1281    68666260 :     arguments[2] = v3;
    1282    68666260 :     arguments[3] = v4;
    1283             : 
    1284             :     /*
    1285             :      * find the hash bucket in which to look for the tuple
    1286             :      */
    1287    68666260 :     hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
    1288    68666260 :     hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
    1289             : 
    1290             :     /*
    1291             :      * scan the hash bucket until we find a match or exhaust our tuples
    1292             :      *
    1293             :      * Note: it's okay to use dlist_foreach here, even though we modify the
    1294             :      * dlist within the loop, because we don't continue the loop afterwards.
    1295             :      */
    1296    68666260 :     bucket = &cache->cc_bucket[hashIndex];
    1297    73106498 :     dlist_foreach(iter, bucket)
    1298             :     {
    1299    68833776 :         ct = dlist_container(CatCTup, cache_elem, iter.cur);
    1300             : 
    1301    68833776 :         if (ct->dead)
    1302           2 :             continue;           /* ignore dead entries */
    1303             : 
    1304    68833774 :         if (ct->hash_value != hashValue)
    1305     4440236 :             continue;           /* quickly skip entry if wrong hash val */
    1306             : 
    1307    64393538 :         if (!CatalogCacheCompareTuple(cache, nkeys, ct->keys, arguments))
    1308           0 :             continue;
    1309             : 
    1310             :         /*
    1311             :          * We found a match in the cache.  Move it to the front of the list
    1312             :          * for its hashbucket, in order to speed subsequent searches.  (The
    1313             :          * most frequently accessed elements in any hashbucket will tend to be
    1314             :          * near the front of the hashbucket's list.)
    1315             :          */
    1316    64393538 :         dlist_move_head(bucket, &ct->cache_elem);
    1317             : 
    1318             :         /*
    1319             :          * If it's a positive entry, bump its refcount and return it. If it's
    1320             :          * negative, we can report failure to the caller.
    1321             :          */
    1322    64393538 :         if (!ct->negative)
    1323             :         {
    1324    61235456 :             ResourceOwnerEnlarge(CurrentResourceOwner);
    1325    61235456 :             ct->refcount++;
    1326    61235456 :             ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
    1327             : 
    1328             :             CACHE_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",
    1329             :                        cache->cc_relname, hashIndex);
    1330             : 
    1331             : #ifdef CATCACHE_STATS
    1332             :             cache->cc_hits++;
    1333             : #endif
    1334             : 
    1335    61235456 :             return &ct->tuple;
    1336             :         }
    1337             :         else
    1338             :         {
    1339             :             CACHE_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d",
    1340             :                        cache->cc_relname, hashIndex);
    1341             : 
    1342             : #ifdef CATCACHE_STATS
    1343             :             cache->cc_neg_hits++;
    1344             : #endif
    1345             : 
    1346     3158082 :             return NULL;
    1347             :         }
    1348             :     }
    1349             : 
    1350     4272722 :     return SearchCatCacheMiss(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);
    1351             : }
    1352             : 
    1353             : /*
    1354             :  * Search the actual catalogs, rather than the cache.
    1355             :  *
    1356             :  * This is kept separate from SearchCatCacheInternal() to keep the fast-path
    1357             :  * as small as possible.  To avoid that effort being undone by a helpful
    1358             :  * compiler, try to explicitly forbid inlining.
    1359             :  */
    1360             : static pg_noinline HeapTuple
    1361     4272722 : SearchCatCacheMiss(CatCache *cache,
    1362             :                    int nkeys,
    1363             :                    uint32 hashValue,
    1364             :                    Index hashIndex,
    1365             :                    Datum v1,
    1366             :                    Datum v2,
    1367             :                    Datum v3,
    1368             :                    Datum v4)
    1369             : {
    1370             :     ScanKeyData cur_skey[CATCACHE_MAXKEYS];
    1371             :     Relation    relation;
    1372             :     SysScanDesc scandesc;
    1373             :     HeapTuple   ntp;
    1374             :     CatCTup    *ct;
    1375             :     Datum       arguments[CATCACHE_MAXKEYS];
    1376             : 
    1377             :     /* Initialize local parameter array */
    1378     4272722 :     arguments[0] = v1;
    1379     4272722 :     arguments[1] = v2;
    1380     4272722 :     arguments[2] = v3;
    1381     4272722 :     arguments[3] = v4;
    1382             : 
    1383             :     /*
    1384             :      * Ok, need to make a lookup in the relation, copy the scankey and fill
    1385             :      * out any per-call fields.
    1386             :      */
    1387     4272722 :     memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * nkeys);
    1388     4272722 :     cur_skey[0].sk_argument = v1;
    1389     4272722 :     cur_skey[1].sk_argument = v2;
    1390     4272722 :     cur_skey[2].sk_argument = v3;
    1391     4272722 :     cur_skey[3].sk_argument = v4;
    1392             : 
    1393             :     /*
    1394             :      * Tuple was not found in cache, so we have to try to retrieve it directly
    1395             :      * from the relation.  If found, we will add it to the cache; if not
    1396             :      * found, we will add a negative cache entry instead.
    1397             :      *
    1398             :      * NOTE: it is possible for recursive cache lookups to occur while reading
    1399             :      * the relation --- for example, due to shared-cache-inval messages being
    1400             :      * processed during table_open().  This is OK.  It's even possible for one
    1401             :      * of those lookups to find and enter the very same tuple we are trying to
    1402             :      * fetch here.  If that happens, we will enter a second copy of the tuple
    1403             :      * into the cache.  The first copy will never be referenced again, and
    1404             :      * will eventually age out of the cache, so there's no functional problem.
    1405             :      * This case is rare enough that it's not worth expending extra cycles to
    1406             :      * detect.
    1407             :      */
    1408     4272722 :     relation = table_open(cache->cc_reloid, AccessShareLock);
    1409             : 
    1410     4272722 :     scandesc = systable_beginscan(relation,
    1411             :                                   cache->cc_indexoid,
    1412     4272722 :                                   IndexScanOK(cache, cur_skey),
    1413             :                                   NULL,
    1414             :                                   nkeys,
    1415             :                                   cur_skey);
    1416             : 
    1417     4272722 :     ct = NULL;
    1418             : 
    1419     4272722 :     while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
    1420             :     {
    1421     2966186 :         ct = CatalogCacheCreateEntry(cache, ntp, arguments,
    1422             :                                      hashValue, hashIndex,
    1423             :                                      false);
    1424             :         /* immediately set the refcount to 1 */
    1425     2966186 :         ResourceOwnerEnlarge(CurrentResourceOwner);
    1426     2966186 :         ct->refcount++;
    1427     2966186 :         ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
    1428     2966186 :         break;                  /* assume only one match */
    1429             :     }
    1430             : 
    1431     4272720 :     systable_endscan(scandesc);
    1432             : 
    1433     4272720 :     table_close(relation, AccessShareLock);
    1434             : 
    1435             :     /*
    1436             :      * If tuple was not found, we need to build a negative cache entry
    1437             :      * containing a fake tuple.  The fake tuple has the correct key columns,
    1438             :      * but nulls everywhere else.
    1439             :      *
    1440             :      * In bootstrap mode, we don't build negative entries, because the cache
    1441             :      * invalidation mechanism isn't alive and can't clear them if the tuple
    1442             :      * gets created later.  (Bootstrap doesn't do UPDATEs, so it doesn't need
    1443             :      * cache inval for that.)
    1444             :      */
    1445     4272720 :     if (ct == NULL)
    1446             :     {
    1447     1306534 :         if (IsBootstrapProcessingMode())
    1448       34752 :             return NULL;
    1449             : 
    1450     1271782 :         ct = CatalogCacheCreateEntry(cache, NULL, arguments,
    1451             :                                      hashValue, hashIndex,
    1452             :                                      true);
    1453             : 
    1454             :         CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
    1455             :                    cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
    1456             :         CACHE_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d",
    1457             :                    cache->cc_relname, hashIndex);
    1458             : 
    1459             :         /*
    1460             :          * We are not returning the negative entry to the caller, so leave its
    1461             :          * refcount zero.
    1462             :          */
    1463             : 
    1464     1271782 :         return NULL;
    1465             :     }
    1466             : 
    1467             :     CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
    1468             :                cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
    1469             :     CACHE_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d",
    1470             :                cache->cc_relname, hashIndex);
    1471             : 
    1472             : #ifdef CATCACHE_STATS
    1473             :     cache->cc_newloads++;
    1474             : #endif
    1475             : 
    1476     2966186 :     return &ct->tuple;
    1477             : }
    1478             : 
    1479             : /*
    1480             :  *  ReleaseCatCache
    1481             :  *
    1482             :  *  Decrement the reference count of a catcache entry (releasing the
    1483             :  *  hold grabbed by a successful SearchCatCache).
    1484             :  *
    1485             :  *  NOTE: if compiled with -DCATCACHE_FORCE_RELEASE then catcache entries
    1486             :  *  will be freed as soon as their refcount goes to zero.  In combination
    1487             :  *  with aset.c's CLOBBER_FREED_MEMORY option, this provides a good test
    1488             :  *  to catch references to already-released catcache entries.
    1489             :  */
    1490             : void
    1491    64191700 : ReleaseCatCache(HeapTuple tuple)
    1492             : {
    1493    64191700 :     ReleaseCatCacheWithOwner(tuple, CurrentResourceOwner);
    1494    64191700 : }
    1495             : 
    1496             : static void
    1497    64201642 : ReleaseCatCacheWithOwner(HeapTuple tuple, ResourceOwner resowner)
    1498             : {
    1499    64201642 :     CatCTup    *ct = (CatCTup *) (((char *) tuple) -
    1500             :                                   offsetof(CatCTup, tuple));
    1501             : 
    1502             :     /* Safety checks to ensure we were handed a cache entry */
    1503             :     Assert(ct->ct_magic == CT_MAGIC);
    1504             :     Assert(ct->refcount > 0);
    1505             : 
    1506    64201642 :     ct->refcount--;
    1507    64201642 :     if (resowner)
    1508    64191700 :         ResourceOwnerForgetCatCacheRef(CurrentResourceOwner, &ct->tuple);
    1509             : 
    1510    64201642 :     if (
    1511             : #ifndef CATCACHE_FORCE_RELEASE
    1512    64201642 :         ct->dead &&
    1513             : #endif
    1514        1150 :         ct->refcount == 0 &&
    1515        1042 :         (ct->c_list == NULL || ct->c_list->refcount == 0))
    1516        1042 :         CatCacheRemoveCTup(ct->my_cache, ct);
    1517    64201642 : }
    1518             : 
    1519             : 
    1520             : /*
    1521             :  *  GetCatCacheHashValue
    1522             :  *
    1523             :  *      Compute the hash value for a given set of search keys.
    1524             :  *
    1525             :  * The reason for exposing this as part of the API is that the hash value is
    1526             :  * exposed in cache invalidation operations, so there are places outside the
    1527             :  * catcache code that need to be able to compute the hash values.
    1528             :  */
    1529             : uint32
    1530      133016 : GetCatCacheHashValue(CatCache *cache,
    1531             :                      Datum v1,
    1532             :                      Datum v2,
    1533             :                      Datum v3,
    1534             :                      Datum v4)
    1535             : {
    1536             :     /*
    1537             :      * one-time startup overhead for each cache
    1538             :      */
    1539      133016 :     if (cache->cc_tupdesc == NULL)
    1540       20186 :         CatalogCacheInitializeCache(cache);
    1541             : 
    1542             :     /*
    1543             :      * calculate the hash value
    1544             :      */
    1545      133016 :     return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, v1, v2, v3, v4);
    1546             : }
    1547             : 
    1548             : 
    1549             : /*
    1550             :  *  SearchCatCacheList
    1551             :  *
    1552             :  *      Generate a list of all tuples matching a partial key (that is,
    1553             :  *      a key specifying just the first K of the cache's N key columns).
    1554             :  *
    1555             :  *      It doesn't make any sense to specify all of the cache's key columns
    1556             :  *      here: since the key is unique, there could be at most one match, so
    1557             :  *      you ought to use SearchCatCache() instead.  Hence this function takes
    1558             :  *      one fewer Datum argument than SearchCatCache() does.
    1559             :  *
    1560             :  *      The caller must not modify the list object or the pointed-to tuples,
    1561             :  *      and must call ReleaseCatCacheList() when done with the list.
    1562             :  */
    1563             : CatCList *
    1564     2741802 : SearchCatCacheList(CatCache *cache,
    1565             :                    int nkeys,
    1566             :                    Datum v1,
    1567             :                    Datum v2,
    1568             :                    Datum v3)
    1569             : {
    1570     2741802 :     Datum       v4 = 0;         /* dummy last-column value */
    1571             :     Datum       arguments[CATCACHE_MAXKEYS];
    1572             :     uint32      lHashValue;
    1573             :     dlist_iter  iter;
    1574             :     CatCList   *cl;
    1575             :     CatCTup    *ct;
    1576             :     List       *volatile ctlist;
    1577             :     ListCell   *ctlist_item;
    1578             :     int         nmembers;
    1579             :     bool        ordered;
    1580             :     HeapTuple   ntp;
    1581             :     MemoryContext oldcxt;
    1582             :     int         i;
    1583             : 
    1584             :     /*
    1585             :      * one-time startup overhead for each cache
    1586             :      */
    1587     2741802 :     if (cache->cc_tupdesc == NULL)
    1588       28250 :         CatalogCacheInitializeCache(cache);
    1589             : 
    1590             :     Assert(nkeys > 0 && nkeys < cache->cc_nkeys);
    1591             : 
    1592             : #ifdef CATCACHE_STATS
    1593             :     cache->cc_lsearches++;
    1594             : #endif
    1595             : 
    1596             :     /* Initialize local parameter array */
    1597     2741802 :     arguments[0] = v1;
    1598     2741802 :     arguments[1] = v2;
    1599     2741802 :     arguments[2] = v3;
    1600     2741802 :     arguments[3] = v4;
    1601             : 
    1602             :     /*
    1603             :      * compute a hash value of the given keys for faster search.  We don't
    1604             :      * presently divide the CatCList items into buckets, but this still lets
    1605             :      * us skip non-matching items quickly most of the time.
    1606             :      */
    1607     2741802 :     lHashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
    1608             : 
    1609             :     /*
    1610             :      * scan the items until we find a match or exhaust our list
    1611             :      *
    1612             :      * Note: it's okay to use dlist_foreach here, even though we modify the
    1613             :      * dlist within the loop, because we don't continue the loop afterwards.
    1614             :      */
    1615    27375796 :     dlist_foreach(iter, &cache->cc_lists)
    1616             :     {
    1617    27136746 :         cl = dlist_container(CatCList, cache_elem, iter.cur);
    1618             : 
    1619    27136746 :         if (cl->dead)
    1620           0 :             continue;           /* ignore dead entries */
    1621             : 
    1622    27136746 :         if (cl->hash_value != lHashValue)
    1623    24633994 :             continue;           /* quickly skip entry if wrong hash val */
    1624             : 
    1625             :         /*
    1626             :          * see if the cached list matches our key.
    1627             :          */
    1628     2502752 :         if (cl->nkeys != nkeys)
    1629           0 :             continue;
    1630             : 
    1631     2502752 :         if (!CatalogCacheCompareTuple(cache, nkeys, cl->keys, arguments))
    1632           0 :             continue;
    1633             : 
    1634             :         /*
    1635             :          * We found a matching list.  Move the list to the front of the
    1636             :          * cache's list-of-lists, to speed subsequent searches.  (We do not
    1637             :          * move the members to the fronts of their hashbucket lists, however,
    1638             :          * since there's no point in that unless they are searched for
    1639             :          * individually.)
    1640             :          */
    1641     2502752 :         dlist_move_head(&cache->cc_lists, &cl->cache_elem);
    1642             : 
    1643             :         /* Bump the list's refcount and return it */
    1644     2502752 :         ResourceOwnerEnlarge(CurrentResourceOwner);
    1645     2502752 :         cl->refcount++;
    1646     2502752 :         ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
    1647             : 
    1648             :         CACHE_elog(DEBUG2, "SearchCatCacheList(%s): found list",
    1649             :                    cache->cc_relname);
    1650             : 
    1651             : #ifdef CATCACHE_STATS
    1652             :         cache->cc_lhits++;
    1653             : #endif
    1654             : 
    1655     2502752 :         return cl;
    1656             :     }
    1657             : 
    1658             :     /*
    1659             :      * List was not found in cache, so we have to build it by reading the
    1660             :      * relation.  For each matching tuple found in the relation, use an
    1661             :      * existing cache entry if possible, else build a new one.
    1662             :      *
    1663             :      * We have to bump the member refcounts temporarily to ensure they won't
    1664             :      * get dropped from the cache while loading other members. We use a PG_TRY
    1665             :      * block to ensure we can undo those refcounts if we get an error before
    1666             :      * we finish constructing the CatCList.
    1667             :      */
    1668      239050 :     ctlist = NIL;
    1669             : 
    1670      239050 :     PG_TRY();
    1671             :     {
    1672             :         ScanKeyData cur_skey[CATCACHE_MAXKEYS];
    1673             :         Relation    relation;
    1674             :         SysScanDesc scandesc;
    1675             : 
    1676             :         /*
    1677             :          * Ok, need to make a lookup in the relation, copy the scankey and
    1678             :          * fill out any per-call fields.
    1679             :          */
    1680      239050 :         memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * cache->cc_nkeys);
    1681      239050 :         cur_skey[0].sk_argument = v1;
    1682      239050 :         cur_skey[1].sk_argument = v2;
    1683      239050 :         cur_skey[2].sk_argument = v3;
    1684      239050 :         cur_skey[3].sk_argument = v4;
    1685             : 
    1686      239050 :         relation = table_open(cache->cc_reloid, AccessShareLock);
    1687             : 
    1688      478100 :         scandesc = systable_beginscan(relation,
    1689             :                                       cache->cc_indexoid,
    1690      239050 :                                       IndexScanOK(cache, cur_skey),
    1691             :                                       NULL,
    1692             :                                       nkeys,
    1693             :                                       cur_skey);
    1694             : 
    1695             :         /* The list will be ordered iff we are doing an index scan */
    1696      239050 :         ordered = (scandesc->irel != NULL);
    1697             : 
    1698      989552 :         while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
    1699             :         {
    1700             :             uint32      hashValue;
    1701             :             Index       hashIndex;
    1702      750502 :             bool        found = false;
    1703             :             dlist_head *bucket;
    1704             : 
    1705             :             /*
    1706             :              * See if there's an entry for this tuple already.
    1707             :              */
    1708      750502 :             ct = NULL;
    1709      750502 :             hashValue = CatalogCacheComputeTupleHashValue(cache, cache->cc_nkeys, ntp);
    1710      750502 :             hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
    1711             : 
    1712      750502 :             bucket = &cache->cc_bucket[hashIndex];
    1713     1040514 :             dlist_foreach(iter, bucket)
    1714             :             {
    1715      401960 :                 ct = dlist_container(CatCTup, cache_elem, iter.cur);
    1716             : 
    1717      401960 :                 if (ct->dead || ct->negative)
    1718         686 :                     continue;   /* ignore dead and negative entries */
    1719             : 
    1720      401274 :                 if (ct->hash_value != hashValue)
    1721      273906 :                     continue;   /* quickly skip entry if wrong hash val */
    1722             : 
    1723      127368 :                 if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self)))
    1724           0 :                     continue;   /* not same tuple */
    1725             : 
    1726             :                 /*
    1727             :                  * Found a match, but can't use it if it belongs to another
    1728             :                  * list already
    1729             :                  */
    1730      127368 :                 if (ct->c_list)
    1731       15420 :                     continue;
    1732             : 
    1733      111948 :                 found = true;
    1734      111948 :                 break;          /* A-OK */
    1735             :             }
    1736             : 
    1737      750502 :             if (!found)
    1738             :             {
    1739             :                 /* We didn't find a usable entry, so make a new one */
    1740      638554 :                 ct = CatalogCacheCreateEntry(cache, ntp, arguments,
    1741             :                                              hashValue, hashIndex,
    1742             :                                              false);
    1743             :             }
    1744             : 
    1745             :             /* Careful here: add entry to ctlist, then bump its refcount */
    1746             :             /* This way leaves state correct if lappend runs out of memory */
    1747      750502 :             ctlist = lappend(ctlist, ct);
    1748      750502 :             ct->refcount++;
    1749             :         }
    1750             : 
    1751      239050 :         systable_endscan(scandesc);
    1752             : 
    1753      239050 :         table_close(relation, AccessShareLock);
    1754             : 
    1755             :         /* Make sure the resource owner has room to remember this entry. */
    1756      239050 :         ResourceOwnerEnlarge(CurrentResourceOwner);
    1757             : 
    1758             :         /* Now we can build the CatCList entry. */
    1759      239050 :         oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
    1760      239050 :         nmembers = list_length(ctlist);
    1761             :         cl = (CatCList *)
    1762      239050 :             palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *));
    1763             : 
    1764             :         /* Extract key values */
    1765      239050 :         CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno,
    1766      239050 :                          arguments, cl->keys);
    1767      239050 :         MemoryContextSwitchTo(oldcxt);
    1768             : 
    1769             :         /*
    1770             :          * We are now past the last thing that could trigger an elog before we
    1771             :          * have finished building the CatCList and remembering it in the
    1772             :          * resource owner.  So it's OK to fall out of the PG_TRY, and indeed
    1773             :          * we'd better do so before we start marking the members as belonging
    1774             :          * to the list.
    1775             :          */
    1776             :     }
    1777           0 :     PG_CATCH();
    1778             :     {
    1779           0 :         foreach(ctlist_item, ctlist)
    1780             :         {
    1781           0 :             ct = (CatCTup *) lfirst(ctlist_item);
    1782             :             Assert(ct->c_list == NULL);
    1783             :             Assert(ct->refcount > 0);
    1784           0 :             ct->refcount--;
    1785           0 :             if (
    1786             : #ifndef CATCACHE_FORCE_RELEASE
    1787           0 :                 ct->dead &&
    1788             : #endif
    1789           0 :                 ct->refcount == 0 &&
    1790           0 :                 (ct->c_list == NULL || ct->c_list->refcount == 0))
    1791           0 :                 CatCacheRemoveCTup(cache, ct);
    1792             :         }
    1793             : 
    1794           0 :         PG_RE_THROW();
    1795             :     }
    1796      239050 :     PG_END_TRY();
    1797             : 
    1798      239050 :     cl->cl_magic = CL_MAGIC;
    1799      239050 :     cl->my_cache = cache;
    1800      239050 :     cl->refcount = 0;            /* for the moment */
    1801      239050 :     cl->dead = false;
    1802      239050 :     cl->ordered = ordered;
    1803      239050 :     cl->nkeys = nkeys;
    1804      239050 :     cl->hash_value = lHashValue;
    1805      239050 :     cl->n_members = nmembers;
    1806             : 
    1807      239050 :     i = 0;
    1808      989552 :     foreach(ctlist_item, ctlist)
    1809             :     {
    1810      750502 :         cl->members[i++] = ct = (CatCTup *) lfirst(ctlist_item);
    1811             :         Assert(ct->c_list == NULL);
    1812      750502 :         ct->c_list = cl;
    1813             :         /* release the temporary refcount on the member */
    1814             :         Assert(ct->refcount > 0);
    1815      750502 :         ct->refcount--;
    1816             :         /* mark list dead if any members already dead */
    1817      750502 :         if (ct->dead)
    1818           0 :             cl->dead = true;
    1819             :     }
    1820             :     Assert(i == nmembers);
    1821             : 
    1822      239050 :     dlist_push_head(&cache->cc_lists, &cl->cache_elem);
    1823             : 
    1824             :     /* Finally, bump the list's refcount and return it */
    1825      239050 :     cl->refcount++;
    1826      239050 :     ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
    1827             : 
    1828             :     CACHE_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",
    1829             :                cache->cc_relname, nmembers);
    1830             : 
    1831      239050 :     return cl;
    1832             : }
    1833             : 
    1834             : /*
    1835             :  *  ReleaseCatCacheList
    1836             :  *
    1837             :  *  Decrement the reference count of a catcache list.
    1838             :  */
    1839             : void
    1840     2741766 : ReleaseCatCacheList(CatCList *list)
    1841             : {
    1842     2741766 :     ReleaseCatCacheListWithOwner(list, CurrentResourceOwner);
    1843     2741766 : }
    1844             : 
    1845             : static void
    1846     2741802 : ReleaseCatCacheListWithOwner(CatCList *list, ResourceOwner resowner)
    1847             : {
    1848             :     /* Safety checks to ensure we were handed a cache entry */
    1849             :     Assert(list->cl_magic == CL_MAGIC);
    1850             :     Assert(list->refcount > 0);
    1851     2741802 :     list->refcount--;
    1852     2741802 :     if (resowner)
    1853     2741766 :         ResourceOwnerForgetCatCacheListRef(CurrentResourceOwner, list);
    1854             : 
    1855     2741802 :     if (
    1856             : #ifndef CATCACHE_FORCE_RELEASE
    1857     2741802 :         list->dead &&
    1858             : #endif
    1859           6 :         list->refcount == 0)
    1860           6 :         CatCacheRemoveCList(list->my_cache, list);
    1861     2741802 : }
    1862             : 
    1863             : 
    1864             : /*
    1865             :  * CatalogCacheCreateEntry
    1866             :  *      Create a new CatCTup entry, copying the given HeapTuple and other
    1867             :  *      supplied data into it.  The new entry initially has refcount 0.
    1868             :  */
    1869             : static CatCTup *
    1870     4876522 : CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
    1871             :                         uint32 hashValue, Index hashIndex,
    1872             :                         bool negative)
    1873             : {
    1874             :     CatCTup    *ct;
    1875             :     HeapTuple   dtp;
    1876             :     MemoryContext oldcxt;
    1877             : 
    1878             :     /* negative entries have no tuple associated */
    1879     4876522 :     if (ntp)
    1880             :     {
    1881             :         int         i;
    1882             : 
    1883             :         Assert(!negative);
    1884             : 
    1885             :         /*
    1886             :          * If there are any out-of-line toasted fields in the tuple, expand
    1887             :          * them in-line.  This saves cycles during later use of the catcache
    1888             :          * entry, and also protects us against the possibility of the toast
    1889             :          * tuples being freed before we attempt to fetch them, in case of
    1890             :          * something using a slightly stale catcache entry.
    1891             :          */
    1892     3604740 :         if (HeapTupleHasExternal(ntp))
    1893        2968 :             dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc);
    1894             :         else
    1895     3601772 :             dtp = ntp;
    1896             : 
    1897             :         /* Allocate memory for CatCTup and the cached tuple in one go */
    1898     3604740 :         oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
    1899             : 
    1900     3604740 :         ct = (CatCTup *) palloc(sizeof(CatCTup) +
    1901     3604740 :                                 MAXIMUM_ALIGNOF + dtp->t_len);
    1902     3604740 :         ct->tuple.t_len = dtp->t_len;
    1903     3604740 :         ct->tuple.t_self = dtp->t_self;
    1904     3604740 :         ct->tuple.t_tableOid = dtp->t_tableOid;
    1905     3604740 :         ct->tuple.t_data = (HeapTupleHeader)
    1906     3604740 :             MAXALIGN(((char *) ct) + sizeof(CatCTup));
    1907             :         /* copy tuple contents */
    1908     3604740 :         memcpy((char *) ct->tuple.t_data,
    1909     3604740 :                (const char *) dtp->t_data,
    1910     3604740 :                dtp->t_len);
    1911     3604740 :         MemoryContextSwitchTo(oldcxt);
    1912             : 
    1913     3604740 :         if (dtp != ntp)
    1914        2968 :             heap_freetuple(dtp);
    1915             : 
    1916             :         /* extract keys - they'll point into the tuple if not by-value */
    1917    10568094 :         for (i = 0; i < cache->cc_nkeys; i++)
    1918             :         {
    1919             :             Datum       atp;
    1920             :             bool        isnull;
    1921             : 
    1922     6963354 :             atp = heap_getattr(&ct->tuple,
    1923             :                                cache->cc_keyno[i],
    1924             :                                cache->cc_tupdesc,
    1925             :                                &isnull);
    1926             :             Assert(!isnull);
    1927     6963354 :             ct->keys[i] = atp;
    1928             :         }
    1929             :     }
    1930             :     else
    1931             :     {
    1932             :         Assert(negative);
    1933     1271782 :         oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
    1934     1271782 :         ct = (CatCTup *) palloc(sizeof(CatCTup));
    1935             : 
    1936             :         /*
    1937             :          * Store keys - they'll point into separately allocated memory if not
    1938             :          * by-value.
    1939             :          */
    1940     1271782 :         CatCacheCopyKeys(cache->cc_tupdesc, cache->cc_nkeys, cache->cc_keyno,
    1941     1271782 :                          arguments, ct->keys);
    1942     1271782 :         MemoryContextSwitchTo(oldcxt);
    1943             :     }
    1944             : 
    1945             :     /*
    1946             :      * Finish initializing the CatCTup header, and add it to the cache's
    1947             :      * linked list and counts.
    1948             :      */
    1949     4876522 :     ct->ct_magic = CT_MAGIC;
    1950     4876522 :     ct->my_cache = cache;
    1951     4876522 :     ct->c_list = NULL;
    1952     4876522 :     ct->refcount = 0;            /* for the moment */
    1953     4876522 :     ct->dead = false;
    1954     4876522 :     ct->negative = negative;
    1955     4876522 :     ct->hash_value = hashValue;
    1956             : 
    1957     4876522 :     dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
    1958             : 
    1959     4876522 :     cache->cc_ntup++;
    1960     4876522 :     CacheHdr->ch_ntup++;
    1961             : 
    1962             :     /*
    1963             :      * If the hash table has become too full, enlarge the buckets array. Quite
    1964             :      * arbitrarily, we enlarge when fill factor > 2.
    1965             :      */
    1966     4876522 :     if (cache->cc_ntup > cache->cc_nbuckets * 2)
    1967        4294 :         RehashCatCache(cache);
    1968             : 
    1969     4876522 :     return ct;
    1970             : }
    1971             : 
    1972             : /*
    1973             :  * Helper routine that frees keys stored in the keys array.
    1974             :  */
    1975             : static void
    1976      427948 : CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos, Datum *keys)
    1977             : {
    1978             :     int         i;
    1979             : 
    1980     1306038 :     for (i = 0; i < nkeys; i++)
    1981             :     {
    1982      878090 :         int         attnum = attnos[i];
    1983             :         Form_pg_attribute att;
    1984             : 
    1985             :         /* system attribute are not supported in caches */
    1986             :         Assert(attnum > 0);
    1987             : 
    1988      878090 :         att = TupleDescAttr(tupdesc, attnum - 1);
    1989             : 
    1990      878090 :         if (!att->attbyval)
    1991      383456 :             pfree(DatumGetPointer(keys[i]));
    1992             :     }
    1993      427948 : }
    1994             : 
    1995             : /*
    1996             :  * Helper routine that copies the keys in the srckeys array into the dstkeys
    1997             :  * one, guaranteeing that the datums are fully allocated in the current memory
    1998             :  * context.
    1999             :  */
    2000             : static void
    2001     1510832 : CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
    2002             :                  Datum *srckeys, Datum *dstkeys)
    2003             : {
    2004             :     int         i;
    2005             : 
    2006             :     /*
    2007             :      * XXX: memory and lookup performance could possibly be improved by
    2008             :      * storing all keys in one allocation.
    2009             :      */
    2010             : 
    2011     4713630 :     for (i = 0; i < nkeys; i++)
    2012             :     {
    2013     3202798 :         int         attnum = attnos[i];
    2014     3202798 :         Form_pg_attribute att = TupleDescAttr(tupdesc, attnum - 1);
    2015     3202798 :         Datum       src = srckeys[i];
    2016             :         NameData    srcname;
    2017             : 
    2018             :         /*
    2019             :          * Must be careful in case the caller passed a C string where a NAME
    2020             :          * is wanted: convert the given argument to a correctly padded NAME.
    2021             :          * Otherwise the memcpy() done by datumCopy() could fall off the end
    2022             :          * of memory.
    2023             :          */
    2024     3202798 :         if (att->atttypid == NAMEOID)
    2025             :         {
    2026      643010 :             namestrcpy(&srcname, DatumGetCString(src));
    2027      643010 :             src = NameGetDatum(&srcname);
    2028             :         }
    2029             : 
    2030     3202798 :         dstkeys[i] = datumCopy(src,
    2031     3202798 :                                att->attbyval,
    2032     3202798 :                                att->attlen);
    2033             :     }
    2034     1510832 : }
    2035             : 
    2036             : /*
    2037             :  *  PrepareToInvalidateCacheTuple()
    2038             :  *
    2039             :  *  This is part of a rather subtle chain of events, so pay attention:
    2040             :  *
    2041             :  *  When a tuple is inserted or deleted, it cannot be flushed from the
    2042             :  *  catcaches immediately, for reasons explained at the top of cache/inval.c.
    2043             :  *  Instead we have to add entry(s) for the tuple to a list of pending tuple
    2044             :  *  invalidations that will be done at the end of the command or transaction.
    2045             :  *
    2046             :  *  The lists of tuples that need to be flushed are kept by inval.c.  This
    2047             :  *  routine is a helper routine for inval.c.  Given a tuple belonging to
    2048             :  *  the specified relation, find all catcaches it could be in, compute the
    2049             :  *  correct hash value for each such catcache, and call the specified
    2050             :  *  function to record the cache id and hash value in inval.c's lists.
    2051             :  *  SysCacheInvalidate will be called later, if appropriate,
    2052             :  *  using the recorded information.
    2053             :  *
    2054             :  *  For an insert or delete, tuple is the target tuple and newtuple is NULL.
    2055             :  *  For an update, we are called just once, with tuple being the old tuple
    2056             :  *  version and newtuple the new version.  We should make two list entries
    2057             :  *  if the tuple's hash value changed, but only one if it didn't.
    2058             :  *
    2059             :  *  Note that it is irrelevant whether the given tuple is actually loaded
    2060             :  *  into the catcache at the moment.  Even if it's not there now, it might
    2061             :  *  be by the end of the command, or there might be a matching negative entry
    2062             :  *  to flush --- or other backends' caches might have such entries --- so
    2063             :  *  we have to make list entries to flush it later.
    2064             :  *
    2065             :  *  Also note that it's not an error if there are no catcaches for the
    2066             :  *  specified relation.  inval.c doesn't know exactly which rels have
    2067             :  *  catcaches --- it will call this routine for any tuple that's in a
    2068             :  *  system relation.
    2069             :  */
    2070             : void
    2071     2333758 : PrepareToInvalidateCacheTuple(Relation relation,
    2072             :                               HeapTuple tuple,
    2073             :                               HeapTuple newtuple,
    2074             :                               void (*function) (int, uint32, Oid))
    2075             : {
    2076             :     slist_iter  iter;
    2077             :     Oid         reloid;
    2078             : 
    2079             :     CACHE_elog(DEBUG2, "PrepareToInvalidateCacheTuple: called");
    2080             : 
    2081             :     /*
    2082             :      * sanity checks
    2083             :      */
    2084             :     Assert(RelationIsValid(relation));
    2085             :     Assert(HeapTupleIsValid(tuple));
    2086             :     Assert(PointerIsValid(function));
    2087             :     Assert(CacheHdr != NULL);
    2088             : 
    2089     2333758 :     reloid = RelationGetRelid(relation);
    2090             : 
    2091             :     /* ----------------
    2092             :      *  for each cache
    2093             :      *     if the cache contains tuples from the specified relation
    2094             :      *         compute the tuple's hash value(s) in this cache,
    2095             :      *         and call the passed function to register the information.
    2096             :      * ----------------
    2097             :      */
    2098             : 
    2099   196035672 :     slist_foreach(iter, &CacheHdr->ch_caches)
    2100             :     {
    2101   193701914 :         CatCache   *ccp = slist_container(CatCache, cc_next, iter.cur);
    2102             :         uint32      hashvalue;
    2103             :         Oid         dbid;
    2104             : 
    2105   193701914 :         if (ccp->cc_reloid != reloid)
    2106   189446298 :             continue;
    2107             : 
    2108             :         /* Just in case cache hasn't finished initialization yet... */
    2109     4255616 :         if (ccp->cc_tupdesc == NULL)
    2110        7600 :             CatalogCacheInitializeCache(ccp);
    2111             : 
    2112     4255616 :         hashvalue = CatalogCacheComputeTupleHashValue(ccp, ccp->cc_nkeys, tuple);
    2113     4255616 :         dbid = ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId;
    2114             : 
    2115     4255616 :         (*function) (ccp->id, hashvalue, dbid);
    2116             : 
    2117     4255616 :         if (newtuple)
    2118             :         {
    2119             :             uint32      newhashvalue;
    2120             : 
    2121      290298 :             newhashvalue = CatalogCacheComputeTupleHashValue(ccp, ccp->cc_nkeys, newtuple);
    2122             : 
    2123      290298 :             if (newhashvalue != hashvalue)
    2124        5332 :                 (*function) (ccp->id, newhashvalue, dbid);
    2125             :         }
    2126             :     }
    2127     2333758 : }
    2128             : 
    2129             : /* ResourceOwner callbacks */
    2130             : 
    2131             : static void
    2132        9942 : ResOwnerReleaseCatCache(Datum res)
    2133             : {
    2134        9942 :     ReleaseCatCacheWithOwner((HeapTuple) DatumGetPointer(res), NULL);
    2135        9942 : }
    2136             : 
    2137             : static char *
    2138           0 : ResOwnerPrintCatCache(Datum res)
    2139             : {
    2140           0 :     HeapTuple   tuple = (HeapTuple) DatumGetPointer(res);
    2141           0 :     CatCTup    *ct = (CatCTup *) (((char *) tuple) -
    2142             :                                   offsetof(CatCTup, tuple));
    2143             : 
    2144             :     /* Safety check to ensure we were handed a cache entry */
    2145             :     Assert(ct->ct_magic == CT_MAGIC);
    2146             : 
    2147           0 :     return psprintf("cache %s (%d), tuple %u/%u has count %d",
    2148           0 :                     ct->my_cache->cc_relname, ct->my_cache->id,
    2149           0 :                     ItemPointerGetBlockNumber(&(tuple->t_self)),
    2150           0 :                     ItemPointerGetOffsetNumber(&(tuple->t_self)),
    2151             :                     ct->refcount);
    2152             : }
    2153             : 
    2154             : static void
    2155          36 : ResOwnerReleaseCatCacheList(Datum res)
    2156             : {
    2157          36 :     ReleaseCatCacheListWithOwner((CatCList *) DatumGetPointer(res), NULL);
    2158          36 : }
    2159             : 
    2160             : static char *
    2161           0 : ResOwnerPrintCatCacheList(Datum res)
    2162             : {
    2163           0 :     CatCList   *list = (CatCList *) DatumGetPointer(res);
    2164             : 
    2165           0 :     return psprintf("cache %s (%d), list %p has count %d",
    2166           0 :                     list->my_cache->cc_relname, list->my_cache->id,
    2167             :                     list, list->refcount);
    2168             : }

Generated by: LCOV version 1.14