From 8683941485516e594174f8cb04d437962e4698f8 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 30 Apr 2023 16:05:46 +1200
Subject: [PATCH 10/11] Teach pg_upgrade to concatenate segmented files.

When using copy mode, segmented relation forks are automatically
concatenated into modern large format.

When using hard link or clone mode, segment files continue to exist in
the destination cluster.

We lose the ability to use the Windows CopyFile() optimization, because
it doesn't support concatenation.  XXX Could be restored as a way of
copying segment 0.

XXX Allow user to opt out of concatenation for copy mode too?
---
 src/bin/pg_upgrade/file.c          | 40 ++++++++++++++++++++----------
 src/bin/pg_upgrade/relfilenumber.c |  4 +++
 2 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 836b2bbbd0..b4e991f95d 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -82,10 +82,11 @@ void
 copyFile(const char *src, const char *dst,
 		 const char *schemaName, const char *relName)
 {
-#ifndef WIN32
 	int			src_fd;
 	int			dest_fd;
 	char	   *buffer;
+	pgoff_t		total_bytes = 0;
+	int			segno = 0;
 
 	if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
 		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %s",
@@ -155,25 +156,38 @@ copyFile(const char *src, const char *dst,
 			}
 		}
 
+		total_bytes += nbytes;
+
 		if (nbytes == 0)
-			break;
+		{
+			char next_path[MAXPGPATH];
+			int next_fd;
+
+			/* If not at a segment boundary size, this must be the end. */
+			if (total_bytes % (RELSEG_SIZE * BLCKSZ) != 0)
+				break;
+
+			/* Is there another segment? */
+			snprintf(next_path, sizeof(next_path), "%s.%d", src, ++segno);
+			next_fd = open(next_path, O_RDONLY | PG_BINARY, 0);
+			if (next_fd < 0)
+			{
+				if (errno == ENOENT)
+					break;
+				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s",
+						 schemaName, relName, next_path, strerror(errno));
+			}
+
+			/* Yes.  Start copying from that one. */
+			close(src_fd);
+			src_fd = next_fd;
+		}
 	}
 
 	if (buffer)
 		pg_free(buffer);
 	close(src_fd);
 	close(dest_fd);
-
-#else							/* WIN32 */
-
-	if (CopyFile(src, dst, true) == 0)
-	{
-		_dosmaperr(GetLastError());
-		pg_fatal("error while copying relation \"%s.%s\" (\"%s\" to \"%s\"): %s",
-				 schemaName, relName, src, dst, strerror(errno));
-	}
-
-#endif							/* WIN32 */
 }
 
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index 34bc9c1504..ea2abfb00f 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -185,6 +185,10 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 	 */
 	for (segno = 0;; segno++)
 	{
+		/* Copy mode knows how to find higher numbered segments itself. */
+		if (user_opts.transfer_mode == TRANSFER_MODE_COPY && segno > 0)
+			break;
+
 		if (segno == 0)
 			extent_suffix[0] = '\0';
 		else
-- 
2.40.1

