From c0c4fae8722ed0a8d5ceae7e2ef925477ee4db02 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Wed, 1 Sep 2021 14:16:35 +0530
Subject: [PATCH v3 2/6] Extend relmap interfaces

Support new interfaces in relmapper, 1) Support copying the
relmap file from one database path to the other database path.
2) Like RelationMapOidToFilenode, provide another interface
which do the same but instead of getting it for the database
we are connected to it will get it for the input database
path.

These interfaces are required for next patch for supporting the
wal logged created database.
---
 src/backend/utils/cache/relmapper.c | 131 ++++++++++++++++++++++++----
 src/include/utils/relmapper.h       |   6 +-
 2 files changed, 119 insertions(+), 18 deletions(-)

diff --git a/src/backend/utils/cache/relmapper.c b/src/backend/utils/cache/relmapper.c
index ae6291018a..182054eec7 100644
--- a/src/backend/utils/cache/relmapper.c
+++ b/src/backend/utils/cache/relmapper.c
@@ -141,7 +141,7 @@ static void read_relmap_file(char *mapfilename, RelMapFile *map,
 static void write_relmap_file_internal(char *mapfilename, RelMapFile *newmap,
 									   bool write_wal, bool send_sinval,
 									   bool preserve_files, Oid dbid, Oid tsid,
-									   const char *dbpath);
+									   const char *dbpath, bool create);
 static void load_relmap_file(bool shared, bool lock_held);
 static void write_relmap_file(bool shared, RelMapFile *newmap,
 							  bool write_wal, bool send_sinval, bool preserve_files,
@@ -255,6 +255,40 @@ RelationMapFilenodeToOid(Oid filenode, bool shared)
 	return InvalidOid;
 }
 
+/*
+ * RelationMapOidToFilenodeForDatabase
+ *
+ * Find relfilenode for the given relation id in the dbpath.  Returns
+ * InvalidOid if the relationId is not found in the relmap.
+ *
+ * This function is only called during CREATE DATABASE command, so we can pass
+ * lock_held as true while reading the relmap file since we are already holding
+ * the exclusive lock on the database.
+ */
+Oid
+RelationMapOidToFilenodeForDatabase(char *dbpath, Oid relationId)
+{
+	RelMapFile	map;
+	int			i;
+	char		mapfilename[MAXPGPATH];
+
+	/* Relmap file path for the given dbpath. */
+	snprintf(mapfilename, sizeof(mapfilename), "%s/%s",
+			 dbpath, RELMAPPER_FILENAME);
+
+	/* Read the relmap file from the source database. */
+	read_relmap_file(mapfilename, &map, true);
+
+	/* Iterate over the relmap entries to find the input relation oid. */
+	for (i = 0; i < map.num_mappings; i++)
+	{
+		if (relationId == map.mappings[i].mapoid)
+			return map.mappings[i].mapfilenode;
+	}
+
+	return InvalidOid;
+}
+
 /*
  * RelationMapUpdateMap
  *
@@ -693,10 +727,47 @@ RestoreRelationMap(char *startAddress)
 }
 
 /*
- * load_relmap_file -- load data from the shared or local map file
+ * CopyRelationMap
  *
- * Because the map file is essential for access to core system catalogs,
- * failure to read it is a fatal error.
+ * Copy relmapfile from source db path to the destination db path and WAL log
+ * the operation.  This function is only called during the create database so
+ * the caller must hold the exclusive lock on the source database.  Destination
+ * database is not yet created so we don't have any issue.
+ */
+void
+CopyRelationMap(Oid dbid, Oid tsid, char *srcdbpath, char *dstdbpath)
+{
+	RelMapFile map;
+	char mapfilename[MAXPGPATH];
+
+	/* Relmap file path of the source database. */
+	snprintf(mapfilename, sizeof(mapfilename), "%s/%s",
+			 srcdbpath, RELMAPPER_FILENAME);
+
+	/*
+	 * Read the relmap file from the source database.  We are not connected to
+	 * the database so we can not take the relmap lock, but we are already
+	 * holding exclusive lock on the database so pass lock_held as true.
+	 */
+	read_relmap_file(mapfilename, &map, true);
+
+	/* Relmap file path of the destination database. */
+	snprintf(mapfilename, sizeof(mapfilename), "%s/%s",
+			 dstdbpath, RELMAPPER_FILENAME);
+
+	/*
+	 * Write map contents into the destination database's relmap file.
+	 * write_relmap_file_internal, expects that the CRC should have been
+	 * computed and stored in the input map.  But, since we have read this map
+	 * from the source database and directly writing to the destination file
+	 * without updating it so we don't need to recompute it.
+	 */
+	write_relmap_file_internal(mapfilename, &map, true, false, true, dbid,
+							   tsid, dstdbpath, true);
+}
+
+/*
+ * read_relmap_file - read the relmap file data.
  *
  * lock_held, pass true if caller already have the relation mapping or higher
  * level lock.
@@ -802,15 +873,18 @@ load_relmap_file(bool shared, bool lock_held)
 }
 
 /*
- * Helper function for write_relmap_file, Read comments atop write_relmap_file
- * for more details.  The CRC should be computed by the caller and stored in
- * the newmap.
+ * Helper function for write_relmap_file and CopyRelationMap, Read comments
+ * atop write_relmap_file for more details.  The CRC should be computed by the
+ * caller and stored in the newmap.
+ *
+ * Pass the create = true, if we are copying the relmap file during CREATE
+ * DATABASE command.
  */
 static void
 write_relmap_file_internal(char *mapfilename, RelMapFile *newmap,
 						   bool write_wal, bool send_sinval,
 						   bool preserve_files, Oid dbid, Oid tsid,
-						   const char *dbpath)
+						   const char *dbpath, bool create)
 {
 	int			fd;
 
@@ -836,6 +910,7 @@ write_relmap_file_internal(char *mapfilename, RelMapFile *newmap,
 		xlrec.dbid = dbid;
 		xlrec.tsid = tsid;
 		xlrec.nbytes = sizeof(RelMapFile);
+		xlrec.create = create;
 
 		XLogBeginInsert();
 		XLogRegisterData((char *) (&xlrec), MinSizeOfRelmapUpdate);
@@ -977,7 +1052,7 @@ write_relmap_file(bool shared, RelMapFile *newmap,
 	/* Write the map to the relmap file. */
 	write_relmap_file_internal(mapfilename, newmap, write_wal,
 							   send_sinval, preserve_files, dbid, tsid,
-							   dbpath);
+							   dbpath, false);
 
 	/*
 	 * Success, update permanent copy.  During bootstrap, we might be working
@@ -1069,15 +1144,37 @@ relmap_redo(XLogReaderState *record)
 		 * Write out the new map and send sinval, but of course don't write a
 		 * new WAL entry.  There's no surrounding transaction to tell to
 		 * preserve files, either.
-		 *
-		 * There shouldn't be anyone else updating relmaps during WAL replay,
-		 * but grab the lock to interlock against load_relmap_file().
 		 */
-		LWLockAcquire(RelationMappingLock, LW_EXCLUSIVE);
-		write_relmap_file((xlrec->dbid == InvalidOid), &newmap,
-						  false, true, false,
-						  xlrec->dbid, xlrec->tsid, dbpath);
-		LWLockRelease(RelationMappingLock);
+		if (!xlrec->create)
+		{
+			/*
+			 * There shouldn't be anyone else updating relmaps during WAL
+			 * replay, but grab the lock to interlock against
+			 * load_relmap_file().
+			 */
+			LWLockAcquire(RelationMappingLock, LW_EXCLUSIVE);
+			write_relmap_file((xlrec->dbid == InvalidOid), &newmap,
+							false, true, false,
+							xlrec->dbid, xlrec->tsid, dbpath);
+			LWLockRelease(RelationMappingLock);
+		}
+		else
+		{
+			char		mapfilename[MAXPGPATH];
+
+			/* Construct the mapfilename. */
+			snprintf(mapfilename, sizeof(mapfilename), "%s/%s",
+					 dbpath, RELMAPPER_FILENAME);
+
+			/*
+			 * We don't need to take relmap lock because this wal is logged
+			 * while creating a new database, so there could be no one else
+			 * reading/writing the relmap file.
+			 */
+			write_relmap_file_internal(mapfilename, &newmap, false, false,
+									   false, xlrec->dbid, xlrec->tsid, dbpath,
+									   true);
+		}
 
 		pfree(dbpath);
 	}
diff --git a/src/include/utils/relmapper.h b/src/include/utils/relmapper.h
index c0d14daad9..4165f0990b 100644
--- a/src/include/utils/relmapper.h
+++ b/src/include/utils/relmapper.h
@@ -29,6 +29,7 @@ typedef struct xl_relmap_update
 	Oid			dbid;			/* database ID, or 0 for shared map */
 	Oid			tsid;			/* database's tablespace, or pg_global */
 	int32		nbytes;			/* size of relmap data */
+	bool		create;			/* true if creating new relmap */
 	char		data[FLEXIBLE_ARRAY_MEMBER];
 } xl_relmap_update;
 
@@ -39,6 +40,8 @@ extern Oid	RelationMapOidToFilenode(Oid relationId, bool shared);
 
 extern Oid	RelationMapFilenodeToOid(Oid relationId, bool shared);
 
+extern Oid RelationMapOidToFilenodeForDatabase(char *dbpath, Oid relationId);
+
 extern void RelationMapUpdateMap(Oid relationId, Oid fileNode, bool shared,
 								 bool immediate);
 
@@ -62,7 +65,8 @@ extern void RelationMapInitializePhase3(void);
 extern Size EstimateRelationMapSpace(void);
 extern void SerializeRelationMap(Size maxSize, char *startAddress);
 extern void RestoreRelationMap(char *startAddress);
-
+extern void CopyRelationMap(Oid dbid, Oid tsid, char *srcdbpath,
+							char *dstdbpath);
 extern void relmap_redo(XLogReaderState *record);
 extern void relmap_desc(StringInfo buf, XLogReaderState *record);
 extern const char *relmap_identify(uint8 info);
-- 
2.23.0

