LCOV - code coverage report
Current view: top level - src/backend/utils/cache - catcache.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 508 547 92.9 %
Date: 2022-01-20 08:08:49 Functions: 43 45 95.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14