At Wed, 21 Dec 2016 10:21:09 +0900 (Tokyo Standard Time), Kyotaro HORIGUCHI 
<horiguchi.kyot...@lab.ntt.co.jp> wrote in 
<20161221.102109.51106943.horiguchi.kyot...@lab.ntt.co.jp>
> At Tue, 20 Dec 2016 15:10:21 -0500, Tom Lane <t...@sss.pgh.pa.us> wrote in 
> <23492.1482264...@sss.pgh.pa.us>
> > The bigger picture here though is that we used to have limits on syscache
> > size, and we got rid of them (commit 8b9bc234a, see also
> > https://www.postgresql.org/message-id/flat/5141.1150327541%40sss.pgh.pa.us)
> > not only because of the problem you mentioned about performance falling
> > off a cliff once the working-set size exceeded the arbitrary limit, but
> > also because enforcing the limit added significant overhead --- and did so
> > whether or not you got any benefit from it, ie even if the limit is never
> > reached.  Maybe the present patch avoids imposing a pile of overhead in
> > situations where no pruning is needed, but it doesn't really look very
> > promising from that angle in a quick once-over.
> 
> Indeed. As mentioned in the mail at the beginning of this thread,
> it hits the whole-cache scanning if at least one negative cache
> exists even it is not in a relation with the target relid, and it
> can be significantly long on a fat cache.
> 
> Lists of negative entries like CatCacheList would help but needs
> additional memeory.
> 
> > BTW, I don't see the point of the second patch at all?  Surely, if
> > an object is deleted or updated, we already have code that flushes
> > related catcache entries.  Otherwise the caches would deliver wrong
> > data.
> 
> Maybe you take the patch wrongly. Negative entires won't be
> flushed by any means. Deletion of a namespace causes cascaded
> object deletion according to dependency then finaly goes to
> non-neative cache invalidation. But a removal of *negative
> entries* in RELNAMENSP won't happen.
> 
> The test script for the case (gen2.pl) does the following thing,
> 
> CREATE SCHEMA foo;
> SELECT * FROM foo.invalid;
> DROP SCHEMA foo;
> 
> Removing the schema foo leaves a negative cache entry for
> 'foo.invalid' in RELNAMENSP.
> 
> However, I'm not sure the above situation happens so frequent
> that it is worthwhile to amend.

Since 1753b1b conflicts this patch, I rebased this onto the
current master HEAD. I'll register this to the next CF.

The points of discussion are the following, I think.

1. The first patch seems working well. It costs the time to scan
   the whole of a catcache that have negative entries for other
   reloids. However, such negative entries are created by rather
   unusual usages. Accesing to undefined columns, and accessing
   columns on which no statistics have created. The
   whole-catcache scan occurs on ATTNAME, ATTNUM and
   STATRELATTINH for every invalidation of a relcache entry.

2. The second patch also works, but flushing negative entries by
   hash values is inefficient. It scans the bucket corresponding
   to given hash value for OIDs, then flushing negative entries
   iterating over all the collected OIDs. So this costs more time
   than 1 and flushes involving entries that is not necessary to
   be removed. If this feature is valuable but such side effects
   are not acceptable, new invalidation category based on
   cacheid-oid pair would be needed.

regards,

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
>From ee0cc13f70d79f23ec9507cf977228bba091bc49 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyot...@lab.ntt.co.jp>
Date: Thu, 15 Dec 2016 17:43:03 +0900
Subject: [PATCH 1/2] Cleanup negative cache of pg_statistic when dropping a
 relation.

Accessing columns that don't have statistics causes 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 can be
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 | 277 +++++++++++++++++++++++++++----------
 src/include/utils/catcache.h       |   3 +
 src/include/utils/syscache.h       |   2 +
 4 files changed, 265 insertions(+), 74 deletions(-)

diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 6016d19..c1d9d2f 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;
@@ -637,6 +642,49 @@ ResetCatalogCache(CatCache *cache)
 }
 
 /*
+ *		CleanupCatCacheNegEntries
+ *
+ *	Remove negative cache tuples maching 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 needs 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;
+
+			/*
+			 * a negative cache entry cannot be refferenced so we can remove
+			 * it unconditionally
+			 */
+			CatCacheRemoveCTup(cache, ct);
+		}
+	}
+}
+
+
+/*
  *		ResetCatalogCaches
  *
  * Reset all caches when a shared cache inval event forces it
@@ -783,6 +831,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)
@@ -1279,8 +1328,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
@@ -1731,6 +1780,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 e87fe0e..e1ba693 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -70,6 +70,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"
 
 
@@ -115,6 +117,9 @@ 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;		/* attr number of reloid, 0 if nothing to do */
 };
 
 static const struct cachedesc cacheinfo[] = {
@@ -127,7 +132,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		16
+		16,
+		0
 	},
 	{AccessMethodRelationId,	/* AMNAME */
 		AmNameIndexId,
@@ -138,7 +144,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{AccessMethodRelationId,	/* AMOID */
 		AmOidIndexId,
@@ -149,7 +156,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{AccessMethodOperatorRelationId,	/* AMOPOPID */
 		AccessMethodOperatorIndexId,
@@ -160,7 +168,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amop_amopfamily,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{AccessMethodOperatorRelationId,	/* AMOPSTRATEGY */
 		AccessMethodStrategyIndexId,
@@ -171,7 +180,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amop_amoprighttype,
 			Anum_pg_amop_amopstrategy
 		},
-		64
+		64,
+		0
 	},
 	{AccessMethodProcedureRelationId,	/* AMPROCNUM */
 		AccessMethodProcedureIndexId,
@@ -182,7 +192,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amproc_amprocrighttype,
 			Anum_pg_amproc_amprocnum
 		},
-		16
+		16,
+		0
 	},
 	{AttributeRelationId,		/* ATTNAME */
 		AttributeRelidNameIndexId,
@@ -193,7 +204,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		32
+		32,
+		Anum_pg_attribute_attrelid
 	},
 	{AttributeRelationId,		/* ATTNUM */
 		AttributeRelidNumIndexId,
@@ -204,7 +216,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		128
+		128,
+		Anum_pg_attribute_attrelid
 	},
 	{AuthMemRelationId,			/* AUTHMEMMEMROLE */
 		AuthMemMemRoleIndexId,
@@ -215,7 +228,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{AuthMemRelationId,			/* AUTHMEMROLEMEM */
 		AuthMemRoleMemIndexId,
@@ -226,7 +240,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{AuthIdRelationId,			/* AUTHNAME */
 		AuthIdRolnameIndexId,
@@ -237,7 +252,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{AuthIdRelationId,			/* AUTHOID */
 		AuthIdOidIndexId,
@@ -248,10 +264,10 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
-	{
-		CastRelationId,			/* CASTSOURCETARGET */
+	{CastRelationId,			/* CASTSOURCETARGET */
 		CastSourceTargetIndexId,
 		2,
 		{
@@ -260,7 +276,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		256
+		256,
+		0
 	},
 	{OperatorClassRelationId,	/* CLAAMNAMENSP */
 		OpclassAmNameNspIndexId,
@@ -271,7 +288,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_opclass_opcnamespace,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{OperatorClassRelationId,	/* CLAOID */
 		OpclassOidIndexId,
@@ -282,7 +300,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{CollationRelationId,		/* COLLNAMEENCNSP */
 		CollationNameEncNspIndexId,
@@ -293,7 +312,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_collation_collnamespace,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{CollationRelationId,		/* COLLOID */
 		CollationOidIndexId,
@@ -304,7 +324,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{ConversionRelationId,		/* CONDEFAULT */
 		ConversionDefaultIndexId,
@@ -315,7 +336,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_conversion_contoencoding,
 			ObjectIdAttributeNumber,
 		},
-		8
+		8,
+		0
 	},
 	{ConversionRelationId,		/* CONNAMENSP */
 		ConversionNameNspIndexId,
@@ -326,7 +348,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{ConstraintRelationId,		/* CONSTROID */
 		ConstraintOidIndexId,
@@ -337,7 +360,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		16
+		16,
+		0
 	},
 	{ConversionRelationId,		/* CONVOID */
 		ConversionOidIndexId,
@@ -348,7 +372,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{DatabaseRelationId,		/* DATABASEOID */
 		DatabaseOidIndexId,
@@ -359,7 +384,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{DefaultAclRelationId,		/* DEFACLROLENSPOBJ */
 		DefaultAclRoleNspObjIndexId,
@@ -370,7 +396,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_default_acl_defaclobjtype,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{EnumRelationId,			/* ENUMOID */
 		EnumOidIndexId,
@@ -381,7 +408,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{EnumRelationId,			/* ENUMTYPOIDNAME */
 		EnumTypIdLabelIndexId,
@@ -392,7 +420,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{EventTriggerRelationId,	/* EVENTTRIGGERNAME */
 		EventTriggerNameIndexId,
@@ -403,7 +432,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{EventTriggerRelationId,	/* EVENTTRIGGEROID */
 		EventTriggerOidIndexId,
@@ -414,7 +444,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{ForeignDataWrapperRelationId,		/* FOREIGNDATAWRAPPERNAME */
 		ForeignDataWrapperNameIndexId,
@@ -425,7 +456,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{ForeignDataWrapperRelationId,		/* FOREIGNDATAWRAPPEROID */
 		ForeignDataWrapperOidIndexId,
@@ -436,7 +468,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{ForeignServerRelationId,	/* FOREIGNSERVERNAME */
 		ForeignServerNameIndexId,
@@ -447,7 +480,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{ForeignServerRelationId,	/* FOREIGNSERVEROID */
 		ForeignServerOidIndexId,
@@ -458,7 +492,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{ForeignTableRelationId,	/* FOREIGNTABLEREL */
 		ForeignTableRelidIndexId,
@@ -469,7 +504,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{IndexRelationId,			/* INDEXRELID */
 		IndexRelidIndexId,
@@ -480,7 +516,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{LanguageRelationId,		/* LANGNAME */
 		LanguageNameIndexId,
@@ -491,7 +528,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{LanguageRelationId,		/* LANGOID */
 		LanguageOidIndexId,
@@ -502,7 +540,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{NamespaceRelationId,		/* NAMESPACENAME */
 		NamespaceNameIndexId,
@@ -513,7 +552,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{NamespaceRelationId,		/* NAMESPACEOID */
 		NamespaceOidIndexId,
@@ -524,7 +564,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		16
+		16,
+		0
 	},
 	{OperatorRelationId,		/* OPERNAMENSP */
 		OperatorNameNspIndexId,
@@ -535,7 +576,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_operator_oprright,
 			Anum_pg_operator_oprnamespace
 		},
-		256
+		256,
+		0
 	},
 	{OperatorRelationId,		/* OPEROID */
 		OperatorOidIndexId,
@@ -546,7 +588,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		32
+		32,
+		0
 	},
 	{OperatorFamilyRelationId,	/* OPFAMILYAMNAMENSP */
 		OpfamilyAmNameNspIndexId,
@@ -557,7 +600,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_opfamily_opfnamespace,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{OperatorFamilyRelationId,	/* OPFAMILYOID */
 		OpfamilyOidIndexId,
@@ -568,7 +612,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{PartitionedRelationId,		/* PARTRELID */
 		PartitionedRelidIndexId,
@@ -579,7 +624,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		32
+		32,
+		0
 	},
 	{ProcedureRelationId,		/* PROCNAMEARGSNSP */
 		ProcedureNameArgsNspIndexId,
@@ -590,7 +636,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_proc_pronamespace,
 			0
 		},
-		128
+		128,
+		0
 	},
 	{ProcedureRelationId,		/* PROCOID */
 		ProcedureOidIndexId,
@@ -601,7 +648,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		128
+		128,
+		0
 	},
 	{RangeRelationId,			/* RANGETYPE */
 		RangeTypidIndexId,
@@ -612,7 +660,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		4
+		4,
+		0
 	},
 	{RelationRelationId,		/* RELNAMENSP */
 		ClassNameNspIndexId,
@@ -623,7 +672,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		128
+		128,
+		0
 	},
 	{RelationRelationId,		/* RELOID */
 		ClassOidIndexId,
@@ -634,7 +684,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		128
+		128,
+		0
 	},
 	{ReplicationOriginRelationId,		/* REPLORIGIDENT */
 		ReplicationOriginIdentIndex,
@@ -645,7 +696,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		16
+		16,
+		0
 	},
 	{ReplicationOriginRelationId,		/* REPLORIGNAME */
 		ReplicationOriginNameIndex,
@@ -656,7 +708,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		16
+		16,
+		0
 	},
 	{RewriteRelationId,			/* RULERELNAME */
 		RewriteRelRulenameIndexId,
@@ -667,7 +720,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		8
+		8,
+		0
 	},
 	{SequenceRelationId,			/* SEQRELID */
 		SequenceRelidIndexId,
@@ -678,7 +732,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		32
+		32,
+		0
 	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
@@ -689,7 +744,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_statistic_stainherit,
 			0
 		},
-		128
+		128,
+		Anum_pg_statistic_starelid
 	},
 	{TableSpaceRelationId,		/* TABLESPACEOID */
 		TablespaceOidIndexId,
@@ -700,7 +756,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0,
 		},
-		4
+		4,
+		0
 	},
 	{TransformRelationId,		/* TRFOID */
 		TransformOidIndexId,
@@ -711,7 +768,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0,
 		},
-		16
+		16,
+		0
 	},
 	{TransformRelationId,		/* TRFTYPELANG */
 		TransformTypeLangIndexId,
@@ -722,7 +780,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0,
 		},
-		16
+		16,
+		0
 	},
 	{TSConfigMapRelationId,		/* TSCONFIGMAP */
 		TSConfigMapIndexId,
@@ -733,7 +792,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_ts_config_map_mapseqno,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSConfigRelationId,		/* TSCONFIGNAMENSP */
 		TSConfigNameNspIndexId,
@@ -744,7 +804,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSConfigRelationId,		/* TSCONFIGOID */
 		TSConfigOidIndexId,
@@ -755,7 +816,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSDictionaryRelationId,	/* TSDICTNAMENSP */
 		TSDictionaryNameNspIndexId,
@@ -766,7 +828,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSDictionaryRelationId,	/* TSDICTOID */
 		TSDictionaryOidIndexId,
@@ -777,7 +840,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSParserRelationId,		/* TSPARSERNAMENSP */
 		TSParserNameNspIndexId,
@@ -788,7 +852,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSParserRelationId,		/* TSPARSEROID */
 		TSParserOidIndexId,
@@ -799,7 +864,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATENAMENSP */
 		TSTemplateNameNspIndexId,
@@ -810,7 +876,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATEOID */
 		TSTemplateOidIndexId,
@@ -821,7 +888,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{TypeRelationId,			/* TYPENAMENSP */
 		TypeNameNspIndexId,
@@ -832,7 +900,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{TypeRelationId,			/* TYPEOID */
 		TypeOidIndexId,
@@ -843,7 +912,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		64
+		64,
+		0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGOID */
 		UserMappingOidIndexId,
@@ -854,7 +924,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGUSERSERVER */
 		UserMappingUserServerIndexId,
@@ -865,7 +936,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 			0
 		},
-		2
+		2,
+		0
 	}
 };
 
@@ -883,8 +955,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
@@ -925,6 +1012,21 @@ InitCatalogCache(void)
 			cacheinfo[cacheId].indoid;
 		/* see comments for RelationInvalidatesSnapshotsOnly */
 		Assert(!RelationInvalidatesSnapshotsOnly(cacheinfo[cacheId].reloid));
+
+		/*
+		 * If this syscache has something to do with 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));
@@ -949,10 +1051,43 @@ InitCatalogCache(void)
 	}
 	SysCacheSupportingRelOidSize = j + 1;
 
+	/*
+	 * If any of syscache needs relcache invalidation callback, prepare the
+	 * scankey for reloid comparison and register a relcache inval callback.
+	 */
+	if (relinval_callback_count > 0)
+	{
+		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 is 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 253c7b5..cb662c0 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 */
@@ -183,6 +184,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 CatalogCacheIdInvalidate(int cacheId, uint32 hashValue);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4b7631e..22fea26 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -107,6 +107,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

>From 78e201522b53c1e11111f3ad411cff62a4516d10 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyot...@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 in turn is triggered by catcache invalidation. This patch
provides a syscache invalidation callback to flush negative cache
entries corresponding to an 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, 283 insertions(+), 71 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 e1ba693..cfcf4cd 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -108,6 +108,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
@@ -120,6 +130,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[] = {
@@ -133,7 +151,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodRelationId,	/* AMNAME */
 		AmNameIndexId,
@@ -145,7 +164,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodRelationId,	/* AMOID */
 		AmOidIndexId,
@@ -157,7 +177,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodOperatorRelationId,	/* AMOPOPID */
 		AccessMethodOperatorIndexId,
@@ -169,7 +190,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodOperatorRelationId,	/* AMOPSTRATEGY */
 		AccessMethodStrategyIndexId,
@@ -181,7 +203,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amop_amopstrategy
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AccessMethodProcedureRelationId,	/* AMPROCNUM */
 		AccessMethodProcedureIndexId,
@@ -193,7 +216,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_amproc_amprocnum
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AttributeRelationId,		/* ATTNAME */
 		AttributeRelidNameIndexId,
@@ -205,7 +229,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		Anum_pg_attribute_attrelid
+		Anum_pg_attribute_attrelid,
+		-1, 0, 0
 	},
 	{AttributeRelationId,		/* ATTNUM */
 		AttributeRelidNumIndexId,
@@ -217,7 +242,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		Anum_pg_attribute_attrelid
+		Anum_pg_attribute_attrelid,
+		-1, 0, 0
 	},
 	{AuthMemRelationId,			/* AUTHMEMMEMROLE */
 		AuthMemMemRoleIndexId,
@@ -229,7 +255,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AuthMemRelationId,			/* AUTHMEMROLEMEM */
 		AuthMemRoleMemIndexId,
@@ -241,7 +268,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AuthIdRelationId,			/* AUTHNAME */
 		AuthIdRolnameIndexId,
@@ -253,7 +281,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{AuthIdRelationId,			/* AUTHOID */
 		AuthIdOidIndexId,
@@ -265,7 +294,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{CastRelationId,			/* CASTSOURCETARGET */
 		CastSourceTargetIndexId,
@@ -277,7 +307,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		256,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorClassRelationId,	/* CLAAMNAMENSP */
 		OpclassAmNameNspIndexId,
@@ -289,7 +320,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorClassRelationId,	/* CLAOID */
 		OpclassOidIndexId,
@@ -301,7 +333,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{CollationRelationId,		/* COLLNAMEENCNSP */
 		CollationNameEncNspIndexId,
@@ -313,7 +346,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{CollationRelationId,		/* COLLOID */
 		CollationOidIndexId,
@@ -325,7 +359,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConversionRelationId,		/* CONDEFAULT */
 		ConversionDefaultIndexId,
@@ -337,7 +372,8 @@ static const struct cachedesc cacheinfo[] = {
 			ObjectIdAttributeNumber,
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConversionRelationId,		/* CONNAMENSP */
 		ConversionNameNspIndexId,
@@ -349,7 +385,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConstraintRelationId,		/* CONSTROID */
 		ConstraintOidIndexId,
@@ -361,7 +398,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ConversionRelationId,		/* CONVOID */
 		ConversionOidIndexId,
@@ -373,7 +411,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{DatabaseRelationId,		/* DATABASEOID */
 		DatabaseOidIndexId,
@@ -385,7 +424,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{DefaultAclRelationId,		/* DEFACLROLENSPOBJ */
 		DefaultAclRoleNspObjIndexId,
@@ -397,7 +437,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EnumRelationId,			/* ENUMOID */
 		EnumOidIndexId,
@@ -409,7 +450,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EnumRelationId,			/* ENUMTYPOIDNAME */
 		EnumTypIdLabelIndexId,
@@ -421,7 +463,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EventTriggerRelationId,	/* EVENTTRIGGERNAME */
 		EventTriggerNameIndexId,
@@ -433,7 +476,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{EventTriggerRelationId,	/* EVENTTRIGGEROID */
 		EventTriggerOidIndexId,
@@ -445,7 +489,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignDataWrapperRelationId,		/* FOREIGNDATAWRAPPERNAME */
 		ForeignDataWrapperNameIndexId,
@@ -457,7 +502,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignDataWrapperRelationId,		/* FOREIGNDATAWRAPPEROID */
 		ForeignDataWrapperOidIndexId,
@@ -469,7 +515,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignServerRelationId,	/* FOREIGNSERVERNAME */
 		ForeignServerNameIndexId,
@@ -481,7 +528,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignServerRelationId,	/* FOREIGNSERVEROID */
 		ForeignServerOidIndexId,
@@ -493,7 +541,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ForeignTableRelationId,	/* FOREIGNTABLEREL */
 		ForeignTableRelidIndexId,
@@ -505,7 +554,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{IndexRelationId,			/* INDEXRELID */
 		IndexRelidIndexId,
@@ -517,7 +567,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{LanguageRelationId,		/* LANGNAME */
 		LanguageNameIndexId,
@@ -529,7 +580,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{LanguageRelationId,		/* LANGOID */
 		LanguageOidIndexId,
@@ -541,7 +593,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{NamespaceRelationId,		/* NAMESPACENAME */
 		NamespaceNameIndexId,
@@ -553,7 +606,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{NamespaceRelationId,		/* NAMESPACEOID */
 		NamespaceOidIndexId,
@@ -565,7 +619,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorRelationId,		/* OPERNAMENSP */
 		OperatorNameNspIndexId,
@@ -577,7 +632,8 @@ static const struct cachedesc cacheinfo[] = {
 			Anum_pg_operator_oprnamespace
 		},
 		256,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorRelationId,		/* OPEROID */
 		OperatorOidIndexId,
@@ -589,7 +645,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorFamilyRelationId,	/* OPFAMILYAMNAMENSP */
 		OpfamilyAmNameNspIndexId,
@@ -601,7 +658,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{OperatorFamilyRelationId,	/* OPFAMILYOID */
 		OpfamilyOidIndexId,
@@ -613,7 +671,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{PartitionedRelationId,		/* PARTRELID */
 		PartitionedRelidIndexId,
@@ -625,7 +684,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ProcedureRelationId,		/* PROCNAMEARGSNSP */
 		ProcedureNameArgsNspIndexId,
@@ -637,7 +697,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ProcedureRelationId,		/* PROCOID */
 		ProcedureOidIndexId,
@@ -649,7 +710,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		-1, 0, 0
 	},
 	{RangeRelationId,			/* RANGETYPE */
 		RangeTypidIndexId,
@@ -661,7 +723,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{RelationRelationId,		/* RELNAMENSP */
 		ClassNameNspIndexId,
@@ -673,7 +736,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		NAMESPACEOID, ObjectIdAttributeNumber, Anum_pg_class_relnamespace
 	},
 	{RelationRelationId,		/* RELOID */
 		ClassOidIndexId,
@@ -685,7 +749,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ReplicationOriginRelationId,		/* REPLORIGIDENT */
 		ReplicationOriginIdentIndex,
@@ -697,7 +762,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{ReplicationOriginRelationId,		/* REPLORIGNAME */
 		ReplicationOriginNameIndex,
@@ -709,7 +775,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{RewriteRelationId,			/* RULERELNAME */
 		RewriteRelRulenameIndexId,
@@ -721,7 +788,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		8,
-		0
+		0,
+		-1, 0, 0
 	},
 	{SequenceRelationId,			/* SEQRELID */
 		SequenceRelidIndexId,
@@ -733,7 +801,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		32,
-		0
+		0,
+		-1, 0, 0
 	},
 	{StatisticRelationId,		/* STATRELATTINH */
 		StatisticRelidAttnumInhIndexId,
@@ -745,7 +814,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		128,
-		Anum_pg_statistic_starelid
+		Anum_pg_statistic_starelid,
+		-1, 0, 0
 	},
 	{TableSpaceRelationId,		/* TABLESPACEOID */
 		TablespaceOidIndexId,
@@ -757,7 +827,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 		},
 		4,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TransformRelationId,		/* TRFOID */
 		TransformOidIndexId,
@@ -769,7 +840,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TransformRelationId,		/* TRFTYPELANG */
 		TransformTypeLangIndexId,
@@ -781,7 +853,8 @@ static const struct cachedesc cacheinfo[] = {
 			0,
 		},
 		16,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSConfigMapRelationId,		/* TSCONFIGMAP */
 		TSConfigMapIndexId,
@@ -793,7 +866,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSConfigRelationId,		/* TSCONFIGNAMENSP */
 		TSConfigNameNspIndexId,
@@ -805,7 +879,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSConfigRelationId,		/* TSCONFIGOID */
 		TSConfigOidIndexId,
@@ -817,7 +892,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSDictionaryRelationId,	/* TSDICTNAMENSP */
 		TSDictionaryNameNspIndexId,
@@ -829,7 +905,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSDictionaryRelationId,	/* TSDICTOID */
 		TSDictionaryOidIndexId,
@@ -841,7 +918,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSParserRelationId,		/* TSPARSERNAMENSP */
 		TSParserNameNspIndexId,
@@ -853,7 +931,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSParserRelationId,		/* TSPARSEROID */
 		TSParserOidIndexId,
@@ -865,7 +944,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATENAMENSP */
 		TSTemplateNameNspIndexId,
@@ -877,7 +957,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TSTemplateRelationId,		/* TSTEMPLATEOID */
 		TSTemplateOidIndexId,
@@ -889,7 +970,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TypeRelationId,			/* TYPENAMENSP */
 		TypeNameNspIndexId,
@@ -901,7 +983,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{TypeRelationId,			/* TYPEOID */
 		TypeOidIndexId,
@@ -913,7 +996,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		64,
-		0
+		0,
+		-1, 0, 0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGOID */
 		UserMappingOidIndexId,
@@ -925,7 +1009,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	},
 	{UserMappingRelationId,		/* USERMAPPINGUSERSERVER */
 		UserMappingUserServerIndexId,
@@ -937,7 +1022,8 @@ static const struct cachedesc cacheinfo[] = {
 			0
 		},
 		2,
-		0
+		0,
+		-1, 0, 0
 	}
 };
 
@@ -972,7 +1058,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
  *
@@ -1027,6 +1114,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));
@@ -1403,6 +1518,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

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to