From 7bfbc9de2f0142584089c6778a09ccb7fe1bc4e9 Mon Sep 17 00:00:00 2001
From: Jingtang Zhang <mrdrivingduck@gmail.com>
Date: Tue, 19 Aug 2025 11:13:54 +0800
Subject: [PATCH] Fix SMgrRelation object memory leak in DropRelationFiles

SMgrRelation object would stay alive until end of a transaction, or by
explicit smgrdestroy* call outside of a transaction by background
processes or WAL redo, after the underlying storage is gone. Currently,
the object is closed but not destroyed during WAL redo, causing memory
leak by those already destroyed relations. Fixed by explicit destroy the
objects at the end of DropRelationFiles.
---
 src/backend/storage/smgr/md.c   |  2 +-
 src/backend/storage/smgr/smgr.c | 10 ++++++++--
 src/include/storage/smgr.h      |  1 +
 3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 2ccb0faceb5..f9d65cb5b65 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -1607,7 +1607,7 @@ DropRelationFiles(RelFileLocator *delrels, int ndelrels, bool isRedo)
 	smgrdounlinkall(srels, ndelrels, isRedo);
 
 	for (i = 0; i < ndelrels; i++)
-		smgrclose(srels[i]);
+		smgrdestroy(srels[i]);
 	pfree(srels);
 }
 
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index bce37a36d51..f30b8957bd5 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -163,7 +163,6 @@ static dlist_head unpinned_relns;
 
 /* local function prototypes */
 static void smgrshutdown(int code, Datum arg);
-static void smgrdestroy(SMgrRelation reln);
 
 static void smgr_aio_reopen(PgAioHandle *ioh);
 static char *smgr_aio_describe_identity(const PgAioTargetData *sd);
@@ -318,8 +317,15 @@ smgrunpin(SMgrRelation reln)
 
 /*
  * smgrdestroy() -- Delete an SMgrRelation object.
+ *
+ * Remove the object out of hashtable. We must ensure there is no dangling
+ * reference to the object by this time.
+ *
+ * Called at the end of a transaction through AtEOXact_SMgr(). Also called
+ * outside of a transaction through smgrdestroyall() or directly itself,
+ * for background processes and WAL redo.
  */
-static void
+void
 smgrdestroy(SMgrRelation reln)
 {
 	ForkNumber	forknum;
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 3964d9334b3..3b07b3ceea1 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -82,6 +82,7 @@ extern bool smgrexists(SMgrRelation reln, ForkNumber forknum);
 extern void smgrpin(SMgrRelation reln);
 extern void smgrunpin(SMgrRelation reln);
 extern void smgrclose(SMgrRelation reln);
+extern void smgrdestroy(SMgrRelation reln);
 extern void smgrdestroyall(void);
 extern void smgrrelease(SMgrRelation reln);
 extern void smgrreleaseall(void);
-- 
2.39.3

