diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index ee33030292f771843c874ff53946c34044dc15b9..5323cefe0e7f36eff733d6652c79f9c8978cb65f 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -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);
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index abe0aa060c0a4eb7cd47082f66261773dec4f1f7..24b8836f07bb2378017bd139b4cb6687f384ce49 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -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;
 }
 
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 7b140228c8c0033ebfdfc1709b17899d104891bd..09f54f7bc913f9feb4660051fe26d43b9243c188 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -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;
 }
 
 /*
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index ad9186d215b8a533f1f0b465401620869e9efa83..786652a757b5af833fb3067480018940afce179d 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -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();