]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/cache/catcache.c
For some reason access/tupmacs.h has been #including utils/memutils.h,
[postgresql] / src / backend / utils / cache / catcache.c
index 1d10a103bdcf873f083d7327cb1aa96a3498cf97..abe0aa060c0a4eb7cd47082f66261773dec4f1f7 100644 (file)
@@ -3,12 +3,12 @@
  * catcache.c
  *       System catalog cache for tuples matching a key.
  *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.100 2002/11/10 07:25:14 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.121 2005/05/06 17:24:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,7 +21,6 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_type.h"
-#include "catalog/catname.h"
 #include "catalog/indexing.h"
 #include "miscadmin.h"
 #ifdef CATCACHE_STATS
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/catcache.h"
+#include "utils/memutils.h"
 #include "utils/relcache.h"
+#include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
- /* #define CACHEDEBUG */      /* turns DEBUG elogs on */
+/* #define CACHEDEBUG */       /* turns DEBUG elogs on */
 
 /*
  * Constants related to size of the catcache.
 /* Cache management header --- pointer is NULL until created */
 static CatCacheHeader *CacheHdr = NULL;
 
-/*
- *             EQPROC is used in CatalogCacheInitializeCache to find the equality
- *             functions for system types that are used as cache key fields.
- *             See also GetCCHashFunc, which should support the same set of types.
- *
- *             XXX this should be replaced by catalog lookups,
- *             but that seems to pose considerable risk of circularity...
- */
-static const Oid eqproc[] = {
-       F_BOOLEQ, InvalidOid, F_CHAREQ, F_NAMEEQ, InvalidOid,
-       F_INT2EQ, F_INT2VECTOREQ, F_INT4EQ, F_OIDEQ, F_TEXTEQ,
-       F_OIDEQ, InvalidOid, InvalidOid, InvalidOid, F_OIDVECTOREQ
-};
-
-#define EQPROC(SYSTEMTYPEOID)  eqproc[(SYSTEMTYPEOID)-BOOLOID]
-
 
 static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
                                                         ScanKey cur_skey);
@@ -119,24 +104,46 @@ static HeapTuple build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys);
  *                                     internal support functions
  */
 
-static PGFunction
-GetCCHashFunc(Oid keytype)
+/*
+ * Look up the hash and equality functions for system types that are used
+ * as cache key fields.
+ *
+ * XXX this should be replaced by catalog lookups,
+ * but that seems to pose considerable risk of circularity...
+ */
+static void
+GetCCHashEqFuncs(Oid keytype, PGFunction *hashfunc, RegProcedure *eqfunc)
 {
        switch (keytype)
        {
                case BOOLOID:
+                       *hashfunc = hashchar;
+                       *eqfunc = F_BOOLEQ;
+                       break;
                case CHAROID:
-                       return hashchar;
+                       *hashfunc = hashchar;
+                       *eqfunc = F_CHAREQ;
+                       break;
                case NAMEOID:
-                       return hashname;
+                       *hashfunc = hashname;
+                       *eqfunc = F_NAMEEQ;
+                       break;
                case INT2OID:
-                       return hashint2;
+                       *hashfunc = hashint2;
+                       *eqfunc = F_INT2EQ;
+                       break;
                case INT2VECTOROID:
-                       return hashint2vector;
+                       *hashfunc = hashint2vector;
+                       *eqfunc = F_INT2VECTOREQ;
+                       break;
                case INT4OID:
-                       return hashint4;
+                       *hashfunc = hashint4;
+                       *eqfunc = F_INT4EQ;
+                       break;
                case TEXTOID:
-                       return hashvarlena;
+                       *hashfunc = hashtext;
+                       *eqfunc = F_TEXTEQ;
+                       break;
                case OIDOID:
                case REGPROCOID:
                case REGPROCEDUREOID:
@@ -144,13 +151,16 @@ GetCCHashFunc(Oid keytype)
                case REGOPERATOROID:
                case REGCLASSOID:
                case REGTYPEOID:
-                       return hashoid;
+                       *hashfunc = hashoid;
+                       *eqfunc = F_OIDEQ;
+                       break;
                case OIDVECTOROID:
-                       return hashoidvector;
+                       *hashfunc = hashoidvector;
+                       *eqfunc = F_OIDVECTOREQ;
+                       break;
                default:
-                       elog(FATAL, "GetCCHashFunc: type %u unsupported as catcache key",
-                                keytype);
-                       return (PGFunction) NULL;
+                       elog(FATAL, "type %u not supported as catcache key", keytype);
+                       break;
        }
 }
 
@@ -164,7 +174,7 @@ CatalogCacheComputeHashValue(CatCache *cache, int nkeys, ScanKey cur_skey)
 {
        uint32          hashValue = 0;
 
-       CACHE4_elog(DEBUG1, "CatalogCacheComputeHashValue %s %d %p",
+       CACHE4_elog(DEBUG2, "CatalogCacheComputeHashValue %s %d %p",
                                cache->cc_relname,
                                nkeys,
                                cache);
@@ -192,7 +202,7 @@ CatalogCacheComputeHashValue(CatCache *cache, int nkeys, ScanKey cur_skey)
                                                                                           cur_skey[0].sk_argument));
                        break;
                default:
-                       elog(FATAL, "CCComputeHashValue: %d nkeys", nkeys);
+                       elog(FATAL, "wrong number of hash keys: %d", nkeys);
                        break;
        }
 
@@ -257,8 +267,7 @@ CatalogCacheComputeTupleHashValue(CatCache *cache, HeapTuple tuple)
                        Assert(!isNull);
                        break;
                default:
-                       elog(FATAL, "CCComputeTupleHashValue: %d cc_nkeys",
-                                cache->cc_nkeys);
+                       elog(FATAL, "wrong number of hash keys: %d", cache->cc_nkeys);
                        break;
        }
 
@@ -281,16 +290,16 @@ CatCachePrintStats(void)
        long            cc_lsearches = 0;
        long            cc_lhits = 0;
 
-       elog(DEBUG1, "Catcache stats dump: %d/%d tuples in catcaches",
+       elog(DEBUG2, "catcache stats dump: %d/%d tuples in catcaches",
                 CacheHdr->ch_ntup, CacheHdr->ch_maxtup);
 
        for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next)
        {
                if (cache->cc_ntup == 0 && cache->cc_searches == 0)
                        continue;                       /* don't print unused caches */
-               elog(DEBUG1, "Catcache %s/%s: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld discards, %ld lsrch, %ld lhits",
+               elog(DEBUG2, "catcache %s/%u: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld discards, %ld lsrch, %ld lhits",
                         cache->cc_relname,
-                        cache->cc_indname,
+                        cache->cc_indexoid,
                         cache->cc_ntup,
                         cache->cc_searches,
                         cache->cc_hits,
@@ -312,7 +321,7 @@ CatCachePrintStats(void)
                cc_lsearches += cache->cc_lsearches;
                cc_lhits += cache->cc_lhits;
        }
-       elog(DEBUG1, "Catcache totals: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld discards, %ld lsrch, %ld lhits",
+       elog(DEBUG2, "catcache totals: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld discards, %ld lsrch, %ld lhits",
                 CacheHdr->ch_ntup,
                 cc_searches,
                 cc_hits,
@@ -419,7 +428,7 @@ CatalogCacheIdInvalidate(int cacheId,
         * sanity checks
         */
        Assert(ItemPointerIsValid(pointer));
-       CACHE1_elog(DEBUG1, "CatalogCacheIdInvalidate: called");
+       CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: called");
 
        /*
         * inspect caches to find the proper cache
@@ -476,7 +485,7 @@ CatalogCacheIdInvalidate(int cacheId,
                                        ct->dead = true;
                                else
                                        CatCacheRemoveCTup(ccp, ct);
-                               CACHE1_elog(DEBUG1, "CatalogCacheIdInvalidate: invalidated");
+                               CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: invalidated");
 #ifdef CATCACHE_STATS
                                ccp->cc_invals++;
 #endif
@@ -519,7 +528,7 @@ CreateCacheMemoryContext(void)
 /*
  *             AtEOXact_CatCache
  *
- * Clean up catcaches at end of transaction (either commit or abort)
+ * Clean up catcaches at end of main transaction (either commit or abort)
  *
  * We scan the caches to reset refcounts to zero.  This is of course
  * necessary in the abort case, since elog() may have interrupted routines.
@@ -547,8 +556,7 @@ AtEOXact_CatCache(bool isCommit)
                        if (cl->refcount != 0)
                        {
                                if (isCommit)
-                                       elog(WARNING, "Cache reference leak: cache %s (%d), list %p has count %d",
-                                                ccp->cc_relname, ccp->id, cl, cl->refcount);
+                                       PrintCatCacheListLeakWarning(cl);
                                cl->refcount = 0;
                        }
 
@@ -570,10 +578,7 @@ AtEOXact_CatCache(bool isCommit)
                if (ct->refcount != 0)
                {
                        if (isCommit)
-                               elog(WARNING, "Cache reference leak: cache %s (%d), tuple %u has count %d",
-                                        ct->my_cache->cc_relname, ct->my_cache->id,
-                                        HeapTupleGetOid(&ct->tuple),
-                                        ct->refcount);
+                               PrintCatCacheLeakWarning(&ct->tuple);
                        ct->refcount = 0;
                }
 
@@ -641,12 +646,12 @@ ResetCatalogCaches(void)
 {
        CatCache   *cache;
 
-       CACHE1_elog(DEBUG1, "ResetCatalogCaches called");
+       CACHE1_elog(DEBUG2, "ResetCatalogCaches called");
 
        for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next)
                ResetCatalogCache(cache);
 
-       CACHE1_elog(DEBUG1, "end of ResetCatalogCaches call");
+       CACHE1_elog(DEBUG2, "end of ResetCatalogCaches call");
 }
 
 /*
@@ -674,7 +679,7 @@ CatalogCacheFlushRelation(Oid relId)
 {
        CatCache   *cache;
 
-       CACHE2_elog(DEBUG1, "CatalogCacheFlushRelation called for %u", relId);
+       CACHE2_elog(DEBUG2, "CatalogCacheFlushRelation called for %u", relId);
 
        for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next)
        {
@@ -744,7 +749,7 @@ CatalogCacheFlushRelation(Oid relId)
                }
        }
 
-       CACHE1_elog(DEBUG1, "end of CatalogCacheFlushRelation call");
+       CACHE1_elog(DEBUG2, "end of CatalogCacheFlushRelation call");
 }
 
 /*
@@ -756,20 +761,21 @@ CatalogCacheFlushRelation(Oid relId)
  *     structure initialized on the first access.
  */
 #ifdef CACHEDEBUG
-#define InitCatCache_DEBUG1 \
+#define InitCatCache_DEBUG2 \
 do { \
-       elog(DEBUG1, "InitCatCache: rel=%s id=%d nkeys=%d size=%d\n", \
-               cp->cc_relname, cp->id, cp->cc_nkeys, cp->cc_nbuckets); \
+       elog(DEBUG2, "InitCatCache: rel=%u ind=%u id=%d nkeys=%d size=%d", \
+                cp->cc_reloid, cp->cc_indexoid, cp->id, \
+                cp->cc_nkeys, cp->cc_nbuckets); \
 } while(0)
 
 #else
-#define InitCatCache_DEBUG1
+#define InitCatCache_DEBUG2
 #endif
 
 CatCache *
 InitCatCache(int id,
-                        const char *relname,
-                        const char *indname,
+                        Oid reloid,
+                        Oid indexoid,
                         int reloidattr,
                         int nkeys,
                         const int *key)
@@ -816,9 +822,9 @@ InitCatCache(int id,
         * other internal fields.  But don't open the relation yet.
         */
        cp->id = id;
-       cp->cc_relname = relname;
-       cp->cc_indname = indname;
-       cp->cc_reloid = InvalidOid; /* temporary */
+       cp->cc_relname = "(not known yet)";
+       cp->cc_reloid = reloid;
+       cp->cc_indexoid = indexoid;
        cp->cc_relisshared = false; /* temporary */
        cp->cc_tupdesc = (TupleDesc) NULL;
        cp->cc_reloidattr = reloidattr;
@@ -832,7 +838,7 @@ InitCatCache(int id,
         * new cache is initialized as far as we can go for now. print some
         * debugging information, if appropriate.
         */
-       InitCatCache_DEBUG1;
+       InitCatCache_DEBUG2;
 
        /*
         * add completed cache to top of group header's list
@@ -857,17 +863,17 @@ InitCatCache(int id,
  */
 #ifdef CACHEDEBUG
 #define CatalogCacheInitializeCache_DEBUG1 \
-       elog(DEBUG1, "CatalogCacheInitializeCache: cache @%p %s", cache, \
-                cache->cc_relname)
+       elog(DEBUG2, "CatalogCacheInitializeCache: cache @%p rel=%u", cache, \
+                cache->cc_reloid)
 
 #define CatalogCacheInitializeCache_DEBUG2 \
 do { \
                if (cache->cc_key[i] > 0) { \
-                       elog(DEBUG1, "CatalogCacheInitializeCache: load %d/%d w/%d, %u", \
+                       elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d, %u", \
                                i+1, cache->cc_nkeys, cache->cc_key[i], \
                                 tupdesc->attrs[cache->cc_key[i] - 1]->atttypid); \
                } else { \
-                       elog(DEBUG1, "CatalogCacheInitializeCache: load %d/%d w/%d", \
+                       elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d", \
                                i+1, cache->cc_nkeys, cache->cc_key[i]); \
                } \
 } while(0)
@@ -891,7 +897,7 @@ CatalogCacheInitializeCache(CatCache *cache)
         * Open the relation without locking --- we only need the tupdesc,
         * which we assume will never change ...
         */
-       relation = heap_openr(cache->cc_relname, NoLock);
+       relation = heap_open(cache->cc_reloid, NoLock);
        Assert(RelationIsValid(relation));
 
        /*
@@ -908,9 +914,10 @@ CatalogCacheInitializeCache(CatCache *cache)
        tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
 
        /*
-        * get the relation's OID and relisshared flag, too
+        * save the relation's name and relisshared flag, too (cc_relname
+        * is used only for debugging purposes)
         */
-       cache->cc_reloid = RelationGetRelid(relation);
+       cache->cc_relname = pstrdup(RelationGetRelationName(relation));
        cache->cc_relisshared = RelationGetForm(relation)->relisshared;
 
        /*
@@ -920,7 +927,7 @@ CatalogCacheInitializeCache(CatCache *cache)
 
        heap_close(relation, NoLock);
 
-       CACHE3_elog(DEBUG1, "CatalogCacheInitializeCache: %s, %d keys",
+       CACHE3_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys",
                                cache->cc_relname, cache->cc_nkeys);
 
        /*
@@ -929,6 +936,7 @@ CatalogCacheInitializeCache(CatCache *cache)
        for (i = 0; i < cache->cc_nkeys; ++i)
        {
                Oid                     keytype;
+               RegProcedure eqfunc;
 
                CatalogCacheInitializeCache_DEBUG2;
 
@@ -937,28 +945,32 @@ CatalogCacheInitializeCache(CatCache *cache)
                else
                {
                        if (cache->cc_key[i] != ObjectIdAttributeNumber)
-                               elog(FATAL, "CatalogCacheInit: only sys attr supported is OID");
+                               elog(FATAL, "only sys attr supported in caches is OID");
                        keytype = OIDOID;
                }
 
-               cache->cc_hashfunc[i] = GetCCHashFunc(keytype);
+               GetCCHashEqFuncs(keytype,
+                                                &cache->cc_hashfunc[i],
+                                                &eqfunc);
 
                cache->cc_isname[i] = (keytype == NAMEOID);
 
                /*
-                * If GetCCHashFunc liked the type, safe to index into eqproc[]
+                * Do equality-function lookup (we assume this won't need a
+                * catalog lookup for any supported type)
                 */
-               cache->cc_skey[i].sk_procedure = EQPROC(keytype);
-
-               /* Do function lookup */
-               fmgr_info_cxt(cache->cc_skey[i].sk_procedure,
+               fmgr_info_cxt(eqfunc,
                                          &cache->cc_skey[i].sk_func,
                                          CacheMemoryContext);
 
                /* Initialize sk_attno suitably for HeapKeyTest() and heap scans */
                cache->cc_skey[i].sk_attno = cache->cc_key[i];
 
-               CACHE4_elog(DEBUG1, "CatalogCacheInit %s %d %p",
+               /* Fill in sk_strategy as well --- always standard equality */
+               cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber;
+               cache->cc_skey[i].sk_subtype = InvalidOid;
+
+               CACHE4_elog(DEBUG2, "CatalogCacheInit %s %d %p",
                                        cache->cc_relname,
                                        i,
                                        cache);
@@ -989,7 +1001,7 @@ InitCatCachePhase2(CatCache *cache)
        {
                Relation        idesc;
 
-               idesc = index_openr(cache->cc_indname);
+               idesc = index_open(cache->cc_indexoid);
                index_close(idesc);
        }
 }
@@ -1148,9 +1160,11 @@ SearchCatCache(CatCache *cache,
                 */
                if (!ct->negative)
                {
+                       ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
                        ct->refcount++;
+                       ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
 
-                       CACHE3_elog(DEBUG1, "SearchCatCache(%s): found in bucket %d",
+                       CACHE3_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",
                                                cache->cc_relname, hashIndex);
 
 #ifdef CATCACHE_STATS
@@ -1161,7 +1175,7 @@ SearchCatCache(CatCache *cache,
                }
                else
                {
-                       CACHE3_elog(DEBUG1, "SearchCatCache(%s): found neg entry in bucket %d",
+                       CACHE3_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d",
                                                cache->cc_relname, hashIndex);
 
 #ifdef CATCACHE_STATS
@@ -1190,7 +1204,7 @@ SearchCatCache(CatCache *cache,
        relation = heap_open(cache->cc_reloid, AccessShareLock);
 
        scandesc = systable_beginscan(relation,
-                                                                 cache->cc_indname,
+                                                                 cache->cc_indexoid,
                                                                  IndexScanOK(cache, cur_skey),
                                                                  SnapshotNow,
                                                                  cache->cc_nkeys,
@@ -1203,6 +1217,10 @@ SearchCatCache(CatCache *cache,
                ct = CatalogCacheCreateEntry(cache, ntp,
                                                                         hashValue, hashIndex,
                                                                         false);
+               /* immediately set the refcount to 1 */
+               ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
+               ct->refcount++;
+               ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
                break;                                  /* assume only one match */
        }
 
@@ -1214,32 +1232,39 @@ SearchCatCache(CatCache *cache,
         * If tuple was not found, we need to build a negative cache entry
         * containing a fake tuple.  The fake tuple has the correct key
         * columns, but nulls everywhere else.
+        *
+        * In bootstrap mode, we don't build negative entries, because the
+        * cache invalidation mechanism isn't alive and can't clear them
+        * if the tuple gets created later.  (Bootstrap doesn't do UPDATEs,
+        * so it doesn't need cache inval for that.)
         */
        if (ct == NULL)
        {
+               if (IsBootstrapProcessingMode())
+                       return NULL;
+
                ntp = build_dummy_tuple(cache, cache->cc_nkeys, cur_skey);
                ct = CatalogCacheCreateEntry(cache, ntp,
                                                                         hashValue, hashIndex,
                                                                         true);
                heap_freetuple(ntp);
 
-               CACHE4_elog(DEBUG1, "SearchCatCache(%s): Contains %d/%d tuples",
+               CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
                                        cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
-               CACHE3_elog(DEBUG1, "SearchCatCache(%s): put neg entry in bucket %d",
+               CACHE3_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d",
                                        cache->cc_relname, hashIndex);
 
                /*
-                * We are not returning the new entry to the caller, so reset its
-                * refcount.
+                * We are not returning the negative entry to the caller, so leave
+                * its refcount zero.
                 */
-               ct->refcount = 0;               /* negative entries never have refs */
 
                return NULL;
        }
 
-       CACHE4_elog(DEBUG1, "SearchCatCache(%s): Contains %d/%d tuples",
+       CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
                                cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
-       CACHE3_elog(DEBUG1, "SearchCatCache(%s): put in bucket %d",
+       CACHE3_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d",
                                cache->cc_relname, hashIndex);
 
 #ifdef CATCACHE_STATS
@@ -1271,6 +1296,7 @@ ReleaseCatCache(HeapTuple tuple)
        Assert(ct->refcount > 0);
 
        ct->refcount--;
+       ResourceOwnerForgetCatCacheRef(CurrentResourceOwner, &ct->tuple);
 
        if (ct->refcount == 0
 #ifndef CATCACHE_FORCE_RELEASE
@@ -1304,6 +1330,7 @@ SearchCatCacheList(CatCache *cache,
        CatCList   *cl;
        CatCTup    *ct;
        List       *ctlist;
+       ListCell   *ctlist_item;
        int                     nmembers;
        Relation        relation;
        SysScanDesc scandesc;
@@ -1377,7 +1404,10 @@ SearchCatCacheList(CatCache *cache,
                 * do not move the members to the fronts of their hashbucket
                 * lists, however, since there's no point in that unless they are
                 * searched for individually.)  Also bump the members' refcounts.
+                * (member refcounts are NOT registered separately with the
+                * resource owner.)
                 */
+               ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
                for (i = 0; i < cl->n_members; i++)
                {
                        cl->members[i]->refcount++;
@@ -1387,8 +1417,9 @@ SearchCatCacheList(CatCache *cache,
 
                /* Bump the list's refcount and return it */
                cl->refcount++;
+               ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
 
-               CACHE2_elog(DEBUG1, "SearchCatCacheList(%s): found list",
+               CACHE2_elog(DEBUG2, "SearchCatCacheList(%s): found list",
                                        cache->cc_relname);
 
 #ifdef CATCACHE_STATS
@@ -1406,7 +1437,7 @@ SearchCatCacheList(CatCache *cache,
        relation = heap_open(cache->cc_reloid, AccessShareLock);
 
        scandesc = systable_beginscan(relation,
-                                                                 cache->cc_indname,
+                                                                 cache->cc_indexoid,
                                                                  true,
                                                                  SnapshotNow,
                                                                  nkeys,
@@ -1452,9 +1483,7 @@ SearchCatCacheList(CatCache *cache,
                        if (ct->c_list)
                                continue;
 
-                       /* Found a match, so bump its refcount and move to front */
-                       ct->refcount++;
-
+                       /* Found a match, so move it to front */
                        DLMoveToFront(&ct->lrulist_elem);
 
                        break;
@@ -1468,7 +1497,17 @@ SearchCatCacheList(CatCache *cache,
                                                                                 false);
                }
 
-               ctlist = lcons(ct, ctlist);
+               /*
+                * We have to bump the member refcounts immediately to ensure they
+                * won't get dropped from the cache while loading other members.
+                * If we get an error before we finish constructing the CatCList
+                * then we will leak those reference counts.  This is annoying but
+                * it has no real consequence beyond possibly generating some
+                * warning messages at the next transaction commit, so it's not
+                * worth fixing.
+                */
+               ct->refcount++;
+               ctlist = lappend(ctlist, ct);
                nmembers++;
        }
 
@@ -1489,30 +1528,37 @@ SearchCatCacheList(CatCache *cache,
 
        cl->cl_magic = CL_MAGIC;
        cl->my_cache = cache;
-       DLInitElem(&cl->cache_elem, (void *) cl);
-       cl->refcount = 1;                       /* count this first reference */
+       DLInitElem(&cl->cache_elem, cl);
+       cl->refcount = 0;                       /* for the moment */
        cl->dead = false;
        cl->ordered = ordered;
        cl->nkeys = nkeys;
        cl->hash_value = lHashValue;
        cl->n_members = nmembers;
-       /* The list is backwards because we built it with lcons */
-       for (i = nmembers; --i >= 0;)
+
+       Assert(nmembers == list_length(ctlist));
+       ctlist_item = list_head(ctlist);
+       for (i = 0; i < nmembers; i++)
        {
-               cl->members[i] = ct = (CatCTup *) lfirst(ctlist);
+               cl->members[i] = ct = (CatCTup *) lfirst(ctlist_item);
                Assert(ct->c_list == NULL);
                ct->c_list = cl;
                /* mark list dead if any members already dead */
                if (ct->dead)
                        cl->dead = true;
-               ctlist = lnext(ctlist);
+               ctlist_item = lnext(ctlist_item);
        }
 
        DLAddHead(&cache->cc_lists, &cl->cache_elem);
 
-       CACHE3_elog(DEBUG1, "SearchCatCacheList(%s): made list of %d members",
+       CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",
                                cache->cc_relname, nmembers);
 
+       /* Finally, bump the list's refcount and return it */
+       ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
+       cl->refcount++;
+       ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
+
        return cl;
 }
 
@@ -1544,6 +1590,7 @@ ReleaseCatCacheList(CatCList *list)
        }
 
        list->refcount--;
+       ResourceOwnerForgetCatCacheListRef(CurrentResourceOwner, list);
 
        if (list->refcount == 0
 #ifndef CATCACHE_FORCE_RELEASE
@@ -1557,7 +1604,7 @@ ReleaseCatCacheList(CatCList *list)
 /*
  * CatalogCacheCreateEntry
  *             Create a new CatCTup entry, copying the given HeapTuple and other
- *             supplied data into it.  The new entry is given refcount 1.
+ *             supplied data into it.  The new entry initially has refcount 0.
  */
 static CatCTup *
 CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
@@ -1584,7 +1631,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
        DLInitElem(&ct->lrulist_elem, (void *) ct);
        DLInitElem(&ct->cache_elem, (void *) ct);
        ct->c_list = NULL;
-       ct->refcount = 1;                       /* count this first reference */
+       ct->refcount = 0;                       /* for the moment */
        ct->dead = false;
        ct->negative = negative;
        ct->hash_value = hashValue;
@@ -1597,8 +1644,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
 
        /*
         * If we've exceeded the desired size of the caches, try to throw away
-        * the least recently used entry.  NB: the newly-built entry cannot
-        * get thrown away here, because it has positive refcount.
+        * the least recently used entry.  NB: be careful not to throw away
+        * the newly-built entry...
         */
        if (CacheHdr->ch_ntup > CacheHdr->ch_maxtup)
        {
@@ -1611,9 +1658,9 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
 
                        prevelt = DLGetPred(elt);
 
-                       if (oldct->refcount == 0)
+                       if (oldct->refcount == 0 && oldct != ct)
                        {
-                               CACHE2_elog(DEBUG1, "CatCacheCreateEntry(%s): Overflow, LRU removal",
+                               CACHE2_elog(DEBUG2, "CatCacheCreateEntry(%s): Overflow, LRU removal",
                                                        cache->cc_relname);
 #ifdef CATCACHE_STATS
                                oldct->my_cache->cc_discards++;
@@ -1731,7 +1778,7 @@ PrepareToInvalidateCacheTuple(Relation relation,
        CatCache   *ccp;
        Oid                     reloid;
 
-       CACHE1_elog(DEBUG1, "PrepareToInvalidateCacheTuple: called");
+       CACHE1_elog(DEBUG2, "PrepareToInvalidateCacheTuple: called");
 
        /*
         * sanity checks
@@ -1766,3 +1813,32 @@ PrepareToInvalidateCacheTuple(Relation relation,
                                         ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId);
        }
 }
+
+
+/*
+ * Subroutines for warning about reference leaks.  These are exported so
+ * that resowner.c can call them.
+ */
+void
+PrintCatCacheLeakWarning(HeapTuple tuple)
+{
+       CatCTup    *ct = (CatCTup *) (((char *) tuple) -
+                                                                 offsetof(CatCTup, tuple));
+
+       /* Safety check to ensure we were handed a cache entry */
+       Assert(ct->ct_magic == CT_MAGIC);
+
+       elog(WARNING, "cache reference leak: cache %s (%d), tuple %u/%u has count %d",
+                ct->my_cache->cc_relname, ct->my_cache->id,
+                ItemPointerGetBlockNumber(&(tuple->t_self)),
+                ItemPointerGetOffsetNumber(&(tuple->t_self)),
+                ct->refcount);
+}
+
+void
+PrintCatCacheListLeakWarning(CatCList *list)
+{
+       elog(WARNING, "cache reference leak: cache %s (%d), list %p has count %d",
+                list->my_cache->cc_relname, list->my_cache->id,
+                list, list->refcount);
+}