From 9b7e523bd4960d42847b849cde226f7d97deb1b9 Mon Sep 17 00:00:00 2001
From: Palak <palakchaturvedi2843@gmail.com>
Date: Fri, 30 Jun 2023 08:21:06 +0000
Subject: [PATCH v4] Invalidate Buffer By Bufnum

---
 contrib/pg_buffercache/Makefile               |  2 +-
 .../expected/pg_buffercache.out               | 31 ++++++++
 contrib/pg_buffercache/meson.build            |  1 +
 .../pg_buffercache--1.4--1.5.sql              |  6 ++
 contrib/pg_buffercache/pg_buffercache.control |  2 +-
 contrib/pg_buffercache/pg_buffercache_pages.c | 16 ++++
 contrib/pg_buffercache/sql/pg_buffercache.sql |  9 +++
 src/backend/storage/buffer/bufmgr.c           | 74 +++++++++++++++++++
 src/include/storage/bufmgr.h                  |  2 +
 9 files changed, 141 insertions(+), 2 deletions(-)
 create mode 100644 contrib/pg_buffercache/pg_buffercache--1.4--1.5.sql

diff --git a/contrib/pg_buffercache/Makefile b/contrib/pg_buffercache/Makefile
index d6b58d4da9..eae65ead9e 100644
--- a/contrib/pg_buffercache/Makefile
+++ b/contrib/pg_buffercache/Makefile
@@ -8,7 +8,7 @@ OBJS = \
 EXTENSION = pg_buffercache
 DATA = pg_buffercache--1.2.sql pg_buffercache--1.2--1.3.sql \
 	pg_buffercache--1.1--1.2.sql pg_buffercache--1.0--1.1.sql \
-	pg_buffercache--1.3--1.4.sql
+	pg_buffercache--1.3--1.4.sql pg_buffercache--1.4--1.5.sql
 PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time"
 
 REGRESS = pg_buffercache
diff --git a/contrib/pg_buffercache/expected/pg_buffercache.out b/contrib/pg_buffercache/expected/pg_buffercache.out
index b745dc69ea..75d6f41164 100644
--- a/contrib/pg_buffercache/expected/pg_buffercache.out
+++ b/contrib/pg_buffercache/expected/pg_buffercache.out
@@ -55,3 +55,34 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
  t
 (1 row)
 
+-- Check pg_buffercache_invalidate
+SELECT pg_buffercache_invalidate(NULL, NULL);
+ pg_buffercache_invalidate 
+---------------------------
+ 
+(1 row)
+
+SELECT pg_buffercache_invalidate(1, NULL);
+ pg_buffercache_invalidate 
+---------------------------
+ 
+(1 row)
+
+SELECT pg_buffercache_invalidate(NULL, false);
+ pg_buffercache_invalidate 
+---------------------------
+ 
+(1 row)
+
+SELECT pg_buffercache_invalidate(NULL);
+ pg_buffercache_invalidate 
+---------------------------
+ 
+(1 row)
+
+SELECT pg_buffercache_invalidate(1);
+ pg_buffercache_invalidate 
+---------------------------
+ t
+(1 row)
+
diff --git a/contrib/pg_buffercache/meson.build b/contrib/pg_buffercache/meson.build
index c86e33cc95..1ca3452918 100644
--- a/contrib/pg_buffercache/meson.build
+++ b/contrib/pg_buffercache/meson.build
@@ -22,6 +22,7 @@ install_data(
   'pg_buffercache--1.2--1.3.sql',
   'pg_buffercache--1.2.sql',
   'pg_buffercache--1.3--1.4.sql',
+  'pg_buffercache--1.4--1.5.sql',
   'pg_buffercache.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/pg_buffercache/pg_buffercache--1.4--1.5.sql b/contrib/pg_buffercache/pg_buffercache--1.4--1.5.sql
new file mode 100644
index 0000000000..c582260f50
--- /dev/null
+++ b/contrib/pg_buffercache/pg_buffercache--1.4--1.5.sql
@@ -0,0 +1,6 @@
+\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.5'" to load this file. \quit
+
+CREATE FUNCTION pg_buffercache_invalidate(IN int, IN bool default true)
+RETURNS bool
+AS 'MODULE_PATHNAME', 'pg_buffercache_invalidate'
+LANGUAGE C PARALLEL SAFE STRICT;
diff --git a/contrib/pg_buffercache/pg_buffercache.control b/contrib/pg_buffercache/pg_buffercache.control
index a82ae5f9bb..5ee875f77d 100644
--- a/contrib/pg_buffercache/pg_buffercache.control
+++ b/contrib/pg_buffercache/pg_buffercache.control
@@ -1,5 +1,5 @@
 # pg_buffercache extension
 comment = 'examine the shared buffer cache'
-default_version = '1.4'
+default_version = '1.5'
 module_pathname = '$libdir/pg_buffercache'
 relocatable = true
diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c
index 3316732365..b010f13c98 100644
--- a/contrib/pg_buffercache/pg_buffercache_pages.c
+++ b/contrib/pg_buffercache/pg_buffercache_pages.c
@@ -63,6 +63,7 @@ typedef struct
 PG_FUNCTION_INFO_V1(pg_buffercache_pages);
 PG_FUNCTION_INFO_V1(pg_buffercache_summary);
 PG_FUNCTION_INFO_V1(pg_buffercache_usage_counts);
+PG_FUNCTION_INFO_V1(pg_buffercache_invalidate);
 
 Datum
 pg_buffercache_pages(PG_FUNCTION_ARGS)
@@ -347,3 +348,18 @@ pg_buffercache_usage_counts(PG_FUNCTION_ARGS)
 
 	return (Datum) 0;
 }
+
+/*
+ * Perform buffer invaidation.
+ */
+Datum
+pg_buffercache_invalidate(PG_FUNCTION_ARGS)
+{
+	Buffer		buf = PG_GETARG_INT32(0);
+	bool		force = PG_GETARG_BOOL(1);
+
+	if (buf > NBuffers || buf < -NLocBuffer || BufferIsInvalid(buf))
+		elog(ERROR, "bad buffer ID: %d", buf);
+
+	PG_RETURN_BOOL(TryInvalidateBuffer(buf, force));
+}
diff --git a/contrib/pg_buffercache/sql/pg_buffercache.sql b/contrib/pg_buffercache/sql/pg_buffercache.sql
index 944fbb1bea..f6671b96cc 100644
--- a/contrib/pg_buffercache/sql/pg_buffercache.sql
+++ b/contrib/pg_buffercache/sql/pg_buffercache.sql
@@ -26,3 +26,12 @@ SET ROLE pg_monitor;
 SELECT count(*) > 0 FROM pg_buffercache;
 SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary();
 SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
+
+-- Check pg_buffercache_invalidate
+SELECT pg_buffercache_invalidate(NULL, NULL);
+SELECT pg_buffercache_invalidate(1, NULL);
+SELECT pg_buffercache_invalidate(NULL, false);
+SELECT pg_buffercache_invalidate(NULL);
+
+
+SELECT pg_buffercache_invalidate(1);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index f0f8d4259c..4db993d1a6 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -5704,3 +5704,77 @@ ResOwnerPrintBufferPin(Datum res)
 {
 	return DebugPrintBufferRefcount(DatumGetInt32(res));
 }
+
+/*
+ * Try Invalidating a shared buffer.
+ *
+ * If the buffer is invalid, the function returns false.  The function checks
+ * for dirty buffer and flushes the dirty buffer before invalidating.  If the
+ * buffer is still dirty it returns false.
+ */
+bool
+TryInvalidateBuffer(Buffer buf, bool force)
+{
+	BufferDesc *desc;
+	uint32		buf_state;
+
+	ReservePrivateRefCountEntry();
+
+	desc = BufferIsLocal(buf) ? GetLocalBufferDescriptor(-buf - 1) :
+								GetBufferDescriptor(buf - 1);
+
+	buf_state = LockBufHdr(desc);
+	if ((buf_state & BM_VALID) != BM_VALID)
+	{
+		UnlockBufHdr(desc, buf_state);
+		return false;
+	}
+
+	/* The buffer is pinned therefore cannot invalidate. */
+	if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
+	{
+		UnlockBufHdr(desc, buf_state);
+		return false;
+	}
+
+	if ((buf_state & BM_DIRTY) == BM_DIRTY)
+	{
+		/*
+		 * If the buffer is dirty and the user has not asked to clear the
+		 * dirty buffer return false. Otherwise clear the dirty buffer.
+		 */
+		if (!force)
+		{
+			UnlockBufHdr(desc, buf_state);
+			return false;
+		}
+
+		/* Try once to flush the dirty buffer. */
+		ResourceOwnerEnlarge(CurrentResourceOwner);
+		PinBuffer_Locked(desc);
+		LWLockAcquire(BufferDescriptorGetContentLock(desc), LW_SHARED);
+		FlushBuffer(desc, NULL, IOOBJECT_RELATION, IOCONTEXT_NORMAL);
+		LWLockRelease(BufferDescriptorGetContentLock(desc));
+
+		UnpinBuffer(desc);
+
+		/* If buffer is still used by someone, return false. */
+		buf_state = LockBufHdr(desc);
+		if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
+		{
+			UnlockBufHdr(desc, buf_state);
+			return false;
+		}
+
+		/* If its dirty again or not valid anymore give up. */
+		if ((buf_state & (BM_DIRTY | BM_VALID)) != (BM_VALID))
+		{
+			UnlockBufHdr(desc, buf_state);
+			return false;
+		}
+
+	}
+
+	InvalidateBuffer(desc);		/* releases spinlock */
+	return true;
+}
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index d51d46d335..a0069b0e0e 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -250,6 +250,8 @@ extern bool HoldingBufferPinThatDelaysRecovery(void);
 
 extern bool BgBufferSync(struct WritebackContext *wb_context);
 
+extern bool TryInvalidateBuffer(Buffer buf, bool force);
+
 /* in buf_init.c */
 extern void InitBufferPool(void);
 extern Size BufferShmemSize(void);
-- 
2.43.0

