From b9282079afe18270403b08220731faae74c9dfd6 Mon Sep 17 00:00:00 2001
From: jcoleman <jtc331@gmail.com>
Date: Fri, 2 Jun 2023 10:01:06 -0400
Subject: [PATCH v2 1/6] Allow getting lock before calling
 heap_page_prune_opt()

---
 src/backend/access/heap/heapam.c         |  2 +-
 src/backend/access/heap/heapam_handler.c |  4 ++--
 src/backend/access/heap/pruneheap.c      | 15 +++++++++------
 src/include/access/heapam.h              |  2 +-
 4 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 88a123d38a..a1d3593f21 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -415,7 +415,7 @@ heapgetpage(TableScanDesc sscan, BlockNumber block)
 	/*
 	 * Prune and repair fragmentation for the whole page, if possible.
 	 */
-	heap_page_prune_opt(scan->rs_base.rs_rd, buffer);
+	heap_page_prune_opt(scan->rs_base.rs_rd, buffer, false);
 
 	/*
 	 * We must hold share lock on the buffer content while examining tuple
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 7c28dafb72..ff578db37b 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -135,7 +135,7 @@ heapam_index_fetch_tuple(struct IndexFetchTableData *scan,
 		 * Prune page, but only if we weren't already on this page
 		 */
 		if (prev_buf != hscan->xs_cbuf)
-			heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf);
+			heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf, false);
 	}
 
 	/* Obtain share-lock on the buffer so we can examine visibility */
@@ -2150,7 +2150,7 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
 	/*
 	 * Prune and repair fragmentation for the whole page, if possible.
 	 */
-	heap_page_prune_opt(scan->rs_rd, buffer);
+	heap_page_prune_opt(scan->rs_rd, buffer, false);
 
 	/*
 	 * We must hold share lock on the buffer content while examining tuple
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index c5f1abd95a..fa8f5c1dfb 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -81,10 +81,11 @@ static void page_verify_redirects(Page page);
  * Note: this is called quite often.  It's important that it fall out quickly
  * if there's not any use in pruning.
  *
- * Caller must have pin on the buffer, and must *not* have a lock on it.
+ * Caller must have pin on the buffer, and must either have an exclusive lock
+ * (and pass already_locked = true) or not have a lock on it.
  */
 void
-heap_page_prune_opt(Relation relation, Buffer buffer)
+heap_page_prune_opt(Relation relation, Buffer buffer, bool already_locked)
 {
 	Page		page = BufferGetPage(buffer);
 	TransactionId prune_xid;
@@ -135,8 +136,9 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
 
 	if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
 	{
-		/* OK, try to get exclusive buffer lock */
-		if (!ConditionalLockBufferForCleanup(buffer))
+		/* OK, try to get exclusive buffer lock if necessary */
+		if ((!already_locked && !ConditionalLockBufferForCleanup(buffer)) ||
+				(already_locked && !IsBufferCleanupOK(buffer)))
 			return;
 
 		/*
@@ -169,8 +171,9 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
 											   presult.ndeleted - presult.nnewlpdead);
 		}
 
-		/* And release buffer lock */
-		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+		/* And release buffer lock if we acquired it */
+		if (!already_locked)
+			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 
 		/*
 		 * We avoid reuse of any free space created on the page by unrelated
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 62fac1d5d2..d15fa04feb 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -317,7 +317,7 @@ extern TransactionId heap_index_delete_tuples(Relation rel,
 
 /* in heap/pruneheap.c */
 struct GlobalVisState;
-extern void heap_page_prune_opt(Relation relation, Buffer buffer);
+extern void	heap_page_prune_opt(Relation relation, Buffer buffer, bool already_locked);
 extern void heap_page_prune(Relation relation, Buffer buffer,
 							struct GlobalVisState *vistest,
 							PruneResult *presult,
-- 
2.39.3 (Apple Git-145)

