From bc3a756e71bef744f1c9cf652c0eb0e4933c965b Mon Sep 17 00:00:00 2001
From: Shenhao Wang <wangsh.fnst@fujitsu.com>
Date: Sat, 2 Apr 2022 11:03:44 +0800
Subject: [PATCH] Check the duplicated relfilenode when create table or
 truncate table.

---
 src/backend/catalog/catalog.c | 102 ++++++++++++++++++++++++++--------
 1 file changed, 78 insertions(+), 24 deletions(-)

diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index dfd5fb669e..7f51445573 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -55,6 +55,8 @@
 #define GETNEWOID_LOG_THRESHOLD 1000000
 #define GETNEWOID_LOG_MAX_INTERVAL 128000000
 
+static bool isRelNodeCollision(RelFileNodeBackend rnode, Relation relation);
+
 /*
  * IsSystemRelation
  *		True iff the relation is either a system catalog or a toast table.
@@ -490,8 +492,7 @@ Oid
 GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 {
 	RelFileNodeBackend rnode;
-	char	   *rpath;
-	bool		collides;
+	bool		usedAsOID;
 	BackendId	backend;
 
 	/*
@@ -515,6 +516,16 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 			return InvalidOid;	/* placate compiler */
 	}
 
+	if (pg_class != NULL)
+	{
+		usedAsOID = true;
+	}
+	else
+	{
+		pg_class = table_open(RelationRelationId, AccessShareLock);
+		usedAsOID = false;
+	}
+
 	/* This logic should match RelationInitPhysicalAddr */
 	rnode.node.spcNode = reltablespace ? reltablespace : MyDatabaseTableSpace;
 	rnode.node.dbNode = (rnode.node.spcNode == GLOBALTABLESPACE_OID) ? InvalidOid : MyDatabaseId;
@@ -531,34 +542,16 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 		CHECK_FOR_INTERRUPTS();
 
 		/* Generate the OID */
-		if (pg_class)
+		if (usedAsOID)
 			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 (isRelNodeCollision(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 (!usedAsOID)
+		table_close(pg_class, AccessShareLock);
 
 	return rnode.node.relNode;
 }
@@ -666,3 +659,64 @@ pg_stop_making_pinned_objects(PG_FUNCTION_ARGS)
 
 	PG_RETURN_VOID();
 }
+
+/*
+ * isRelNodeCollision
+ *		Checking the rnode collides with existing one.
+ */
+static bool
+isRelNodeCollision(RelFileNodeBackend rnode, Relation relation)
+{
+	char	   *rpath;
+	bool		collides;
+	SysScanDesc scandesc;
+	static ScanKeyData skey[2];
+
+	/* Check for existing file of same name */
+	rpath = relpath(rnode, MAIN_FORKNUM);
+
+	if (access(rpath, F_OK) == 0)
+	{
+		/* definite collision */
+		collides = true;
+	}
+	else
+	{
+		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));
+
+		scandesc = systable_beginscan(relation,
+									  ClassTblspcRelfilenodeIndexId,
+									  true, NULL, 2, skey);
+
+		if (HeapTupleIsValid(systable_getnext(scandesc)))
+		{
+			/* duplicate tablespace and relfilenode in pg_class */
+			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;
+		}
+
+		systable_endscan(scandesc);
+	}
+
+	pfree(rpath);
+	return collides;
+}
-- 
2.32.0

