diff --git a/src/backend/access/rmgrdesc/standbydesc.c b/src/backend/access/rmgrdesc/standbydesc.c
index a7f367c..79823c2 100644
*** a/src/backend/access/rmgrdesc/standbydesc.c
--- b/src/backend/access/rmgrdesc/standbydesc.c
*************** standby_desc_invalidations(StringInfo bu
*** 113,120 ****
  
  		switch ((SharedInvalMsgType) msg->id)
  		{
! 			case SharedInvalCatcache:
! 				appendStringInfo(buf, " catcache %d", msg->cc.cacheId);
  				break;
  			case SharedInvalCatalog:
  				appendStringInfo(buf, " catalog %u", msg->cat.catId);
--- 113,123 ----
  
  		switch ((SharedInvalMsgType) msg->id)
  		{
! 			case SharedInvalCatcacheHash:
! 				appendStringInfo(buf, " catcache %d by hash", msg->cch.cacheId);
! 				break;
! 			case SharedInvalCatcacheOid:
! 				appendStringInfo(buf, " catcache %d by OID", msg->cco.cacheId);
  				break;
  			case SharedInvalCatalog:
  				appendStringInfo(buf, " catalog %u", msg->cat.catId);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 472285d..ebf4321 100644
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
*************** RemoveStatistics(Oid relid, AttrNumber a
*** 3025,3030 ****
--- 3025,3048 ----
  
  	systable_endscan(scan);
  
+ 	/*
+ 	 * Aside from removing the catalog entries, issue sinval messages to
+ 	 * remove any negative catcache entries for stats that weren't present.
+ 	 * (Positive entries will get flushed as a consequence of deleting the
+ 	 * catalog entries.)  Without this, repeatedly creating and dropping temp
+ 	 * tables tends to lead to catcache bloat, since any negative catcache
+ 	 * entries created by planner lookups won't get dropped.
+ 	 *
+ 	 * We only bother with this for the whole-table case, since (a) it's less
+ 	 * likely to be a problem for DROP COLUMN, and (b) the sinval
+ 	 * infrastructure only supports matching an OID cache key column.
+ 	 * (Alternatively, we could issue the sinval message always, accepting the
+ 	 * collateral damage of losing negative catcache entries for other columns
+ 	 * to be sure we get rid of entries for this one.)
+ 	 */
+ 	if (attnum == 0)
+ 		CacheInvalidateCatcacheByOid(STATRELATTINH, false, 1, relid);
+ 
  	heap_close(pgstatistic, RowExclusiveLock);
  }
  
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 8152f7e..81c01f6 100644
*** a/src/backend/utils/cache/catcache.c
--- b/src/backend/utils/cache/catcache.c
*************** CatCacheRemoveCList(CatCache *cache, Cat
*** 540,546 ****
  
  
  /*
!  *	CatCacheInvalidate
   *
   *	Invalidate entries in the specified cache, given a hash value.
   *
--- 540,546 ----
  
  
  /*
!  *	CatCacheInvalidateByHash
   *
   *	Invalidate entries in the specified cache, given a hash value.
   *
*************** CatCacheRemoveCList(CatCache *cache, Cat
*** 558,569 ****
   *	This routine is only quasi-public: it should only be used by inval.c.
   */
  void
! CatCacheInvalidate(CatCache *cache, uint32 hashValue)
  {
  	Index		hashIndex;
  	dlist_mutable_iter iter;
  
! 	CACHE1_elog(DEBUG2, "CatCacheInvalidate: called");
  
  	/*
  	 * We don't bother to check whether the cache has finished initialization
--- 558,569 ----
   *	This routine is only quasi-public: it should only be used by inval.c.
   */
  void
! CatCacheInvalidateByHash(CatCache *cache, uint32 hashValue)
  {
  	Index		hashIndex;
  	dlist_mutable_iter iter;
  
! 	CACHE1_elog(DEBUG2, "CatCacheInvalidateByHash: called");
  
  	/*
  	 * We don't bother to check whether the cache has finished initialization
*************** CatCacheInvalidate(CatCache *cache, uint
*** 603,609 ****
  			}
  			else
  				CatCacheRemoveCTup(cache, ct);
! 			CACHE1_elog(DEBUG2, "CatCacheInvalidate: invalidated");
  #ifdef CATCACHE_STATS
  			cache->cc_invals++;
  #endif
--- 603,609 ----
  			}
  			else
  				CatCacheRemoveCTup(cache, ct);
! 			CACHE1_elog(DEBUG2, "CatCacheInvalidateByHash: invalidated");
  #ifdef CATCACHE_STATS
  			cache->cc_invals++;
  #endif
*************** CatCacheInvalidate(CatCache *cache, uint
*** 612,617 ****
--- 612,683 ----
  	}
  }
  
+ /*
+  *	CatCacheInvalidateByOid
+  *
+  *	Invalidate negative entries in the specified cache, given a target OID.
+  *
+  *	We delete negative cache entries that have that OID value in column ckey.
+  *	While we could also examine positive entries, there's no need to do so in
+  *	current usage: any relevant positive entries should have been flushed by
+  *	CatCacheInvalidateByHash calls due to deletions of those catalog entries.
+  *
+  *	This routine is only quasi-public: it should only be used by inval.c.
+  */
+ void
+ CatCacheInvalidateByOid(CatCache *cache, int ckey, Oid oid)
+ {
+ 	dlist_mutable_iter iter;
+ 	int			i;
+ 
+ 	CACHE1_elog(DEBUG2, "CatCacheInvalidateByOid: called");
+ 
+ 	/* If the cache hasn't finished initialization, there's nothing to do */
+ 	if (cache->cc_tupdesc == NULL)
+ 		return;
+ 
+ 	/* Assert that an OID column has been targeted */
+ 	Assert(TupleDescAttr(cache->cc_tupdesc,
+ 						 cache->cc_keyno[ckey - 1] - 1)->atttypid == OIDOID);
+ 
+ 	/*
+ 	 * There seems no need to flush CatCLists; removal of negative entries
+ 	 * shouldn't affect the validity of searches.
+ 	 */
+ 
+ 	/*
+ 	 * Scan the whole cache for matches
+ 	 */
+ 	for (i = 0; i < cache->cc_nbuckets; i++)
+ 	{
+ 		dlist_head *bucket = &cache->cc_bucket[i];
+ 
+ 		dlist_foreach_modify(iter, bucket)
+ 		{
+ 			CatCTup    *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+ 
+ 			/* We only care about live negative entries */
+ 			if (ct->dead || !ct->negative)
+ 				continue;
+ 			/* Negative entries won't be in clists */
+ 			Assert(ct->c_list == NULL);
+ 
+ 			if (oid == DatumGetObjectId(ct->keys[ckey - 1]))
+ 			{
+ 				if (ct->refcount > 0)
+ 					ct->dead = true;
+ 				else
+ 					CatCacheRemoveCTup(cache, ct);
+ 				CACHE1_elog(DEBUG2, "CatCacheInvalidateByOid: invalidated");
+ #ifdef CATCACHE_STATS
+ 				cache->cc_invals++;
+ #endif
+ 				/* could be multiple matches, so keep looking! */
+ 			}
+ 		}
+ 	}
+ }
+ 
  /* ----------------------------------------------------------------
   *					   public functions
   * ----------------------------------------------------------------
*************** CatCacheCopyKeys(TupleDesc tupdesc, int 
*** 1995,2001 ****
   *	the specified relation, find all catcaches it could be in, compute the
   *	correct hash value for each such catcache, and call the specified
   *	function to record the cache id and hash value in inval.c's lists.
!  *	SysCacheInvalidate will be called later, if appropriate,
   *	using the recorded information.
   *
   *	For an insert or delete, tuple is the target tuple and newtuple is NULL.
--- 2061,2067 ----
   *	the specified relation, find all catcaches it could be in, compute the
   *	correct hash value for each such catcache, and call the specified
   *	function to record the cache id and hash value in inval.c's lists.
!  *	SysCacheInvalidateByHash will be called later, if appropriate,
   *	using the recorded information.
   *
   *	For an insert or delete, tuple is the target tuple and newtuple is NULL.
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index 5bc08b0..168a97d 100644
*** a/src/backend/utils/cache/inval.c
--- b/src/backend/utils/cache/inval.c
*************** AppendInvalidationMessageList(Invalidati
*** 331,349 ****
   */
  
  /*
!  * Add a catcache inval entry
   */
  static void
! AddCatcacheInvalidationMessage(InvalidationListHeader *hdr,
! 							   int id, uint32 hashValue, Oid dbId)
  {
  	SharedInvalidationMessage msg;
  
  	Assert(id < CHAR_MAX);
! 	msg.cc.id = SharedInvalCatcache;
! 	msg.cc.cacheId = (int8) id;
! 	msg.cc.dbId = dbId;
! 	msg.cc.hashValue = hashValue;
  
  	/*
  	 * Define padding bytes in SharedInvalidationMessage structs to be
--- 331,349 ----
   */
  
  /*
!  * Add a catcache inval-by-hash entry
   */
  static void
! AddCatcacheHashInvalidationMessage(InvalidationListHeader *hdr,
! 								   int id, uint32 hashValue, Oid dbId)
  {
  	SharedInvalidationMessage msg;
  
  	Assert(id < CHAR_MAX);
! 	msg.cch.id = SharedInvalCatcacheHash;
! 	msg.cch.cacheId = (int8) id;
! 	msg.cch.dbId = dbId;
! 	msg.cch.hashValue = hashValue;
  
  	/*
  	 * Define padding bytes in SharedInvalidationMessage structs to be
*************** AddCatcacheInvalidationMessage(Invalidat
*** 360,365 ****
--- 360,386 ----
  }
  
  /*
+  * Add a catcache inval-by-OID entry
+  */
+ static void
+ AddCatcacheOidInvalidationMessage(InvalidationListHeader *hdr,
+ 								  int id, int ckey, Oid oid, Oid dbId)
+ {
+ 	SharedInvalidationMessage msg;
+ 
+ 	Assert(id < CHAR_MAX);
+ 	msg.cco.id = SharedInvalCatcacheOid;
+ 	msg.cco.cacheId = (int8) id;
+ 	msg.cco.ckey = (int8) ckey;
+ 	msg.cco.oid = oid;
+ 	msg.cco.dbId = dbId;
+ 	/* check AddCatcacheHashInvalidationMessage() for an explanation */
+ 	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
+ 
+ 	AddInvalidationMessage(&hdr->cclist, &msg);
+ }
+ 
+ /*
   * Add a whole-catalog inval entry
   */
  static void
*************** AddCatalogInvalidationMessage(Invalidati
*** 371,377 ****
  	msg.cat.id = SharedInvalCatalog;
  	msg.cat.dbId = dbId;
  	msg.cat.catId = catId;
! 	/* check AddCatcacheInvalidationMessage() for an explanation */
  	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
  
  	AddInvalidationMessage(&hdr->cclist, &msg);
--- 392,398 ----
  	msg.cat.id = SharedInvalCatalog;
  	msg.cat.dbId = dbId;
  	msg.cat.catId = catId;
! 	/* check AddCatcacheHashInvalidationMessage() for an explanation */
  	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
  
  	AddInvalidationMessage(&hdr->cclist, &msg);
*************** AddRelcacheInvalidationMessage(Invalidat
*** 401,407 ****
  	msg.rc.id = SharedInvalRelcache;
  	msg.rc.dbId = dbId;
  	msg.rc.relId = relId;
! 	/* check AddCatcacheInvalidationMessage() for an explanation */
  	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
  
  	AddInvalidationMessage(&hdr->rclist, &msg);
--- 422,428 ----
  	msg.rc.id = SharedInvalRelcache;
  	msg.rc.dbId = dbId;
  	msg.rc.relId = relId;
! 	/* check AddCatcacheHashInvalidationMessage() for an explanation */
  	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
  
  	AddInvalidationMessage(&hdr->rclist, &msg);
*************** AddSnapshotInvalidationMessage(Invalidat
*** 427,433 ****
  	msg.sn.id = SharedInvalSnapshot;
  	msg.sn.dbId = dbId;
  	msg.sn.relId = relId;
! 	/* check AddCatcacheInvalidationMessage() for an explanation */
  	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
  
  	AddInvalidationMessage(&hdr->rclist, &msg);
--- 448,454 ----
  	msg.sn.id = SharedInvalSnapshot;
  	msg.sn.dbId = dbId;
  	msg.sn.relId = relId;
! 	/* check AddCatcacheHashInvalidationMessage() for an explanation */
  	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
  
  	AddInvalidationMessage(&hdr->rclist, &msg);
*************** ProcessInvalidationMessagesMulti(Invalid
*** 477,493 ****
   */
  
  /*
!  * RegisterCatcacheInvalidation
   *
!  * Register an invalidation event for a catcache tuple entry.
   */
  static void
! RegisterCatcacheInvalidation(int cacheId,
! 							 uint32 hashValue,
! 							 Oid dbId)
  {
! 	AddCatcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
! 								   cacheId, hashValue, dbId);
  }
  
  /*
--- 498,529 ----
   */
  
  /*
!  * RegisterCatcacheHashInvalidation
   *
!  * Register an invalidation event for a catcache tuple entry identified
!  * by hash value.
   */
  static void
! RegisterCatcacheHashInvalidation(int cacheId,
! 								 uint32 hashValue,
! 								 Oid dbId)
  {
! 	AddCatcacheHashInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
! 									   cacheId, hashValue, dbId);
! }
! 
! /*
!  * RegisterCatcacheOidInvalidation
!  *
!  * Register an invalidation event for catcache tuple entries having
!  * the specified OID in a particular cache key column.
!  */
! static void
! RegisterCatcacheOidInvalidation(int cacheId,
! 								int ckey, Oid oid, Oid dbId)
! {
! 	AddCatcacheOidInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
! 									  cacheId, ckey, oid, dbId);
  }
  
  /*
*************** LocalExecuteInvalidationMessage(SharedIn
*** 556,569 ****
  {
  	switch ((SharedInvalMsgType) msg->id)
  	{
! 		case SharedInvalCatcache:
! 			if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid)
  			{
  				InvalidateCatalogSnapshot();
  
! 				SysCacheInvalidate(msg->cc.cacheId, msg->cc.hashValue);
  
! 				CallSyscacheCallbacks(msg->cc.cacheId, msg->cc.hashValue);
  			}
  			break;
  		case SharedInvalCatalog:
--- 592,612 ----
  {
  	switch ((SharedInvalMsgType) msg->id)
  	{
! 		case SharedInvalCatcacheHash:
! 			if (msg->cch.dbId == MyDatabaseId || msg->cch.dbId == InvalidOid)
  			{
  				InvalidateCatalogSnapshot();
  
! 				SysCacheInvalidateByHash(msg->cch.cacheId, msg->cch.hashValue);
  
! 				CallSyscacheCallbacks(msg->cch.cacheId, msg->cch.hashValue);
! 			}
! 			break;
! 		case SharedInvalCatcacheOid:
! 			if (msg->cco.dbId == MyDatabaseId || msg->cco.dbId == InvalidOid)
! 			{
! 				SysCacheInvalidateByOid(msg->cco.cacheId, msg->cco.ckey,
! 										msg->cco.oid);
  			}
  			break;
  		case SharedInvalCatalog:
*************** CacheInvalidateHeapTuple(Relation relati
*** 1157,1163 ****
  	}
  	else
  		PrepareToInvalidateCacheTuple(relation, tuple, newtuple,
! 									  RegisterCatcacheInvalidation);
  
  	/*
  	 * Now, is this tuple one of the primary definers of a relcache entry? See
--- 1200,1206 ----
  	}
  	else
  		PrepareToInvalidateCacheTuple(relation, tuple, newtuple,
! 									  RegisterCatcacheHashInvalidation);
  
  	/*
  	 * Now, is this tuple one of the primary definers of a relcache entry? See
*************** CacheInvalidateHeapTuple(Relation relati
*** 1217,1222 ****
--- 1260,1286 ----
  }
  
  /*
+  * CacheInvalidateCatcacheByOid
+  *		Register invalidation of catcache entries referencing a given OID.
+  *
+  * This is used to kill negative catcache entries that are believed to be
+  * no longer useful.  The entries are identified by which cache they are
+  * in, the cache key column to look at, and the target OID.
+  *
+  * Note: we expect caller to know whether the specified cache is on a
+  * shared or local system catalog.  We could ask syscache.c for that info,
+  * but it seems probably not worth the trouble, since this is likely to
+  * have few callers.
+  */
+ void
+ CacheInvalidateCatcacheByOid(int cacheId, bool isshared, int ckey, Oid oid)
+ {
+ 	Oid			dbId = isshared ? (Oid) 0 : MyDatabaseId;
+ 
+ 	RegisterCatcacheOidInvalidation(cacheId, ckey, oid, dbId);
+ }
+ 
+ /*
   * CacheInvalidateCatalog
   *		Register invalidation of the whole content of a system catalog.
   *
*************** CacheInvalidateSmgr(RelFileNodeBackend r
*** 1359,1365 ****
  	msg.sm.backend_hi = rnode.backend >> 16;
  	msg.sm.backend_lo = rnode.backend & 0xffff;
  	msg.sm.rnode = rnode.node;
! 	/* check AddCatcacheInvalidationMessage() for an explanation */
  	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
  
  	SendSharedInvalidMessages(&msg, 1);
--- 1423,1429 ----
  	msg.sm.backend_hi = rnode.backend >> 16;
  	msg.sm.backend_lo = rnode.backend & 0xffff;
  	msg.sm.rnode = rnode.node;
! 	/* check AddCatcacheHashInvalidationMessage() for an explanation */
  	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
  
  	SendSharedInvalidMessages(&msg, 1);
*************** CacheInvalidateRelmap(Oid databaseId)
*** 1387,1393 ****
  
  	msg.rm.id = SharedInvalRelmap;
  	msg.rm.dbId = databaseId;
! 	/* check AddCatcacheInvalidationMessage() for an explanation */
  	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
  
  	SendSharedInvalidMessages(&msg, 1);
--- 1451,1457 ----
  
  	msg.rm.id = SharedInvalRelmap;
  	msg.rm.dbId = databaseId;
! 	/* check AddCatcacheHashInvalidationMessage() for an explanation */
  	VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
  
  	SendSharedInvalidMessages(&msg, 1);
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index ac98c19..3e5acd5 100644
*** a/src/backend/utils/cache/syscache.c
--- b/src/backend/utils/cache/syscache.c
*************** SearchSysCacheList(int cacheId, int nkey
*** 1434,1448 ****
  }
  
  /*
!  * SysCacheInvalidate
   *
   *	Invalidate entries in the specified cache, given a hash value.
!  *	See CatCacheInvalidate() for more info.
   *
   *	This routine is only quasi-public: it should only be used by inval.c.
   */
  void
! SysCacheInvalidate(int cacheId, uint32 hashValue)
  {
  	if (cacheId < 0 || cacheId >= SysCacheSize)
  		elog(ERROR, "invalid cache ID: %d", cacheId);
--- 1434,1448 ----
  }
  
  /*
!  * SysCacheInvalidateByHash
   *
   *	Invalidate entries in the specified cache, given a hash value.
!  *	See CatCacheInvalidateByHash() for more info.
   *
   *	This routine is only quasi-public: it should only be used by inval.c.
   */
  void
! SysCacheInvalidateByHash(int cacheId, uint32 hashValue)
  {
  	if (cacheId < 0 || cacheId >= SysCacheSize)
  		elog(ERROR, "invalid cache ID: %d", cacheId);
*************** SysCacheInvalidate(int cacheId, uint32 h
*** 1451,1457 ****
  	if (!PointerIsValid(SysCache[cacheId]))
  		return;
  
! 	CatCacheInvalidate(SysCache[cacheId], hashValue);
  }
  
  /*
--- 1451,1478 ----
  	if (!PointerIsValid(SysCache[cacheId]))
  		return;
  
! 	CatCacheInvalidateByHash(SysCache[cacheId], hashValue);
! }
! 
! /*
!  * SysCacheInvalidateByOid
!  *
!  *	Invalidate negative entries in the specified cache, given a target OID.
!  *	See CatCacheInvalidateByOid() for more info.
!  *
!  *	This routine is only quasi-public: it should only be used by inval.c.
!  */
! void
! SysCacheInvalidateByOid(int cacheId, int ckey, Oid oid)
! {
! 	if (cacheId < 0 || cacheId >= SysCacheSize)
! 		elog(ERROR, "invalid cache ID: %d", cacheId);
! 
! 	/* if this cache isn't initialized yet, no need to do anything */
! 	if (!PointerIsValid(SysCache[cacheId]))
! 		return;
! 
! 	CatCacheInvalidateByOid(SysCache[cacheId], ckey, oid);
  }
  
  /*
diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h
index d0d9ece..004cb45 100644
*** a/src/include/storage/sinval.h
--- b/src/include/storage/sinval.h
***************
*** 20,26 ****
  
  /*
   * We support several types of shared-invalidation messages:
!  *	* invalidate a specific tuple in a specific catcache
   *	* invalidate all catcache entries from a given system catalog
   *	* invalidate a relcache entry for a specific logical relation
   *	* invalidate all relcache entries
--- 20,27 ----
  
  /*
   * We support several types of shared-invalidation messages:
!  *	* invalidate a specific tuple (identified by hash) in a specific catcache
!  *	* invalidate negative entries matching a given OID in a specific catcache
   *	* invalidate all catcache entries from a given system catalog
   *	* invalidate a relcache entry for a specific logical relation
   *	* invalidate all relcache entries
***************
*** 30,36 ****
   * More types could be added if needed.  The message type is identified by
   * the first "int8" field of the message struct.
   *
!  * Catcache inval events are initially driven by detecting tuple inserts,
   * updates and deletions in system catalogs (see CacheInvalidateHeapTuple).
   * An update can generate two inval events, one for the old tuple and one for
   * the new, but this is reduced to one event if the tuple's hash key doesn't
--- 31,37 ----
   * More types could be added if needed.  The message type is identified by
   * the first "int8" field of the message struct.
   *
!  * Catcache hash inval events are initially driven by detecting tuple inserts,
   * updates and deletions in system catalogs (see CacheInvalidateHeapTuple).
   * An update can generate two inval events, one for the old tuple and one for
   * the new, but this is reduced to one event if the tuple's hash key doesn't
***************
*** 57,63 ****
  
  typedef enum SharedInvalMsgType
  {
! 	SharedInvalCatcache,
  	SharedInvalCatalog,
  	SharedInvalRelcache,
  	SharedInvalSmgr,
--- 58,65 ----
  
  typedef enum SharedInvalMsgType
  {
! 	SharedInvalCatcacheHash,
! 	SharedInvalCatcacheOid,
  	SharedInvalCatalog,
  	SharedInvalRelcache,
  	SharedInvalSmgr,
*************** typedef struct
*** 71,77 ****
  	int8		cacheId;		/* cache ID */
  	Oid			dbId;			/* database ID, or 0 if a shared relation */
  	uint32		hashValue;		/* hash value of key for this catcache */
! } SharedInvalCatcacheMsg;
  
  typedef struct
  {
--- 73,88 ----
  	int8		cacheId;		/* cache ID */
  	Oid			dbId;			/* database ID, or 0 if a shared relation */
  	uint32		hashValue;		/* hash value of key for this catcache */
! } SharedInvalCatcacheHashMsg;
! 
! typedef struct
! {
! 	int8		id;				/* type field --- must be first */
! 	int8		cacheId;		/* cache ID */
! 	int8		ckey;			/* cache key column (1..CATCACHE_MAXKEYS) */
! 	Oid			oid;			/* OID of cache entries to remove */
! 	Oid			dbId;			/* database ID, or 0 if a shared relation */
! } SharedInvalCatcacheOidMsg;
  
  typedef struct
  {
*************** typedef struct
*** 112,118 ****
  typedef union
  {
  	int8		id;				/* type field --- must be first */
! 	SharedInvalCatcacheMsg cc;
  	SharedInvalCatalogMsg cat;
  	SharedInvalRelcacheMsg rc;
  	SharedInvalSmgrMsg sm;
--- 123,130 ----
  typedef union
  {
  	int8		id;				/* type field --- must be first */
! 	SharedInvalCatcacheHashMsg cch;
! 	SharedInvalCatcacheOidMsg cco;
  	SharedInvalCatalogMsg cat;
  	SharedInvalRelcacheMsg rc;
  	SharedInvalSmgrMsg sm;
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index 65d816a..47b72d6 100644
*** a/src/include/utils/catcache.h
--- b/src/include/utils/catcache.h
*************** extern void ReleaseCatCacheList(CatCList
*** 219,225 ****
  
  extern void ResetCatalogCaches(void);
  extern void CatalogCacheFlushCatalog(Oid catId);
! extern void CatCacheInvalidate(CatCache *cache, uint32 hashValue);
  extern void PrepareToInvalidateCacheTuple(Relation relation,
  							  HeapTuple tuple,
  							  HeapTuple newtuple,
--- 219,226 ----
  
  extern void ResetCatalogCaches(void);
  extern void CatalogCacheFlushCatalog(Oid catId);
! extern void CatCacheInvalidateByHash(CatCache *cache, uint32 hashValue);
! extern void CatCacheInvalidateByOid(CatCache *cache, int ckey, Oid oid);
  extern void PrepareToInvalidateCacheTuple(Relation relation,
  							  HeapTuple tuple,
  							  HeapTuple newtuple,
diff --git a/src/include/utils/inval.h b/src/include/utils/inval.h
index c557640..d1181bc 100644
*** a/src/include/utils/inval.h
--- b/src/include/utils/inval.h
*************** extern void CacheInvalidateHeapTuple(Rel
*** 39,44 ****
--- 39,47 ----
  						 HeapTuple tuple,
  						 HeapTuple newtuple);
  
+ extern void CacheInvalidateCatcacheByOid(int cacheId, bool isshared,
+ 							 int ckey, Oid oid);
+ 
  extern void CacheInvalidateCatalog(Oid catalogId);
  
  extern void CacheInvalidateRelcache(Relation relation);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 95ee489..983fd00 100644
*** a/src/include/utils/syscache.h
--- b/src/include/utils/syscache.h
*************** struct catclist;
*** 159,165 ****
  extern struct catclist *SearchSysCacheList(int cacheId, int nkeys,
  				   Datum key1, Datum key2, Datum key3);
  
! extern void SysCacheInvalidate(int cacheId, uint32 hashValue);
  
  extern bool RelationInvalidatesSnapshotsOnly(Oid relid);
  extern bool RelationHasSysCache(Oid relid);
--- 159,166 ----
  extern struct catclist *SearchSysCacheList(int cacheId, int nkeys,
  				   Datum key1, Datum key2, Datum key3);
  
! extern void SysCacheInvalidateByHash(int cacheId, uint32 hashValue);
! extern void SysCacheInvalidateByOid(int cacheId, int ckey, Oid oid);
  
  extern bool RelationInvalidatesSnapshotsOnly(Oid relid);
  extern bool RelationHasSysCache(Oid relid);
