From 4887f7d178b41a1a4729931a12bd396b9a8e8ee0 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Mon, 28 Aug 2017 11:36:21 +0900
Subject: [PATCH 1/2] Cleanup negative cache of pg_statistic when dropping a
 relation.

Accessing columns that don't have statistics leaves negative entries
in catcache for pg_statstic, but there's no chance to remove
them. Especially when repeatedly creating then dropping temporary
tables bloats catcache so much that memory pressure becomes
significant. This patch removes negative entries in STATRELATTINH,
ATTNAME and ATTNUM when corresponding relation is dropped.
---
 src/backend/utils/cache/catcache.c |  57 ++++++-
 src/backend/utils/cache/syscache.c | 299 +++++++++++++++++++++++++++----------
 src/include/utils/catcache.h       |   3 +
 src/include/utils/syscache.h       |   2 +
 4 files changed, 279 insertions(+), 82 deletions(-)

diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index e092801..e50c997 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -304,10 +304,11 @@ CatCachePrintStats(int code, Datum arg)
 
 		if (cache->cc_ntup == 0 && cache->cc_searches == 0)
 			continue;			/* don't print unused caches */
-		elog(DEBUG2, "catcache %s/%u: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld lsrch, %ld lhits",
+		elog(DEBUG2, "catcache %s/%u: %d tup, %d negtup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld lsrch, %ld lhits",
 			 cache->cc_relname,
 			 cache->cc_indexoid,
 			 cache->cc_ntup,
+			 cache->cc_nnegtup,
 			 cache->cc_searches,
 			 cache->cc_hits,
 			 cache->cc_neg_hits,
@@ -374,6 +375,10 @@ CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
 	/* free associated tuple data */
 	if (ct->tuple.t_data != NULL)
 		pfree(ct->tuple.t_data);
+
+	if (ct->negative)
+		--cache->cc_nnegtup;
+
 	pfree(ct);
 
 	--cache->cc_ntup;
@@ -572,6 +577,49 @@ ResetCatalogCache(CatCache *cache)
 }
 
 /*
+ *		CleanupCatCacheNegEntries
+ *
+ *	Remove negative cache tuples matching a partial key.
+ *
+ */
+void
+CleanupCatCacheNegEntries(CatCache *cache, ScanKeyData *skey)
+{
+	int i;
+
+	/* If this cache has no negative entries, nothing to do */
+	if (cache->cc_nnegtup == 0)
+		return;
+
+	/* searching with a paritial key means scanning the whole cache */
+	for (i = 0; i < cache->cc_nbuckets; i++)
+	{
+		dlist_head *bucket = &cache->cc_bucket[i];
+		dlist_mutable_iter iter;
+
+		dlist_foreach_modify(iter, bucket)
+		{
+			CatCTup    *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+			bool		res;
+
+			if (!ct->negative)
+				continue;
+
+			HeapKeyTest(&ct->tuple, cache->cc_tupdesc, 1, skey, res);
+			if (!res)
+				continue;
+
+			/*
+			 * the negative cache entries can no longer be referenced, so we
+			 * can remove it unconditionally
+			 */
+			CatCacheRemoveCTup(cache, ct);
+		}
+	}
+}
+
+
+/*
  *		ResetCatalogCaches
  *
  * Reset all caches when a shared cache inval event forces it
@@ -718,6 +766,7 @@ InitCatCache(int id,
 	cp->cc_relisshared = false; /* temporary */
 	cp->cc_tupdesc = (TupleDesc) NULL;
 	cp->cc_ntup = 0;
+	cp->cc_nnegtup = 0;
 	cp->cc_nbuckets = nbuckets;
 	cp->cc_nkeys = nkeys;
 	for (i = 0; i < nkeys; ++i)
@@ -1215,8 +1264,8 @@ SearchCatCache(CatCache *cache,
 
 		CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
 					cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
-		CACHE3_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d",
-					cache->cc_relname, hashIndex);
+		CACHE4_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d, total %d",
+					cache->cc_relname, hashIndex, cache->cc_nnegtup);
 
 		/*
 		 * We are not returning the negative entry to the caller, so leave its
@@ -1667,6 +1716,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
 
 	cache->cc_ntup++;
 	CacheHdr->ch_ntup++;
+	if (negative)
+		cache->cc_nnegtup++;
 
 	/*
 	 * If the hash table has become too full, enlarge the buckets array. Quite
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 607fe9d..7d48939 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -75,6 +75,8 @@
 #include "catalog/pg_user_mapping.h"
 #include "utils/rel.h"
 #include "utils/catcache.h"
+#include "utils/fmgroids.h"
+#include "utils/inval.h"
 #include "utils/syscache.h"
 
 
@@ -118,6 +120,10 @@ struct cachedesc
 	int			nkeys;			/* # of keys needed for cache lookup */
 	int			key[4];			/* attribute numbers of key attrs */
 	int			nbuckets;		/* number of hash buckets for this cache */
+
+	/* relcache invalidation stuff */
+	AttrNumber	relattrnum;		/* attrnum to retrieve reloid for
+								 * invalidation, 0 if not needed */
 };
 
 static const struct cachedesc cacheinfo[] = {
@@ -130,7 +136,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		16
+		16,
+		0
 	},
 	{AccessMethodRelationId,	/* AMNAME */
 		AmNameIndexId,
@@ -141,7 +148,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{AccessMethodRelationId,	/* AMOID */
 		AmOidIndexId,
@@ -152,7 +160,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{AccessMethodOperatorRelationId,	/* AMOPOPID */
 		AccessMethodOperatorIndexId,
@@ -163,7 +172,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amop_amopfamily,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{AccessMethodOperatorRelationId,	/* AMOPSTRATEGY */
 		AccessMethodStrategyIndexId,
@@ -174,7 +184,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amop_amoprighttype,
 			Anum_pg_amop_amopstrategy
 		},
-		64
+		64,
+		0
 	},
 	{AccessMethodProcedureRelationId,	/* AMPROCNUM */
 		AccessMethodProcedureIndexId,
@@ -185,7 +196,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amproc_amprocrighttype,
 			Anum_pg_amproc_amprocnum
 		},
-		16
+		16,
+		0
 	},
 	{AttributeRelationId,		/* ATTNAME */
 		AttributeRelidNameIndexId,
@@ -196,7 +208,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		32
+		32,
+		Anum_pg_attribute_attrelid
 	},
 	{AttributeRelationId,		/* ATTNUM */
 		AttributeRelidNumIndexId,
@@ -207,7 +220,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		128
+		128,
+		Anum_pg_attribute_attrelid
 	},
 	{AuthMemRelationId,			/* AUTHMEMMEMROLE */
 		AuthMemMemRoleIndexId,
@@ -218,7 +232,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{AuthMemRelationId,			/* AUTHMEMROLEMEM */
 		AuthMemRoleMemIndexId,
@@ -229,7 +244,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{AuthIdRelationId,			/* AUTHNAME */
 		AuthIdRolnameIndexId,
@@ -240,7 +256,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{AuthIdRelationId,			/* AUTHOID */
 		AuthIdOidIndexId,
@@ -251,10 +268,10 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
-	{
-		CastRelationId,			/* CASTSOURCETARGET */
+	{CastRelationId,			/* CASTSOURCETARGET */
 		CastSourceTargetIndexId,
 		2,
 		{
@@ -263,7 +280,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		256
+		256,
+		0
 	},
 	{OperatorClassRelationId,	/* CLAAMNAMENSP */
 		OpclassAmNameNspIndexId,
@@ -274,7 +292,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_opclass_opcnamespace,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{OperatorClassRelationId,	/* CLAOID */
 		OpclassOidIndexId,
@@ -285,7 +304,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{CollationRelationId,		/* COLLNAMEENCNSP */
 		CollationNameEncNspIndexId,
@@ -296,7 +316,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_collation_collnamespace,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{CollationRelationId,		/* COLLOID */
 		CollationOidIndexId,
@@ -307,7 +328,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{ConversionRelationId,		/* CONDEFAULT */
 		ConversionDefaultIndexId,
@@ -318,7 +340,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_conversion_contoencoding,
 			ObjectIdAttributeNumber,
 		},
-		8
+		8,
+		0
 	},
 	{ConversionRelationId,		/* CONNAMENSP */
 		ConversionNameNspIndexId,
@@ -329,7 +352,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{ConstraintRelationId,		/* CONSTROID */
 		ConstraintOidIndexId,
@@ -340,7 +364,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		16
+		16,
+		0
 	},
 	{ConversionRelationId,		/* CONVOID */
 		ConversionOidIndexId,
@@ -351,7 +376,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{DatabaseRelationId,		/* DATABASEOID */
 		DatabaseOidIndexId,
@@ -362,7 +388,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{DefaultAclRelationId,		/* DEFACLROLENSPOBJ */
 		DefaultAclRoleNspObjIndexId,
@@ -373,7 +400,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_default_acl_defaclobjtype,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{EnumRelationId,			/* ENUMOID */
 		EnumOidIndexId,
@@ -384,7 +412,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{EnumRelationId,			/* ENUMTYPOIDNAME */
 		EnumTypIdLabelIndexId,
@@ -395,7 +424,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{EventTriggerRelationId,	/* EVENTTRIGGERNAME */
 		EventTriggerNameIndexId,
@@ -406,7 +436,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{EventTriggerRelationId,	/* EVENTTRIGGEROID */
 		EventTriggerOidIndexId,
@@ -417,7 +448,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{ForeignDataWrapperRelationId,	/* FOREIGNDATAWRAPPERNAME */
 		ForeignDataWrapperNameIndexId,
@@ -428,7 +460,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{ForeignDataWrapperRelationId,	/* FOREIGNDATAWRAPPEROID */
 		ForeignDataWrapperOidIndexId,
@@ -439,7 +472,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{ForeignServerRelationId,	/* FOREIGNSERVERNAME */
 		ForeignServerNameIndexId,
@@ -450,7 +484,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{ForeignServerRelationId,	/* FOREIGNSERVEROID */
 		ForeignServerOidIndexId,
@@ -461,7 +496,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{ForeignTableRelationId,	/* FOREIGNTABLEREL */
 		ForeignTableRelidIndexId,
@@ -472,7 +508,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{IndexRelationId,			/* INDEXRELID */
 		IndexRelidIndexId,
@@ -483,7 +520,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{LanguageRelationId,		/* LANGNAME */
 		LanguageNameIndexId,
@@ -494,7 +532,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{LanguageRelationId,		/* LANGOID */
 		LanguageOidIndexId,
@@ -505,7 +544,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{NamespaceRelationId,		/* NAMESPACENAME */
 		NamespaceNameIndexId,
@@ -516,7 +556,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{NamespaceRelationId,		/* NAMESPACEOID */
 		NamespaceOidIndexId,
@@ -527,7 +568,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		16
+		16,
+		0
 	},
 	{OperatorRelationId,		/* OPERNAMENSP */
 		OperatorNameNspIndexId,
@@ -538,7 +580,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_operator_oprright,
 			Anum_pg_operator_oprnamespace
 		},
-		256
+		256,
+		0
 	},
 	{OperatorRelationId,		/* OPEROID */
 		OperatorOidIndexId,
@@ -549,7 +592,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		32
+		32,
+		0
 	},
 	{OperatorFamilyRelationId,	/* OPFAMILYAMNAMENSP */
 		OpfamilyAmNameNspIndexId,
@@ -560,7 +604,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_opfamily_opfnamespace,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{OperatorFamilyRelationId,	/* OPFAMILYOID */
 		OpfamilyOidIndexId,
@@ -571,7 +616,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{PartitionedRelationId,		/* PARTRELID */
 		PartitionedRelidIndexId,
@@ -582,7 +628,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		32
+		32,
+		0
 	},
 	{ProcedureRelationId,		/* PROCNAMEARGSNSP */
 		ProcedureNameArgsNspIndexId,
@@ -593,7 +640,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_proc_pronamespace,
 			0
 		},
-		128
+		128,
+		0
 	},
 	{ProcedureRelationId,		/* PROCOID */
 		ProcedureOidIndexId,
@@ -604,7 +652,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		128
+		128,
+		0
 	},
 	{PublicationRelationId,		/* PUBLICATIONNAME */
 		PublicationNameIndexId,
@@ -615,7 +664,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{PublicationRelationId,		/* PUBLICATIONOID */
 		PublicationObjectIndexId,
@@ -626,7 +676,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{PublicationRelRelationId,	/* PUBLICATIONREL */
 		PublicationRelObjectIndexId,
@@ -637,7 +688,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{PublicationRelRelationId,	/* PUBLICATIONRELMAP */
 		PublicationRelPrrelidPrpubidIndexId,
@@ -648,7 +700,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{RangeRelationId,			/* RANGETYPE */
 		RangeTypidIndexId,
@@ -659,7 +712,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{RelationRelationId,		/* RELNAMENSP */
 		ClassNameNspIndexId,
@@ -670,7 +724,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		128
+		128,
+		0
 	},
 	{RelationRelationId,		/* RELOID */
 		ClassOidIndexId,
@@ -681,7 +736,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		128
+		128,
+		0
 	},
 	{ReplicationOriginRelationId,	/* REPLORIGIDENT */
 		ReplicationOriginIdentIndex,
@@ -692,7 +748,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		16
+		16,
+		0
 	},
 	{ReplicationOriginRelationId,	/* REPLORIGNAME */
 		ReplicationOriginNameIndex,
@@ -703,7 +760,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		16
+		16,
+		0
 	},
 	{RewriteRelationId,			/* RULERELNAME */
 		RewriteRelRulenameIndexId,
@@ -714,7 +772,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{SequenceRelationId,		/* SEQRELID */
 		SequenceRelidIndexId,
@@ -725,7 +784,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		32
+		32,
+		0
 	},
 	{StatisticExtRelationId,	/* STATEXTNAMENSP */
 		StatisticExtNameIndexId,
@@ -747,7 +807,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
@@ -758,7 +819,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_statistic_stainherit,
 			0
 		},
-		128
+		128,
+		Anum_pg_statistic_starelid
 	},
 	{SubscriptionRelationId,	/* SUBSCRIPTIONNAME */
 		SubscriptionNameIndexId,
@@ -769,7 +831,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{SubscriptionRelationId,	/* SUBSCRIPTIONOID */
 		SubscriptionObjectIndexId,
@@ -780,7 +843,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{SubscriptionRelRelationId, /* SUBSCRIPTIONRELMAP */
 		SubscriptionRelSrrelidSrsubidIndexId,
@@ -791,7 +855,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{TableSpaceRelationId,		/* TABLESPACEOID */
 		TablespaceOidIndexId,
@@ -802,7 +867,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0,
 		},
-		4
+		4,
+		0
 	},
 	{TransformRelationId,		/* TRFOID */
 		TransformOidIndexId,
@@ -813,7 +879,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0,
 		},
-		16
+		16,
+		0
 	},
 	{TransformRelationId,		/* TRFTYPELANG */
 		TransformTypeLangIndexId,
@@ -824,7 +891,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0,
 		},
-		16
+		16,
+		0
 	},
 	{TSConfigMapRelationId,		/* TSCONFIGMAP */
 		TSConfigMapIndexId,
@@ -835,7 +903,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_ts_config_map_mapseqno,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSConfigRelationId,		/* TSCONFIGNAMENSP */
 		TSConfigNameNspIndexId,
@@ -846,7 +915,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSConfigRelationId,		/* TSCONFIGOID */
 		TSConfigOidIndexId,
@@ -857,7 +927,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSDictionaryRelationId,	/* TSDICTNAMENSP */
 		TSDictionaryNameNspIndexId,
@@ -868,7 +939,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSDictionaryRelationId,	/* TSDICTOID */
 		TSDictionaryOidIndexId,
@@ -879,7 +951,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSParserRelationId,		/* TSPARSERNAMENSP */
 		TSParserNameNspIndexId,
@@ -890,7 +963,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSParserRelationId,		/* TSPARSEROID */
 		TSParserOidIndexId,
@@ -901,7 +975,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATENAMENSP */
 		TSTemplateNameNspIndexId,
@@ -912,7 +987,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATEOID */
 		TSTemplateOidIndexId,
@@ -923,7 +999,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TypeRelationId,			/* TYPENAMENSP */
 		TypeNameNspIndexId,
@@ -934,7 +1011,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{TypeRelationId,			/* TYPEOID */
 		TypeOidIndexId,
@@ -945,7 +1023,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGOID */
 		UserMappingOidIndexId,
@@ -956,7 +1035,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGUSERSERVER */
 		UserMappingUserServerIndexId,
@@ -967,7 +1047,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	}
 };
 
@@ -983,8 +1064,23 @@ static int	SysCacheRelationOidSize;
 static Oid	SysCacheSupportingRelOid[SysCacheSize * 2];
 static int	SysCacheSupportingRelOidSize;
 
-static int	oid_compare(const void *a, const void *b);
+/*
+ * stuff for negative cache flushing by relcache invalidation
+ */
+#define MAX_RELINVAL_CALLBACKS 4
+typedef struct RELINVALCBParam
+{
+	CatCache *cache;
+	int		  relkeynum;
+}  RELINVALCBParam;
+
+RELINVALCBParam relinval_callback_list[MAX_RELINVAL_CALLBACKS];
+static int relinval_callback_count = 0;
 
+static ScanKeyData	oideqscankey; /* ScanKey for reloid match  */
+
+static int	oid_compare(const void *a, const void *b);
+static void SysCacheRelInvalCallback(Datum arg, Oid reloid);
 
 /*
  * InitCatalogCache - initialize the caches
@@ -1028,6 +1124,21 @@ InitCatalogCache(void)
 			cacheinfo[cacheId].indoid;
 		/* see comments for RelationInvalidatesSnapshotsOnly */
 		Assert(!RelationInvalidatesSnapshotsOnly(cacheinfo[cacheId].reloid));
+
+		/*
+		 * If this syscache is requesting relcache invalidation, register a
+		 * callback
+		 */
+		if (cacheinfo[cacheId].relattrnum > 0)
+		{
+			Assert(relinval_callback_count < MAX_RELINVAL_CALLBACKS);
+
+			relinval_callback_list[relinval_callback_count].cache  =
+				SysCache[cacheId];
+			relinval_callback_list[relinval_callback_count].relkeynum =
+				cacheinfo[cacheId].relattrnum;
+			relinval_callback_count++;
+		}
 	}
 
 	Assert(SysCacheRelationOidSize <= lengthof(SysCacheRelationOid));
@@ -1052,10 +1163,40 @@ InitCatalogCache(void)
 	}
 	SysCacheSupportingRelOidSize = j + 1;
 
+	/*
+	 * prepare the scankey for reloid comparison and register a relcache inval
+	 * callback.
+	 */
+	oideqscankey.sk_strategy = BTEqualStrategyNumber;
+	oideqscankey.sk_subtype = InvalidOid;
+	oideqscankey.sk_collation = InvalidOid;
+	fmgr_info_cxt(F_OIDEQ, &oideqscankey.sk_func, CacheMemoryContext);
+	CacheRegisterRelcacheCallback(SysCacheRelInvalCallback, (Datum) 0);
+
 	CacheInitialized = true;
 }
 
 /*
+ * Callback function for negative cache flushing by relcache invalidation
+ * scankey for this funciton has been prepared in InitCatalogCache.
+ */
+static void
+SysCacheRelInvalCallback(Datum arg, Oid reloid)
+{
+	int i;
+
+	for(i = 0 ; i < relinval_callback_count ; i++)
+	{
+		ScanKeyData skey;
+
+		memcpy(&skey, &oideqscankey, sizeof(skey));
+		skey.sk_attno = relinval_callback_list[i].relkeynum;
+		skey.sk_argument = ObjectIdGetDatum(reloid);
+		CleanupCatCacheNegEntries(relinval_callback_list[i].cache, &skey);
+	}
+}
+
+/*
  * InitCatalogCachePhase2 - finish initializing the caches
  *
  * Finish initializing all the caches, including necessary database
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index 200a302..6ef4e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -44,6 +44,7 @@ typedef struct catcache
 	bool		cc_relisshared; /* is relation shared across databases? */
 	TupleDesc	cc_tupdesc;		/* tuple descriptor (copied from reldesc) */
 	int			cc_ntup;		/* # of tuples currently in this cache */
+	int			cc_nnegtup;		/* # of negative tuples */
 	int			cc_nbuckets;	/* # of hash buckets in this cache */
 	int			cc_nkeys;		/* # of keys (1..CATCACHE_MAXKEYS) */
 	int			cc_key[CATCACHE_MAXKEYS];	/* AttrNumber of each key */
@@ -187,6 +188,8 @@ extern CatCList *SearchCatCacheList(CatCache *cache, int nkeys,
 				   Datum v3, Datum v4);
 extern void ReleaseCatCacheList(CatCList *list);
 
+extern void
+CleanupCatCacheNegEntries(CatCache *cache, ScanKeyData *skey);
 extern void ResetCatalogCaches(void);
 extern void CatalogCacheFlushCatalog(Oid catId);
 extern void CatCacheInvalidate(CatCache *cache, uint32 hashValue);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 8352b40..ca3ddd5 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -118,6 +118,8 @@ extern void InitCatalogCachePhase2(void);
 extern HeapTuple SearchSysCache(int cacheId,
 			   Datum key1, Datum key2, Datum key3, Datum key4);
 extern void ReleaseSysCache(HeapTuple tuple);
+extern void CleanupNegativeCache(int cacheid, int nkeys,
+							Datum key1, Datum key2, Datum key3, Datum key4);
 
 /* convenience routines */
 extern HeapTuple SearchSysCacheCopy(int cacheId,
-- 
2.9.2

