Modify AtEOXact_CatCache and AtEOXact_RelationCache to assume that the
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 8 Aug 2005 19:17:23 +0000 (19:17 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 8 Aug 2005 19:17:23 +0000 (19:17 +0000)
ResourceOwner mechanism already released all reference counts for the
cache entries; therefore, we do not need to scan the catcache or relcache
at transaction end, unless we want to do it as a debugging crosscheck.
Do the crosscheck only in Assert mode.  This is the same logic we had
previously installed in AtEOXact_Buffers to avoid overhead with large
numbers of shared buffers.  I thought it'd be a good idea to do it here
too, in view of Kari Lavikka's recent report showing a real-world case
where AtEOXact_CatCache is taking a significant fraction of runtime.

src/backend/access/transam/xact.c
src/backend/utils/cache/catcache.c
src/backend/utils/cache/relcache.c
src/backend/utils/resowner/resowner.c

index ee33030292f771843c874ff53946c34044dc15b9..5323cefe0e7f36eff733d6652c79f9c8978cb65f 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.211 2005/07/25 22:12:31 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.212 2005/08/08 19:17:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1549,6 +1549,9 @@ CommitTransaction(void)
        /* Check we've released all buffer pins */
        AtEOXact_Buffers(true);
 
+       /* Clean up the relation cache */
+       AtEOXact_RelationCache(true);
+
        /*
         * Make catalog changes visible to all backends.  This has to happen
         * after relcache references are dropped (see comments for
@@ -1576,6 +1579,9 @@ CommitTransaction(void)
                                                 RESOURCE_RELEASE_AFTER_LOCKS,
                                                 true, true);
 
+       /* Check we've released all catcache entries */
+       AtEOXact_CatCache(true);
+
        AtEOXact_GUC(true, false);
        AtEOXact_SPI(true);
        AtEOXact_on_commit_actions(true);
@@ -1768,6 +1774,9 @@ PrepareTransaction(void)
        /* Check we've released all buffer pins */
        AtEOXact_Buffers(true);
 
+       /* Clean up the relation cache */
+       AtEOXact_RelationCache(true);
+
        /* notify and flatfiles don't need a postprepare call */
 
        PostPrepare_Inval();
@@ -1785,6 +1794,9 @@ PrepareTransaction(void)
                                                 RESOURCE_RELEASE_AFTER_LOCKS,
                                                 true, true);
 
+       /* Check we've released all catcache entries */
+       AtEOXact_CatCache(true);
+
        /* PREPARE acts the same as COMMIT as far as GUC is concerned */
        AtEOXact_GUC(true, false);
        AtEOXact_SPI(true);
@@ -1922,6 +1934,7 @@ AbortTransaction(void)
                                                 RESOURCE_RELEASE_BEFORE_LOCKS,
                                                 false, true);
        AtEOXact_Buffers(false);
+       AtEOXact_RelationCache(false);
        AtEOXact_Inval(false);
        smgrDoPendingDeletes(false);
        AtEOXact_MultiXact();
@@ -1931,6 +1944,7 @@ AbortTransaction(void)
        ResourceOwnerRelease(TopTransactionResourceOwner,
                                                 RESOURCE_RELEASE_AFTER_LOCKS,
                                                 false, true);
+       AtEOXact_CatCache(false);
 
        AtEOXact_GUC(false, false);
        AtEOXact_SPI(false);
index abe0aa060c0a4eb7cd47082f66261773dec4f1f7..24b8836f07bb2378017bd139b4cb6687f384ce49 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.121 2005/05/06 17:24:54 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.122 2005/08/08 19:17:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -530,62 +530,43 @@ CreateCacheMemoryContext(void)
  *
  * 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.
- * In the commit case, any nonzero counts indicate failure to call
- * ReleaseSysCache, so we put out a notice for debugging purposes.
+ * As of PostgreSQL 8.1, catcache pins should get released by the
+ * ResourceOwner mechanism.  This routine is just a debugging
+ * cross-check that no pins remain.
  */
 void
 AtEOXact_CatCache(bool isCommit)
 {
-       CatCache   *ccp;
-       Dlelem     *elt,
-                          *nextelt;
-
-       /*
-        * First clean up CatCLists
-        */
-       for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
+#ifdef USE_ASSERT_CHECKING
+       if (assert_enabled)
        {
-               for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt)
-               {
-                       CatCList   *cl = (CatCList *) DLE_VAL(elt);
-
-                       nextelt = DLGetSucc(elt);
+               CatCache   *ccp;
+               Dlelem     *elt;
 
-                       if (cl->refcount != 0)
+               /* Check CatCLists */
+               for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
+               {
+                       for (elt = DLGetHead(&ccp->cc_lists); elt; elt = DLGetSucc(elt))
                        {
-                               if (isCommit)
-                                       PrintCatCacheListLeakWarning(cl);
-                               cl->refcount = 0;
-                       }
+                               CatCList   *cl = (CatCList *) DLE_VAL(elt);
 
-                       /* Clean up any now-deletable dead entries */
-                       if (cl->dead)
-                               CatCacheRemoveCList(ccp, cl);
+                               Assert(cl->cl_magic == CL_MAGIC);
+                               Assert(cl->refcount == 0);
+                               Assert(!cl->dead);
+                       }
                }
-       }
-
-       /*
-        * Now clean up tuples; we can scan them all using the global LRU list
-        */
-       for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = nextelt)
-       {
-               CatCTup    *ct = (CatCTup *) DLE_VAL(elt);
-
-               nextelt = DLGetSucc(elt);
 
-               if (ct->refcount != 0)
+               /* Check individual tuples */
+               for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = DLGetSucc(elt))
                {
-                       if (isCommit)
-                               PrintCatCacheLeakWarning(&ct->tuple);
-                       ct->refcount = 0;
-               }
+                       CatCTup    *ct = (CatCTup *) DLE_VAL(elt);
 
-               /* Clean up any now-deletable dead entries */
-               if (ct->dead)
-                       CatCacheRemoveCTup(ct->my_cache, ct);
+                       Assert(ct->ct_magic == CT_MAGIC);
+                       Assert(ct->refcount == 0);
+                       Assert(!ct->dead);
+               }
        }
+#endif
 }
 
 /*
@@ -1329,11 +1310,9 @@ SearchCatCacheList(CatCache *cache,
        Dlelem     *elt;
        CatCList   *cl;
        CatCTup    *ct;
-       List       *ctlist;
+       List       * volatile ctlist;
        ListCell   *ctlist_item;
        int                     nmembers;
-       Relation        relation;
-       SysScanDesc scandesc;
        bool            ordered;
        HeapTuple       ntp;
        MemoryContext oldcxt;
@@ -1433,98 +1412,131 @@ SearchCatCacheList(CatCache *cache,
         * List was not found in cache, so we have to build it by reading the
         * relation.  For each matching tuple found in the relation, use an
         * existing cache entry if possible, else build a new one.
+        *
+        * We have to bump the member refcounts immediately to ensure they
+        * won't get dropped from the cache while loading other members.
+        * We use a PG_TRY block to ensure we can undo those refcounts if
+        * we get an error before we finish constructing the CatCList.
         */
-       relation = heap_open(cache->cc_reloid, AccessShareLock);
-
-       scandesc = systable_beginscan(relation,
-                                                                 cache->cc_indexoid,
-                                                                 true,
-                                                                 SnapshotNow,
-                                                                 nkeys,
-                                                                 cur_skey);
-
-       /* The list will be ordered iff we are doing an index scan */
-       ordered = (scandesc->irel != NULL);
+       ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
 
        ctlist = NIL;
-       nmembers = 0;
 
-       while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
+       PG_TRY();
        {
-               uint32          hashValue;
-               Index           hashIndex;
+               Relation        relation;
+               SysScanDesc scandesc;
 
-               /*
-                * See if there's an entry for this tuple already.
-                */
-               ct = NULL;
-               hashValue = CatalogCacheComputeTupleHashValue(cache, ntp);
-               hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
+               relation = heap_open(cache->cc_reloid, AccessShareLock);
 
-               for (elt = DLGetHead(&cache->cc_bucket[hashIndex]);
-                        elt;
-                        elt = DLGetSucc(elt))
-               {
-                       ct = (CatCTup *) DLE_VAL(elt);
+               scandesc = systable_beginscan(relation,
+                                                                         cache->cc_indexoid,
+                                                                         true,
+                                                                         SnapshotNow,
+                                                                         nkeys,
+                                                                         cur_skey);
 
-                       if (ct->dead || ct->negative)
-                               continue;               /* ignore dead and negative entries */
+               /* The list will be ordered iff we are doing an index scan */
+               ordered = (scandesc->irel != NULL);
 
-                       if (ct->hash_value != hashValue)
-                               continue;               /* quickly skip entry if wrong hash val */
-
-                       if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self)))
-                               continue;               /* not same tuple */
+               while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
+               {
+                       uint32          hashValue;
+                       Index           hashIndex;
 
                        /*
-                        * Found a match, but can't use it if it belongs to another
-                        * list already
+                        * See if there's an entry for this tuple already.
                         */
-                       if (ct->c_list)
-                               continue;
+                       ct = NULL;
+                       hashValue = CatalogCacheComputeTupleHashValue(cache, ntp);
+                       hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
 
-                       /* Found a match, so move it to front */
-                       DLMoveToFront(&ct->lrulist_elem);
+                       for (elt = DLGetHead(&cache->cc_bucket[hashIndex]);
+                                elt;
+                                elt = DLGetSucc(elt))
+                       {
+                               ct = (CatCTup *) DLE_VAL(elt);
 
-                       break;
-               }
+                               if (ct->dead || ct->negative)
+                                       continue;               /* ignore dead and negative entries */
 
-               if (elt == NULL)
-               {
-                       /* We didn't find a usable entry, so make a new one */
-                       ct = CatalogCacheCreateEntry(cache, ntp,
-                                                                                hashValue, hashIndex,
-                                                                                false);
+                               if (ct->hash_value != hashValue)
+                                       continue;               /* quickly skip entry if wrong hash val */
+
+                               if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self)))
+                                       continue;               /* not same tuple */
+
+                               /*
+                                * Found a match, but can't use it if it belongs to another
+                                * list already
+                                */
+                               if (ct->c_list)
+                                       continue;
+
+                               /* Found a match, so move it to front */
+                               DLMoveToFront(&ct->lrulist_elem);
+
+                               break;
+                       }
+
+                       if (elt == NULL)
+                       {
+                               /* We didn't find a usable entry, so make a new one */
+                               ct = CatalogCacheCreateEntry(cache, ntp,
+                                                                                        hashValue, hashIndex,
+                                                                                        false);
+                       }
+
+                       /* Careful here: add entry to ctlist, then bump its refcount */
+                       ctlist = lappend(ctlist, ct);
+                       ct->refcount++;
                }
 
+               systable_endscan(scandesc);
+
+               heap_close(relation, AccessShareLock);
+
                /*
-                * 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.
+                * Now we can build the CatCList entry.  First we need a dummy tuple
+                * containing the key values...
                 */
-               ct->refcount++;
-               ctlist = lappend(ctlist, ct);
-               nmembers++;
-       }
+               ntp = build_dummy_tuple(cache, nkeys, cur_skey);
+               oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+               nmembers = list_length(ctlist);
+               cl = (CatCList *)
+                       palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *));
+               heap_copytuple_with_tuple(ntp, &cl->tuple);
+               MemoryContextSwitchTo(oldcxt);
+               heap_freetuple(ntp);
 
-       systable_endscan(scandesc);
+               /*
+                * We are now past the last thing that could trigger an elog before
+                * we have finished building the CatCList and remembering it in the
+                * resource owner.  So it's OK to fall out of the PG_TRY, and indeed
+                * we'd better do so before we start marking the members as belonging
+                * to the list.
+                */
 
-       heap_close(relation, AccessShareLock);
+       }
+       PG_CATCH();
+       {
+               foreach(ctlist_item, ctlist)
+               {
+                       ct = (CatCTup *) lfirst(ctlist_item);
+                       Assert(ct->c_list == NULL);
+                       Assert(ct->refcount > 0);
+                       ct->refcount--;
+                       if (ct->refcount == 0
+#ifndef CATCACHE_FORCE_RELEASE
+                               && ct->dead
+#endif
+                               )
+                               CatCacheRemoveCTup(cache, ct);
+               }
 
-       /*
-        * Now we can build the CatCList entry.  First we need a dummy tuple
-        * containing the key values...
-        */
-       ntp = build_dummy_tuple(cache, nkeys, cur_skey);
-       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-       cl = (CatCList *) palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *));
-       heap_copytuple_with_tuple(ntp, &cl->tuple);
-       MemoryContextSwitchTo(oldcxt);
-       heap_freetuple(ntp);
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
 
        cl->cl_magic = CL_MAGIC;
        cl->my_cache = cache;
@@ -1536,29 +1548,27 @@ SearchCatCacheList(CatCache *cache,
        cl->hash_value = lHashValue;
        cl->n_members = nmembers;
 
-       Assert(nmembers == list_length(ctlist));
-       ctlist_item = list_head(ctlist);
-       for (i = 0; i < nmembers; i++)
+       i = 0;
+       foreach(ctlist_item, ctlist)
        {
-               cl->members[i] = ct = (CatCTup *) lfirst(ctlist_item);
+               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_item = lnext(ctlist_item);
        }
+       Assert(i == nmembers);
 
        DLAddHead(&cache->cc_lists, &cl->cache_elem);
 
-       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);
 
+       CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",
+                               cache->cc_relname, nmembers);
+
        return cl;
 }
 
index 7b140228c8c0033ebfdfc1709b17899d104891bd..09f54f7bc913f9feb4660051fe26d43b9243c188 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.225 2005/05/29 04:23:05 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.226 2005/08/08 19:17:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -122,9 +122,9 @@ static long relcacheInvalsReceived = 0L;
 static List *initFileRelationIds = NIL;
 
 /*
- * This flag lets us optimize away work in AtEOSubXact_RelationCache().
+ * This flag lets us optimize away work in AtEO(Sub)Xact_RelationCache().
  */
-static bool need_eosubxact_work = false;
+static bool need_eoxact_work = false;
 
 
 /*
@@ -1816,6 +1816,12 @@ RelationCacheInvalidate(void)
  * In the case of abort, we don't want to try to rebuild any invalidated
  * cache entries (since we can't safely do database accesses).  Therefore
  * we must reset refcnts before handling pending invalidations.
+ *
+ * As of PostgreSQL 8.1, relcache refcnts should get released by the
+ * ResourceOwner mechanism.  This routine just does a debugging
+ * cross-check that no pins remain.  However, we also need to do special
+ * cleanup when the current transaction created any relations or made use
+ * of forced index lists.
  */
 void
 AtEOXact_RelationCache(bool isCommit)
@@ -1823,12 +1829,47 @@ AtEOXact_RelationCache(bool isCommit)
        HASH_SEQ_STATUS status;
        RelIdCacheEnt *idhentry;
 
+       /*
+        * To speed up transaction exit, we want to avoid scanning the relcache
+        * unless there is actually something for this routine to do.  Other
+        * than the debug-only Assert checks, most transactions don't create
+        * any work for us to do here, so we keep a static flag that gets set
+        * if there is anything to do.  (Currently, this means either a relation
+        * is created in the current xact, or an index list is forced.)  For
+        * simplicity, the flag remains set till end of top-level transaction,
+        * even though we could clear it at subtransaction end in some cases.
+        */
+       if (!need_eoxact_work
+#ifdef USE_ASSERT_CHECKING
+               && !assert_enabled
+#endif
+               )
+               return;
+
        hash_seq_init(&status, RelationIdCache);
 
        while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
        {
                Relation        relation = idhentry->reldesc;
-               int                     expected_refcnt;
+
+               /*
+                * The relcache entry's ref count should be back to its normal
+                * not-in-a-transaction state: 0 unless it's nailed in cache.
+                *
+                * In bootstrap mode, this is NOT true, so don't check it ---
+                * the bootstrap code expects relations to stay open across
+                * start/commit transaction calls.  (That seems bogus, but it's
+                * not worth fixing.)
+                */
+#ifdef USE_ASSERT_CHECKING
+               if (!IsBootstrapProcessingMode())
+               {
+                       int                     expected_refcnt;
+
+                       expected_refcnt = relation->rd_isnailed ? 1 : 0;
+                       Assert(relation->rd_refcnt == expected_refcnt);
+               }
+#endif
 
                /*
                 * Is it a relation created in the current transaction?
@@ -1851,40 +1892,6 @@ AtEOXact_RelationCache(bool isCommit)
                        }
                }
 
-               /*
-                * During transaction abort, we must also reset relcache entry ref
-                * counts to their normal not-in-a-transaction state.  A ref count
-                * may be too high because some routine was exited by ereport()
-                * between incrementing and decrementing the count.
-                *
-                * During commit, we should not have to do this, but it's still
-                * useful to check that the counts are correct to catch missed
-                * relcache closes.
-                *
-                * In bootstrap mode, do NOT reset the refcnt nor complain that it's
-                * nonzero --- the bootstrap code expects relations to stay open
-                * across start/commit transaction calls.  (That seems bogus, but
-                * it's not worth fixing.)
-                */
-               expected_refcnt = relation->rd_isnailed ? 1 : 0;
-
-               if (isCommit)
-               {
-                       if (relation->rd_refcnt != expected_refcnt &&
-                               !IsBootstrapProcessingMode())
-                       {
-                               elog(WARNING, "relcache reference leak: relation \"%s\" has refcnt %d instead of %d",
-                                        RelationGetRelationName(relation),
-                                        relation->rd_refcnt, expected_refcnt);
-                               relation->rd_refcnt = expected_refcnt;
-                       }
-               }
-               else
-               {
-                       /* abort case, just reset it quietly */
-                       relation->rd_refcnt = expected_refcnt;
-               }
-
                /*
                 * Flush any temporary index list.
                 */
@@ -1896,8 +1903,8 @@ AtEOXact_RelationCache(bool isCommit)
                }
        }
 
-       /* Once done with the transaction, we can reset need_eosubxact_work */
-       need_eosubxact_work = false;
+       /* Once done with the transaction, we can reset need_eoxact_work */
+       need_eoxact_work = false;
 }
 
 /*
@@ -1915,18 +1922,10 @@ AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
        RelIdCacheEnt *idhentry;
 
        /*
-        * In the majority of subtransactions there is not anything for this
-        * routine to do, and since there are usually many entries in the
-        * relcache, uselessly scanning the cache represents a surprisingly
-        * large fraction of the subtransaction entry/exit overhead.  To avoid
-        * this, we keep a static flag that must be set whenever a condition
-        * is created that requires subtransaction-end work.  (Currently, this
-        * means either a relation is created in the current xact, or an index
-        * list is forced.)  For simplicity, the flag remains set till end of
-        * top-level transaction, even though we could clear it earlier in some
-        * cases.
+        * Skip the relcache scan if nothing to do --- see notes for
+        * AtEOXact_RelationCache.
         */
-       if (!need_eosubxact_work)
+       if (!need_eoxact_work)
                return;
 
        hash_seq_init(&status, RelationIdCache);
@@ -2032,7 +2031,7 @@ RelationBuildLocalRelation(const char *relname,
        rel->rd_createSubid = GetCurrentSubTransactionId();
 
        /* must flag that we have rels created in this transaction */
-       need_eosubxact_work = true;
+       need_eoxact_work = true;
 
        /* is it a temporary relation? */
        rel->rd_istemp = isTempNamespace(relnamespace);
@@ -2626,7 +2625,7 @@ RelationSetIndexList(Relation relation, List *indexIds)
        relation->rd_indexlist = indexIds;
        relation->rd_indexvalid = 2;    /* mark list as forced */
        /* must flag that we have a forced index list */
-       need_eosubxact_work = true;
+       need_eoxact_work = true;
 }
 
 /*
index ad9186d215b8a533f1f0b465401620869e9efa83..786652a757b5af833fb3067480018940afce179d 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.12 2005/04/06 04:34:22 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.13 2005/08/08 19:17:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -213,32 +213,19 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
                        ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
                }
 
-               /* Release relcache references */
-               if (isTopLevel)
-               {
-                       /*
-                        * For a top-level xact we are going to release all
-                        * references, so just do a single relcache call at the top of
-                        * the recursion.
-                        */
-                       if (owner == TopTransactionResourceOwner)
-                               AtEOXact_RelationCache(isCommit);
-                       /* Mark object as owning no relrefs, just for sanity */
-                       owner->nrelrefs = 0;
-               }
-               else
+               /*
+                * Release relcache references.  Note that RelationClose will
+                * remove the relref entry from my list, so I just have to
+                * iterate till there are none.
+                *
+                * As with buffer pins, warn if any are left at commit time,
+                * and release back-to-front for speed.
+                */
+               while (owner->nrelrefs > 0)
                {
-                       /*
-                        * Release relcache refs retail.  Note that RelationClose will
-                        * remove the relref entry from my list, so I just have to
-                        * iterate till there are none.
-                        */
-                       while (owner->nrelrefs > 0)
-                       {
-                               if (isCommit)
-                                       PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
-                               RelationClose(owner->relrefs[owner->nrelrefs - 1]);
-                       }
+                       if (isCommit)
+                               PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
+                       RelationClose(owner->relrefs[owner->nrelrefs - 1]);
                }
        }
        else if (phase == RESOURCE_RELEASE_LOCKS)
@@ -269,40 +256,27 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
        }
        else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
        {
-               /* Release catcache references */
-               if (isTopLevel)
+               /*
+                * Release catcache references.  Note that ReleaseCatCache
+                * will remove the catref entry from my list, so I just have
+                * to iterate till there are none.      Ditto for catcache lists.
+                *
+                * As with buffer pins, warn if any are left at commit time,
+                * and release back-to-front for speed.
+                */
+               while (owner->ncatrefs > 0)
                {
-                       /*
-                        * For a top-level xact we are going to release all
-                        * references, so just do a single catcache call at the top of
-                        * the recursion.
-                        */
-                       if (owner == TopTransactionResourceOwner)
-                               AtEOXact_CatCache(isCommit);
-                       /* Mark object as owning no catrefs, just for sanity */
-                       owner->ncatrefs = 0;
-                       owner->ncatlistrefs = 0;
+                       if (isCommit)
+                               PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
+                       ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
                }
-               else
+               while (owner->ncatlistrefs > 0)
                {
-                       /*
-                        * Release catcache refs retail.  Note that ReleaseCatCache
-                        * will remove the catref entry from my list, so I just have
-                        * to iterate till there are none.      Ditto for catcache lists.
-                        */
-                       while (owner->ncatrefs > 0)
-                       {
-                               if (isCommit)
-                                       PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
-                               ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
-                       }
-                       while (owner->ncatlistrefs > 0)
-                       {
-                               if (isCommit)
-                                       PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
-                               ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
-                       }
+                       if (isCommit)
+                               PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
+                       ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
                }
+
                /* Clean up index scans too */
                ReleaseResources_gist();
                ReleaseResources_hash();