From 5ee9ae963e3a6143975f543761f51b1bc0dc5de8 Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Fri, 23 Jul 2021 16:00:31 +0500
Subject: [PATCH] Avoid duplication in relcache and syscache callbacks

---
 src/backend/utils/cache/inval.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index dcfd9e8389..692ec8f04f 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -1430,17 +1430,32 @@ CacheInvalidateRelmap(Oid databaseId)
  * Yes, there's a possibility of a false match to zero, but it doesn't seem
  * worth troubling over, especially since most of the current callees just
  * flush all cached state anyway.
+ * Each callback\arg will we called only once, even if it was registered
+ * many times.
  */
 void
 CacheRegisterSyscacheCallback(int cacheid,
 							  SyscacheCallbackFunction func,
 							  Datum arg)
 {
+	int i;
 	if (cacheid < 0 || cacheid >= SysCacheSize)
 		elog(FATAL, "invalid cache ID: %d", cacheid);
 	if (syscache_callback_count >= MAX_SYSCACHE_CALLBACKS)
 		elog(FATAL, "out of syscache_callback_list slots");
 
+	for (i = 0; i < syscache_callback_count; i++)
+	{
+		if (syscache_callback_list[i].id == cacheid &&
+			syscache_callback_list[i].function == func &&
+			syscache_callback_list[i].arg == arg)
+			{
+				elog(DEBUG1, "Duplicate registartion of syscache callback");
+				/* Already registered */
+				return;
+			}
+	}
+
 	if (syscache_callback_links[cacheid] == 0)
 	{
 		/* first callback for this cache */
@@ -1472,14 +1487,28 @@ CacheRegisterSyscacheCallback(int cacheid,
  *
  * NOTE: InvalidOid will be passed if a cache reset request is received.
  * In this case the called routines should flush all cached state.
+ * Each callback\arg will we called only once, even if it was registered
+ * many times.
  */
 void
 CacheRegisterRelcacheCallback(RelcacheCallbackFunction func,
 							  Datum arg)
 {
+	int i;
 	if (relcache_callback_count >= MAX_RELCACHE_CALLBACKS)
 		elog(FATAL, "out of relcache_callback_list slots");
 
+	for (i = 0; i < relcache_callback_count; i++)
+	{
+		if (relcache_callback_list[i].function == func &&
+			relcache_callback_list[i].arg == arg)
+			{
+				/* Already registered */
+				elog(DEBUG1, "Duplicate registartion of relcache callback");
+				return;
+			}
+	}
+
 	relcache_callback_list[relcache_callback_count].function = func;
 	relcache_callback_list[relcache_callback_count].arg = arg;
 
-- 
2.24.3 (Apple Git-128)

