From 9f2c81dbc9bc344cafd6995dfc5969d55a8457d9 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 |  58 ++++++-
 src/backend/utils/cache/syscache.c | 302 +++++++++++++++++++++++++++----------
 src/include/utils/catcache.h       |   3 +
 src/include/utils/syscache.h       |   2 +
 4 files changed, 282 insertions(+), 83 deletions(-)

diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 95a0742..bd303f3 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -423,10 +423,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,
@@ -495,8 +496,11 @@ CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
 	 * point into tuple, allocated together with the CatCTup.
 	 */
 	if (ct->negative)
+	{
 		CatCacheFreeKeys(cache->cc_tupdesc, cache->cc_nkeys,
 						 cache->cc_keyno, ct->keys);
+		--cache->cc_nnegtup;
+	}
 
 	pfree(ct);
 
@@ -697,6 +701,51 @@ 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 partial 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)
+		{
+			const CCFastEqualFN *cc_fastequal = cache->cc_fastequal;
+			CatCTup    *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+			int			oid_attnum = skey->sk_attno - 1;
+
+			if (!ct->negative)
+				continue;
+
+			/* Compare the OIDs */
+			if (!(cc_fastequal[oid_attnum])(ct->keys[oid_attnum],
+											skey[0].sk_argument))
+				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
@@ -845,6 +894,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)
@@ -1420,8 +1470,8 @@ SearchCatCacheMiss(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
@@ -1906,6 +1956,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
 
 	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 888edbb..753c5f1 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,
@@ -736,7 +796,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{StatisticExtRelationId,	/* STATEXTOID */
 		StatisticExtOidIndexId,
@@ -747,7 +808,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
@@ -758,7 +820,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_statistic_stainherit,
 			0
 		},
-		128
+		128,
+		Anum_pg_statistic_starelid
 	},
 	{SubscriptionRelationId,	/* SUBSCRIPTIONNAME */
 		SubscriptionNameIndexId,
@@ -769,7 +832,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{SubscriptionRelationId,	/* SUBSCRIPTIONOID */
 		SubscriptionObjectIndexId,
@@ -780,7 +844,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{SubscriptionRelRelationId, /* SUBSCRIPTIONRELMAP */
 		SubscriptionRelSrrelidSrsubidIndexId,
@@ -791,7 +856,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{TableSpaceRelationId,		/* TABLESPACEOID */
 		TablespaceOidIndexId,
@@ -802,7 +868,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0,
 		},
-		4
+		4,
+		0
 	},
 	{TransformRelationId,		/* TRFOID */
 		TransformOidIndexId,
@@ -813,7 +880,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0,
 		},
-		16
+		16,
+		0
 	},
 	{TransformRelationId,		/* TRFTYPELANG */
 		TransformTypeLangIndexId,
@@ -824,7 +892,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0,
 		},
-		16
+		16,
+		0
 	},
 	{TSConfigMapRelationId,		/* TSCONFIGMAP */
 		TSConfigMapIndexId,
@@ -835,7 +904,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_ts_config_map_mapseqno,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSConfigRelationId,		/* TSCONFIGNAMENSP */
 		TSConfigNameNspIndexId,
@@ -846,7 +916,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSConfigRelationId,		/* TSCONFIGOID */
 		TSConfigOidIndexId,
@@ -857,7 +928,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSDictionaryRelationId,	/* TSDICTNAMENSP */
 		TSDictionaryNameNspIndexId,
@@ -868,7 +940,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSDictionaryRelationId,	/* TSDICTOID */
 		TSDictionaryOidIndexId,
@@ -879,7 +952,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSParserRelationId,		/* TSPARSERNAMENSP */
 		TSParserNameNspIndexId,
@@ -890,7 +964,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSParserRelationId,		/* TSPARSEROID */
 		TSParserOidIndexId,
@@ -901,7 +976,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATENAMENSP */
 		TSTemplateNameNspIndexId,
@@ -912,7 +988,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATEOID */
 		TSTemplateOidIndexId,
@@ -923,7 +1000,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TypeRelationId,			/* TYPENAMENSP */
 		TypeNameNspIndexId,
@@ -934,7 +1012,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{TypeRelationId,			/* TYPEOID */
 		TypeOidIndexId,
@@ -945,7 +1024,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGOID */
 		UserMappingOidIndexId,
@@ -956,7 +1036,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGUSERSERVER */
 		UserMappingUserServerIndexId,
@@ -967,7 +1048,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	}
 };
 
@@ -983,8 +1065,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 +1125,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 +1164,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 function 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 74535eb..7564f42 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -59,6 +59,7 @@ typedef struct catcache
 	Oid			cc_indexoid;	/* OID of index matching cache keys */
 	bool		cc_relisshared; /* is relation shared across databases? */
 	slist_node	cc_next;		/* list link */
+	int			cc_nnegtup;		/* # of negative tuples */
 	ScanKeyData cc_skey[CATCACHE_MAXKEYS];	/* precomputed key info for heap
 											 * scans */
 
@@ -217,6 +218,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 8a0be41..26ac57c 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -132,6 +132,8 @@ extern HeapTuple SearchSysCache4(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

