From 6ea8f495737f35f9d34a6c0d96b1174117066702 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 6 Oct 2006 18:23:35 +0000
Subject: [PATCH] Fix SysCacheGetAttr() to handle the case where the specified
 syscache has not been initialized yet.  This can happen because there are
 code paths that call SysCacheGetAttr() on a tuple originally fetched from a
 different syscache (hopefully on the same catalog) than the one specified in
 the call.  It doesn't seem useful or robust to try to prevent that from
 happening, so just improve the function to cope instead.  Per bug#2678 from
 Jeff Trout.  The specific example shown by Jeff is new in 8.1, but to be on
 the safe side I'm backpatching 8.0 as well.  We could patch 7.x similarly but
 I think that's probably overkill, given the lack of evidence of old bugs of
 this ilk.

---
 src/backend/utils/cache/catcache.c | 18 ++++++++++--------
 src/backend/utils/cache/syscache.c | 24 +++++++++++++++---------
 src/include/utils/catcache.h       |  4 ++--
 3 files changed, 27 insertions(+), 19 deletions(-)

diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 0d1ae15b39c..55b9d5baf07 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.133 2006/10/04 00:30:00 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.134 2006/10/06 18:23:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -976,7 +976,7 @@ CatalogCacheInitializeCache(CatCache *cache)
 		cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber;
 		cache->cc_skey[i].sk_subtype = InvalidOid;
 
-		CACHE4_elog(DEBUG2, "CatalogCacheInit %s %d %p",
+		CACHE4_elog(DEBUG2, "CatalogCacheInitializeCache %s %d %p",
 					cache->cc_relname,
 					i,
 					cache);
@@ -991,18 +991,20 @@ CatalogCacheInitializeCache(CatCache *cache)
 /*
  * InitCatCachePhase2 -- external interface for CatalogCacheInitializeCache
  *
- * The only reason to call this routine is to ensure that the relcache
- * has created entries for all the catalogs and indexes referenced by
- * catcaches.  Therefore, open the index too.  An exception is the indexes
- * on pg_am, which we don't use (cf. IndexScanOK).
+ * One reason to call this routine is to ensure that the relcache has
+ * created entries for all the catalogs and indexes referenced by catcaches.
+ * Therefore, provide an option to open the index as well as fixing the
+ * cache itself.  An exception is the indexes on pg_am, which we don't use
+ * (cf. IndexScanOK).
  */
 void
-InitCatCachePhase2(CatCache *cache)
+InitCatCachePhase2(CatCache *cache, bool touch_index)
 {
 	if (cache->cc_tupdesc == NULL)
 		CatalogCacheInitializeCache(cache);
 
-	if (cache->id != AMOID &&
+	if (touch_index &&
+		cache->id != AMOID &&
 		cache->id != AMNAME)
 	{
 		Relation	idesc;
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 79aba4ebc82..e19fba05840 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.107 2006/10/04 00:30:00 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.108 2006/10/06 18:23:35 tgl Exp $
  *
  * NOTES
  *	  These routines allow the parser/planner/executor to perform
@@ -576,7 +576,7 @@ InitCatalogCachePhase2(void)
 	Assert(CacheInitialized);
 
 	for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
-		InitCatCachePhase2(SysCache[cacheId]);
+		InitCatCachePhase2(SysCache[cacheId], true);
 }
 
 
@@ -773,6 +773,9 @@ SearchSysCacheExistsAttName(Oid relid, const char *attname)
  * As with heap_getattr(), if the attribute is of a pass-by-reference type
  * then a pointer into the tuple data area is returned --- the caller must
  * not modify or pfree the datum!
+ *
+ * Note: it is legal to use SysCacheGetAttr() with a cacheId referencing
+ * a different cache for the same catalog the tuple was fetched from.
  */
 Datum
 SysCacheGetAttr(int cacheId, HeapTuple tup,
@@ -781,15 +784,18 @@ SysCacheGetAttr(int cacheId, HeapTuple tup,
 {
 	/*
 	 * We just need to get the TupleDesc out of the cache entry, and then we
-	 * can apply heap_getattr().  We expect that the cache control data is
-	 * currently valid --- if the caller recently fetched the tuple, then it
-	 * should be.
+	 * can apply heap_getattr().  Normally the cache control data is already
+	 * valid (because the caller recently fetched the tuple via this same
+	 * cache), but there are cases where we have to initialize the cache here.
 	 */
-	if (cacheId < 0 || cacheId >= SysCacheSize)
+	if (cacheId < 0 || cacheId >= SysCacheSize ||
+		!PointerIsValid(SysCache[cacheId]))
 		elog(ERROR, "invalid cache id: %d", cacheId);
-	if (!PointerIsValid(SysCache[cacheId]) ||
-		!PointerIsValid(SysCache[cacheId]->cc_tupdesc))
-		elog(ERROR, "missing cache data for cache id %d", cacheId);
+	if (!PointerIsValid(SysCache[cacheId]->cc_tupdesc))
+	{
+		InitCatCachePhase2(SysCache[cacheId], false);
+		Assert(PointerIsValid(SysCache[cacheId]->cc_tupdesc));
+	}
 
 	return heap_getattr(tup, attributeNumber,
 						SysCache[cacheId]->cc_tupdesc,
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index cbd6d9945a4..c24133ab5e9 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/catcache.h,v 1.62 2006/10/04 00:30:10 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/catcache.h,v 1.63 2006/10/06 18:23:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -166,7 +166,7 @@ extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
 			 int reloidattr,
 			 int nkeys, const int *key,
 			 int nbuckets);
-extern void InitCatCachePhase2(CatCache *cache);
+extern void InitCatCachePhase2(CatCache *cache, bool touch_index);
 
 extern HeapTuple SearchCatCache(CatCache *cache,
 			   Datum v1, Datum v2,
-- 
GitLab