From 1b42210b5506a11ffd4d8a87a24f44a75d47c652 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Fri, 16 Dec 2016 16:44:40 +0900
Subject: [PATCH 2/2] Cleanup negative cache of pg_class when dropping a schema

This feature inturn is triggered by catcache invalidation. This patch
provides a syscache invalidation callback to flush negative cache
entries corresponding to the invalidated object.
---
 src/backend/utils/cache/catcache.c |  42 ++++++
 src/backend/utils/cache/inval.c    |   8 +-
 src/backend/utils/cache/syscache.c | 301 ++++++++++++++++++++++++++++---------
 src/include/utils/catcache.h       |   3 +
 4 files changed, 284 insertions(+), 70 deletions(-)

diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index c1d9d2f..094bc60 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1424,6 +1424,48 @@ GetCatCacheHashValue(CatCache *cache,
 	return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);
 }
 
+/*
+ * CollectOIDsForHashValue
+ *
+ * Collect OIDs corresnpond to a hash value. attnum is the column to retrieve
+ * the OIDs.
+ */
+List *
+CollectOIDsForHashValue(CatCache *cache, uint32 hashValue, int attnum)
+{
+	Index		 hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
+	dlist_head	*bucket = &cache->cc_bucket[hashIndex];
+	dlist_iter	 iter;
+	List *ret = NIL;
+
+	/* Nothing to return before initialized */
+	if (cache->cc_tupdesc == NULL)
+		return ret;
+
+	/* Currently only OID key is supported */
+	Assert(attnum <= cache->cc_tupdesc->natts);
+	Assert(attnum < 0 ? attnum == ObjectIdAttributeNumber :
+		   cache->cc_tupdesc->attrs[attnum]->atttypid == OIDOID);
+
+	dlist_foreach(iter, bucket)
+	{
+		CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+		bool	isNull;
+		Datum	oid;
+
+		if (ct->dead)
+			continue;			/* ignore dead entries */
+
+		if (ct->hash_value != hashValue)
+			continue;			/* quickly skip entry if wrong hash val */
+
+		oid = heap_getattr(&ct->tuple, attnum, cache->cc_tupdesc, &isNull);
+		if (!isNull)
+			ret = lappend_oid(ret, DatumGetObjectId(oid));
+	}
+
+	return ret;
+}
 
 /*
  *	SearchCatCacheList
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index 5803518..0290974 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -543,9 +543,13 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
 		{
 			InvalidateCatalogSnapshot();
 
-			CatalogCacheIdInvalidate(msg->cc.id, msg->cc.hashValue);
-
+			/*
+			 * Call the callbacks first so that the callbacks can access the
+			 * entries corresponding to the hashValue.
+			 */
 			CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue);
+
+			CatalogCacheIdInvalidate(msg->cc.id, msg->cc.hashValue);
 		}
 	}
 	else if (msg->id == SHAREDINVALCATALOG_ID)
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index bc38113..f1fc5f3 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -107,6 +107,16 @@
 */
 
 /*
+ *	struct for flushing negative cache by syscache invalidation
+ */
+typedef struct SysCacheCBParam_T
+{
+	int	trig_attnum;
+	int	target_cacheid;
+	ScanKeyData skey;
+} SysCacheCBParam;
+
+/*
  *		struct cachedesc: information defining a single syscache
  */
 struct cachedesc
@@ -119,6 +129,14 @@ struct cachedesc
 
 	/* relcache invalidation stuff */
 	AttrNumber	relattrnum;		/* attr number of reloid, 0 if nothing to do */
+
+	/* catcache invalidation stuff */
+	int			trig_cacheid;	/* cache id of triggering syscache: -1 means
+								 * no triggering cache */
+	int16		trig_attnum;	/* key column in triggering cache. Must be an
+								 * OID */
+	int16		target_attnum;	/* corresponding column in this cache. Must be
+								 * an OID*/
 };
 
 static const struct cachedesc cacheinfo[] = {
@@ -132,7 +150,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodRelationId,	/* AMNAME */
 		AmNameIndexId,
@@ -144,7 +163,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodRelationId,	/* AMOID */
 		AmOidIndexId,
@@ -156,7 +176,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodOperatorRelationId,	/* AMOPOPID */
 		AccessMethodOperatorIndexId,
@@ -168,7 +189,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodOperatorRelationId,	/* AMOPSTRATEGY */
 		AccessMethodStrategyIndexId,
@@ -180,7 +202,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amop_amopstrategy
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodProcedureRelationId,	/* AMPROCNUM */
 		AccessMethodProcedureIndexId,
@@ -192,7 +215,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amproc_amprocnum
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AttributeRelationId,		/* ATTNAME */
 		AttributeRelidNameIndexId,
@@ -204,7 +228,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		Anum_pg_attribute_attrelid
+		Anum_pg_attribute_attrelid,
+		-1, 0, 0
 	},
 	{AttributeRelationId,		/* ATTNUM */
 		AttributeRelidNumIndexId,
@@ -216,7 +241,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		Anum_pg_attribute_attrelid
+		Anum_pg_attribute_attrelid,
+		-1, 0, 0
 	},
 	{AuthMemRelationId,			/* AUTHMEMMEMROLE */
 		AuthMemMemRoleIndexId,
@@ -228,7 +254,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AuthMemRelationId,			/* AUTHMEMROLEMEM */
 		AuthMemRoleMemIndexId,
@@ -240,7 +267,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AuthIdRelationId,			/* AUTHNAME */
 		AuthIdRolnameIndexId,
@@ -252,7 +280,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AuthIdRelationId,			/* AUTHOID */
 		AuthIdOidIndexId,
@@ -264,7 +293,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{CastRelationId,			/* CASTSOURCETARGET */
 		CastSourceTargetIndexId,
@@ -276,7 +306,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		256,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorClassRelationId,	/* CLAAMNAMENSP */
 		OpclassAmNameNspIndexId,
@@ -288,7 +319,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorClassRelationId,	/* CLAOID */
 		OpclassOidIndexId,
@@ -300,7 +332,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{CollationRelationId,		/* COLLNAMEENCNSP */
 		CollationNameEncNspIndexId,
@@ -312,7 +345,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{CollationRelationId,		/* COLLOID */
 		CollationOidIndexId,
@@ -324,7 +358,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConversionRelationId,		/* CONDEFAULT */
 		ConversionDefaultIndexId,
@@ -336,7 +371,8 @@ static const struct cachedesc cacheinfo[] = {
 			ObjectIdAttributeNumber,
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConversionRelationId,		/* CONNAMENSP */
 		ConversionNameNspIndexId,
@@ -348,7 +384,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConstraintRelationId,		/* CONSTROID */
 		ConstraintOidIndexId,
@@ -360,7 +397,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConversionRelationId,		/* CONVOID */
 		ConversionOidIndexId,
@@ -372,7 +410,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{DatabaseRelationId,		/* DATABASEOID */
 		DatabaseOidIndexId,
@@ -384,7 +423,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{DefaultAclRelationId,		/* DEFACLROLENSPOBJ */
 		DefaultAclRoleNspObjIndexId,
@@ -396,7 +436,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EnumRelationId,			/* ENUMOID */
 		EnumOidIndexId,
@@ -408,7 +449,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EnumRelationId,			/* ENUMTYPOIDNAME */
 		EnumTypIdLabelIndexId,
@@ -420,7 +462,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EventTriggerRelationId,	/* EVENTTRIGGERNAME */
 		EventTriggerNameIndexId,
@@ -432,7 +475,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EventTriggerRelationId,	/* EVENTTRIGGEROID */
 		EventTriggerOidIndexId,
@@ -444,7 +488,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignDataWrapperRelationId,		/* FOREIGNDATAWRAPPERNAME */
 		ForeignDataWrapperNameIndexId,
@@ -456,7 +501,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignDataWrapperRelationId,		/* FOREIGNDATAWRAPPEROID */
 		ForeignDataWrapperOidIndexId,
@@ -468,7 +514,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignServerRelationId,	/* FOREIGNSERVERNAME */
 		ForeignServerNameIndexId,
@@ -480,7 +527,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignServerRelationId,	/* FOREIGNSERVEROID */
 		ForeignServerOidIndexId,
@@ -492,7 +540,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignTableRelationId,	/* FOREIGNTABLEREL */
 		ForeignTableRelidIndexId,
@@ -504,7 +553,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{IndexRelationId,			/* INDEXRELID */
 		IndexRelidIndexId,
@@ -516,7 +566,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{LanguageRelationId,		/* LANGNAME */
 		LanguageNameIndexId,
@@ -528,7 +579,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{LanguageRelationId,		/* LANGOID */
 		LanguageOidIndexId,
@@ -540,7 +592,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{NamespaceRelationId,		/* NAMESPACENAME */
 		NamespaceNameIndexId,
@@ -552,7 +605,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{NamespaceRelationId,		/* NAMESPACEOID */
 		NamespaceOidIndexId,
@@ -564,7 +618,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorRelationId,		/* OPERNAMENSP */
 		OperatorNameNspIndexId,
@@ -576,7 +631,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_operator_oprnamespace
 		},
 		256,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorRelationId,		/* OPEROID */
 		OperatorOidIndexId,
@@ -588,7 +644,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorFamilyRelationId,	/* OPFAMILYAMNAMENSP */
 		OpfamilyAmNameNspIndexId,
@@ -600,7 +657,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorFamilyRelationId,	/* OPFAMILYOID */
 		OpfamilyOidIndexId,
@@ -612,7 +670,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{PartitionedRelationId,		/* PARTRELID */
 		PartitionedRelidIndexId,
@@ -624,7 +683,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ProcedureRelationId,		/* PROCNAMEARGSNSP */
 		ProcedureNameArgsNspIndexId,
@@ -636,7 +696,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ProcedureRelationId,		/* PROCOID */
 		ProcedureOidIndexId,
@@ -648,7 +709,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		-1, 0, 0
 	},
 	{RangeRelationId,			/* RANGETYPE */
 		RangeTypidIndexId,
@@ -660,7 +722,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{RelationRelationId,		/* RELNAMENSP */
 		ClassNameNspIndexId,
@@ -672,7 +735,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		NAMESPACEOID, ObjectIdAttributeNumber, Anum_pg_class_relnamespace
 	},
 	{RelationRelationId,		/* RELOID */
 		ClassOidIndexId,
@@ -684,7 +748,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ReplicationOriginRelationId,		/* REPLORIGIDENT */
 		ReplicationOriginIdentIndex,
@@ -696,7 +761,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ReplicationOriginRelationId,		/* REPLORIGNAME */
 		ReplicationOriginNameIndex,
@@ -708,7 +774,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{RewriteRelationId,			/* RULERELNAME */
 		RewriteRelRulenameIndexId,
@@ -720,7 +787,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
@@ -732,7 +800,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		Anum_pg_statistic_starelid
+		Anum_pg_statistic_starelid,
+		-1, 0, 0
 	},
 	{TableSpaceRelationId,		/* TABLESPACEOID */
 		TablespaceOidIndexId,
@@ -744,7 +813,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TransformRelationId,		/* TRFOID */
 		TransformOidIndexId,
@@ -756,7 +826,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TransformRelationId,		/* TRFTYPELANG */
 		TransformTypeLangIndexId,
@@ -768,7 +839,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSConfigMapRelationId,		/* TSCONFIGMAP */
 		TSConfigMapIndexId,
@@ -780,7 +852,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSConfigRelationId,		/* TSCONFIGNAMENSP */
 		TSConfigNameNspIndexId,
@@ -792,7 +865,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSConfigRelationId,		/* TSCONFIGOID */
 		TSConfigOidIndexId,
@@ -804,7 +878,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSDictionaryRelationId,	/* TSDICTNAMENSP */
 		TSDictionaryNameNspIndexId,
@@ -816,7 +891,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSDictionaryRelationId,	/* TSDICTOID */
 		TSDictionaryOidIndexId,
@@ -828,7 +904,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSParserRelationId,		/* TSPARSERNAMENSP */
 		TSParserNameNspIndexId,
@@ -840,7 +917,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSParserRelationId,		/* TSPARSEROID */
 		TSParserOidIndexId,
@@ -852,7 +930,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATENAMENSP */
 		TSTemplateNameNspIndexId,
@@ -864,7 +943,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATEOID */
 		TSTemplateOidIndexId,
@@ -876,7 +956,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TypeRelationId,			/* TYPENAMENSP */
 		TypeNameNspIndexId,
@@ -888,7 +969,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TypeRelationId,			/* TYPEOID */
 		TypeOidIndexId,
@@ -900,7 +982,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGOID */
 		UserMappingOidIndexId,
@@ -912,7 +995,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGUSERSERVER */
 		UserMappingUserServerIndexId,
@@ -924,7 +1008,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	}
 };
 
@@ -959,7 +1044,8 @@ static ScanKeyData	oideqscankey; /* ScanKey for reloid match  */
 
 static int	oid_compare(const void *a, const void *b);
 static void SysCacheRelInvalCallback(Datum arg, Oid reloid);
-
+static void SysCacheSysCacheInvalCallback(Datum arg, int cacheid,
+										  uint32 hashvalue);
 /*
  * InitCatalogCache - initialize the caches
  *
@@ -1014,6 +1100,37 @@ InitCatalogCache(void)
 				cacheinfo[cacheId].relattrnum;
 			relinval_callback_count++;
 		}
+
+		/*
+		 * If this syscache has syscache invalidation trigger, register
+		 * it.
+		 */
+		if (cacheinfo[cacheId].trig_cacheid >= 0)
+		{
+			SysCacheCBParam *param;
+			RegProcedure eqfunc;
+
+			param = MemoryContextAlloc(CacheMemoryContext,
+									   sizeof(SysCacheCBParam));
+			param->target_cacheid = cacheId;
+
+			/*
+			 * XXXX: Create a scankeydata for OID comparison. We don't have a
+			 * means to check the type of the column in the system catalog at
+			 * this time. So we have to belive the definition is correct.
+			 */
+			eqfunc = F_OIDEQ;
+
+			fmgr_info_cxt(eqfunc, &param->skey.sk_func, CacheMemoryContext);
+			param->skey.sk_attno = cacheinfo[cacheId].target_attnum;
+			param->trig_attnum = cacheinfo[cacheId].trig_attnum;
+			param->skey.sk_strategy = BTEqualStrategyNumber;
+			param->skey.sk_subtype = InvalidOid;
+			param->skey.sk_collation = InvalidOid;
+			CacheRegisterSyscacheCallback(cacheinfo[cacheId].trig_cacheid,
+										  SysCacheSysCacheInvalCallback,
+										  PointerGetDatum(param));
+		}
 	}
 
 	Assert(SysCacheRelationOidSize <= lengthof(SysCacheRelationOid));
@@ -1390,6 +1507,54 @@ RelationInvalidatesSnapshotsOnly(Oid relid)
 }
 
 /*
+ * SysCacheSysCacheInvalCallback
+ *
+ * Callback function for negative cache flushing by syscache invalidation.
+ * Fetches an OID (not restricted to system oid column) from the invalidated
+ * tuple and flushes negative entries that matches the OID in the target
+ * syscache.
+ */
+static void
+SysCacheSysCacheInvalCallback(Datum arg, int cacheid, uint32 hashValue)
+{
+	SysCacheCBParam *param;
+	CatCache	*trigger_cache;		/* triggering catcache */
+	CatCache	*target_cache;		/* target catcache */
+	List *oids;
+	ListCell *lc;
+	int			trigger_cacheid = cacheid;
+	int			target_cacheid;
+
+	param = (SysCacheCBParam *)DatumGetPointer(arg);
+	target_cacheid = param->target_cacheid;
+
+	trigger_cache = SysCache[trigger_cacheid];
+	target_cache = SysCache[target_cacheid];
+
+	/*
+	 * collect OIDs for target syscache entries. oids contains one value for
+	 * most cases, but two or more for the case hashvalue has synonyms. At
+	 * least one of them is the right OID but is cannot be distinguished using
+	 * the given hash value.
+	 *
+	 * As the result some unnecessary entries may be flushed but it won't harm
+	 * so much than letting them bloat catcaches.
+	 */
+	oids =
+		CollectOIDsForHashValue(trigger_cache, hashValue, param->trig_attnum);
+
+	foreach (lc, oids)
+	{
+		ScanKeyData skey;
+		Oid oid = lfirst_oid (lc);
+
+		memcpy(&skey, &param->skey, sizeof(skey));
+		skey.sk_argument = ObjectIdGetDatum(oid);
+		CleanupCatCacheNegEntries(target_cache, &skey);
+	}
+}
+
+/*
  * Test whether a relation has a system cache.
  */
 bool
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index cb662c0..b279174 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -179,6 +179,9 @@ extern uint32 GetCatCacheHashValue(CatCache *cache,
 					 Datum v1, Datum v2,
 					 Datum v3, Datum v4);
 
+extern List *CollectOIDsForHashValue(CatCache *cache,
+									 uint32 hashValue, int attnum);
+
 extern CatCList *SearchCatCacheList(CatCache *cache, int nkeys,
 				   Datum v1, Datum v2,
 				   Datum v3, Datum v4);
-- 
2.9.2

