From 38929d63b0bf91ac2e5014f8c97abfb03f7da678 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Mon, 28 Aug 2017 12:18:17 +0900
Subject: [PATCH 2/2] Cleanup negative cache of pg_class when dropping a schema

This feature in turn is triggered by catcache invalidation. This patch
provides a syscache invalidation callback to flush negative cache
entries corresponding to invalidated objects.
---
 src/backend/utils/cache/catcache.c |  42 +++++
 src/backend/utils/cache/inval.c    |   7 +-
 src/backend/utils/cache/syscache.c | 324 ++++++++++++++++++++++++++++---------
 src/include/utils/catcache.h       |   3 +
 4 files changed, 298 insertions(+), 78 deletions(-)

diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index e50c997..edd7b28 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1360,6 +1360,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 initialization */
+	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 d0e54b8..57528a7 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -559,9 +559,14 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
 		{
 			InvalidateCatalogSnapshot();
 
+			/*
+			 * Call the callbacks first so that the callbacks can access the
+			 * entries corresponding to the hashValue.
+			 */
+			CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue);
+
 			SysCacheInvalidate(msg->cc.id, msg->cc.hashValue);
 
-			CallSyscacheCallbacks(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 7d48939..487815b 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -111,6 +111,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
@@ -124,6 +134,14 @@ struct cachedesc
 	/* relcache invalidation stuff */
 	AttrNumber	relattrnum;		/* attrnum to retrieve reloid for
 								 * invalidation, 0 if not needed */
+
+	/* 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[] = {
@@ -137,7 +155,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodRelationId,	/* AMNAME */
 		AmNameIndexId,
@@ -149,7 +168,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodRelationId,	/* AMOID */
 		AmOidIndexId,
@@ -161,7 +181,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodOperatorRelationId,	/* AMOPOPID */
 		AccessMethodOperatorIndexId,
@@ -173,7 +194,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodOperatorRelationId,	/* AMOPSTRATEGY */
 		AccessMethodStrategyIndexId,
@@ -185,7 +207,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amop_amopstrategy
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodProcedureRelationId,	/* AMPROCNUM */
 		AccessMethodProcedureIndexId,
@@ -197,7 +220,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amproc_amprocnum
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AttributeRelationId,		/* ATTNAME */
 		AttributeRelidNameIndexId,
@@ -209,7 +233,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		Anum_pg_attribute_attrelid
+		Anum_pg_attribute_attrelid,
+		-1, 0, 0
 	},
 	{AttributeRelationId,		/* ATTNUM */
 		AttributeRelidNumIndexId,
@@ -221,7 +246,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		Anum_pg_attribute_attrelid
+		Anum_pg_attribute_attrelid,
+		-1, 0, 0
 	},
 	{AuthMemRelationId,			/* AUTHMEMMEMROLE */
 		AuthMemMemRoleIndexId,
@@ -233,7 +259,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AuthMemRelationId,			/* AUTHMEMROLEMEM */
 		AuthMemRoleMemIndexId,
@@ -245,7 +272,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AuthIdRelationId,			/* AUTHNAME */
 		AuthIdRolnameIndexId,
@@ -257,7 +285,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AuthIdRelationId,			/* AUTHOID */
 		AuthIdOidIndexId,
@@ -269,7 +298,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{CastRelationId,			/* CASTSOURCETARGET */
 		CastSourceTargetIndexId,
@@ -281,7 +311,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		256,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorClassRelationId,	/* CLAAMNAMENSP */
 		OpclassAmNameNspIndexId,
@@ -293,7 +324,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorClassRelationId,	/* CLAOID */
 		OpclassOidIndexId,
@@ -305,7 +337,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{CollationRelationId,		/* COLLNAMEENCNSP */
 		CollationNameEncNspIndexId,
@@ -317,7 +350,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{CollationRelationId,		/* COLLOID */
 		CollationOidIndexId,
@@ -329,7 +363,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConversionRelationId,		/* CONDEFAULT */
 		ConversionDefaultIndexId,
@@ -341,7 +376,8 @@ static const struct cachedesc cacheinfo[] = {
 			ObjectIdAttributeNumber,
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConversionRelationId,		/* CONNAMENSP */
 		ConversionNameNspIndexId,
@@ -353,7 +389,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConstraintRelationId,		/* CONSTROID */
 		ConstraintOidIndexId,
@@ -365,7 +402,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConversionRelationId,		/* CONVOID */
 		ConversionOidIndexId,
@@ -377,7 +415,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{DatabaseRelationId,		/* DATABASEOID */
 		DatabaseOidIndexId,
@@ -389,7 +428,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{DefaultAclRelationId,		/* DEFACLROLENSPOBJ */
 		DefaultAclRoleNspObjIndexId,
@@ -401,7 +441,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EnumRelationId,			/* ENUMOID */
 		EnumOidIndexId,
@@ -413,7 +454,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EnumRelationId,			/* ENUMTYPOIDNAME */
 		EnumTypIdLabelIndexId,
@@ -425,7 +467,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EventTriggerRelationId,	/* EVENTTRIGGERNAME */
 		EventTriggerNameIndexId,
@@ -437,7 +480,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EventTriggerRelationId,	/* EVENTTRIGGEROID */
 		EventTriggerOidIndexId,
@@ -449,7 +493,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignDataWrapperRelationId,	/* FOREIGNDATAWRAPPERNAME */
 		ForeignDataWrapperNameIndexId,
@@ -461,7 +506,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignDataWrapperRelationId,	/* FOREIGNDATAWRAPPEROID */
 		ForeignDataWrapperOidIndexId,
@@ -473,7 +519,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignServerRelationId,	/* FOREIGNSERVERNAME */
 		ForeignServerNameIndexId,
@@ -485,7 +532,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignServerRelationId,	/* FOREIGNSERVEROID */
 		ForeignServerOidIndexId,
@@ -497,7 +545,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignTableRelationId,	/* FOREIGNTABLEREL */
 		ForeignTableRelidIndexId,
@@ -509,7 +558,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{IndexRelationId,			/* INDEXRELID */
 		IndexRelidIndexId,
@@ -521,7 +571,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{LanguageRelationId,		/* LANGNAME */
 		LanguageNameIndexId,
@@ -533,7 +584,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{LanguageRelationId,		/* LANGOID */
 		LanguageOidIndexId,
@@ -545,7 +597,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{NamespaceRelationId,		/* NAMESPACENAME */
 		NamespaceNameIndexId,
@@ -557,7 +610,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{NamespaceRelationId,		/* NAMESPACEOID */
 		NamespaceOidIndexId,
@@ -569,7 +623,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorRelationId,		/* OPERNAMENSP */
 		OperatorNameNspIndexId,
@@ -581,7 +636,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_operator_oprnamespace
 		},
 		256,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorRelationId,		/* OPEROID */
 		OperatorOidIndexId,
@@ -593,7 +649,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorFamilyRelationId,	/* OPFAMILYAMNAMENSP */
 		OpfamilyAmNameNspIndexId,
@@ -605,7 +662,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorFamilyRelationId,	/* OPFAMILYOID */
 		OpfamilyOidIndexId,
@@ -617,7 +675,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{PartitionedRelationId,		/* PARTRELID */
 		PartitionedRelidIndexId,
@@ -629,7 +688,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ProcedureRelationId,		/* PROCNAMEARGSNSP */
 		ProcedureNameArgsNspIndexId,
@@ -641,7 +701,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ProcedureRelationId,		/* PROCOID */
 		ProcedureOidIndexId,
@@ -653,7 +714,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		-1, 0, 0
 	},
 	{PublicationRelationId,		/* PUBLICATIONNAME */
 		PublicationNameIndexId,
@@ -665,7 +727,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{PublicationRelationId,		/* PUBLICATIONOID */
 		PublicationObjectIndexId,
@@ -677,7 +740,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{PublicationRelRelationId,	/* PUBLICATIONREL */
 		PublicationRelObjectIndexId,
@@ -689,7 +753,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{PublicationRelRelationId,	/* PUBLICATIONRELMAP */
 		PublicationRelPrrelidPrpubidIndexId,
@@ -701,7 +766,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{RangeRelationId,			/* RANGETYPE */
 		RangeTypidIndexId,
@@ -713,7 +779,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{RelationRelationId,		/* RELNAMENSP */
 		ClassNameNspIndexId,
@@ -725,7 +792,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		NAMESPACEOID, ObjectIdAttributeNumber, Anum_pg_class_relnamespace
 	},
 	{RelationRelationId,		/* RELOID */
 		ClassOidIndexId,
@@ -737,7 +805,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ReplicationOriginRelationId,	/* REPLORIGIDENT */
 		ReplicationOriginIdentIndex,
@@ -749,7 +818,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ReplicationOriginRelationId,	/* REPLORIGNAME */
 		ReplicationOriginNameIndex,
@@ -761,7 +831,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{RewriteRelationId,			/* RULERELNAME */
 		RewriteRelRulenameIndexId,
@@ -773,7 +844,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{SequenceRelationId,		/* SEQRELID */
 		SequenceRelidIndexId,
@@ -785,7 +857,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		0
+		0,
+		-1, 0, 0
 	},
 	{StatisticExtRelationId,	/* STATEXTNAMENSP */
 		StatisticExtNameIndexId,
@@ -796,7 +869,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		-1, 0, 0
 	},
 	{StatisticExtRelationId,	/* STATEXTOID */
 		StatisticExtOidIndexId,
@@ -808,7 +882,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
@@ -832,7 +907,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{SubscriptionRelationId,	/* SUBSCRIPTIONOID */
 		SubscriptionObjectIndexId,
@@ -844,7 +920,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{SubscriptionRelRelationId, /* SUBSCRIPTIONRELMAP */
 		SubscriptionRelSrrelidSrsubidIndexId,
@@ -856,7 +933,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TableSpaceRelationId,		/* TABLESPACEOID */
 		TablespaceOidIndexId,
@@ -868,7 +946,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TransformRelationId,		/* TRFOID */
 		TransformOidIndexId,
@@ -880,7 +959,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TransformRelationId,		/* TRFTYPELANG */
 		TransformTypeLangIndexId,
@@ -892,7 +972,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSConfigMapRelationId,		/* TSCONFIGMAP */
 		TSConfigMapIndexId,
@@ -904,7 +985,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSConfigRelationId,		/* TSCONFIGNAMENSP */
 		TSConfigNameNspIndexId,
@@ -916,7 +998,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSConfigRelationId,		/* TSCONFIGOID */
 		TSConfigOidIndexId,
@@ -928,7 +1011,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSDictionaryRelationId,	/* TSDICTNAMENSP */
 		TSDictionaryNameNspIndexId,
@@ -940,7 +1024,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSDictionaryRelationId,	/* TSDICTOID */
 		TSDictionaryOidIndexId,
@@ -952,7 +1037,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSParserRelationId,		/* TSPARSERNAMENSP */
 		TSParserNameNspIndexId,
@@ -964,7 +1050,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSParserRelationId,		/* TSPARSEROID */
 		TSParserOidIndexId,
@@ -976,7 +1063,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATENAMENSP */
 		TSTemplateNameNspIndexId,
@@ -988,7 +1076,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATEOID */
 		TSTemplateOidIndexId,
@@ -1000,7 +1089,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TypeRelationId,			/* TYPENAMENSP */
 		TypeNameNspIndexId,
@@ -1012,7 +1102,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TypeRelationId,			/* TYPEOID */
 		TypeOidIndexId,
@@ -1024,7 +1115,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGOID */
 		UserMappingOidIndexId,
@@ -1036,7 +1128,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGUSERSERVER */
 		UserMappingUserServerIndexId,
@@ -1048,7 +1141,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	}
 };
 
@@ -1081,7 +1175,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
  *
@@ -1139,6 +1234,34 @@ 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;
+
+			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.
+			 */
+			fmgr_info_cxt(F_OIDEQ, &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));
@@ -1533,6 +1656,53 @@ 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 candidate OIDs for target syscache entries. The result contains
+	 * just one value for most cases, or two or more for the case hashvalue
+	 * has synonyms. At least one of them is the right OID but it is
+	 * undistinguisable from others by 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 6ef4e38..7ead032 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -183,6 +183,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

