From a7c872e45e04f0079a150b844b7827305de77d6c Mon Sep 17 00:00:00 2001
From: Shenhao Wang <wangsh.fnst@fujitsu.com>
Date: Mon, 11 Apr 2022 18:12:34 +0800
Subject: [PATCH v2] Check the duplicated relfilenode when create table or
 truncate table.

---
 src/backend/catalog/catalog.c | 95 ++++++++++++++++++++++++++---------
 1 file changed, 71 insertions(+), 24 deletions(-)

diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 520f77971b..4bf3a181e6 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -56,6 +56,8 @@
 #define GETNEWOID_LOG_THRESHOLD 1000000
 #define GETNEWOID_LOG_MAX_INTERVAL 128000000
 
+static bool IsCollidedRelNode(RelFileNodeBackend rnode, Relation relation);
+
 /*
  * IsSystemRelation
  *		True iff the relation is either a system catalog or a toast table.
@@ -496,9 +498,8 @@ Oid
 GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 {
 	RelFileNodeBackend rnode;
-	char	   *rpath;
-	bool		collides;
 	BackendId	backend;
+	bool		isCreateTable = (pg_class != NULL);
 
 	/*
 	 * If we ever get here during pg_upgrade, there's something wrong; all
@@ -532,39 +533,28 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 	 */
 	rnode.backend = backend;
 
+	/*
+	 * We should check the new relfilenode is duplicated in pg_class, so we
+	 * should open pg_class.
+	 */
+	if (!isCreateTable)
+		pg_class = table_open(RelationRelationId, AccessShareLock);
+
 	do
 	{
 		CHECK_FOR_INTERRUPTS();
 
 		/* Generate the OID */
-		if (pg_class)
+		if (isCreateTable)
 			rnode.node.relNode = GetNewOidWithIndex(pg_class, ClassOidIndexId,
 													Anum_pg_class_oid);
 		else
 			rnode.node.relNode = GetNewObjectId();
 
-		/* Check for existing file of same name */
-		rpath = relpath(rnode, MAIN_FORKNUM);
+	} while (IsCollidedRelNode(rnode, pg_class));
 
-		if (access(rpath, F_OK) == 0)
-		{
-			/* definite collision */
-			collides = true;
-		}
-		else
-		{
-			/*
-			 * Here we have a little bit of a dilemma: if errno is something
-			 * other than ENOENT, should we declare a collision and loop? In
-			 * practice it seems best to go ahead regardless of the errno.  If
-			 * there is a colliding file we will get an smgr failure when we
-			 * attempt to create the new relation file.
-			 */
-			collides = false;
-		}
-
-		pfree(rpath);
-	} while (collides);
+	if (!isCreateTable)
+		table_close(pg_class, AccessShareLock);
 
 	return rnode.node.relNode;
 }
@@ -672,3 +662,60 @@ pg_stop_making_pinned_objects(PG_FUNCTION_ARGS)
 
 	PG_RETURN_VOID();
 }
+
+/*
+ * IsCollidedRelNode
+ *		Checking the rnode collides with existing one.
+ */
+static bool
+IsCollidedRelNode(RelFileNodeBackend rnode, Relation pg_class)
+{
+	char	   *rpath;
+	bool		collides;
+
+	Assert(pg_class);
+
+	/* Check for existing file of same name */
+	rpath = relpath(rnode, MAIN_FORKNUM);
+
+	if (access(rpath, F_OK) == 0)
+	{
+		/* definite collision */
+		collides = true;
+	}
+	else
+	{
+		SysScanDesc scandesc;
+		ScanKeyData skey[2];
+
+		ScanKeyInit(&skey[0],
+					Anum_pg_class_reltablespace,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(
+						rnode.node.spcNode == MyDatabaseTableSpace ?
+						InvalidOid : MyDatabaseTableSpace));
+		ScanKeyInit(&skey[1],
+					Anum_pg_class_relfilenode,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(rnode.node.relNode));
+
+		/* See notes in GetNewOidWithIndex about using SnapshotAny */
+		scandesc = systable_beginscan(pg_class,
+									  ClassTblspcRelfilenodeIndexId,
+									  true, SnapshotAny, 2, skey);
+
+		/*
+		 * Here we have a little bit of a dilemma: if errno is something
+		 * other than ENOENT, should we declare a collision and loop? In
+		 * practice it seems best to go ahead regardless of the errno.  If
+		 * there is a colliding file we will get an smgr failure when we
+		 * attempt to create the new relation file.
+		 */
+		collides = HeapTupleIsValid(systable_getnext(scandesc));
+
+		systable_endscan(scandesc);
+	}
+
+	pfree(rpath);
+	return collides;
+}
-- 
2.32.0

